summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
Diffstat (limited to 'sql')
-rw-r--r--sql/CMakeLists.txt69
-rw-r--r--sql/authors.h68
-rw-r--r--sql/bounded_queue.h195
-rw-r--r--sql/client_settings.h7
-rw-r--r--sql/compat56.cc445
-rw-r--r--sql/compat56.h46
-rw-r--r--sql/contributors.h21
-rw-r--r--sql/create_options.cc255
-rw-r--r--sql/create_options.h29
-rw-r--r--sql/datadict.cc134
-rw-r--r--sql/datadict.h22
-rw-r--r--sql/debug_sync.cc44
-rw-r--r--sql/debug_sync.h3
-rw-r--r--sql/derror.cc25
-rw-r--r--sql/des_key_file.cc2
-rw-r--r--sql/discover.cc172
-rw-r--r--sql/discover.h21
-rw-r--r--sql/event_data_objects.cc30
-rw-r--r--sql/event_db_repository.cc46
-rw-r--r--sql/event_parse_data.cc9
-rw-r--r--sql/event_queue.cc31
-rw-r--r--sql/event_queue.h8
-rw-r--r--sql/event_scheduler.cc70
-rw-r--r--sql/event_scheduler.h8
-rw-r--r--sql/events.cc263
-rw-r--r--sql/events.h21
-rw-r--r--sql/field.cc1613
-rw-r--r--sql/field.h1055
-rw-r--r--sql/field_conv.cc143
-rw-r--r--sql/filesort.cc609
-rw-r--r--sql/filesort.h4
-rw-r--r--sql/filesort_utils.cc143
-rw-r--r--sql/filesort_utils.h129
-rw-r--r--sql/frm_crypt.cc37
-rw-r--r--sql/frm_crypt.h23
-rw-r--r--sql/gcalc_slicescan.cc266
-rw-r--r--sql/gcalc_slicescan.h20
-rw-r--r--sql/gcalc_tools.cc6
-rw-r--r--sql/gen_lex_token.cc359
-rw-r--r--sql/gstream.cc1
-rw-r--r--sql/ha_ndbcluster.cc136
-rw-r--r--sql/ha_ndbcluster_binlog.cc69
-rw-r--r--sql/ha_ndbcluster_cond.cc5
-rw-r--r--sql/ha_ndbcluster_cond.h12
-rw-r--r--sql/ha_partition.cc2662
-rw-r--r--sql/ha_partition.h260
-rw-r--r--sql/handler.cc1852
-rw-r--r--sql/handler.h1575
-rw-r--r--sql/hash_filo.cc1
-rw-r--r--sql/hash_filo.h112
-rw-r--r--sql/hostname.cc725
-rw-r--r--sql/hostname.h166
-rw-r--r--sql/init.cc4
-rw-r--r--sql/innodb_priv.h4
-rw-r--r--sql/item.cc729
-rw-r--r--sql/item.h618
-rw-r--r--sql/item_buff.cc14
-rw-r--r--sql/item_cmpfunc.cc702
-rw-r--r--sql/item_cmpfunc.h287
-rw-r--r--sql/item_create.cc636
-rw-r--r--sql/item_create.h15
-rw-r--r--sql/item_func.cc1057
-rw-r--r--sql/item_func.h125
-rw-r--r--sql/item_geofunc.cc35
-rw-r--r--sql/item_geofunc.h14
-rw-r--r--sql/item_inetfunc.cc831
-rw-r--r--sql/item_inetfunc.h244
-rw-r--r--sql/item_row.cc5
-rw-r--r--sql/item_strfunc.cc1192
-rw-r--r--sql/item_strfunc.h210
-rw-r--r--sql/item_subselect.cc581
-rw-r--r--sql/item_subselect.h97
-rw-r--r--sql/item_sum.cc54
-rw-r--r--sql/item_sum.h4
-rw-r--r--sql/item_timefunc.cc206
-rw-r--r--sql/item_timefunc.h78
-rw-r--r--sql/item_xmlfunc.cc183
-rw-r--r--sql/item_xmlfunc.h57
-rw-r--r--sql/key.cc48
-rw-r--r--sql/key.h2
-rw-r--r--sql/keycaches.cc73
-rw-r--r--sql/keycaches.h13
-rw-r--r--sql/lex.h34
-rw-r--r--sql/lock.cc74
-rw-r--r--sql/log.cc2961
-rw-r--r--sql/log.h198
-rw-r--r--sql/log_event.cc3342
-rw-r--r--sql/log_event.h703
-rw-r--r--sql/log_event_old.cc237
-rw-r--r--sql/log_event_old.h48
-rw-r--r--sql/log_slow.h27
-rw-r--r--sql/mdl.cc773
-rw-r--r--sql/mdl.h242
-rw-r--r--sql/mf_iocache.cc3
-rw-r--r--sql/multi_range_read.cc84
-rw-r--r--sql/multi_range_read.h10
-rw-r--r--sql/my_apc.cc270
-rw-r--r--sql/my_apc.h142
-rw-r--r--sql/my_decimal.cc8
-rw-r--r--sql/my_decimal.h4
-rw-r--r--sql/mysql_install_db.cc31
-rw-r--r--sql/mysqld.cc2893
-rw-r--r--sql/mysqld.h329
-rw-r--r--sql/net_serv.cc170
-rw-r--r--sql/opt_index_cond_pushdown.cc8
-rw-r--r--sql/opt_range.cc1201
-rw-r--r--sql/opt_range.h47
-rw-r--r--sql/opt_range_mrr.cc8
-rw-r--r--sql/opt_subselect.cc164
-rw-r--r--sql/opt_subselect.h22
-rw-r--r--sql/opt_sum.cc23
-rw-r--r--sql/opt_table_elimination.cc22
-rw-r--r--sql/parse_file.cc4
-rw-r--r--sql/partition_element.h8
-rw-r--r--sql/partition_info.cc796
-rw-r--r--sql/partition_info.h112
-rw-r--r--sql/password.c100
-rw-r--r--sql/procedure.cc1
-rw-r--r--sql/protocol.cc60
-rw-r--r--sql/protocol.h5
-rw-r--r--sql/records.cc12
-rw-r--r--sql/records.h3
-rw-r--r--sql/repl_failsafe.cc12
-rw-r--r--sql/replication.h32
-rw-r--r--sql/rpl_constants.h40
-rw-r--r--sql/rpl_filter.cc15
-rw-r--r--sql/rpl_filter.h3
-rw-r--r--sql/rpl_gtid.cc2362
-rw-r--r--sql/rpl_gtid.h298
-rw-r--r--sql/rpl_handler.cc25
-rw-r--r--sql/rpl_handler.h2
-rw-r--r--sql/rpl_injector.cc10
-rw-r--r--sql/rpl_injector.h4
-rw-r--r--sql/rpl_mi.cc867
-rw-r--r--sql/rpl_mi.h118
-rw-r--r--sql/rpl_parallel.cc2626
-rw-r--r--sql/rpl_parallel.h351
-rw-r--r--sql/rpl_record.cc22
-rw-r--r--sql/rpl_record.h4
-rw-r--r--sql/rpl_record_old.cc8
-rw-r--r--sql/rpl_record_old.h2
-rw-r--r--sql/rpl_reporting.cc5
-rw-r--r--sql/rpl_reporting.h5
-rw-r--r--sql/rpl_rli.cc1037
-rw-r--r--sql/rpl_rli.h570
-rw-r--r--sql/rpl_tblmap.cc4
-rw-r--r--sql/rpl_utility.cc116
-rw-r--r--sql/rpl_utility.h21
-rw-r--r--sql/scheduler.cc64
-rw-r--r--sql/set_var.cc175
-rw-r--r--sql/set_var.h32
-rw-r--r--sql/share/errmsg-utf8.txt2827
-rw-r--r--sql/signal_handler.cc2
-rw-r--r--sql/slave.cc2528
-rw-r--r--sql/slave.h32
-rw-r--r--sql/sp.cc157
-rw-r--r--sql/sp.h2
-rw-r--r--sql/sp_cache.cc1
-rw-r--r--sql/sp_head.cc462
-rw-r--r--sql/sp_head.h115
-rw-r--r--sql/sp_pcontext.cc564
-rw-r--r--sql/sp_pcontext.h813
-rw-r--r--sql/sp_rcontext.cc742
-rw-r--r--sql/sp_rcontext.h589
-rw-r--r--sql/spatial.cc2
-rw-r--r--sql/spatial.h4
-rw-r--r--sql/sql_acl.cc6084
-rw-r--r--sql/sql_acl.h124
-rw-r--r--sql/sql_admin.cc305
-rw-r--r--sql/sql_admin.h83
-rw-r--r--sql/sql_alter.cc285
-rw-r--r--sql/sql_alter.h399
-rw-r--r--sql/sql_analyse.cc70
-rw-r--r--sql/sql_analyse.h16
-rw-r--r--sql/sql_array.h205
-rw-r--r--sql/sql_audit.cc3
-rw-r--r--sql/sql_audit.h111
-rw-r--r--sql/sql_base.cc3005
-rw-r--r--sql/sql_base.h181
-rw-r--r--sql/sql_binlog.cc15
-rw-r--r--sql/sql_bitmap.h11
-rw-r--r--sql/sql_bootstrap.cc119
-rw-r--r--sql/sql_bootstrap.h47
-rw-r--r--sql/sql_cache.cc245
-rw-r--r--sql/sql_cache.h8
-rw-r--r--sql/sql_class.cc1811
-rw-r--r--sql/sql_class.h1418
-rw-r--r--sql/sql_client.cc3
-rw-r--r--sql/sql_cmd.h165
-rw-r--r--sql/sql_connect.cc176
-rw-r--r--sql/sql_const.h24
-rw-r--r--sql/sql_crypt.cc1
-rw-r--r--sql/sql_crypt.h2
-rw-r--r--sql/sql_cursor.cc46
-rw-r--r--sql/sql_db.cc287
-rw-r--r--sql/sql_db.h1
-rw-r--r--sql/sql_delete.cc375
-rw-r--r--sql/sql_delete.h7
-rw-r--r--sql/sql_derived.cc27
-rw-r--r--sql/sql_derived.h1
-rw-r--r--sql/sql_digest.cc690
-rw-r--r--sql/sql_digest.h129
-rw-r--r--sql/sql_digest_stream.h51
-rw-r--r--sql/sql_do.cc1
-rw-r--r--sql/sql_error.cc424
-rw-r--r--sql/sql_error.h791
-rw-r--r--sql/sql_explain.cc950
-rw-r--r--sql/sql_explain.h550
-rw-r--r--sql/sql_expression_cache.cc3
-rw-r--r--sql/sql_get_diagnostics.cc342
-rw-r--r--sql/sql_get_diagnostics.h318
-rw-r--r--sql/sql_handler.cc32
-rw-r--r--sql/sql_help.cc3
-rw-r--r--sql/sql_hset.h20
-rw-r--r--sql/sql_insert.cc729
-rw-r--r--sql/sql_insert.h1
-rw-r--r--sql/sql_join_cache.cc1098
-rw-r--r--sql/sql_join_cache.h27
-rw-r--r--sql/sql_lex.cc247
-rw-r--r--sql/sql_lex.h407
-rw-r--r--sql/sql_load.cc152
-rw-r--r--sql/sql_locale.cc73
-rw-r--r--sql/sql_manager.cc4
-rw-r--r--sql/sql_parse.cc2223
-rw-r--r--sql/sql_parse.h45
-rw-r--r--sql/sql_partition.cc980
-rw-r--r--sql/sql_partition.h20
-rw-r--r--sql/sql_partition_admin.cc738
-rw-r--r--sql/sql_partition_admin.h189
-rw-r--r--sql/sql_plist.h30
-rw-r--r--sql/sql_plugin.cc636
-rw-r--r--sql/sql_plugin.h28
-rw-r--r--sql/sql_plugin_services.h35
-rw-r--r--sql/sql_prepare.cc178
-rw-r--r--sql/sql_prepare.h16
-rw-r--r--sql/sql_priv.h56
-rw-r--r--sql/sql_profile.cc7
-rw-r--r--sql/sql_reload.cc147
-rw-r--r--sql/sql_reload.h2
-rw-r--r--sql/sql_rename.cc114
-rw-r--r--sql/sql_rename.h3
-rw-r--r--sql/sql_repl.cc2352
-rw-r--r--sql/sql_repl.h19
-rw-r--r--sql/sql_select.cc2889
-rw-r--r--sql/sql_select.h169
-rw-r--r--sql/sql_servers.cc16
-rw-r--r--sql/sql_show.cc1495
-rw-r--r--sql/sql_show.h48
-rw-r--r--sql/sql_signal.cc129
-rw-r--r--sql/sql_signal.h76
-rw-r--r--sql/sql_sort.h50
-rw-r--r--sql/sql_statistics.cc3840
-rw-r--r--sql/sql_statistics.h428
-rw-r--r--sql/sql_string.cc224
-rw-r--r--sql/sql_string.h114
-rw-r--r--sql/sql_table.cc7040
-rw-r--r--sql/sql_table.h133
-rw-r--r--sql/sql_tablespace.cc5
-rw-r--r--sql/sql_test.cc71
-rw-r--r--sql/sql_time.cc238
-rw-r--r--sql/sql_time.h63
-rw-r--r--sql/sql_trigger.cc138
-rw-r--r--sql/sql_trigger.h6
-rw-r--r--sql/sql_truncate.cc104
-rw-r--r--sql/sql_truncate.h17
-rw-r--r--sql/sql_udf.cc52
-rw-r--r--sql/sql_udf.h4
-rw-r--r--sql/sql_union.cc40
-rw-r--r--sql/sql_update.cc438
-rw-r--r--sql/sql_view.cc181
-rw-r--r--sql/sql_view.h4
-rw-r--r--sql/sql_yacc.yy2674
-rw-r--r--sql/strfunc.cc12
-rw-r--r--sql/strfunc.h2
-rw-r--r--sql/structs.h37
-rw-r--r--sql/sys_vars.cc1664
-rw-r--r--sql/sys_vars.h449
-rw-r--r--sql/table.cc2179
-rw-r--r--sql/table.h476
-rw-r--r--sql/table_cache.cc1220
-rw-r--r--sql/table_cache.h137
-rw-r--r--sql/thr_malloc.cc13
-rw-r--r--sql/thr_malloc.h3
-rw-r--r--sql/threadpool.h1
-rw-r--r--sql/threadpool_common.cc24
-rw-r--r--sql/threadpool_unix.cc15
-rw-r--r--sql/threadpool_win.cc8
-rw-r--r--sql/transaction.cc162
-rw-r--r--sql/tztime.cc34
-rw-r--r--sql/uniques.cc69
-rw-r--r--sql/unireg.cc836
-rw-r--r--sql/unireg.h74
-rw-r--r--sql/wsrep_applier.cc106
-rw-r--r--sql/wsrep_applier.h3
-rw-r--r--sql/wsrep_binlog.cc40
-rw-r--r--sql/wsrep_binlog.h4
-rw-r--r--sql/wsrep_hton.cc138
-rw-r--r--sql/wsrep_mysqld.cc409
-rw-r--r--sql/wsrep_mysqld.h60
-rw-r--r--sql/wsrep_priv.h4
-rw-r--r--sql/wsrep_sst.cc177
-rw-r--r--sql/wsrep_sst.h3
-rw-r--r--sql/wsrep_thd.cc158
-rw-r--r--sql/wsrep_thd.h13
-rw-r--r--sql/wsrep_utils.cc55
-rw-r--r--sql/wsrep_var.cc53
-rw-r--r--sql/wsrep_xid.cc150
-rw-r--r--sql/wsrep_xid.h33
308 files changed, 85022 insertions, 29999 deletions
diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt
index 7fcdf601d81..de46f4b64c1 100644
--- a/sql/CMakeLists.txt
+++ b/sql/CMakeLists.txt
@@ -21,7 +21,7 @@ ENDIF()
INCLUDE_DIRECTORIES(
${CMAKE_SOURCE_DIR}/include
${CMAKE_SOURCE_DIR}/sql
-${CMAKE_SOURCE_DIR}/regex
+${PCRE_INCLUDES}
${ZLIB_INCLUDE_DIR}
${SSL_INCLUDE_DIRS}
${CMAKE_BINARY_DIR}/sql
@@ -33,22 +33,42 @@ ${CMAKE_CURRENT_BINARY_DIR}/sql_yacc.h
${CMAKE_CURRENT_BINARY_DIR}/sql_yacc.cc
${CMAKE_CURRENT_BINARY_DIR}/lex_hash.h
)
+SET(GEN_DIGEST_SOURCES
+ ${CMAKE_CURRENT_BINARY_DIR}/lex_token.h
+)
-SET_SOURCE_FILES_PROPERTIES(${GEN_SOURCES} PROPERTIES GENERATED 1)
+SET_SOURCE_FILES_PROPERTIES(${GEN_SOURCES}
+ ${GEN_DIGEST_SOURCES}
+ PROPERTIES GENERATED 1)
+
+# Gen_lex_token
+# Make sure sql_yacc.h is generated before compiling gen_lex_token
+IF(NOT CMAKE_CROSSCOMPILING)
+ ADD_EXECUTABLE(gen_lex_token gen_lex_token.cc)
+ ADD_DEPENDENCIES(gen_lex_token GenServerSource)
+ENDIF()
+
+ADD_CUSTOM_COMMAND(
+ OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/lex_token.h
+ COMMAND gen_lex_token > lex_token.h
+ DEPENDS gen_lex_token
+)
ADD_DEFINITIONS(-DMYSQL_SERVER -DHAVE_EVENT_SCHEDULER)
IF(SSL_DEFINES)
ADD_DEFINITIONS(${SSL_DEFINES})
ENDIF()
+
IF(WITH_WSREP)
SET(WSREP_SOURCES
+ wsrep_utils.cc
+ wsrep_xid.cc
wsrep_check_opts.cc
wsrep_hton.cc
wsrep_mysqld.cc
wsrep_notify.cc
wsrep_sst.cc
- wsrep_utils.cc
wsrep_var.cc
wsrep_binlog.cc
wsrep_applier.cc
@@ -58,8 +78,9 @@ IF(WITH_WSREP)
ENDIF()
SET (SQL_SOURCE
- ../sql-common/client.c derror.cc des_key_file.cc
+ ../sql-common/client.c compat56.cc derror.cc des_key_file.cc
discover.cc ../libmysql/errmsg.c field.cc field_conv.cc
+ filesort_utils.cc
filesort.cc gstream.cc sha2.cc
signal_handler.cc
handler.cc hash_filo.h sql_plugin_services.h
@@ -78,12 +99,16 @@ SET (SQL_SOURCE
slave.cc sp.cc sp_cache.cc sp_head.cc sp_pcontext.cc
sp_rcontext.cc spatial.cc sql_acl.cc sql_analyse.cc sql_base.cc
sql_cache.cc sql_class.cc sql_client.cc sql_crypt.cc sql_crypt.h
- sql_cursor.cc sql_db.cc sql_delete.cc sql_derived.cc sql_do.cc
- sql_error.cc sql_handler.cc sql_help.cc sql_insert.cc sql_lex.cc
- sql_list.cc sql_load.cc sql_manager.cc sql_parse.cc
+ sql_cursor.cc sql_db.cc sql_delete.cc sql_derived.cc
+ sql_digest.cc sql_do.cc
+ sql_error.cc sql_handler.cc sql_get_diagnostics.cc
+ sql_help.cc sql_insert.cc sql_lex.cc
+ sql_list.cc sql_load.cc sql_manager.cc
+ sql_parse.cc sql_bootstrap.cc sql_bootstrap.h
sql_partition.cc sql_plugin.cc sql_prepare.cc sql_rename.cc
debug_sync.cc debug_sync.h
- sql_repl.cc sql_select.cc sql_show.cc sql_state.c sql_string.cc
+ sql_repl.cc sql_select.cc sql_show.cc sql_state.c
+ sql_statistics.cc sql_string.cc
sql_table.cc sql_test.cc sql_trigger.cc sql_udf.cc sql_union.cc
sql_update.cc sql_view.cc strfunc.cc table.cc thr_malloc.cc
sql_time.cc tztime.cc uniques.cc unireg.cc item_xmlfunc.cc
@@ -96,9 +121,9 @@ SET (SQL_SOURCE
sql_profile.cc event_parse_data.cc sql_alter.cc
sql_signal.cc rpl_handler.cc mdl.cc sql_admin.cc
transaction.cc sys_vars.cc sql_truncate.cc datadict.cc
- sql_reload.cc
-
+ sql_reload.cc sql_cmd.h item_inetfunc.cc
# added in MariaDB:
+ sql_explain.h sql_explain.cc
sql_lifo_buffer.h sql_join_cache.h sql_join_cache.cc
create_options.cc multi_range_read.cc
opt_index_cond_pushdown.cc opt_subselect.cc
@@ -106,9 +131,13 @@ SET (SQL_SOURCE
gcalc_slicescan.cc gcalc_tools.cc
threadpool_common.cc
../sql-common/mysql_async.c
- ${CMAKE_CURRENT_BINARY_DIR}/sql_builtin.cc
+ my_apc.cc my_apc.h
+ rpl_gtid.cc rpl_parallel.cc
${WSREP_SOURCES}
+ table_cache.cc
+ ${CMAKE_CURRENT_BINARY_DIR}/sql_builtin.cc
${GEN_SOURCES}
+ ${GEN_DIGEST_SOURCES}
${MYSYS_LIBWRAP_SOURCE}
)
@@ -129,9 +158,10 @@ RECOMPILE_FOR_EMBEDDED)
ADD_LIBRARY(sql STATIC ${SQL_SOURCE})
ADD_DEPENDENCIES(sql GenServerSource)
+ADD_DEPENDENCIES(sql GenDigestServerSource)
DTRACE_INSTRUMENT(sql)
TARGET_LINK_LIBRARIES(sql ${MYSQLD_STATIC_PLUGIN_LIBS}
- mysys dbug strings vio regex ${LIBJEMALLOC}
+ mysys mysys_ssl dbug strings vio pcre ${LIBJEMALLOC}
${LIBWRAP} ${LIBCRYPT} ${LIBDL} ${CMAKE_THREAD_LIBS_INIT}
${WSREP_LIB}
${SSL_LIBRARIES})
@@ -167,7 +197,7 @@ IF(NOT WITHOUT_DYNAMIC_PLUGINS)
# incremental appears to crash from time to time,if used with /DEF option
SET_TARGET_PROPERTIES(mysqld PROPERTIES LINK_FLAGS "${mysqld_link_flags} /DEF:mysqld.def /INCREMENTAL:NO")
- FOREACH (CORELIB sql mysys dbug strings)
+ FOREACH (CORELIB sql mysys mysys_ssl dbug strings)
GET_TARGET_PROPERTY(LOC ${CORELIB} LOCATION)
FILE(TO_NATIVE_PATH ${LOC} LOC)
SET (LIB_LOCATIONS ${LIB_LOCATIONS} ${LOC})
@@ -197,7 +227,7 @@ ENDIF()
# On Solaris, some extra effort is required in order to get dtrace probes
# from static libraries
DTRACE_INSTRUMENT_STATIC_LIBS(mysqld
- "sql;mysys;${MYSQLD_STATIC_PLUGIN_LIBS}")
+ "sql;mysys;mysys_ssl;${MYSQLD_STATIC_PLUGIN_LIBS}")
SET(WITH_MYSQLD_LDFLAGS "" CACHE STRING "Additional linker flags for mysqld")
@@ -240,7 +270,9 @@ RUN_BISON(
)
# Gen_lex_hash
-ADD_EXECUTABLE(gen_lex_hash gen_lex_hash.cc)
+IF(NOT CMAKE_CROSSCOMPILING)
+ ADD_EXECUTABLE(gen_lex_hash gen_lex_hash.cc)
+ENDIF()
ADD_CUSTOM_COMMAND(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/lex_hash.h
@@ -250,13 +282,18 @@ ADD_CUSTOM_COMMAND(
MYSQL_ADD_EXECUTABLE(mysql_tzinfo_to_sql tztime.cc COMPONENT Server)
SET_TARGET_PROPERTIES(mysql_tzinfo_to_sql PROPERTIES COMPILE_FLAGS "-DTZINFO2SQL")
-TARGET_LINK_LIBRARIES(mysql_tzinfo_to_sql mysys)
+TARGET_LINK_LIBRARIES(mysql_tzinfo_to_sql mysys mysys_ssl)
ADD_CUSTOM_TARGET(
GenServerSource
DEPENDS ${GEN_SOURCES}
)
+ADD_CUSTOM_TARGET(
+ GenDigestServerSource
+ DEPENDS ${GEN_DIGEST_SOURCES}
+)
+
#Need this only for embedded
SET_TARGET_PROPERTIES(GenServerSource PROPERTIES EXCLUDE_FROM_ALL TRUE)
diff --git a/sql/authors.h b/sql/authors.h
index 84c29f8c127..3a8f5497248 100644
--- a/sql/authors.h
+++ b/sql/authors.h
@@ -31,41 +31,73 @@ struct show_table_authors_st {
Don't be offended if your name is not in here, just add it!
- IMPORTANT: Names should be added in alphabetical order (by last name).
+ Active people in the MariaDB are listed first, active people in MySQL
+ then, not active last.
Names should be encoded using UTF-8.
+
+ See also https://mariadb.com/kb/en/log-of-mariadb-contributions/
*/
struct show_table_authors_st show_table_authors[]= {
+ /* Active people on MariaDB */
{ "Michael (Monty) Widenius", "Tusby, Finland",
"Lead developer and main author" },
- { "David Axmark", "London, England",
- "MySQL founder; Small stuff long time ago, Monty ripped it out!" },
{ "Sergei Golubchik", "Kerpen, Germany",
- "Full-text search, precision math" },
+ "Architect, Full-text search, precision math, plugin framework, merges etc" },
{ "Igor Babaev", "Bellevue, USA", "Optimizer, keycache, core work"},
{ "Sergey Petrunia", "St. Petersburg, Russia", "Optimizer"},
{ "Oleksandr Byelkin", "Lugansk, Ukraine",
"Query Cache (4.0), Subqueries (4.1), Views (5.0)" },
- { "Brian (Krow) Aker", "Seattle, WA, USA",
- "Architecture, archive, federated, bunch of little stuff :)" },
- { "Marc Alff", "Denver, CO, USA", "Signal, Resignal, Performance schema" },
- { "Venu Anuganti", "", "Client/server protocol (4.1)" },
+ { "Timour Katchaounov", "Sofia , Bulgaria", "Optimizer"},
{ "Kristian Nielsen", "Copenhagen, Denmark",
- "General build stuff," },
+ "Replication, Async client prototocol, General buildbot stuff" },
{ "Alexander (Bar) Barkov", "Izhevsk, Russia",
- "Unicode and character sets (4.1)" },
- { "Guilhem Bichot", "Bordeaux, France", "Replication (since 4.0)" },
+ "Unicode and character sets" },
+ { "Alexey Botchkov (Holyfoot)", "Izhevsk, Russia",
+ "GIS extensions, embedded server, precision math"},
+ { "Daniel Bartholomew", "Raleigh, USA", "MariaDB documentation, Buildbot, releases"},
+ { "Colin Charles", "Selangor, Malesia", "MariaDB documentation, talks at a LOT of conferences"},
+ { "Sergey Vojtovich", "Izhevsk, Russia",
+ "initial implementation of plugin architecture, maintained native storage engines (MyISAM, MEMORY, ARCHIVE, etc), rewrite of table cache"},
+ { "Vladislav Vaintroub", "Mannheim, Germany", "MariaDB Java connector, new thread pool, Windows optimizations"},
+ { "Elena Stepanova", "Sankt Petersburg, Russia", "QA, test cases"},
+ { "Georg Richter", "Heidelberg, Germany", "New LGPL C connector, PHP connector"},
+ { "Jan Lindström", "Ylämylly, Finland", "Working on InnoDB"},
+ { "Lixun Peng", "Hangzhou, China", "Multi Source replication" },
+ { "Olivier Bertrand", "Paris, France", "CONNECT storage engine"},
+ { "Kentoku Shiba", "Tokyo, Japan", "Spider storage engine, metadata_lock_info Information schema"},
+ { "Percona", "CA, USA", "XtraDB, microslow patches, extensions to slow log"},
+ { "Vicentiu Ciorbaru", "Bucharest, Romania", "Roles"},
+ { "Sudheera Palihakkara", "", "PCRE Regular Expressions" },
+ { "Pavel Ivanov", "USA", "Some patches and bug fixes"},
{ "Konstantin Osipov", "Moscow, Russia",
- "Prepared statements (4.1), Cursors (5.0)" },
+ "Prepared statements (4.1), Cursors (5.0), GET_LOCK (10.0)" },
+ { "Ian Gilfillan", "South Africa", "MariaDB documentation"},
+ { "Federico Razolli", "Italy", "MariaDB documentation Italian translation"},
+
+ /* People working on MySQL code base (not NDB) */
+ { "Guilhem Bichot", "Bordeaux, France", "Replication (since 4.0)" },
+ { "Andrei Elkin", "Espoo, Finland", "Replication" },
{ "Dmitri Lenev", "Moscow, Russia",
"Time zones support (4.1), Triggers (5.0)" },
+ { "Marc Alff", "Denver, CO, USA", "Signal, Resignal, Performance schema" },
+ { "Mikael Ronström", "Stockholm, Sweden",
+ "NDB Cluster, Partitioning, online alter table" },
+ { "Ingo Strüwing", "Berlin, Germany",
+ "Bug fixing in MyISAM, Merge tables etc" },
+ {"Marko Mäkelä", "Helsinki, Finland", "InnoDB core developer"},
+
+ /* People not active anymore */
+ { "David Axmark", "London, England",
+ "MySQL founder; Small stuff long time ago, Monty ripped it out!" },
+ { "Brian (Krow) Aker", "Seattle, WA, USA",
+ "Architecture, archive, blackhole, federated, bunch of little stuff :)" },
+ { "Venu Anuganti", "", "Client/server protocol (4.1)" },
{ "Omer BarNir", "Sunnyvale, CA, USA",
"Testing (sometimes) and general QA stuff" },
{ "John Birrell", "", "Emulation of pthread_mutex() for OS/2" },
{ "Andreas F. Bobak", "", "AGGREGATE extension to user-defined functions" },
- { "Alexey Botchkov (Holyfoot)", "Izhevsk, Russia",
- "GIS extensions (4.1), embedded server (4.1), precision math (5.0)"},
{ "Reggie Burnett", "Nashville, TN, USA", "Windows development, Connectors" },
{ "Kent Boortz", "Orebro, Sweden", "Test platform, and general build stuff" },
{ "Tim Bunce", "", "mysqlhotcopy" },
@@ -81,7 +113,6 @@ struct show_table_authors_st show_table_authors[]= {
{ "Antony T. Curtis", "Norwalk, CA, USA",
"Parser, port to OS/2, storage engines and some random stuff" },
{ "Yuri Dario", "", "OS/2 port" },
- { "Andrei Elkin", "Espoo, Finland", "Replication" },
{ "Patrick Galbraith", "Sharon, NH", "Federated Engine, mysqlslap" },
{ "Lenz Grimmer", "Hamburg, Germany",
"Production (build and release) engineering" },
@@ -96,7 +127,6 @@ struct show_table_authors_st show_table_authors[]= {
{ "Mats Kindahl", "Storvreta, Sweden", "Replication" },
{ "Serge Kozlov", "Velikie Luki, Russia", "Testing - Cluster" },
{ "Hakan Küçükyılmaz", "Walldorf, Germany", "Testing - Server" },
- { "Greg (Groggy) Lehey", "Uchunga, SA, Australia", "Backup" },
{ "Matthias Leich", "Berlin, Germany", "Testing - Server" },
{ "Arjen Lentz", "Brisbane, Australia",
"Documentation (2001-2004), Dutch error messages, LOG2()" },
@@ -125,9 +155,7 @@ struct show_table_authors_st show_table_authors[]= {
"Extended MERGE storage engine to handle INSERT" },
{ "Igor Romanenko", "",
"mysqldump" },
- { "Mikael Ronström", "Stockholm, Sweden",
- "NDB Cluster, Partitioning (5.1), Optimizations" },
- { "Tõnu Samuel", "",
+ { "Tõnu Samuel", "Estonia",
"VIO interface, other miscellaneous features" },
{ "Carsten Segieth (Pino)", "Fredersdorf, Germany", "Testing - Server"},
{ "Martin Sköld", "Stockholm, Sweden",
@@ -138,7 +166,6 @@ struct show_table_authors_st show_table_authors[]= {
"Windows development, Windows NT service"},
{ "Punita Srivastava", "Austin, TX, USA", "Testing - Merlin"},
{ "Alexey Stroganov (Ranger)", "Lugansk, Ukraine", "Testing - Benchmarks"},
- { "Ingo Strüwing", "Berlin, Germany", "Bug fixing" },
{ "Magnus Svensson", "Öregrund, Sweden",
"NDB Cluster: Integration into MySQL, test framework" },
{ "Zeev Suraski", "", "FROM_UNIXTIME(), ENCRYPT()" },
@@ -157,7 +184,6 @@ struct show_table_authors_st show_table_authors[]= {
{ "Peter Zaitsev", "Tacoma, WA, USA",
"SHA1(), AES_ENCRYPT(), AES_DECRYPT(), bug fixing" },
{"Mark Mark Callaghan", "Texas, USA", "Statistics patches"},
- {"Percona", "CA, USA", "Microslow patches"},
{NULL, NULL, NULL}
};
diff --git a/sql/bounded_queue.h b/sql/bounded_queue.h
new file mode 100644
index 00000000000..2d4e6cff96d
--- /dev/null
+++ b/sql/bounded_queue.h
@@ -0,0 +1,195 @@
+/* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+
+ 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 */
+
+#ifndef BOUNDED_QUEUE_INCLUDED
+#define BOUNDED_QUEUE_INCLUDED
+
+#include <string.h>
+#include "my_global.h"
+#include "my_base.h"
+#include "my_sys.h"
+#include "queues.h"
+
+class Sort_param;
+
+/**
+ A priority queue with a fixed, limited size.
+
+ This is a wrapper on top of QUEUE and the queue_xxx() functions.
+ It keeps the top-N elements which are inserted.
+
+ Elements of type Element_type are pushed into the queue.
+ For each element, we call a user-supplied keymaker_function,
+ to generate a key of type Key_type for the element.
+ Instances of Key_type are compared with the user-supplied compare_function.
+
+ The underlying QUEUE implementation needs one extra element for replacing
+ the lowest/highest element when pushing into a full queue.
+ */
+template<typename Element_type, typename Key_type>
+class Bounded_queue
+{
+public:
+ Bounded_queue()
+ {
+ memset(&m_queue, 0, sizeof(m_queue));
+ }
+
+ ~Bounded_queue()
+ {
+ delete_queue(&m_queue);
+ }
+
+ /**
+ Function for making sort-key from input data.
+ @param param Sort parameters.
+ @param to Where to put the key.
+ @param from The input data.
+ */
+ typedef void (*keymaker_function)(Sort_param *param,
+ Key_type *to,
+ Element_type *from);
+
+ /**
+ Function for comparing two keys.
+ @param n Pointer to number of bytes to compare.
+ @param a First key.
+ @param b Second key.
+ @retval -1, 0, or 1 depending on whether the left argument is
+ less than, equal to, or greater than the right argument.
+ */
+ typedef int (*compare_function)(size_t *n, Key_type **a, Key_type **b);
+
+ /**
+ Initialize the queue.
+
+ @param max_elements The size of the queue.
+ @param max_at_top Set to true if you want biggest element on top.
+ false: We keep the n largest elements.
+ pop() will return the smallest key in the result set.
+ true: We keep the n smallest elements.
+ pop() will return the largest key in the result set.
+ @param compare Compare function for elements, takes 3 arguments.
+ If NULL, we use get_ptr_compare(compare_length).
+ @param compare_length Length of the data (i.e. the keys) used for sorting.
+ @param keymaker Function which generates keys for elements.
+ @param sort_param Sort parameters.
+ @param sort_keys Array of pointers to keys to sort.
+
+ @retval 0 OK, 1 Could not allocate memory.
+
+ We do *not* take ownership of any of the input pointer arguments.
+ */
+ int init(ha_rows max_elements, bool max_at_top,
+ compare_function compare, size_t compare_length,
+ keymaker_function keymaker, Sort_param *sort_param,
+ Key_type **sort_keys);
+
+ /**
+ Pushes an element on the queue.
+ If the queue is already full, we discard one element.
+ Calls keymaker_function to generate a key for the element.
+
+ @param element The element to be pushed.
+ */
+ void push(Element_type *element);
+
+ /**
+ Removes the top element from the queue.
+
+ @retval Pointer to the (key of the) removed element.
+
+ @note This function is for unit testing, where we push elements into to the
+ queue, and test that the appropriate keys are retained.
+ Interleaving of push() and pop() operations has not been tested.
+ */
+ Key_type **pop()
+ {
+ // Don't return the extra element to the client code.
+ if (queue_is_full((&m_queue)))
+ queue_remove(&m_queue, 0);
+ DBUG_ASSERT(m_queue.elements > 0);
+ if (m_queue.elements == 0)
+ return NULL;
+ return reinterpret_cast<Key_type**>(queue_remove(&m_queue, 0));
+ }
+
+ /**
+ The number of elements in the queue.
+ */
+ uint num_elements() const { return m_queue.elements; }
+
+ /**
+ Is the queue initialized?
+ */
+ bool is_initialized() const { return m_queue.max_elements > 0; }
+
+private:
+ Key_type **m_sort_keys;
+ size_t m_compare_length;
+ keymaker_function m_keymaker;
+ Sort_param *m_sort_param;
+ st_queue m_queue;
+};
+
+
+template<typename Element_type, typename Key_type>
+int Bounded_queue<Element_type, Key_type>::init(ha_rows max_elements,
+ bool max_at_top,
+ compare_function compare,
+ size_t compare_length,
+ keymaker_function keymaker,
+ Sort_param *sort_param,
+ Key_type **sort_keys)
+{
+ DBUG_ASSERT(sort_keys != NULL);
+
+ m_sort_keys= sort_keys;
+ m_compare_length= compare_length;
+ m_keymaker= keymaker;
+ m_sort_param= sort_param;
+ // init_queue() takes an uint, and also does (max_elements + 1)
+ if (max_elements >= (UINT_MAX - 1))
+ return 1;
+ if (compare == NULL)
+ compare=
+ reinterpret_cast<compare_function>(get_ptr_compare(compare_length));
+ // We allocate space for one extra element, for replace when queue is full.
+ return init_queue(&m_queue, (uint) max_elements + 1,
+ 0, max_at_top,
+ reinterpret_cast<queue_compare>(compare),
+ &m_compare_length, 0, 0);
+}
+
+
+template<typename Element_type, typename Key_type>
+void Bounded_queue<Element_type, Key_type>::push(Element_type *element)
+{
+ DBUG_ASSERT(is_initialized());
+ if (queue_is_full((&m_queue)))
+ {
+ // Replace top element with new key, and re-order the queue.
+ Key_type **pq_top= reinterpret_cast<Key_type **>(queue_top(&m_queue));
+ (*m_keymaker)(m_sort_param, *pq_top, element);
+ queue_replace_top(&m_queue);
+ } else {
+ // Insert new key into the queue.
+ (*m_keymaker)(m_sort_param, m_sort_keys[m_queue.elements], element);
+ queue_insert(&m_queue,
+ reinterpret_cast<uchar*>(&m_sort_keys[m_queue.elements]));
+ }
+}
+
+#endif // BOUNDED_QUEUE_INCLUDED
diff --git a/sql/client_settings.h b/sql/client_settings.h
index 54cb72f9412..f2ad1797b8e 100644
--- a/sql/client_settings.h
+++ b/sql/client_settings.h
@@ -30,13 +30,14 @@
*/
#define CLIENT_CAPABILITIES (CLIENT_LONG_PASSWORD | \
CLIENT_LONG_FLAG | \
- CLIENT_SECURE_CONNECTION | \
CLIENT_TRANSACTIONS | \
CLIENT_PROTOCOL_41 | \
CLIENT_SECURE_CONNECTION | \
- CLIENT_PLUGIN_AUTH)
+ CLIENT_PLUGIN_AUTH | \
+ CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA | \
+ CLIENT_CONNECT_ATTRS)
-#define read_user_name(A) {}
+#define read_user_name(A) A[0]= 0
#undef _CUSTOMCONFIG_
#define mysql_server_init(a,b,c) mysql_client_plugin_init()
diff --git a/sql/compat56.cc b/sql/compat56.cc
new file mode 100644
index 00000000000..3bd6b21a154
--- /dev/null
+++ b/sql/compat56.cc
@@ -0,0 +1,445 @@
+/*
+ Copyright (c) 2004, 2012, Oracle and/or its affiliates.
+ Copyright (c) 2013, MariaDB Foundation.
+
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "my_global.h"
+#include "compat56.h"
+#include "myisampack.h"
+#include "my_time.h"
+
+/*** MySQL56 TIME low-level memory and disk representation routines ***/
+
+/*
+ In-memory format:
+
+ 1 bit sign (Used for sign, when on disk)
+ 1 bit unused (Reserved for wider hour range, e.g. for intervals)
+ 10 bit hour (0-836)
+ 6 bit minute (0-59)
+ 6 bit second (0-59)
+ 24 bits microseconds (0-999999)
+
+ Total: 48 bits = 6 bytes
+ Suhhhhhh.hhhhmmmm.mmssssss.ffffffff.ffffffff.ffffffff
+*/
+
+
+/**
+ Convert time value to MySQL56 numeric packed representation.
+
+ @param ltime The value to convert.
+ @return Numeric packed representation.
+*/
+longlong TIME_to_longlong_time_packed(const MYSQL_TIME *ltime)
+{
+ /* If month is 0, we mix day with hours: "1 00:10:10" -> "24:00:10" */
+ long hms= (((ltime->month ? 0 : ltime->day * 24) + ltime->hour) << 12) |
+ (ltime->minute << 6) | ltime->second;
+ longlong tmp= MY_PACKED_TIME_MAKE(hms, ltime->second_part);
+ return ltime->neg ? -tmp : tmp;
+}
+
+
+
+/**
+ Convert MySQL56 time packed numeric representation to time.
+
+ @param OUT ltime The MYSQL_TIME variable to set.
+ @param tmp The packed numeric representation.
+*/
+void TIME_from_longlong_time_packed(MYSQL_TIME *ltime, longlong tmp)
+{
+ long hms;
+ if ((ltime->neg= (tmp < 0)))
+ tmp= -tmp;
+ hms= MY_PACKED_TIME_GET_INT_PART(tmp);
+ ltime->year= (uint) 0;
+ ltime->month= (uint) 0;
+ ltime->day= (uint) 0;
+ ltime->hour= (uint) (hms >> 12) % (1 << 10); /* 10 bits starting at 12th */
+ ltime->minute= (uint) (hms >> 6) % (1 << 6); /* 6 bits starting at 6th */
+ ltime->second= (uint) hms % (1 << 6); /* 6 bits starting at 0th */
+ ltime->second_part= MY_PACKED_TIME_GET_FRAC_PART(tmp);
+ ltime->time_type= MYSQL_TIMESTAMP_TIME;
+}
+
+
+/**
+ Calculate binary size of MySQL56 packed numeric time representation.
+
+ @param dec Precision.
+*/
+uint my_time_binary_length(uint dec)
+{
+ DBUG_ASSERT(dec <= TIME_SECOND_PART_DIGITS);
+ return 3 + (dec + 1) / 2;
+}
+
+
+/*
+ On disk we convert from signed representation to unsigned
+ representation using TIMEF_OFS, so all values become binary comparable.
+*/
+#define TIMEF_OFS 0x800000000000LL
+#define TIMEF_INT_OFS 0x800000LL
+
+
+/**
+ Convert MySQL56 in-memory numeric time representation to on-disk representation
+
+ @param nr Value in packed numeric time format.
+ @param OUT ptr The buffer to put value at.
+ @param dec Precision.
+*/
+void my_time_packed_to_binary(longlong nr, uchar *ptr, uint dec)
+{
+ DBUG_ASSERT(dec <= TIME_SECOND_PART_DIGITS);
+ /* Make sure the stored value was previously properly rounded or truncated */
+ DBUG_ASSERT((MY_PACKED_TIME_GET_FRAC_PART(nr) %
+ (int) log_10_int[TIME_SECOND_PART_DIGITS - dec]) == 0);
+
+ switch (dec)
+ {
+ case 0:
+ default:
+ mi_int3store(ptr, TIMEF_INT_OFS + MY_PACKED_TIME_GET_INT_PART(nr));
+ break;
+
+ case 1:
+ case 2:
+ mi_int3store(ptr, TIMEF_INT_OFS + MY_PACKED_TIME_GET_INT_PART(nr));
+ ptr[3]= (unsigned char) (char) (MY_PACKED_TIME_GET_FRAC_PART(nr) / 10000);
+ break;
+
+ case 4:
+ case 3:
+ mi_int3store(ptr, TIMEF_INT_OFS + MY_PACKED_TIME_GET_INT_PART(nr));
+ mi_int2store(ptr + 3, MY_PACKED_TIME_GET_FRAC_PART(nr) / 100);
+ break;
+
+ case 5:
+ case 6:
+ mi_int6store(ptr, nr + TIMEF_OFS);
+ break;
+ }
+}
+
+
+/**
+ Convert MySQL56 on-disk time representation to in-memory packed numeric
+ representation.
+
+ @param ptr The pointer to read the value at.
+ @param dec Precision.
+ @return Packed numeric time representation.
+*/
+longlong my_time_packed_from_binary(const uchar *ptr, uint dec)
+{
+ DBUG_ASSERT(dec <= TIME_SECOND_PART_DIGITS);
+
+ switch (dec)
+ {
+ case 0:
+ default:
+ {
+ longlong intpart= mi_uint3korr(ptr) - TIMEF_INT_OFS;
+ return MY_PACKED_TIME_MAKE_INT(intpart);
+ }
+ case 1:
+ case 2:
+ {
+ longlong intpart= mi_uint3korr(ptr) - TIMEF_INT_OFS;
+ int frac= (uint) ptr[3];
+ if (intpart < 0 && frac)
+ {
+ /*
+ Negative values are stored with reverse fractional part order,
+ for binary sort compatibility.
+
+ Disk value intpart frac Time value Memory value
+ 800000.00 0 0 00:00:00.00 0000000000.000000
+ 7FFFFF.FF -1 255 -00:00:00.01 FFFFFFFFFF.FFD8F0
+ 7FFFFF.9D -1 99 -00:00:00.99 FFFFFFFFFF.F0E4D0
+ 7FFFFF.00 -1 0 -00:00:01.00 FFFFFFFFFF.000000
+ 7FFFFE.FF -1 255 -00:00:01.01 FFFFFFFFFE.FFD8F0
+ 7FFFFE.F6 -2 246 -00:00:01.10 FFFFFFFFFE.FE7960
+
+ Formula to convert fractional part from disk format
+ (now stored in "frac" variable) to absolute value: "0x100 - frac".
+ To reconstruct in-memory value, we shift
+ to the next integer value and then substruct fractional part.
+ */
+ intpart++; /* Shift to the next integer value */
+ frac-= 0x100; /* -(0x100 - frac) */
+ }
+ return MY_PACKED_TIME_MAKE(intpart, frac * 10000);
+ }
+
+ case 3:
+ case 4:
+ {
+ longlong intpart= mi_uint3korr(ptr) - TIMEF_INT_OFS;
+ int frac= mi_uint2korr(ptr + 3);
+ if (intpart < 0 && frac)
+ {
+ /*
+ Fix reverse fractional part order: "0x10000 - frac".
+ See comments for FSP=1 and FSP=2 above.
+ */
+ intpart++; /* Shift to the next integer value */
+ frac-= 0x10000; /* -(0x10000-frac) */
+ }
+ return MY_PACKED_TIME_MAKE(intpart, frac * 100);
+ }
+
+ case 5:
+ case 6:
+ return ((longlong) mi_uint6korr(ptr)) - TIMEF_OFS;
+ }
+}
+
+
+/*** MySQL56 DATETIME low-level memory and disk representation routines ***/
+
+/*
+ 1 bit sign (used when on disk)
+ 17 bits year*13+month (year 0-9999, month 0-12)
+ 5 bits day (0-31)
+ 5 bits hour (0-23)
+ 6 bits minute (0-59)
+ 6 bits second (0-59)
+ 24 bits microseconds (0-999999)
+
+ Total: 64 bits = 8 bytes
+
+ SYYYYYYY.YYYYYYYY.YYdddddh.hhhhmmmm.mmssssss.ffffffff.ffffffff.ffffffff
+*/
+
+/**
+ Convert datetime to MySQL56 packed numeric datetime representation.
+ @param ltime The value to convert.
+ @return Packed numeric representation of ltime.
+*/
+longlong TIME_to_longlong_datetime_packed(const MYSQL_TIME *ltime)
+{
+ longlong ymd= ((ltime->year * 13 + ltime->month) << 5) | ltime->day;
+ longlong hms= (ltime->hour << 12) | (ltime->minute << 6) | ltime->second;
+ longlong tmp= MY_PACKED_TIME_MAKE(((ymd << 17) | hms), ltime->second_part);
+ DBUG_ASSERT(!check_datetime_range(ltime)); /* Make sure no overflow */
+ return ltime->neg ? -tmp : tmp;
+}
+
+
+/**
+ Convert MySQL56 packed numeric datetime representation to MYSQL_TIME.
+ @param OUT ltime The datetime variable to convert to.
+ @param tmp The packed numeric datetime value.
+*/
+void TIME_from_longlong_datetime_packed(MYSQL_TIME *ltime, longlong tmp)
+{
+ longlong ymd, hms;
+ longlong ymdhms, ym;
+ if ((ltime->neg= (tmp < 0)))
+ tmp= -tmp;
+
+ ltime->second_part= MY_PACKED_TIME_GET_FRAC_PART(tmp);
+ ymdhms= MY_PACKED_TIME_GET_INT_PART(tmp);
+
+ ymd= ymdhms >> 17;
+ ym= ymd >> 5;
+ hms= ymdhms % (1 << 17);
+
+ ltime->day= ymd % (1 << 5);
+ ltime->month= ym % 13;
+ ltime->year= ym / 13;
+
+ ltime->second= hms % (1 << 6);
+ ltime->minute= (hms >> 6) % (1 << 6);
+ ltime->hour= (hms >> 12);
+
+ ltime->time_type= MYSQL_TIMESTAMP_DATETIME;
+}
+
+
+/**
+ Calculate binary size of MySQL56 packed datetime representation.
+ @param dec Precision.
+*/
+uint my_datetime_binary_length(uint dec)
+{
+ DBUG_ASSERT(dec <= TIME_SECOND_PART_DIGITS);
+ return 5 + (dec + 1) / 2;
+}
+
+
+/*
+ On disk we store as unsigned number with DATETIMEF_INT_OFS offset,
+ for HA_KETYPE_BINARY compatibilty purposes.
+*/
+#define DATETIMEF_INT_OFS 0x8000000000LL
+
+
+/**
+ Convert MySQL56 on-disk datetime representation
+ to in-memory packed numeric representation.
+
+ @param ptr The pointer to read value at.
+ @param dec Precision.
+ @return In-memory packed numeric datetime representation.
+*/
+longlong my_datetime_packed_from_binary(const uchar *ptr, uint dec)
+{
+ longlong intpart= mi_uint5korr(ptr) - DATETIMEF_INT_OFS;
+ int frac;
+ DBUG_ASSERT(dec <= TIME_SECOND_PART_DIGITS);
+ switch (dec)
+ {
+ case 0:
+ default:
+ return MY_PACKED_TIME_MAKE_INT(intpart);
+ case 1:
+ case 2:
+ frac= ((int) (signed char) ptr[5]) * 10000;
+ break;
+ case 3:
+ case 4:
+ frac= mi_sint2korr(ptr + 5) * 100;
+ break;
+ case 5:
+ case 6:
+ frac= mi_sint3korr(ptr + 5);
+ break;
+ }
+ return MY_PACKED_TIME_MAKE(intpart, frac);
+}
+
+
+/**
+ Store MySQL56 in-memory numeric packed datetime representation to disk.
+
+ @param nr In-memory numeric packed datetime representation.
+ @param OUT ptr The pointer to store at.
+ @param dec Precision, 1-6.
+*/
+void my_datetime_packed_to_binary(longlong nr, uchar *ptr, uint dec)
+{
+ DBUG_ASSERT(dec <= TIME_SECOND_PART_DIGITS);
+ /* The value being stored must have been properly rounded or truncated */
+ DBUG_ASSERT((MY_PACKED_TIME_GET_FRAC_PART(nr) %
+ (int) log_10_int[TIME_SECOND_PART_DIGITS - dec]) == 0);
+
+ mi_int5store(ptr, MY_PACKED_TIME_GET_INT_PART(nr) + DATETIMEF_INT_OFS);
+ switch (dec)
+ {
+ case 0:
+ default:
+ break;
+ case 1:
+ case 2:
+ ptr[5]= (unsigned char) (char) (MY_PACKED_TIME_GET_FRAC_PART(nr) / 10000);
+ break;
+ case 3:
+ case 4:
+ mi_int2store(ptr + 5, MY_PACKED_TIME_GET_FRAC_PART(nr) / 100);
+ break;
+ case 5:
+ case 6:
+ mi_int3store(ptr + 5, MY_PACKED_TIME_GET_FRAC_PART(nr));
+ }
+}
+
+
+/*** MySQL56 TIMESTAMP low-level memory and disk representation routines ***/
+
+/**
+ Calculate on-disk size of a timestamp value.
+
+ @param dec Precision.
+*/
+uint my_timestamp_binary_length(uint dec)
+{
+ DBUG_ASSERT(dec <= TIME_SECOND_PART_DIGITS);
+ return 4 + (dec + 1) / 2;
+}
+
+
+/**
+ Convert MySQL56 binary timestamp representation to in-memory representation.
+
+ @param OUT tm The variable to convert to.
+ @param ptr The pointer to read the value from.
+ @param dec Precision.
+*/
+void my_timestamp_from_binary(struct timeval *tm, const uchar *ptr, uint dec)
+{
+ DBUG_ASSERT(dec <= TIME_SECOND_PART_DIGITS);
+ tm->tv_sec= mi_uint4korr(ptr);
+ switch (dec)
+ {
+ case 0:
+ default:
+ tm->tv_usec= 0;
+ break;
+ case 1:
+ case 2:
+ tm->tv_usec= ((int) ptr[4]) * 10000;
+ break;
+ case 3:
+ case 4:
+ tm->tv_usec= mi_sint2korr(ptr + 4) * 100;
+ break;
+ case 5:
+ case 6:
+ tm->tv_usec= mi_sint3korr(ptr + 4);
+ }
+}
+
+
+/**
+ Convert MySQL56 in-memory timestamp representation to on-disk representation.
+
+ @param tm The value to convert.
+ @param OUT ptr The pointer to store the value to.
+ @param dec Precision.
+*/
+void my_timestamp_to_binary(const struct timeval *tm, uchar *ptr, uint dec)
+{
+ DBUG_ASSERT(dec <= TIME_SECOND_PART_DIGITS);
+ /* Stored value must have been previously properly rounded or truncated */
+ DBUG_ASSERT((tm->tv_usec %
+ (int) log_10_int[TIME_SECOND_PART_DIGITS - dec]) == 0);
+ mi_int4store(ptr, tm->tv_sec);
+ switch (dec)
+ {
+ case 0:
+ default:
+ break;
+ case 1:
+ case 2:
+ ptr[4]= (unsigned char) (char) (tm->tv_usec / 10000);
+ break;
+ case 3:
+ case 4:
+ mi_int2store(ptr + 4, tm->tv_usec / 100);
+ break;
+ /* Impossible second precision. Fall through */
+ case 5:
+ case 6:
+ mi_int3store(ptr + 4, tm->tv_usec);
+ }
+}
+
+/****************************************/
diff --git a/sql/compat56.h b/sql/compat56.h
new file mode 100644
index 00000000000..bb5e2670f7d
--- /dev/null
+++ b/sql/compat56.h
@@ -0,0 +1,46 @@
+#ifndef COMPAT56_H_INCLUDED
+#define COMPAT56_H_INCLUDED
+/*
+ Copyright (c) 2004, 2012, Oracle and/or its affiliates.
+ Copyright (c) 2013 MariaDB Foundation.
+
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+
+/** MySQL56 routines and macros **/
+#define MY_PACKED_TIME_GET_INT_PART(x) ((x) >> 24)
+#define MY_PACKED_TIME_GET_FRAC_PART(x) ((x) % (1LL << 24))
+#define MY_PACKED_TIME_MAKE(i, f) ((((longlong) (i)) << 24) + (f))
+#define MY_PACKED_TIME_MAKE_INT(i) ((((longlong) (i)) << 24))
+
+longlong TIME_to_longlong_datetime_packed(const MYSQL_TIME *);
+longlong TIME_to_longlong_time_packed(const MYSQL_TIME *);
+
+void TIME_from_longlong_datetime_packed(MYSQL_TIME *ltime, longlong nr);
+void TIME_from_longlong_time_packed(MYSQL_TIME *ltime, longlong nr);
+
+void my_datetime_packed_to_binary(longlong nr, uchar *ptr, uint dec);
+longlong my_datetime_packed_from_binary(const uchar *ptr, uint dec);
+uint my_datetime_binary_length(uint dec);
+
+void my_time_packed_to_binary(longlong nr, uchar *ptr, uint dec);
+longlong my_time_packed_from_binary(const uchar *ptr, uint dec);
+uint my_time_binary_length(uint dec);
+
+void my_timestamp_to_binary(const struct timeval *tm, uchar *ptr, uint dec);
+void my_timestamp_from_binary(struct timeval *tm, const uchar *ptr, uint dec);
+uint my_timestamp_binary_length(uint dec);
+/** End of MySQL routines and macros **/
+
+#endif /* COMPAT56_H_INCLUDED */
diff --git a/sql/contributors.h b/sql/contributors.h
index b1ba2c20997..255decd19cc 100644
--- a/sql/contributors.h
+++ b/sql/contributors.h
@@ -30,12 +30,29 @@ struct show_table_contributors_st {
Get permission before editing.
- IMPORTANT: Names should be left in historical order.
-
Names should be encoded using UTF-8.
+
+ See also https://mariadb.com/kb/en/log-of-mariadb-contributions/
*/
struct show_table_contributors_st show_table_contributors[]= {
+ /* MariaDB foundation members, in contribution, size , time order */
+ {"Booking.com", "http://www.booking.com", "Founding member of the MariaDB Foundation"},
+ {"MariaDB Corporation", "https://mariadb.com", "Founding member of the MariaDB Foundation"},
+ {"Auttomattic", "http://automattic.com", "Member of the MariaDB Foundation"},
+ {"Parallels", "http://www.parallels.com/products/plesk", "Founding member of the MariaDB Foundation"},
+ {"Acronis", "http://www.acronis.com", "Member of the MariaDB Foundation"},
+
+ /* Smaller sponsors, newer per year */
+ {"Verkkokauppa.com", "Finland", "Sponsor of the MariaDB Foundation"},
+ {"Webyog", "Bangalore", "Sponsor of the MariaDB Foundation"},
+ {"Wikimedia Foundation", "USA", "Sponsor of the MariaDB Foundation"},
+
+ /* Sponsors of important features */
+ {"Google", "USA", "Sponsoring parallel replication and GTID" },
+ {"Facebook", "USA", "Sponsoring non-blocking API, LIMIT ROWS EXAMINED etc"},
+
+ /* Individual contributors, names in historical order, newer first */
{"Ronald Bradford", "Brisbane, Australia", "EFF contribution for UC2006 Auction"},
{"Sheeri Kritzer", "Boston, Mass. USA", "EFF contribution for UC2006 Auction"},
{"Mark Shuttleworth", "London, UK.", "EFF contribution for UC2006 Auction"},
diff --git a/sql/create_options.cc b/sql/create_options.cc
index 5cedfa03a63..09153f7e35c 100644
--- a/sql/create_options.cc
+++ b/sql/create_options.cc
@@ -21,6 +21,7 @@
#include "create_options.h"
#include <my_getopt.h>
+#include "set_var.h"
#define FRM_QUOTED_VALUE 0x8000
@@ -74,7 +75,7 @@ void engine_option_value::link(engine_option_value **start,
}
static bool report_wrong_value(THD *thd, const char *name, const char *val,
- my_bool suppress_warning)
+ bool suppress_warning)
{
if (suppress_warning)
return 0;
@@ -86,13 +87,13 @@ static bool report_wrong_value(THD *thd, const char *name, const char *val,
return 1;
}
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_BAD_OPTION_VALUE,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_BAD_OPTION_VALUE,
ER(ER_BAD_OPTION_VALUE), val, name);
return 0;
}
static bool report_unknown_option(THD *thd, engine_option_value *val,
- my_bool suppress_warning)
+ bool suppress_warning)
{
DBUG_ENTER("report_unknown_option");
@@ -109,14 +110,16 @@ static bool report_unknown_option(THD *thd, engine_option_value *val,
DBUG_RETURN(TRUE);
}
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_UNKNOWN_OPTION, ER(ER_UNKNOWN_OPTION), val->name.str);
DBUG_RETURN(FALSE);
}
+#define value_ptr(STRUCT,OPT) ((char*)(STRUCT) + (OPT)->offset)
+
static bool set_one_value(ha_create_table_option *opt,
- THD *thd, LEX_STRING *value, void *base,
- my_bool suppress_warning,
+ THD *thd, const LEX_STRING *value, void *base,
+ bool suppress_warning,
MEM_ROOT *root)
{
DBUG_ENTER("set_one_value");
@@ -126,9 +129,11 @@ static bool set_one_value(ha_create_table_option *opt,
(value->str ? value->str : "<DEFAULT>")));
switch (opt->type)
{
+ case HA_OPTION_TYPE_SYSVAR:
+ DBUG_ASSERT(0); // HA_OPTION_TYPE_SYSVAR's are replaced in resolve_sysvars()
case HA_OPTION_TYPE_ULL:
{
- ulonglong *val= (ulonglong*)((char*)base + opt->offset);
+ ulonglong *val= (ulonglong*)value_ptr(base, opt);
if (!value->str)
{
*val= opt->def_value;
@@ -152,7 +157,7 @@ static bool set_one_value(ha_create_table_option *opt,
}
case HA_OPTION_TYPE_STRING:
{
- char **val= (char **)((char *)base + opt->offset);
+ char **val= (char **)value_ptr(base, opt);
if (!value->str)
{
*val= 0;
@@ -165,7 +170,7 @@ static bool set_one_value(ha_create_table_option *opt,
}
case HA_OPTION_TYPE_ENUM:
{
- uint *val= (uint *)((char *)base + opt->offset), num;
+ uint *val= (uint *)value_ptr(base, opt), num;
*val= (uint) opt->def_value;
if (!value->str)
@@ -197,7 +202,7 @@ static bool set_one_value(ha_create_table_option *opt,
}
case HA_OPTION_TYPE_BOOL:
{
- bool *val= (bool *)((char *)base + opt->offset);
+ bool *val= (bool *)value_ptr(base, opt);
*val= opt->def_value;
if (!value->str)
@@ -257,52 +262,92 @@ static const size_t ha_option_type_sizeof[]=
@retval FALSE OK
*/
-my_bool parse_option_list(THD* thd, void *option_struct_arg,
- engine_option_value *option_list,
- ha_create_table_option *rules,
- my_bool suppress_warning,
- MEM_ROOT *root)
+bool parse_option_list(THD* thd, handlerton *hton, void *option_struct_arg,
+ engine_option_value **option_list,
+ ha_create_table_option *rules,
+ bool suppress_warning, MEM_ROOT *root)
{
ha_create_table_option *opt;
size_t option_struct_size= 0;
- engine_option_value *val= option_list;
+ engine_option_value *val, *last;
void **option_struct= (void**)option_struct_arg;
DBUG_ENTER("parse_option_list");
DBUG_PRINT("enter",
- ("struct: 0x%lx list: 0x%lx rules: 0x%lx suppres %u root 0x%lx",
- (ulong) *option_struct, (ulong)option_list, (ulong)rules,
- (uint) suppress_warning, (ulong) root));
+ ("struct: %p list: %p rules: %p suppress_warning: %u root: %p",
+ *option_struct, *option_list, rules,
+ (uint) suppress_warning, root));
if (rules)
{
- LEX_STRING default_val= {NULL, 0};
for (opt= rules; opt->name; opt++)
set_if_bigger(option_struct_size, opt->offset +
ha_option_type_sizeof[opt->type]);
*option_struct= alloc_root(root, option_struct_size);
-
- /* set all values to default */
- for (opt= rules; opt->name; opt++)
- set_one_value(opt, thd, &default_val, *option_struct,
- suppress_warning, root);
}
- for (; val; val= val->next)
+ for (opt= rules; rules && opt->name; opt++)
{
- for (opt= rules; opt && opt->name; opt++)
+ bool seen=false;
+ for (val= *option_list; val; val= val->next)
{
+ last= val;
if (my_strnncoll(system_charset_info,
(uchar*)opt->name, opt->name_length,
(uchar*)val->name.str, val->name.length))
continue;
+ seen=true;
+
+ if (val->parsed && !val->value.str)
+ continue;
+
if (set_one_value(opt, thd, &val->value,
*option_struct, suppress_warning || val->parsed, root))
DBUG_RETURN(TRUE);
val->parsed= true;
break;
}
+ if (!seen)
+ {
+ LEX_STRING default_val= null_lex_str;
+
+ /*
+ If it's CREATE/ALTER TABLE parsing mode (options are created in the
+ transient thd->mem_root, not in the long living TABLE_SHARE::mem_root),
+ and variable-backed option was not explicitly set.
+
+ If it's not create, but opening of the existing frm (that was,
+ probably, created with the older version of the storage engine and
+ does not have this option stored), we take the *default* value of the
+ sysvar, not the *current* value. Because we don't want to have
+ different option values for the same table if it's opened many times.
+ */
+ if (root == thd->mem_root && opt->var)
+ {
+ // take a value from the variable and add it to the list
+ sys_var *sysvar= find_hton_sysvar(hton, opt->var);
+ DBUG_ASSERT(sysvar);
+
+ char buf[256];
+ String sbuf(buf, sizeof(buf), system_charset_info), *str;
+ if ((str= sysvar->val_str(&sbuf, thd, OPT_SESSION, 0)))
+ {
+ LEX_STRING name= { const_cast<char*>(opt->name), opt->name_length };
+ default_val.str= strmake_root(root, str->ptr(), str->length());
+ default_val.length= str->length();
+ val= new (root) engine_option_value(name, default_val, true,
+ option_list, &last);
+ val->parsed= true;
+ }
+ }
+ set_one_value(opt, thd, &default_val, *option_struct,
+ suppress_warning, root);
+ }
+ }
+
+ for (val= *option_list; val; val= val->next)
+ {
if (report_unknown_option(thd, val, suppress_warning))
DBUG_RETURN(TRUE);
val->parsed= true;
@@ -313,6 +358,103 @@ my_bool parse_option_list(THD* thd, void *option_struct_arg,
/**
+ Resolves all HA_OPTION_TYPE_SYSVAR elements.
+
+ This is done when an engine is loaded.
+*/
+static bool resolve_sysvars(handlerton *hton, ha_create_table_option *rules)
+{
+ for (ha_create_table_option *opt= rules; rules && opt->name; opt++)
+ {
+ if (opt->type == HA_OPTION_TYPE_SYSVAR)
+ {
+ struct my_option optp;
+ plugin_opt_set_limits(&optp, opt->var);
+ switch(optp.var_type) {
+ case GET_ULL:
+ case GET_ULONG:
+ case GET_UINT:
+ opt->type= HA_OPTION_TYPE_ULL;
+ opt->def_value= (ulonglong)optp.def_value;
+ opt->min_value= (ulonglong)optp.min_value;
+ opt->max_value= (ulonglong)optp.max_value;
+ opt->block_size= (ulonglong)optp.block_size;
+ break;
+ case GET_STR:
+ case GET_STR_ALLOC:
+ opt->type= HA_OPTION_TYPE_STRING;
+ break;
+ case GET_BOOL:
+ opt->type= HA_OPTION_TYPE_BOOL;
+ opt->def_value= optp.def_value;
+ break;
+ case GET_ENUM:
+ {
+ opt->type= HA_OPTION_TYPE_ENUM;
+ opt->def_value= optp.def_value;
+
+ char buf[256];
+ String str(buf, sizeof(buf), system_charset_info);
+ str.length(0);
+ for (const char **s= optp.typelib->type_names; *s; s++)
+ {
+ if (str.append(*s) || str.append(','))
+ return 1;
+ }
+ DBUG_ASSERT(str.length());
+ opt->values= my_strndup(str.ptr(), str.length()-1, MYF(MY_WME));
+ if (!opt->values)
+ return 1;
+ break;
+ }
+ default:
+ DBUG_ASSERT(0);
+ }
+ }
+ }
+ return 0;
+}
+
+bool resolve_sysvar_table_options(handlerton *hton)
+{
+ return resolve_sysvars(hton, hton->table_options) ||
+ resolve_sysvars(hton, hton->field_options) ||
+ resolve_sysvars(hton, hton->index_options);
+}
+
+/*
+ Restore HA_OPTION_TYPE_SYSVAR options back as they were
+ before resolve_sysvars().
+
+ This is done when the engine is unloaded, so that we could
+ call resolve_sysvars() if the engine is installed again.
+*/
+static void free_sysvars(handlerton *hton, ha_create_table_option *rules)
+{
+ for (ha_create_table_option *opt= rules; rules && opt->name; opt++)
+ {
+ if (opt->var)
+ {
+ my_free(const_cast<char*>(opt->values));
+ opt->type= HA_OPTION_TYPE_SYSVAR;
+ opt->def_value= 0;
+ opt->min_value= 0;
+ opt->max_value= 0;
+ opt->block_size= 0;
+ opt->values= 0;
+ }
+ }
+}
+
+void free_sysvar_table_options(handlerton *hton)
+{
+ free_sysvars(hton, hton->table_options);
+ free_sysvars(hton, hton->field_options);
+ free_sysvars(hton, hton->index_options);
+}
+
+
+/**
Parses all table/fields/keys options
@param thd thread handler
@@ -323,27 +465,27 @@ my_bool parse_option_list(THD* thd, void *option_struct_arg,
@retval FALSE OK
*/
-my_bool parse_engine_table_options(THD *thd, handlerton *ht,
- TABLE_SHARE *share)
+bool parse_engine_table_options(THD *thd, handlerton *ht, TABLE_SHARE *share)
{
MEM_ROOT *root= &share->mem_root;
DBUG_ENTER("parse_engine_table_options");
- if (parse_option_list(thd, &share->option_struct, share->option_list,
+ if (parse_option_list(thd, ht, &share->option_struct, & share->option_list,
ht->table_options, TRUE, root))
DBUG_RETURN(TRUE);
for (Field **field= share->field; *field; field++)
{
- if (parse_option_list(thd, &(*field)->option_struct, (*field)->option_list,
+ if (parse_option_list(thd, ht, &(*field)->option_struct,
+ & (*field)->option_list,
ht->field_options, TRUE, root))
DBUG_RETURN(TRUE);
}
for (uint index= 0; index < share->keys; index ++)
{
- if (parse_option_list(thd, &share->key_info[index].option_struct,
- share->key_info[index].option_list,
+ if (parse_option_list(thd, ht, &share->key_info[index].option_struct,
+ & share->key_info[index].option_list,
ht->index_options, TRUE, root))
DBUG_RETURN(TRUE);
}
@@ -352,6 +494,26 @@ my_bool parse_engine_table_options(THD *thd, handlerton *ht,
}
+bool engine_options_differ(void *old_struct, void *new_struct,
+ ha_create_table_option *rules)
+{
+ ha_create_table_option *opt;
+ for (opt= rules; rules && opt->name; opt++)
+ {
+ char **old_val= (char**)value_ptr(old_struct, opt);
+ char **new_val= (char**)value_ptr(new_struct, opt);
+ int neq;
+ if (opt->type == HA_OPTION_TYPE_STRING)
+ neq= (*old_val && *new_val) ? strcmp(*old_val, *new_val) : *old_val != *new_val;
+ else
+ neq= memcmp(old_val, new_val, ha_option_type_sizeof[opt->type]);
+ if (neq)
+ return true;
+ }
+ return false;
+}
+
+
/**
Returns representation length of key and value in the frm file
*/
@@ -543,8 +705,8 @@ uchar *engine_option_value::frm_read(const uchar *buff, engine_option_value **st
@retval FALSE OK
*/
-my_bool engine_table_options_frm_read(const uchar *buff, uint length,
- TABLE_SHARE *share)
+bool engine_table_options_frm_read(const uchar *buff, uint length,
+ TABLE_SHARE *share)
{
const uchar *buff_end= buff + length;
engine_option_value *UNINIT_VAR(end);
@@ -604,12 +766,29 @@ engine_option_value *merge_engine_table_options(engine_option_value *first,
DBUG_ENTER("merge_engine_table_options");
LINT_INIT(end);
- /* find last element */
- if (first && second)
- for (end= first; end->next; end= end->next) /* no-op */;
+ /* Create copy of first list */
+ for (opt= first, first= 0; opt; opt= opt->next)
+ new (root) engine_option_value(opt, &first, &end);
for (opt= second; opt; opt= opt->next)
new (root) engine_option_value(opt->name, opt->value, opt->quoted_value,
&first, &end);
DBUG_RETURN(first);
}
+
+bool is_engine_option_known(engine_option_value *opt,
+ ha_create_table_option *rules)
+{
+ if (!rules)
+ return false;
+
+ for (; rules->name; rules++)
+ {
+ if (!my_strnncoll(system_charset_info,
+ (uchar*)rules->name, rules->name_length,
+ (uchar*)opt->name.str, opt->name.length))
+ return true;
+ }
+ return false;
+}
+
diff --git a/sql/create_options.h b/sql/create_options.h
index ae918f6cea1..eb21f291ff4 100644
--- a/sql/create_options.h
+++ b/sql/create_options.h
@@ -34,6 +34,13 @@ class engine_option_value: public Sql_alloc
bool parsed; ///< to detect unrecognized options
bool quoted_value; ///< option=VAL vs. option='VAL'
+ engine_option_value(engine_option_value *src,
+ engine_option_value **start, engine_option_value **end) :
+ name(src->name), value(src->value),
+ next(NULL), parsed(src->parsed), quoted_value(src->quoted_value)
+ {
+ link(start, end);
+ }
engine_option_value(LEX_STRING &name_arg, LEX_STRING &value_arg, bool quoted,
engine_option_value **start, engine_option_value **end) :
name(name_arg), value(value_arg),
@@ -69,16 +76,15 @@ class engine_option_value: public Sql_alloc
typedef struct st_key KEY;
class Create_field;
-my_bool parse_engine_table_options(THD *thd, handlerton *ht,
+bool resolve_sysvar_table_options(handlerton *hton);
+void free_sysvar_table_options(handlerton *hton);
+bool parse_engine_table_options(THD *thd, handlerton *ht, TABLE_SHARE *share);
+bool parse_option_list(THD* thd, handlerton *hton, void *option_struct,
+ engine_option_value **option_list,
+ ha_create_table_option *rules,
+ bool suppress_warning, MEM_ROOT *root);
+bool engine_table_options_frm_read(const uchar *buff, uint length,
TABLE_SHARE *share);
-my_bool parse_option_list(THD* thd, void *option_struct,
- engine_option_value *option_list,
- ha_create_table_option *rules,
- my_bool suppress_warning,
- MEM_ROOT *root);
-my_bool engine_table_options_frm_read(const uchar *buff,
- uint length,
- TABLE_SHARE *share);
engine_option_value *merge_engine_table_options(engine_option_value *source,
engine_option_value *changes,
MEM_ROOT *root);
@@ -90,4 +96,9 @@ uchar *engine_table_options_frm_image(uchar *buff,
engine_option_value *table_option_list,
List<Create_field> &create_fields,
uint keys, KEY *key_info);
+
+bool engine_options_differ(void *old_struct, void *new_struct,
+ ha_create_table_option *rules);
+bool is_engine_option_known(engine_option_value *opt,
+ ha_create_table_option *rules);
#endif
diff --git a/sql/datadict.cc b/sql/datadict.cc
index 4e4fcafa31b..62d60ed15a1 100644
--- a/sql/datadict.cc
+++ b/sql/datadict.cc
@@ -13,11 +13,28 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+#include <my_global.h>
#include "datadict.h"
#include "sql_priv.h"
#include "sql_class.h"
#include "sql_table.h"
+static int read_string(File file, uchar**to, size_t length)
+{
+ DBUG_ENTER("read_string");
+
+ my_free(*to);
+ if (!(*to= (uchar*) my_malloc(length+1,MYF(MY_WME))) ||
+ mysql_file_read(file, *to, length, MYF(MY_NABP)))
+ {
+ my_free(*to);
+ *to= 0;
+ DBUG_RETURN(1);
+ }
+ *((char*) *to+length)= '\0'; // C-style safety
+ DBUG_RETURN (0);
+}
+
/**
Check type of .frm if we are not going to parse it.
@@ -60,9 +77,7 @@ frm_type_enum dd_frm_type(THD *thd, char *path, enum legacy_db_type *dbt)
if the following test is true (arg #3). This should not have effect
on return value from this function (default FRMTYPE_TABLE)
*/
- if (header[0] != (uchar) 254 || header[1] != 1 ||
- (header[2] != FRM_VER && header[2] != FRM_VER+1 &&
- (header[2] < FRM_VER+3 || header[2] > FRM_VER+4)))
+ if (!is_binary_frm_header(header))
goto err;
*dbt= (enum legacy_db_type) (uint) *(header + 3);
@@ -84,9 +99,9 @@ frm_type_enum dd_frm_type(THD *thd, char *path, enum legacy_db_type *dbt)
if ((n_length= uint4korr(frm_image+55)))
{
- uint record_offset= (uint2korr(frm_image+6)+
+ uint record_offset= uint2korr(frm_image+6)+
((uint2korr(frm_image+14) == 0xffff ?
- uint4korr(frm_image+47) : uint2korr(frm_image+14))));
+ uint4korr(frm_image+47) : uint2korr(frm_image+14)));
uint reclength= uint2korr(frm_image+16);
uchar *next_chunk= frm_image + record_offset + reclength;
@@ -117,117 +132,44 @@ err:
}
-/**
- Given a table name, check type of .frm and legacy table type.
-
- @param[in] thd The current session.
- @param[in] db Table schema.
- @param[in] table_name Table database.
- @param[out] table_type handlerton of the table if FRMTYPE_TABLE,
- otherwise undefined.
-
- @return FALSE if FRMTYPE_TABLE and storage engine found. TRUE otherwise.
-*/
-
-bool dd_frm_storage_engine(THD *thd, const char *db, const char *table_name,
- handlerton **table_type)
-{
- char path[FN_REFLEN + 1];
- enum legacy_db_type db_type;
- LEX_STRING db_name = {(char *) db, strlen(db)};
-
- /* There should be at least some lock on the table. */
- DBUG_ASSERT(thd->mdl_context.is_lock_owner(MDL_key::TABLE, db,
- table_name, MDL_SHARED));
-
- if (check_db_name(&db_name))
- {
- my_error(ER_WRONG_DB_NAME, MYF(0), db_name.str);
- return TRUE;
- }
-
- if (check_table_name(table_name, strlen(table_name), FALSE))
- {
- my_error(ER_WRONG_TABLE_NAME, MYF(0), table_name);
- return TRUE;
- }
-
- (void) build_table_filename(path, sizeof(path) - 1, db,
- table_name, reg_ext, 0);
-
- dd_frm_type(thd, path, &db_type);
-
- /* Type is unknown if the object is not found or is not a table. */
- if (db_type == DB_TYPE_UNKNOWN ||
- !(*table_type= ha_resolve_by_legacy_type(thd, db_type)))
- {
- my_error(ER_NO_SUCH_TABLE, MYF(0), db, table_name);
- return TRUE;
- }
-
- return FALSE;
-}
-
-
-/**
- Given a table name, check if the storage engine for the
- table referred by this name supports an option 'flag'.
- Return an error if the table does not exist or is not a
- base table.
-
- @pre Any metadata lock on the table.
-
- @param[in] thd The current session.
- @param[in] db Table schema.
- @param[in] table_name Table database.
- @param[in] flag The option to check.
- @param[out] yes_no The result. Undefined if error.
-*/
-
-bool dd_check_storage_engine_flag(THD *thd,
- const char *db, const char *table_name,
- uint32 flag, bool *yes_no)
-{
- handlerton *table_type;
-
- if (dd_frm_storage_engine(thd, db, table_name, &table_type))
- return TRUE;
-
- *yes_no= ha_check_storage_engine_flag(table_type, flag);
-
- return FALSE;
-}
-
-
/*
Regenerate a metadata locked table.
@param thd Thread context.
@param db Name of the database to which the table belongs to.
@param name Table name.
+ @param path For temporary tables only - path to table files.
+ Otherwise NULL (the path is calculated from db and table names).
@retval FALSE Success.
@retval TRUE Error.
*/
-bool dd_recreate_table(THD *thd, const char *db, const char *table_name)
+bool dd_recreate_table(THD *thd, const char *db, const char *table_name,
+ const char *path)
{
bool error= TRUE;
HA_CREATE_INFO create_info;
- char path[FN_REFLEN + 1];
+ char path_buf[FN_REFLEN + 1];
DBUG_ENTER("dd_recreate_table");
- /* There should be a exclusive metadata lock on the table. */
- DBUG_ASSERT(thd->mdl_context.is_lock_owner(MDL_key::TABLE, db, table_name,
- MDL_EXCLUSIVE));
-
memset(&create_info, 0, sizeof(create_info));
- /* Create a path to the table, but without a extension. */
- build_table_filename(path, sizeof(path) - 1, db, table_name, "", 0);
+ if (path)
+ create_info.options|= HA_LEX_CREATE_TMP_TABLE;
+ else
+ {
+ build_table_filename(path_buf, sizeof(path_buf) - 1,
+ db, table_name, "", 0);
+ path= path_buf;
+
+ /* There should be a exclusive metadata lock on the table. */
+ DBUG_ASSERT(thd->mdl_context.is_lock_owner(MDL_key::TABLE, db, table_name,
+ MDL_EXCLUSIVE));
+ }
/* Attempt to reconstruct the table. */
- error= ha_create_table(thd, path, db, table_name, &create_info, TRUE);
+ error= ha_create_table(thd, path, db, table_name, &create_info, NULL);
DBUG_RETURN(error);
}
diff --git a/sql/datadict.h b/sql/datadict.h
index f852b02f52c..dd80942daca 100644
--- a/sql/datadict.h
+++ b/sql/datadict.h
@@ -28,14 +28,22 @@ enum frm_type_enum
FRMTYPE_VIEW
};
+/*
+ Take extra care when using dd_frm_type() - it only checks the .frm file,
+ and it won't work for any engine that supports discovery.
+
+ Prefer to use ha_table_exists() instead.
+ To check whether it's an frm of a view, use dd_frm_is_view().
+*/
frm_type_enum dd_frm_type(THD *thd, char *path, enum legacy_db_type *dbt);
-bool dd_frm_storage_engine(THD *thd, const char *db, const char *table_name,
- handlerton **table_type);
-bool dd_check_storage_engine_flag(THD *thd,
- const char *db, const char *table_name,
- uint32 flag,
- bool *yes_no);
-bool dd_recreate_table(THD *thd, const char *db, const char *table_name);
+static inline bool dd_frm_is_view(THD *thd, char *path)
+{
+ enum legacy_db_type not_used;
+ return dd_frm_type(thd, path, &not_used) == FRMTYPE_VIEW;
+}
+
+bool dd_recreate_table(THD *thd, const char *db, const char *table_name,
+ const char *path = NULL);
#endif // DATADICT_INCLUDED
diff --git a/sql/debug_sync.cc b/sql/debug_sync.cc
index 4cff1c09ba7..5802d726aa2 100644
--- a/sql/debug_sync.cc
+++ b/sql/debug_sync.cc
@@ -10,11 +10,12 @@
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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+ along with this program; if not, write to the Free Software Foundation,
+ 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */
/* see include/mysql/service_debug_sync.h for debug sync documentation */
+#include <my_global.h>
#include "debug_sync.h"
#if defined(ENABLED_DEBUG_SYNC)
@@ -38,7 +39,7 @@
*/
struct st_debug_sync_action
{
- ulong activation_count; /* max(hit_limit, execute) */
+ ulong activation_count; /* MY_MAX(hit_limit, execute) */
ulong hit_limit; /* hits before kill query */
ulong execute; /* executes before self-clear */
ulong timeout; /* wait_for timeout */
@@ -82,8 +83,6 @@ struct st_debug_sync_globals
};
static st_debug_sync_globals debug_sync_global; /* All globals in one object */
-extern uint opt_debug_sync_timeout;
-
/**
Callbacks from C files.
*/
@@ -112,14 +111,11 @@ static void init_debug_sync_psi_keys(void)
const char* category= "sql";
int count;
- if (PSI_server == NULL)
- return;
-
count= array_elements(all_debug_sync_mutexes);
- PSI_server->register_mutex(category, all_debug_sync_mutexes, count);
+ mysql_mutex_register(category, all_debug_sync_mutexes, count);
count= array_elements(all_debug_sync_conds);
- PSI_server->register_cond(category, all_debug_sync_conds, count);
+ mysql_cond_register(category, all_debug_sync_conds, count);
}
#endif /* HAVE_PSI_INTERFACE */
@@ -239,7 +235,8 @@ void debug_sync_init_thread(THD *thd)
if (opt_debug_sync_timeout)
{
thd->debug_sync_control= (st_debug_sync_control*)
- my_malloc(sizeof(st_debug_sync_control), MYF(MY_WME | MY_ZEROFILL));
+ my_malloc(sizeof(st_debug_sync_control),
+ MYF(MY_WME | MY_ZEROFILL | MY_THREAD_SPECIFIC));
if (!thd->debug_sync_control)
{
/*
@@ -740,7 +737,7 @@ static bool debug_sync_set_action(THD *thd, st_debug_sync_action *action)
DBUG_ASSERT(action);
DBUG_ASSERT(ds_control);
- action->activation_count= max(action->hit_limit, action->execute);
+ action->activation_count= MY_MAX(action->hit_limit, action->execute);
if (!action->activation_count)
{
debug_sync_remove_action(ds_control, action);
@@ -782,7 +779,7 @@ static bool debug_sync_set_action(THD *thd, st_debug_sync_action *action)
point decremented it to 0. In this case the following happened:
- an error message was reported with my_error() and
- - the statement was killed with thd->killed= KILL_QUERY.
+ - the statement was killed with thd->killed= THD::KILL_QUERY.
If a statement reports an error, it must not call send_ok().
The calling functions will not call send_ok(), if we return TRUE
@@ -984,6 +981,7 @@ static bool debug_sync_eval_action(THD *thd, char *action_str)
DBUG_ENTER("debug_sync_eval_action");
DBUG_ASSERT(thd);
DBUG_ASSERT(action_str);
+ DBUG_PRINT("debug_sync", ("action_str: '%s'", action_str));
/*
Get debug sync point name. Or a special command.
@@ -1396,8 +1394,9 @@ static void debug_sync_execute(THD *thd, st_debug_sync_action *action)
if (action->wait_for.length())
{
- mysql_mutex_t *old_mutex;
+ mysql_mutex_t *old_mutex= NULL;
mysql_cond_t *old_cond= NULL;
+ bool restore_current_mutex;
int error= 0;
struct timespec abstime;
@@ -1414,11 +1413,12 @@ static void debug_sync_execute(THD *thd, st_debug_sync_action *action)
{
old_mutex= thd->mysys_var->current_mutex;
old_cond= thd->mysys_var->current_cond;
+ restore_current_mutex = true;
thd->mysys_var->current_mutex= &debug_sync_global.ds_mutex;
thd->mysys_var->current_cond= &debug_sync_global.ds_cond;
}
else
- old_mutex= NULL;
+ restore_current_mutex = false;
set_timespec(abstime, action->timeout);
DBUG_EXECUTE("debug_sync_exec", {
@@ -1448,8 +1448,13 @@ static void debug_sync_execute(THD *thd, st_debug_sync_action *action)
sig_wait, sig_glob, error));});
if (error == ETIMEDOUT || error == ETIME)
{
- push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ // We should not make the statement fail, even if in strict mode.
+ const bool save_abort_on_warning= thd->abort_on_warning;
+ thd->abort_on_warning= false;
+ push_warning(thd, Sql_condition::WARN_LEVEL_WARN,
ER_DEBUG_SYNC_TIMEOUT, ER(ER_DEBUG_SYNC_TIMEOUT));
+ thd->abort_on_warning= save_abort_on_warning;
+ DBUG_EXECUTE_IF("debug_sync_abort_on_timeout", DBUG_ABORT(););
break;
}
error= 0;
@@ -1473,7 +1478,7 @@ static void debug_sync_execute(THD *thd, st_debug_sync_action *action)
is locked. (See comment in THD::exit_cond().)
*/
mysql_mutex_unlock(&debug_sync_global.ds_mutex);
- if (old_mutex)
+ if (restore_current_mutex)
{
mysql_mutex_lock(&thd->mysys_var->mutex);
thd->mysys_var->current_mutex= old_mutex;
@@ -1519,7 +1524,10 @@ static void debug_sync_execute(THD *thd, st_debug_sync_action *action)
static void debug_sync(THD *thd, const char *sync_point_name, size_t name_len)
{
if (!thd)
- thd= current_thd;
+ {
+ if (!(thd= current_thd))
+ return;
+ }
st_debug_sync_control *ds_control= thd->debug_sync_control;
st_debug_sync_action *action;
diff --git a/sql/debug_sync.h b/sql/debug_sync.h
index 4d29d6e7508..bf1b3167dbc 100644
--- a/sql/debug_sync.h
+++ b/sql/debug_sync.h
@@ -32,6 +32,9 @@ class THD;
#if defined(ENABLED_DEBUG_SYNC)
+/* Command line option --debug-sync-timeout. See mysqld.cc. */
+extern MYSQL_PLUGIN_IMPORT uint opt_debug_sync_timeout;
+
/* Default WAIT_FOR timeout if command line option is given without argument. */
#define DEBUG_SYNC_DEFAULT_WAIT_TIMEOUT 300
diff --git a/sql/derror.cc b/sql/derror.cc
index 33835992258..f19f73238fb 100644
--- a/sql/derror.cc
+++ b/sql/derror.cc
@@ -21,6 +21,7 @@
Read language depeneded messagefile
*/
+#include <my_global.h>
#include "sql_priv.h"
#include "unireg.h"
#include "derror.h"
@@ -76,7 +77,7 @@ bool init_errmessage(void)
&errmsgs, ER_ERROR_LAST - ER_ERROR_FIRST + 1) &&
!errmsgs)
{
- free(errmsgs);
+ my_free(errmsgs);
if (org_errmsgs)
{
@@ -99,7 +100,7 @@ bool init_errmessage(void)
}
}
else
- free(org_errmsgs); // Free old language
+ my_free(org_errmsgs); // Free old language
/* Register messages for use with my_error(). */
if (my_error_register(get_server_errmsgs, ER_ERROR_FIRST, ER_ERROR_LAST))
@@ -146,8 +147,8 @@ bool read_texts(const char *file_name, const char *language,
const char ***point, uint error_messages)
{
register uint i;
- uint count,funktpos,textcount;
- size_t length;
+ uint count,funktpos;
+ size_t offset, length;
File file;
char name[FN_REFLEN];
char lang_path[FN_REFLEN];
@@ -186,12 +187,11 @@ bool read_texts(const char *file_name, const char *language,
goto err;
funktpos=2;
if (head[0] != (uchar) 254 || head[1] != (uchar) 254 ||
- head[2] != 2 || head[3] != 1)
+ head[2] != 2 || head[3] != 3)
goto err; /* purecov: inspected */
- textcount=head[4];
error_message_charset_info= system_charset_info;
- length=uint2korr(head+6); count=uint2korr(head+8);
+ length=uint4korr(head+6); count=uint2korr(head+10);
if (count < error_messages)
{
@@ -203,7 +203,7 @@ Error message file '%s' had only %d error messages, but it should contain at lea
}
if (!(*point= (const char**)
- my_malloc((size_t) (length+count*sizeof(char*)),MYF(0))))
+ my_malloc((size_t) (MY_MAX(length,count*2)+count*sizeof(char*)),MYF(0))))
{
funktpos=3; /* purecov: inspected */
goto err; /* purecov: inspected */
@@ -212,18 +212,15 @@ Error message file '%s' had only %d error messages, but it should contain at lea
if (mysql_file_read(file, buff, (size_t) count*2, MYF(MY_NABP)))
goto err;
- for (i=0, pos= buff ; i< count ; i++)
+ for (i=0, offset=0, pos= buff ; i< count ; i++)
{
- (*point)[i]= (char*) buff+uint2korr(pos);
+ (*point)[i]= (char*) buff+offset;
+ offset+= uint2korr(pos);
pos+=2;
}
if (mysql_file_read(file, buff, length, MYF(MY_NABP)))
goto err;
- for (i=1 ; i < textcount ; i++)
- {
- point[i]= *point +uint2korr(head+10+i+i);
- }
(void) mysql_file_close(file, MYF(0));
i= check_error_mesg(file_name, *point);
diff --git a/sql/des_key_file.cc b/sql/des_key_file.cc
index b6b6f4536bc..ede2e9fa9d4 100644
--- a/sql/des_key_file.cc
+++ b/sql/des_key_file.cc
@@ -13,7 +13,7 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
-#include "my_global.h" // HAVE_*
+#include <my_global.h> // HAVE_*
#include "sql_priv.h"
#include "des_key_file.h" // st_des_keyschedule, st_des_keyblock
#include "log.h" // sql_print_error
diff --git a/sql/discover.cc b/sql/discover.cc
index b9dba92a780..82648e94bc5 100644
--- a/sql/discover.cc
+++ b/sql/discover.cc
@@ -21,6 +21,7 @@
Functions for discover of frm file from handler
*/
+#include <my_global.h>
#include "sql_priv.h"
#include "unireg.h"
#include "discover.h"
@@ -45,7 +46,7 @@
3 Could not allocate data for read. Could not read file
*/
-int readfrm(const char *name, uchar **frmdata, size_t *len)
+int readfrm(const char *name, const uchar **frmdata, size_t *len)
{
int error;
char index_file[FN_REFLEN];
@@ -70,13 +71,17 @@ int readfrm(const char *name, uchar **frmdata, size_t *len)
error= 2;
if (mysql_file_fstat(file, &state, MYF(0)))
goto err;
- read_len= (size_t)state.st_size;
+ read_len= (size_t)MY_MIN(FRM_MAX_SIZE, state.st_size); // safety
// Read whole frm file
error= 3;
- read_data= 0; // Nothing to free
- if (read_string(file, &read_data, read_len))
+ if (!(read_data= (uchar*)my_malloc(read_len, MYF(MY_WME))))
goto err;
+ if (mysql_file_read(file, read_data, read_len, MYF(MY_NABP)))
+ {
+ my_free(read_data);
+ goto err;
+ }
// Setup return data
*frmdata= (uchar*) read_data;
@@ -96,7 +101,7 @@ int readfrm(const char *name, uchar **frmdata, size_t *len)
Write the content of a frm data pointer
to a frm file.
- @param name path to table-file "db/name"
+ @param path path to table-file "db/name"
@param frmdata frm data
@param len length of the frmdata
@@ -106,29 +111,160 @@ int readfrm(const char *name, uchar **frmdata, size_t *len)
2 Could not write file
*/
-int writefrm(const char *name, const uchar *frmdata, size_t len)
+int writefrm(const char *path, const char *db, const char *table,
+ bool tmp_table, const uchar *frmdata, size_t len)
{
- File file;
- char index_file[FN_REFLEN];
+ char file_name[FN_REFLEN+1];
int error;
+ int create_flags= O_RDWR | O_TRUNC;
DBUG_ENTER("writefrm");
- DBUG_PRINT("enter",("name: '%s' len: %lu ",name, (ulong) len));
+ DBUG_PRINT("enter",("name: '%s' len: %lu ",path, (ulong) len));
- error= 0;
- if ((file= mysql_file_create(key_file_frm,
- fn_format(index_file, name, "", reg_ext,
- MY_UNPACK_FILENAME | MY_APPEND_EXT),
- CREATE_MODE, O_RDWR | O_TRUNC,
- MYF(MY_WME))) >= 0)
+ if (tmp_table)
+ create_flags|= O_EXCL | O_NOFOLLOW;
+
+ strxnmov(file_name, sizeof(file_name)-1, path, reg_ext, NullS);
+
+ File file= mysql_file_create(key_file_frm, file_name,
+ CREATE_MODE, create_flags, MYF(0));
+
+ if ((error= file < 0))
{
- if (mysql_file_write(file, frmdata, len, MYF(MY_WME | MY_NABP)))
- error= 2;
- (void) mysql_file_close(file, MYF(0));
+ if (my_errno == ENOENT)
+ my_error(ER_BAD_DB_ERROR, MYF(0), db);
+ else
+ my_error(ER_CANT_CREATE_TABLE, MYF(0), db, table, my_errno);
+ }
+ else
+ {
+ error= mysql_file_write(file, frmdata, len, MYF(MY_WME | MY_NABP));
+
+ if (!error && !tmp_table && opt_sync_frm)
+ error= mysql_file_sync(file, MYF(MY_WME)) ||
+ my_sync_dir_by_file(file_name, MYF(MY_WME));
+
+ error|= mysql_file_close(file, MYF(MY_WME));
}
DBUG_RETURN(error);
} /* writefrm */
+static inline void advance(FILEINFO* &from, FILEINFO* &to,
+ FILEINFO* cur, bool &skip)
+{
+ if (skip) // if not copying
+ from= cur; // just advance the start pointer
+ else // if copying
+ if (to == from) // but to the same place (not shifting the data)
+ from= to= cur; // advance both pointers
+ else // otherwise
+ while (from < cur) // have to copy [from...cur) to [to...)
+ *to++ = *from++;
+ skip= false;
+}
+
+/**
+ Go through the directory listing looking for files with a specified
+ extension and add them to the result list
+
+ @details
+ This function may be called many times on the same directory listing
+ but with different extensions. To avoid discovering the same table twice,
+ whenever a table file is discovered, all files with the same name
+ (independently from the extensions) are removed from the list.
+ Example: the list contained
+ { "db.opt", "t1.MYD", "t1.MYI", "t1.frm", "t2.ARZ", "t3.ARZ", "t3.frm" }
+ on discovering all ".frm" files, tables "t1" and "t3" will be found,
+ and list will become
+ { "db.opt", "t2.ARZ" }
+ and now ".ARZ" discovery can discover the table "t2"
+ @note
+ This function assumes that the directory listing is sorted alphabetically.
+ @note Partitioning makes this more complicated. A partitioned table t1 might
+ have files, like t1.frm, t1#P#part1.ibd, t1#P#foo.ibd, etc.
+ That means we need to compare file names only up to the first '#' or '.'
+ whichever comes first.
+*/
+int extension_based_table_discovery(MY_DIR *dirp, const char *ext_meta,
+ handlerton::discovered_list *result)
+{
+ CHARSET_INFO *cs= character_set_filesystem;
+ size_t ext_meta_len= strlen(ext_meta);
+ FILEINFO *from, *to, *cur, *end;
+ bool skip= false;
+
+ from= to= cur= dirp->dir_entry;
+ end= cur + dirp->number_of_files;
+ while (cur < end)
+ {
+ char *octothorp= strrchr(cur->name + 1, '#');
+ char *ext= strchr(octothorp ? octothorp : cur->name, FN_EXTCHAR);
+
+ if (ext)
+ {
+ size_t len= (octothorp ? octothorp : ext) - cur->name;
+ if (from != cur &&
+ (my_strnncoll(cs, (uchar*)from->name, len, (uchar*)cur->name, len) ||
+ (from->name[len] != FN_EXTCHAR && from->name[len] != '#')))
+ advance(from, to, cur, skip);
+
+ if (my_strnncoll(cs, (uchar*)ext, strlen(ext),
+ (uchar*)ext_meta, ext_meta_len) == 0)
+ {
+ *ext = 0;
+ if (result->add_file(cur->name))
+ return 1;
+ *ext = FN_EXTCHAR;
+ skip= true; // table discovered, skip all files with the same name
+ }
+ }
+ else
+ {
+ advance(from, to, cur, skip);
+ from++;
+ }
+
+ cur++;
+ }
+ advance(from, to, cur, skip);
+ dirp->number_of_files= to - dirp->dir_entry;
+ return 0;
+}
+
+/**
+ Simple, not reusable file-based table discovery
+
+ @details
+ simplified version of extension_based_table_discovery(), that does not
+ modify the list of files. It cannot be called many times for the same
+ directory listing, otherwise it'll produce duplicate results.
+*/
+int ext_table_discovery_simple(MY_DIR *dirp,
+ handlerton::discovered_list *result)
+{
+ CHARSET_INFO *cs= character_set_filesystem;
+ FILEINFO *cur, *end;
+
+ cur= dirp->dir_entry;
+ end= cur + dirp->number_of_files;
+ while (cur < end)
+ {
+ char *ext= strrchr(cur->name, FN_EXTCHAR);
+
+ if (ext)
+ {
+ if (my_strnncoll(cs, (uchar*)ext, strlen(ext),
+ (uchar*)reg_ext, reg_ext_length) == 0)
+ {
+ *ext = 0;
+ if (result->add_file(cur->name))
+ return 1;
+ }
+ }
+ cur++;
+ }
+ return 0;
+}
diff --git a/sql/discover.h b/sql/discover.h
index a663e44128d..e1508107235 100644
--- a/sql/discover.h
+++ b/sql/discover.h
@@ -18,7 +18,24 @@
#include "my_global.h" /* uchar */
-int readfrm(const char *name, uchar **data, size_t *length);
-int writefrm(const char* name, const uchar* data, size_t len);
+int extension_based_table_discovery(MY_DIR *dirp, const char *ext,
+ handlerton::discovered_list *tl);
+
+#ifdef MYSQL_SERVER
+int readfrm(const char *name, const uchar **data, size_t *length);
+int writefrm(const char *path, const char *db, const char *table,
+ bool tmp_table, const uchar *frmdata, size_t len);
+
+/* a helper to delete an frm file, given a path w/o .frm extension */
+inline void deletefrm(const char *path)
+{
+ char frm_name[FN_REFLEN];
+ strxmov(frm_name, path, reg_ext, NullS);
+ mysql_file_delete(key_file_frm, frm_name, MYF(0));
+}
+
+int ext_table_discovery_simple(MY_DIR *dirp,
+ handlerton::discovered_list *result);
+#endif
#endif /* DISCOVER_INCLUDED */
diff --git a/sql/event_data_objects.cc b/sql/event_data_objects.cc
index 656881b9977..e7bdc42b2e6 100644
--- a/sql/event_data_objects.cc
+++ b/sql/event_data_objects.cc
@@ -15,7 +15,7 @@
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
#define MYSQL_LEX 1
-#include "my_global.h" /* NO_EMBEDDED_ACCESS_CHECKS */
+#include <my_global.h> /* NO_EMBEDDED_ACCESS_CHECKS */
#include "sql_priv.h"
#include "unireg.h"
#include "sql_parse.h" // parse_sql
@@ -209,7 +209,7 @@ Event_basic::Event_basic()
{
DBUG_ENTER("Event_basic::Event_basic");
/* init memory root */
- init_sql_alloc(&mem_root, 256, 512);
+ init_sql_alloc(&mem_root, 256, 512, MYF(0));
dbname.str= name.str= NULL;
dbname.length= name.length= 0;
time_zone= NULL;
@@ -608,7 +608,7 @@ Event_timed::load_from_row(THD *thd, TABLE *table)
table, &creation_ctx))
{
push_warning_printf(thd,
- MYSQL_ERROR::WARN_LEVEL_WARN,
+ Sql_condition::WARN_LEVEL_WARN,
ER_EVENT_INVALID_CREATION_CTX,
ER(ER_EVENT_INVALID_CREATION_CTX),
(const char *) dbname.str,
@@ -1462,30 +1462,36 @@ end:
NOTE: even if we run in read-only mode, we should be able to lock
the mysql.event table for writing. In order to achieve this, we
should call mysql_lock_tables() under the super-user.
+
+ Same goes for transaction access mode.
+ Temporarily reset it to read-write.
*/
saved_master_access= thd->security_ctx->master_access;
thd->security_ctx->master_access |= SUPER_ACL;
+ bool save_tx_read_only= thd->tx_read_only;
+ thd->tx_read_only= false;
#ifdef WITH_WSREP
if (WSREP(thd)) {
- // sql_print_information("sizeof(LEX) = %d", sizeof(struct LEX));
- // sizeof(LEX) = 4512, so it's relatively safe to allocate it on stack.
- LEX lex;
- lex.sql_command = SQLCOM_DROP_EVENT;
- LEX* saved = thd->lex;
- thd->lex = &lex;
- WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL);
- thd->lex = saved;
+ // sql_print_information("sizeof(LEX) = %d", sizeof(struct LEX));
+ // sizeof(LEX) = 4512, so it's relatively safe to allocate it on stack.
+ LEX lex;
+ LEX* saved = thd->lex;
+ lex.sql_command = SQLCOM_DROP_EVENT;
+ thd->lex = &lex;
+ WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL);
+ thd->lex = saved;
}
#endif
-
+
ret= Events::drop_event(thd, dbname, name, FALSE);
#ifdef WITH_WSREP
WSREP_TO_ISOLATION_END;
error:
#endif
+ thd->tx_read_only= save_tx_read_only;
thd->security_ctx->master_access= saved_master_access;
}
}
diff --git a/sql/event_db_repository.cc b/sql/event_db_repository.cc
index 37dff0da714..30dffc30edd 100644
--- a/sql/event_db_repository.cc
+++ b/sql/event_db_repository.cc
@@ -14,6 +14,7 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+#include <my_global.h>
#include "sql_priv.h"
#include "unireg.h"
#include "sql_base.h" // close_thread_tables
@@ -163,7 +164,7 @@ const TABLE_FIELD_TYPE event_table_fields[ET_FIELD_COUNT] =
};
static const TABLE_FIELD_DEF
- event_table_def= {ET_FIELD_COUNT, event_table_fields};
+event_table_def= {ET_FIELD_COUNT, event_table_fields, 0, (uint*) 0};
class Event_db_intact : public Table_check_intact
{
@@ -222,7 +223,8 @@ mysql_event_fill_row(THD *thd,
Safety: this can only happen if someone started the server
and then altered mysql.event.
*/
- my_error(ER_COL_COUNT_DOESNT_MATCH_CORRUPTED, MYF(0), table->alias.c_ptr(),
+ my_error(ER_COL_COUNT_DOESNT_MATCH_CORRUPTED_V2, MYF(0),
+ table->s->db.str, table->alias.c_ptr(),
(int) ET_FIELD_COUNT, table->s->fields);
DBUG_RETURN(TRUE);
}
@@ -428,11 +430,11 @@ Event_db_repository::index_read_for_db_for_i_s(THD *thd, TABLE *schema_table,
key_info= event_table->key_info;
- if (key_info->key_parts == 0 ||
+ if (key_info->user_defined_key_parts == 0 ||
key_info->key_part[0].field != event_table->field[ET_FIELD_DB])
{
/* Corrupted table: no index or index on a wrong column */
- my_error(ER_CANNOT_LOAD_FROM_TABLE, MYF(0), "event");
+ my_error(ER_CANNOT_LOAD_FROM_TABLE_V2, MYF(0), "mysql", "event");
ret= 1;
goto end;
}
@@ -473,7 +475,7 @@ Event_db_repository::index_read_for_db_for_i_s(THD *thd, TABLE *schema_table,
end:
event_table->file->ha_index_end();
- DBUG_RETURN(test(ret));
+ DBUG_RETURN(MY_TEST(ret));
}
@@ -686,7 +688,7 @@ Event_db_repository::create_event(THD *thd, Event_parse_data *parse_data,
if (create_if_not)
{
*event_already_exists= true;
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
ER_EVENT_ALREADY_EXISTS, ER(ER_EVENT_ALREADY_EXISTS),
parse_data->name.str);
ret= 0;
@@ -745,7 +747,7 @@ end:
thd->mdl_context.rollback_to_savepoint(mdl_savepoint);
thd->variables.sql_mode= saved_mode;
- DBUG_RETURN(test(ret));
+ DBUG_RETURN(MY_TEST(ret));
}
@@ -835,9 +837,6 @@ Event_db_repository::update_event(THD *thd, Event_parse_data *parse_data,
(int) table->field[ET_FIELD_ON_COMPLETION]->val_int()))
goto end;
- /* Don't update create on row update. */
- table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
-
/*
mysql_event_fill_row() calls my_error() in case of error so no need to
handle it here
@@ -863,7 +862,7 @@ end:
thd->mdl_context.rollback_to_savepoint(mdl_savepoint);
thd->variables.sql_mode= saved_mode;
- DBUG_RETURN(test(ret));
+ DBUG_RETURN(MY_TEST(ret));
}
@@ -914,7 +913,7 @@ Event_db_repository::drop_event(THD *thd, LEX_STRING db, LEX_STRING name,
goto end;
}
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
ER_SP_DOES_NOT_EXIST, ER(ER_SP_DOES_NOT_EXIST),
"Event", name.str);
ret= 0;
@@ -923,7 +922,7 @@ end:
close_thread_tables(thd);
thd->mdl_context.rollback_to_savepoint(mdl_savepoint);
- DBUG_RETURN(test(ret));
+ DBUG_RETURN(MY_TEST(ret));
}
@@ -960,7 +959,7 @@ Event_db_repository::find_named_event(LEX_STRING db, LEX_STRING name,
if (db.length > table->field[ET_FIELD_DB]->field_length ||
name.length > table->field[ET_FIELD_NAME]->field_length ||
table->s->keys == 0 ||
- table->key_info[0].key_parts != 2 ||
+ table->key_info[0].user_defined_key_parts != 2 ||
table->key_info[0].key_part[0].fieldnr != ET_FIELD_DB+1 ||
table->key_info[0].key_part[1].fieldnr != ET_FIELD_NAME+1)
DBUG_RETURN(TRUE);
@@ -1090,7 +1089,7 @@ Event_db_repository::load_named_event(THD *thd, LEX_STRING dbname,
if ((ret= find_named_event(dbname, name, event_table.table)))
my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), name.str);
else if ((ret= etn->load_from_row(thd, event_table.table)))
- my_error(ER_CANNOT_LOAD_FROM_TABLE, MYF(0), "event");
+ my_error(ER_CANNOT_LOAD_FROM_TABLE_V2, MYF(0), "mysql", "event");
close_system_tables(thd, &open_tables_backup);
}
@@ -1118,17 +1117,15 @@ update_timing_fields_for_event(THD *thd,
TABLE *table= NULL;
Field **fields;
int ret= 1;
- bool save_binlog_row_based;
+ enum_binlog_format save_binlog_format;
MYSQL_TIME time;
-
DBUG_ENTER("Event_db_repository::update_timing_fields_for_event");
/*
Turn off row binlogging of event timing updates. These are not used
for RBR of events replicated to the slave.
*/
- if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
- thd->clear_current_stmt_binlog_format_row();
+ save_binlog_format= thd->set_current_stmt_binlog_format_stmt();
DBUG_ASSERT(thd->security_ctx->master_access & SUPER_ACL);
@@ -1141,8 +1138,6 @@ update_timing_fields_for_event(THD *thd,
goto end;
store_record(table, record[1]);
- /* Don't update create on row update. */
- table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
my_tz_OFFSET0->gmt_sec_to_TIME(&time, last_executed);
fields[ET_FIELD_LAST_EXECUTED]->set_notnull();
@@ -1163,12 +1158,9 @@ end:
if (table)
close_mysql_tables(thd);
- /* Restore the state of binlog format */
- DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
- if (save_binlog_row_based)
- thd->set_current_stmt_binlog_format_row();
+ thd->restore_stmt_binlog_format(save_binlog_format);
- DBUG_RETURN(test(ret));
+ DBUG_RETURN(MY_TEST(ret));
}
@@ -1246,7 +1238,7 @@ Event_db_repository::check_system_tables(THD *thd)
close_mysql_tables(thd);
}
- DBUG_RETURN(test(ret));
+ DBUG_RETURN(MY_TEST(ret));
}
/**
diff --git a/sql/event_parse_data.cc b/sql/event_parse_data.cc
index ad812a6aa5d..56c6c3cc13c 100644
--- a/sql/event_parse_data.cc
+++ b/sql/event_parse_data.cc
@@ -14,6 +14,7 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+#include <my_global.h>
#include "sql_priv.h"
#include "unireg.h"
#include "sp_head.h"
@@ -126,7 +127,7 @@ Event_parse_data::check_if_in_the_past(THD *thd, my_time_t ltime_utc)
{
switch (thd->lex->sql_command) {
case SQLCOM_CREATE_EVENT:
- push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
+ push_warning(thd, Sql_condition::WARN_LEVEL_NOTE,
ER_EVENT_CANNOT_CREATE_IN_THE_PAST,
ER(ER_EVENT_CANNOT_CREATE_IN_THE_PAST));
break;
@@ -143,7 +144,7 @@ Event_parse_data::check_if_in_the_past(THD *thd, my_time_t ltime_utc)
{
status= Event_parse_data::DISABLED;
status_changed= true;
- push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
+ push_warning(thd, Sql_condition::WARN_LEVEL_NOTE,
ER_EVENT_EXEC_TIME_IN_THE_PAST,
ER(ER_EVENT_EXEC_TIME_IN_THE_PAST));
}
@@ -574,8 +575,8 @@ void Event_parse_data::check_originator_id(THD *thd)
status= Event_parse_data::SLAVESIDE_DISABLED;
status_changed= true;
}
- originator = thd->server_id;
+ originator = thd->variables.server_id;
}
else
- originator = server_id;
+ originator = global_system_variables.server_id;
}
diff --git a/sql/event_queue.cc b/sql/event_queue.cc
index 40fddff094c..35187af23ac 100644
--- a/sql/event_queue.cc
+++ b/sql/event_queue.cc
@@ -10,9 +10,10 @@
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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+ along with this program; if not, write to the Free Software Foundation,
+ 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */
+#include <my_global.h>
#include "sql_priv.h"
#include "unireg.h"
#include "event_queue.h"
@@ -559,9 +560,6 @@ Event_queue::dbug_dump_queue(my_time_t when)
#endif
}
-static const char *queue_empty_msg= "Waiting on empty queue";
-static const char *queue_wait_msg= "Waiting for next activation";
-
/*
Checks whether the top of the queue is elligible for execution and
returns an Event_job_data instance in case it should be executed.
@@ -608,7 +606,7 @@ Event_queue::get_top_for_execution_if_time(THD *thd,
mysql_audit_release(thd);
/* Wait on condition until signaled. Release LOCK_queue while waiting. */
- cond_wait(thd, NULL, queue_empty_msg, SCHED_FUNC, __LINE__);
+ cond_wait(thd, NULL, & stage_waiting_on_empty_queue, SCHED_FUNC, __FILE__, __LINE__);
continue;
}
@@ -629,7 +627,8 @@ Event_queue::get_top_for_execution_if_time(THD *thd,
/* Release any held audit resources before waiting */
mysql_audit_release(thd);
- cond_wait(thd, &top_time, queue_wait_msg, SCHED_FUNC, __LINE__);
+ cond_wait(thd, &top_time, &stage_waiting_for_next_activation, SCHED_FUNC, __FILE__, __LINE__);
+
continue;
}
@@ -759,16 +758,16 @@ Event_queue::unlock_data(const char *func, uint line)
*/
void
-Event_queue::cond_wait(THD *thd, struct timespec *abstime, const char* msg,
- const char *func, uint line)
+Event_queue::cond_wait(THD *thd, struct timespec *abstime, const PSI_stage_info *stage,
+ const char *src_func, const char *src_file, uint src_line)
{
DBUG_ENTER("Event_queue::cond_wait");
waiting_on_cond= TRUE;
- mutex_last_unlocked_at_line= line;
+ mutex_last_unlocked_at_line= src_line;
mutex_queue_data_locked= FALSE;
- mutex_last_unlocked_in_func= func;
+ mutex_last_unlocked_in_func= src_func;
- thd->enter_cond(&COND_queue_state, &LOCK_event_queue, msg);
+ thd->enter_cond(&COND_queue_state, &LOCK_event_queue, stage, NULL, src_func, src_file, src_line);
if (!thd->killed)
{
@@ -779,8 +778,8 @@ Event_queue::cond_wait(THD *thd, struct timespec *abstime, const char* msg,
mysql_cond_timedwait(&COND_queue_state, &LOCK_event_queue, abstime);
}
- mutex_last_locked_in_func= func;
- mutex_last_locked_at_line= line;
+ mutex_last_locked_in_func= src_func;
+ mutex_last_locked_at_line= src_line;
mutex_queue_data_locked= TRUE;
waiting_on_cond= FALSE;
@@ -788,8 +787,8 @@ Event_queue::cond_wait(THD *thd, struct timespec *abstime, const char* msg,
This will free the lock so we need to relock. Not the best thing to
do but we need to obey cond_wait()
*/
- thd->exit_cond("");
- lock_data(func, line);
+ thd->exit_cond(NULL, src_func, src_file, src_line);
+ lock_data(src_func, src_line);
DBUG_VOID_RETURN;
}
diff --git a/sql/event_queue.h b/sql/event_queue.h
index affa306b259..fdd5937ee17 100644
--- a/sql/event_queue.h
+++ b/sql/event_queue.h
@@ -12,8 +12,8 @@
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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+ along with this program; if not, write to the Free Software Foundation,
+ 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */
/**
@@ -94,8 +94,8 @@ private:
unlock_data(const char *func, uint line);
void
- cond_wait(THD *thd, struct timespec *abstime, const char* msg,
- const char *func, uint line);
+ cond_wait(THD *thd, struct timespec *abstime, const PSI_stage_info *stage,
+ const char *src_func, const char *src_file, uint src_line);
void
find_n_remove_event(LEX_STRING db, LEX_STRING name);
diff --git a/sql/event_scheduler.cc b/sql/event_scheduler.cc
index beb3c864662..5c4926c830c 100644
--- a/sql/event_scheduler.cc
+++ b/sql/event_scheduler.cc
@@ -1,4 +1,4 @@
-/* Copyright (c) 2006, 2013, Oracle and/or its affiliates. All rights reserved.
+/* Copyright (c) 2006, 2013, Oracle and/or its affiliates.
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
@@ -13,6 +13,7 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+#include <my_global.h>
#include "sql_priv.h"
#include "unireg.h"
#include "event_scheduler.h"
@@ -38,8 +39,8 @@
#define LOCK_DATA() lock_data(SCHED_FUNC, __LINE__)
#define UNLOCK_DATA() unlock_data(SCHED_FUNC, __LINE__)
-#define COND_STATE_WAIT(mythd, abstime, msg) \
- cond_wait(mythd, abstime, msg, SCHED_FUNC, __LINE__)
+#define COND_STATE_WAIT(mythd, abstime, stage) \
+ cond_wait(mythd, abstime, stage, SCHED_FUNC, __FILE__, __LINE__)
extern pthread_attr_t connection_attrib;
extern ulong event_executed;
@@ -75,9 +76,9 @@ struct scheduler_param {
void
Event_worker_thread::print_warnings(THD *thd, Event_job_data *et)
{
- MYSQL_ERROR *err;
+ const Sql_condition *err;
DBUG_ENTER("evex_print_warnings");
- if (thd->warning_info->is_empty())
+ if (thd->get_stmt_da()->is_warning_info_empty())
DBUG_VOID_RETURN;
char msg_buf[10 * STRING_BUFFER_USUAL_SIZE];
@@ -93,7 +94,8 @@ Event_worker_thread::print_warnings(THD *thd, Event_job_data *et)
prefix.append(et->name.str, et->name.length, system_charset_info);
prefix.append("] ", 2);
- List_iterator_fast<MYSQL_ERROR> it(thd->warning_info->warn_list());
+ Diagnostics_area::Sql_condition_iterator it=
+ thd->get_stmt_da()->sql_conditions();
while ((err= it++))
{
String err_msg(msg_buf, sizeof(msg_buf), system_charset_info);
@@ -132,11 +134,11 @@ post_init_event_thread(THD *thd)
return TRUE;
}
+ thread_safe_increment32(&thread_count, &thread_count_lock);
mysql_mutex_lock(&LOCK_thread_count);
threads.append(thd);
- thread_count++;
- inc_thread_running();
mysql_mutex_unlock(&LOCK_thread_count);
+ inc_thread_running();
return FALSE;
}
@@ -153,15 +155,9 @@ void
deinit_event_thread(THD *thd)
{
thd->proc_info= "Clearing";
- DBUG_ASSERT(thd->net.buff != 0);
- net_end(&thd->net);
DBUG_PRINT("exit", ("Event thread finishing"));
- mysql_mutex_lock(&LOCK_thread_count);
- thread_count--;
- dec_thread_running();
- delete thd;
- mysql_cond_broadcast(&COND_thread_count);
- mysql_mutex_unlock(&LOCK_thread_count);
+
+ delete_running_thd(thd);
}
@@ -182,15 +178,17 @@ deinit_event_thread(THD *thd)
void
pre_init_event_thread(THD* thd)
{
+ THD *orig_thd= current_thd;
DBUG_ENTER("pre_init_event_thread");
+
+ set_current_thd(thd);
thd->client_capabilities= 0;
thd->security_ctx->master_access= 0;
thd->security_ctx->db_access= 0;
thd->security_ctx->host_or_ip= (char*)my_localhost;
- my_net_init(&thd->net, NULL);
+ my_net_init(&thd->net, NULL, MYF(MY_THREAD_SPECIFIC));
thd->security_ctx->set_user((char*)"event_scheduler");
thd->net.read_timeout= slave_net_timeout;
- thd->slave_thread= 0;
thd->variables.option_bits|= OPTION_AUTO_IS_NULL;
thd->client_capabilities|= CLIENT_MULTI_RESULTS;
mysql_mutex_lock(&LOCK_thread_count);
@@ -208,6 +206,7 @@ pre_init_event_thread(THD* thd)
/* Do not use user-supplied timeout value for system threads. */
thd->variables.lock_wait_timeout= LONG_TIMEOUT;
+ set_current_thd(orig_thd);
DBUG_VOID_RETURN;
}
@@ -302,6 +301,9 @@ Event_worker_thread::run(THD *thd, Event_queue_element_for_exec *event)
Event_job_data job_data;
bool res;
+ DBUG_ASSERT(thd->m_digest == NULL);
+ DBUG_ASSERT(thd->m_statement_psi == NULL);
+
thd->thread_stack= &my_stack; // remember where our stack is
res= post_init_event_thread(thd);
@@ -330,6 +332,8 @@ Event_worker_thread::run(THD *thd, Event_queue_element_for_exec *event)
job_data.definer.str,
job_data.dbname.str, job_data.name.str);
end:
+ DBUG_ASSERT(thd->m_statement_psi == NULL);
+ DBUG_ASSERT(thd->m_digest == NULL);
DBUG_PRINT("info", ("Done with Event %s.%s", event->dbname.str,
event->name.str));
@@ -402,17 +406,23 @@ Event_scheduler::start(int *err_no)
ret= true;
goto end;
}
+
pre_init_event_thread(new_thd);
new_thd->system_thread= SYSTEM_THREAD_EVENT_SCHEDULER;
- new_thd->command= COM_DAEMON;
+ new_thd->set_command(COM_DAEMON);
/*
We should run the event scheduler thread under the super-user privileges.
In particular, this is needed to be able to lock the mysql.event table
for writing when the server is running in the read-only mode.
+
+ Same goes for transaction access mode. Set it to read-write for this thd.
*/
new_thd->security_ctx->master_access |= SUPER_ACL;
+ new_thd->variables.tx_read_only= false;
+ new_thd->tx_read_only= false;
+ /* This should not be marked with MY_THREAD_SPECIFIC */
scheduler_param_value=
(struct scheduler_param *)my_malloc(sizeof(struct scheduler_param), MYF(0));
scheduler_param_value->thd= new_thd;
@@ -535,6 +545,7 @@ Event_scheduler::execute_top(Event_queue_element_for_exec *event_name)
pthread_t th;
int res= 0;
DBUG_ENTER("Event_scheduler::execute_top");
+
if (!(new_thd= new THD()))
goto error;
@@ -636,7 +647,7 @@ Event_scheduler::stop()
{
/* Synchronously wait until the scheduler stops. */
while (state != INITIALIZED)
- COND_STATE_WAIT(thd, NULL, "Waiting for the scheduler to stop");
+ COND_STATE_WAIT(thd, NULL, &stage_waiting_for_scheduler_to_stop);
goto end;
}
@@ -678,7 +689,7 @@ Event_scheduler::stop()
*/
struct timespec top_time;
set_timespec(top_time, 2);
- COND_STATE_WAIT(thd, &top_time, "Waiting scheduler to stop");
+ COND_STATE_WAIT(thd, &top_time, &stage_waiting_for_scheduler_to_stop);
} while (state == STOPPING);
DBUG_PRINT("info", ("Scheduler thread has cleaned up. Set state to INIT"));
sql_print_information("Event Scheduler: Stopped");
@@ -772,16 +783,17 @@ Event_scheduler::unlock_data(const char *func, uint line)
*/
void
-Event_scheduler::cond_wait(THD *thd, struct timespec *abstime, const char* msg,
- const char *func, uint line)
+Event_scheduler::cond_wait(THD *thd, struct timespec *abstime, const PSI_stage_info *stage,
+ const char *src_func, const char *src_file, uint src_line)
{
DBUG_ENTER("Event_scheduler::cond_wait");
waiting_on_cond= TRUE;
- mutex_last_unlocked_at_line= line;
+ mutex_last_unlocked_at_line= src_line;
mutex_scheduler_data_locked= FALSE;
- mutex_last_unlocked_in_func= func;
+ mutex_last_unlocked_in_func= src_func;
if (thd)
- thd->enter_cond(&COND_state, &LOCK_scheduler_state, msg);
+ thd->enter_cond(&COND_state, &LOCK_scheduler_state, stage,
+ NULL, src_func, src_file, src_line);
DBUG_PRINT("info", ("mysql_cond_%swait", abstime? "timed":""));
if (!abstime)
@@ -794,11 +806,11 @@ Event_scheduler::cond_wait(THD *thd, struct timespec *abstime, const char* msg,
This will free the lock so we need to relock. Not the best thing to
do but we need to obey cond_wait()
*/
- thd->exit_cond("");
+ thd->exit_cond(NULL, src_func, src_file, src_line);
LOCK_DATA();
}
- mutex_last_locked_in_func= func;
- mutex_last_locked_at_line= line;
+ mutex_last_locked_in_func= src_func;
+ mutex_last_locked_at_line= src_line;
mutex_scheduler_data_locked= TRUE;
waiting_on_cond= FALSE;
DBUG_VOID_RETURN;
diff --git a/sql/event_scheduler.h b/sql/event_scheduler.h
index 538514b8b12..6ec7dccefb9 100644
--- a/sql/event_scheduler.h
+++ b/sql/event_scheduler.h
@@ -12,8 +12,8 @@
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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+ along with this program; if not, write to the Free Software Foundation,
+ 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */
/**
@addtogroup Event_Scheduler
@@ -114,8 +114,8 @@ private:
unlock_data(const char *func, uint line);
void
- cond_wait(THD *thd, struct timespec *abstime, const char* msg,
- const char *func, uint line);
+ cond_wait(THD *thd, struct timespec *abstime, const PSI_stage_info *stage,
+ const char *src_func, const char *src_file, uint src_line);
mysql_mutex_t LOCK_scheduler_state;
diff --git a/sql/events.cc b/sql/events.cc
index 9e15a5f47bd..41f6aac5d64 100644
--- a/sql/events.cc
+++ b/sql/events.cc
@@ -14,6 +14,7 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+#include <my_global.h>
#include "sql_priv.h"
#include "unireg.h"
#include "sql_parse.h" // check_access
@@ -79,7 +80,8 @@ Event_queue *Events::event_queue;
Event_scheduler *Events::scheduler;
Event_db_repository *Events::db_repository;
ulong Events::opt_event_scheduler= Events::EVENTS_OFF;
-bool Events::check_system_tables_error= FALSE;
+ulong Events::startup_state= Events::EVENTS_OFF;
+ulong Events::inited;
/*
@@ -113,7 +115,7 @@ bool Events::check_if_system_tables_error()
{
DBUG_ENTER("Events::check_if_system_tables_error");
- if (check_system_tables_error)
+ if (!inited)
{
my_error(ER_EVENTS_DB_ERROR, MYF(0));
DBUG_RETURN(TRUE);
@@ -256,10 +258,10 @@ common_1_lev_code:
/**
- Create a new query string for removing executable comments
- for avoiding leak and keeping consistency of the execution
+ Create a new query string for removing executable comments
+ for avoiding leak and keeping consistency of the execution
on master and slave.
-
+
@param[in] thd Thread handler
@param[in] buf Query string
@@ -280,7 +282,7 @@ create_query_string(THD *thd, String *buf)
thd->lex->stmt_definition_end -
thd->lex->stmt_definition_begin))
return 1;
-
+
return 0;
}
@@ -306,7 +308,8 @@ Events::create_event(THD *thd, Event_parse_data *parse_data,
bool if_not_exists)
{
bool ret;
- bool save_binlog_row_based, event_already_exists;
+ bool event_already_exists;
+ enum_binlog_format save_binlog_format;
DBUG_ENTER("Events::create_event");
if (check_if_system_tables_error())
@@ -334,12 +337,11 @@ Events::create_event(THD *thd, Event_parse_data *parse_data,
if (parse_data->do_not_create)
DBUG_RETURN(FALSE);
- /*
- Turn off row binlogging of this statement and use statement-based
+ /*
+ Turn off row binlogging of this statement and use statement-based
so that all supporting tables are updated for CREATE EVENT command.
*/
- if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
- thd->clear_current_stmt_binlog_format_row();
+ save_binlog_format= thd->set_current_stmt_binlog_format_stmt();
if (lock_object_name(thd, MDL_key::EVENT,
parse_data->dbname.str, parse_data->name.str))
@@ -383,8 +385,10 @@ Events::create_event(THD *thd, Event_parse_data *parse_data,
String log_query;
if (create_query_string(thd, &log_query))
{
- sql_print_error("Event Error: An error occurred while creating query "
- "string, before writing it into binary log.");
+ my_message_sql(ER_STARTUP,
+ "Event Error: An error occurred while creating query "
+ "string, before writing it into binary log.",
+ MYF(ME_NOREFRESH));
ret= true;
}
else
@@ -398,10 +402,8 @@ Events::create_event(THD *thd, Event_parse_data *parse_data,
}
}
}
- /* Restore the state of binlog format */
- DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
- if (save_binlog_row_based)
- thd->set_current_stmt_binlog_format_row();
+
+ thd->restore_stmt_binlog_format(save_binlog_format);
DBUG_RETURN(ret);
}
@@ -431,7 +433,7 @@ Events::update_event(THD *thd, Event_parse_data *parse_data,
LEX_STRING *new_dbname, LEX_STRING *new_name)
{
int ret;
- bool save_binlog_row_based;
+ enum_binlog_format save_binlog_format;
Event_queue_element *new_element;
DBUG_ENTER("Events::update_event");
@@ -474,12 +476,11 @@ Events::update_event(THD *thd, Event_parse_data *parse_data,
}
}
- /*
- Turn off row binlogging of this statement and use statement-based
+ /*
+ Turn off row binlogging of this statement and use statement-based
so that all supporting tables are updated for UPDATE EVENT command.
*/
- if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
- thd->clear_current_stmt_binlog_format_row();
+ save_binlog_format= thd->set_current_stmt_binlog_format_stmt();
if (lock_object_name(thd, MDL_key::EVENT,
parse_data->dbname.str, parse_data->name.str))
@@ -513,11 +514,8 @@ Events::update_event(THD *thd, Event_parse_data *parse_data,
ret= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
}
}
- /* Restore the state of binlog format */
- DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
- if (save_binlog_row_based)
- thd->set_current_stmt_binlog_format_row();
+ thd->restore_stmt_binlog_format(save_binlog_format);
DBUG_RETURN(ret);
}
@@ -550,7 +548,7 @@ bool
Events::drop_event(THD *thd, LEX_STRING dbname, LEX_STRING name, bool if_exists)
{
int ret;
- bool save_binlog_row_based;
+ enum_binlog_format save_binlog_format;
DBUG_ENTER("Events::drop_event");
if (check_if_system_tables_error())
@@ -563,8 +561,7 @@ Events::drop_event(THD *thd, LEX_STRING dbname, LEX_STRING name, bool if_exists)
Turn off row binlogging of this statement and use statement-based so
that all supporting tables are updated for DROP EVENT command.
*/
- if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
- thd->clear_current_stmt_binlog_format_row();
+ save_binlog_format= thd->set_current_stmt_binlog_format_stmt();
if (lock_object_name(thd, MDL_key::EVENT,
dbname.str, name.str))
@@ -578,10 +575,8 @@ Events::drop_event(THD *thd, LEX_STRING dbname, LEX_STRING name, bool if_exists)
DBUG_ASSERT(thd->query() && thd->query_length());
ret= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
}
- /* Restore the state of binlog format */
- DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
- if (save_binlog_row_based)
- thd->set_current_stmt_binlog_format_row();
+
+ thd->restore_stmt_binlog_format(save_binlog_format);
DBUG_RETURN(ret);
}
@@ -605,6 +600,8 @@ Events::drop_schema_events(THD *thd, char *db)
DBUG_ENTER("Events::drop_schema_events");
DBUG_PRINT("enter", ("dropping events from %s", db));
+ DBUG_ASSERT(ok_for_lower_case_names(db));
+
/*
Sic: no check if the scheduler is disabled or system tables
are damaged, as intended.
@@ -758,6 +755,13 @@ Events::fill_schema_events(THD *thd, TABLE_LIST *tables, COND * /* cond */)
int ret;
DBUG_ENTER("Events::fill_schema_events");
+ /*
+ If we didn't start events because of --skip-grant-tables, return an
+ empty set
+ */
+ if (opt_noacl)
+ DBUG_RETURN(0);
+
if (check_if_system_tables_error())
DBUG_RETURN(1);
@@ -773,6 +777,9 @@ Events::fill_schema_events(THD *thd, TABLE_LIST *tables, COND * /* cond */)
NULL, NULL, 0, 0))
DBUG_RETURN(1);
db= thd->lex->select_lex.db;
+
+ if (lower_case_table_names)
+ my_casedn_str(system_charset_info, db);
}
ret= db_repository->fill_schema_events(thd, tables, db);
@@ -783,6 +790,7 @@ Events::fill_schema_events(THD *thd, TABLE_LIST *tables, COND * /* cond */)
/**
Initializes the scheduler's structures.
+ @param THD or null (if called by init)
@param opt_noacl_or_bootstrap
TRUE if there is --skip-grant-tables or --bootstrap
option. In that case we disable the event scheduler.
@@ -790,34 +798,55 @@ Events::fill_schema_events(THD *thd, TABLE_LIST *tables, COND * /* cond */)
@note This function is not synchronized.
@retval FALSE Perhaps there was an error, and the event scheduler
- is disabled. But the error is not fatal and the
+ is disabled. But the error is not fatal and the
server start up can continue.
@retval TRUE Fatal error. Startup must terminate (call unireg_abort()).
*/
bool
-Events::init(bool opt_noacl_or_bootstrap)
+Events::init(THD *thd, bool opt_noacl_or_bootstrap)
{
-
- THD *thd;
int err_no;
bool res= FALSE;
-
+ bool had_thd= thd != 0;
DBUG_ENTER("Events::init");
+ DBUG_ASSERT(inited == 0);
+
+ /*
+ Was disabled explicitly from the command line
+ */
+ if (opt_event_scheduler == Events::EVENTS_DISABLED ||
+ opt_noacl_or_bootstrap)
+ DBUG_RETURN(FALSE);
+
/* We need a temporary THD during boot */
- if (!(thd= new THD()))
+ if (!thd)
{
- res= TRUE;
- goto end;
+
+ if (!(thd= new THD()))
+ {
+ res= TRUE;
+ goto end;
+ }
+ /*
+ The thread stack does not start from this function but we cannot
+ guess the real value. So better some value that doesn't assert than
+ no value.
+ */
+ thd->thread_stack= (char*) &thd;
+ thd->store_globals();
+ /*
+ Set current time for the thread that handles events.
+ Current time is stored in data member start_time of THD class.
+ Subsequently, this value is used to check whether event was expired
+ when make loading events from storage. Check for event expiration time
+ is done at Event_queue_element::compute_next_execution_time() where
+ event's status set to Event_parse_data::DISABLED and dropped flag set
+ to true if event was expired.
+ */
+ thd->set_time();
}
- /*
- The thread stack does not start from this function but we cannot
- guess the real value. So better some value that doesn't assert than
- no value.
- */
- thd->thread_stack= (char*) &thd;
- thd->store_globals();
/*
We will need Event_db_repository anyway, even if the scheduler is
@@ -838,28 +867,19 @@ Events::init(bool opt_noacl_or_bootstrap)
are most likely not there and we're going to disable the event
scheduler anyway.
*/
- if (opt_noacl_or_bootstrap || Event_db_repository::check_system_tables(thd))
+ if (Event_db_repository::check_system_tables(thd))
{
- if (! opt_noacl_or_bootstrap)
- {
- sql_print_error("Event Scheduler: An error occurred when initializing "
- "system tables. Disabling the Event Scheduler.");
- check_system_tables_error= TRUE;
- }
-
+ delete db_repository;
+ db_repository= 0;
+ my_message(ER_STARTUP,
+ "Event Scheduler: An error occurred when initializing "
+ "system tables. Disabling the Event Scheduler.",
+ MYF(ME_NOREFRESH));
/* Disable the scheduler since the system tables are not up to date */
- opt_event_scheduler= EVENTS_DISABLED;
+ opt_event_scheduler= EVENTS_OFF;
goto end;
}
- /*
- Was disabled explicitly from the command line, or because we're running
- with --skip-grant-tables, or --bootstrap, or because we have no system
- tables.
- */
- if (opt_event_scheduler == Events::EVENTS_DISABLED)
- goto end;
-
DBUG_ASSERT(opt_event_scheduler == Events::EVENTS_ON ||
opt_event_scheduler == Events::EVENTS_OFF);
@@ -874,22 +894,20 @@ Events::init(bool opt_noacl_or_bootstrap)
if (event_queue->init_queue(thd) || load_events_from_db(thd) ||
(opt_event_scheduler == EVENTS_ON && scheduler->start(&err_no)))
{
- sql_print_error("Event Scheduler: Error while loading from disk.");
+ my_message_sql(ER_STARTUP,
+ "Event Scheduler: Error while loading from mysql.event table.",
+ MYF(ME_NOREFRESH));
res= TRUE; /* fatal error: request unireg_abort */
goto end;
}
Event_worker_thread::init(db_repository);
+ inited= 1;
end:
if (res)
- {
- delete db_repository;
- delete event_queue;
- delete scheduler;
- }
- delete thd;
- /* Remember that we don't have a THD */
- my_pthread_setspecific_ptr(THR_THD, NULL);
+ deinit();
+ if (!had_thd)
+ delete thd;
DBUG_RETURN(res);
}
@@ -909,17 +927,14 @@ Events::deinit()
{
DBUG_ENTER("Events::deinit");
- if (opt_event_scheduler != EVENTS_DISABLED)
- {
- delete scheduler;
- scheduler= NULL; /* safety */
- delete event_queue;
- event_queue= NULL; /* safety */
- }
-
+ delete scheduler;
+ scheduler= NULL; /* For restart */
+ delete event_queue;
+ event_queue= NULL; /* For restart */
delete db_repository;
- db_repository= NULL; /* safety */
+ db_repository= NULL; /* For restart */
+ inited= 0;
DBUG_VOID_RETURN;
}
@@ -948,23 +963,37 @@ static PSI_thread_info all_events_threads[]=
{ &key_thread_event_scheduler, "event_scheduler", PSI_FLAG_GLOBAL},
{ &key_thread_event_worker, "event_worker", 0}
};
+#endif /* HAVE_PSI_INTERFACE */
+
+PSI_stage_info stage_waiting_on_empty_queue= { 0, "Waiting on empty queue", 0};
+PSI_stage_info stage_waiting_for_next_activation= { 0, "Waiting for next activation", 0};
+PSI_stage_info stage_waiting_for_scheduler_to_stop= { 0, "Waiting for the scheduler to stop", 0};
+
+#ifdef HAVE_PSI_INTERFACE
+PSI_stage_info *all_events_stages[]=
+{
+ & stage_waiting_on_empty_queue,
+ & stage_waiting_for_next_activation,
+ & stage_waiting_for_scheduler_to_stop
+};
static void init_events_psi_keys(void)
{
const char* category= "sql";
int count;
- if (PSI_server == NULL)
- return;
-
count= array_elements(all_events_mutexes);
- PSI_server->register_mutex(category, all_events_mutexes, count);
+ mysql_mutex_register(category, all_events_mutexes, count);
count= array_elements(all_events_conds);
- PSI_server->register_cond(category, all_events_conds, count);
+ mysql_cond_register(category, all_events_conds, count);
count= array_elements(all_events_threads);
- PSI_server->register_thread(category, all_events_threads, count);
+ mysql_thread_register(category, all_events_threads, count);
+
+ count= array_elements(all_events_stages);
+ mysql_stage_register(category, all_events_stages, count);
+
}
#endif /* HAVE_PSI_INTERFACE */
@@ -1008,7 +1037,7 @@ Events::dump_internal_status()
holding LOCK_global_system_variables.
*/
mysql_mutex_lock(&LOCK_global_system_variables);
- if (opt_event_scheduler == EVENTS_DISABLED)
+ if (!inited)
puts("The Event Scheduler is disabled");
else
{
@@ -1022,11 +1051,13 @@ Events::dump_internal_status()
bool Events::start(int *err_no)
{
+ DBUG_ASSERT(inited);
return scheduler->start(err_no);
}
bool Events::stop()
{
+ DBUG_ASSERT(inited);
return scheduler->stop();
}
@@ -1056,7 +1087,6 @@ Events::load_events_from_db(THD *thd)
bool ret= TRUE;
uint count= 0;
ulong saved_master_access;
-
DBUG_ENTER("Events::load_events_from_db");
DBUG_PRINT("enter", ("thd: 0x%lx", (long) thd));
@@ -1064,18 +1094,26 @@ Events::load_events_from_db(THD *thd)
NOTE: even if we run in read-only mode, we should be able to lock the
mysql.event table for writing. In order to achieve this, we should call
mysql_lock_tables() under the super user.
+
+ Same goes for transaction access mode.
+ Temporarily reset it to read-write.
*/
saved_master_access= thd->security_ctx->master_access;
thd->security_ctx->master_access |= SUPER_ACL;
+ bool save_tx_read_only= thd->tx_read_only;
+ thd->tx_read_only= false;
ret= db_repository->open_event_table(thd, TL_WRITE, &table);
+ thd->tx_read_only= save_tx_read_only;
thd->security_ctx->master_access= saved_master_access;
if (ret)
{
- sql_print_error("Event Scheduler: Failed to open table mysql.event");
+ my_message_sql(ER_STARTUP,
+ "Event Scheduler: Failed to open table mysql.event",
+ MYF(ME_NOREFRESH));
DBUG_RETURN(TRUE);
}
@@ -1088,8 +1126,7 @@ Events::load_events_from_db(THD *thd)
while (!(read_record_info.read_record(&read_record_info)))
{
Event_queue_element *et;
- bool created;
- bool drop_on_completion;
+ bool created, dropped;
if (!(et= new Event_queue_element))
goto end;
@@ -1098,16 +1135,18 @@ Events::load_events_from_db(THD *thd)
if (et->load_from_row(thd, table))
{
- sql_print_error("Event Scheduler: "
- "Error while loading events from mysql.event. "
- "The table probably contains bad data or is corrupted");
+ my_message(ER_STARTUP,
+ "Event Scheduler: "
+ "Error while loading events from mysql.event. "
+ "The table probably contains bad data or is corrupted",
+ MYF(ME_NOREFRESH));
delete et;
goto end;
}
#ifdef WITH_WSREP
// when SST from master node who initials event, the event status is ENABLED
// this is problematic because there are two nodes with same events and both enabled.
- if (et->originator != thd->server_id)
+ if (et->originator != (longlong) thd->variables.server_id)
{
store_record(table, record[1]);
table->field[ET_FIELD_STATUS]->
@@ -1118,10 +1157,12 @@ Events::load_events_from_db(THD *thd)
continue;
}
#endif
- drop_on_completion= (et->on_completion ==
- Event_parse_data::ON_COMPLETION_DROP);
-
-
+ /**
+ Since the Event_queue_element object could be deleted inside
+ Event_queue::create_event we should save the value of dropped flag
+ into the temporary variable.
+ */
+ dropped= et->dropped;
if (event_queue->create_event(thd, et, &created))
{
/* Out of memory */
@@ -1130,7 +1171,7 @@ Events::load_events_from_db(THD *thd)
}
if (created)
count++;
- else if (drop_on_completion)
+ else if (dropped)
{
/*
If not created, a stale event - drop if immediately if
@@ -1149,9 +1190,12 @@ Events::load_events_from_db(THD *thd)
}
}
}
- if (global_system_variables.log_warnings)
- sql_print_information("Event Scheduler: Loaded %d event%s",
- count, (count == 1) ? "" : "s");
+ my_printf_error(ER_STARTUP,
+ "Event Scheduler: Loaded %d event%s",
+ MYF(ME_NOREFRESH |
+ (global_system_variables.log_warnings) ?
+ ME_JUST_INFO: 0),
+ count, (count == 1) ? "" : "s");
ret= FALSE;
end:
@@ -1160,6 +1204,7 @@ end:
close_mysql_tables(thd);
DBUG_RETURN(ret);
}
+
#ifdef WITH_WSREP
int wsrep_create_event_query(THD *thd, uchar** buf, size_t* buf_len)
{
@@ -1167,7 +1212,8 @@ int wsrep_create_event_query(THD *thd, uchar** buf, size_t* buf_len)
if (create_query_string(thd, &log_query))
{
- WSREP_WARN("events create string failed: %s", thd->query());
+ WSREP_WARN("events create string failed: schema: %s, query: %s",
+ (thd->db ? thd->db : "(null)"), thd->query());
return 1;
}
return wsrep_to_buf_helper(thd, log_query.ptr(), log_query.length(), buf, buf_len);
@@ -1194,7 +1240,8 @@ int wsrep_alter_event_query(THD *thd, uchar** buf, size_t* buf_len)
if (wsrep_alter_query_string(thd, &log_query))
{
- WSREP_WARN("events alter string failed: %s", thd->query());
+ WSREP_WARN("events alter string failed: schema: %s, query: %s",
+ (thd->db ? thd->db : "(null)"), thd->query());
return 1;
}
return wsrep_to_buf_helper(thd, log_query.ptr(), log_query.length(), buf, buf_len);
diff --git a/sql/events.h b/sql/events.h
index 223f68c8370..91a0e6f28eb 100644
--- a/sql/events.h
+++ b/sql/events.h
@@ -31,6 +31,11 @@ extern PSI_cond_key key_event_scheduler_COND_state;
extern PSI_thread_key key_thread_event_scheduler, key_thread_event_worker;
#endif /* HAVE_PSI_INTERFACE */
+/* Always defined, for SHOW PROCESSLIST. */
+extern PSI_stage_info stage_waiting_on_empty_queue;
+extern PSI_stage_info stage_waiting_for_next_activation;
+extern PSI_stage_info stage_waiting_for_scheduler_to_stop;
+
#include "sql_string.h" /* LEX_STRING */
#include "my_time.h" /* interval_type */
@@ -74,9 +79,11 @@ public:
and the @@global.event_scheduler SQL variable.
See sys_var.cc
*/
- enum enum_opt_event_scheduler { EVENTS_OFF, EVENTS_ON, EVENTS_DISABLED };
+ enum enum_opt_event_scheduler { EVENTS_OFF, EVENTS_ON, EVENTS_DISABLED,
+ EVENTS_ORIGINAL };
/* Protected using LOCK_global_system_variables only. */
- static ulong opt_event_scheduler;
+ static ulong opt_event_scheduler, startup_state;
+ static ulong inited;
static bool check_if_system_tables_error();
static bool start(int *err_no);
static bool stop();
@@ -86,8 +93,7 @@ public:
static Event_db_repository *
get_db_repository() { return db_repository; }
- static bool
- init(bool opt_noacl);
+ static bool init(THD *thd, bool opt_noacl);
static void
deinit();
@@ -125,6 +131,11 @@ public:
static void
dump_internal_status();
+ static void set_original_state(ulong startup_state_org)
+ {
+ startup_state= startup_state_org;
+ }
+
private:
static bool
@@ -134,8 +145,6 @@ private:
static Event_queue *event_queue;
static Event_scheduler *scheduler;
static Event_db_repository *db_repository;
- /* Set to TRUE if an error at start up */
- static bool check_system_tables_error;
private:
/* Prevent use of these */
diff --git a/sql/field.cc b/sql/field.cc
index ceea0893a3f..b356b126c16 100644
--- a/sql/field.cc
+++ b/sql/field.cc
@@ -1,6 +1,6 @@
/*
Copyright (c) 2000, 2013, Oracle and/or its affiliates.
- Copyright (c) 2008, 2013, Monty Program Ab
+ Copyright (c) 2008, 2013, Monty Program Ab.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -27,6 +27,7 @@
#pragma implementation // gcc: Class implementation
#endif
+#include <my_global.h>
#include "sql_priv.h"
#include "sql_select.h"
#include "rpl_rli.h" // Pull in Relay_log_info
@@ -41,7 +42,6 @@
#include "filesort.h" // change_double_for_sort
#include "log_event.h" // class Table_map_log_event
#include <m_ctype.h>
-#include <errno.h>
// Maximum allowed exponent value for converting string to decimal
#define MAX_EXPONENT 1024
@@ -50,11 +50,6 @@
Instansiate templates and static variables
*****************************************************************************/
-#ifdef HAVE_EXPLICIT_TEMPLATE_INSTANTIATION
-template class List<Create_field>;
-template class List_iterator<Create_field>;
-#endif
-
static const char *zero_timestamp="0000-00-00 00:00:00.000000";
/* number of bytes to store second_part part of the TIMESTAMP(N) */
@@ -73,10 +68,10 @@ const char field_separator=',';
#define LONGLONG_TO_STRING_CONVERSION_BUFFER_SIZE 128
#define DECIMAL_TO_STRING_CONVERSION_BUFFER_SIZE 128
#define BLOB_PACK_LENGTH_TO_MAX_LENGH(arg) \
-((ulong) ((LL(1) << min(arg, 4) * 8) - LL(1)))
+ ((ulong) ((1LL << MY_MIN(arg, 4) * 8) - 1))
#define ASSERT_COLUMN_MARKED_FOR_READ DBUG_ASSERT(!table || (!table->read_set || bitmap_is_set(table->read_set, field_index)))
-#define ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED DBUG_ASSERT(!table || (!table->write_set || bitmap_is_set(table->write_set, field_index) || bitmap_is_set(table->vcol_set, field_index)))
+#define ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED DBUG_ASSERT(is_stat_field || !table || (!table->write_set || bitmap_is_set(table->write_set, field_index) || bitmap_is_set(table->vcol_set, field_index)))
#define FLAGSTR(S,F) ((S) & (F) ? #F " " : "")
@@ -92,6 +87,7 @@ const char field_separator=',';
#define FIELDTYPE_NUM (FIELDTYPE_TEAR_FROM + (255 - FIELDTYPE_TEAR_TO))
static inline int field_type2index (enum_field_types field_type)
{
+ field_type= real_type_to_type(field_type);
return (field_type < FIELDTYPE_TEAR_FROM ?
field_type :
((int)FIELDTYPE_TEAR_FROM) + (field_type - FIELDTYPE_TEAR_TO) - 1);
@@ -952,8 +948,10 @@ static enum_field_types field_types_merge_rules [FIELDTYPE_NUM][FIELDTYPE_NUM]=
enum_field_types Field::field_type_merge(enum_field_types a,
enum_field_types b)
{
- DBUG_ASSERT(a < FIELDTYPE_TEAR_FROM || a > FIELDTYPE_TEAR_TO);
- DBUG_ASSERT(b < FIELDTYPE_TEAR_FROM || b > FIELDTYPE_TEAR_TO);
+ DBUG_ASSERT(real_type_to_type(a) < FIELDTYPE_TEAR_FROM ||
+ real_type_to_type(a) > FIELDTYPE_TEAR_TO);
+ DBUG_ASSERT(real_type_to_type(b) < FIELDTYPE_TEAR_FROM ||
+ real_type_to_type(b) > FIELDTYPE_TEAR_TO);
return field_types_merge_rules[field_type2index(a)]
[field_type2index(b)];
}
@@ -1047,8 +1045,8 @@ CPP_UNNAMED_NS_END
Item_result Field::result_merge_type(enum_field_types field_type)
{
- DBUG_ASSERT(field_type < FIELDTYPE_TEAR_FROM || field_type
- > FIELDTYPE_TEAR_TO);
+ DBUG_ASSERT(real_type_to_type(field_type) < FIELDTYPE_TEAR_FROM ||
+ real_type_to_type(field_type) > FIELDTYPE_TEAR_TO);
return field_types_result_type[field_type2index(field_type)];
}
@@ -1075,13 +1073,13 @@ static void push_numerical_conversion_warning(THD* thd, const char* str,
const char* field_name="UNKNOWN",
ulong row_num=0)
{
- char buf[max(max(DOUBLE_TO_STRING_CONVERSION_BUFFER_SIZE,
+ char buf[MY_MAX(MY_MAX(DOUBLE_TO_STRING_CONVERSION_BUFFER_SIZE,
LONGLONG_TO_STRING_CONVERSION_BUFFER_SIZE),
DECIMAL_TO_STRING_CONVERSION_BUFFER_SIZE)];
String tmp(buf, sizeof(buf), cs);
tmp.copy(str, length, cs);
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
error, ER(error), typestr, tmp.c_ptr(),
field_name, row_num);
}
@@ -1135,6 +1133,120 @@ void Field::make_sort_key(uchar *buff,uint length)
/**
+ @brief
+ Determine the relative position of the field value in a numeric interval
+
+ @details
+ The function returns a double number between 0.0 and 1.0 as the relative
+ position of the value of the this field in the numeric interval of [min,max].
+ If the value is not in the interval the the function returns 0.0 when
+ the value is less than min, and, 1.0 when the value is greater than max.
+
+ @param min value of the left end of the interval
+ @param max value of the right end of the interval
+
+ @return
+ relative position of the field value in the numeric interval [min,max]
+*/
+
+double Field::pos_in_interval_val_real(Field *min, Field *max)
+{
+ double n, d;
+ n= val_real() - min->val_real();
+ if (n < 0)
+ return 0.0;
+ d= max->val_real() - min->val_real();
+ if (d <= 0)
+ return 1.0;
+ return MY_MIN(n/d, 1.0);
+}
+
+
+static
+inline ulonglong char_prefix_to_ulonglong(uchar *src)
+{
+ uint sz= sizeof(ulonglong);
+ for (uint i= 0; i < sz/2; i++)
+ {
+ uchar tmp= src[i];
+ src[i]= src[sz-1-i];
+ src[sz-1-i]= tmp;
+ }
+ return uint8korr(src);
+}
+
+/*
+ Compute res = a - b, without losing precision and taking care that these are
+ unsigned numbers.
+*/
+static inline double safe_substract(ulonglong a, ulonglong b)
+{
+ return (a > b)? double(a - b) : -double(b - a);
+}
+
+
+/**
+ @brief
+ Determine the relative position of the field value in a string interval
+
+ @details
+ The function returns a double number between 0.0 and 1.0 as the relative
+ position of the value of the this field in the string interval of [min,max].
+ If the value is not in the interval the the function returns 0.0 when
+ the value is less than min, and, 1.0 when the value is greater than max.
+
+ @note
+ To calculate the relative position of the string value v in the interval
+ [min, max] the function first converts the beginning of these three
+ strings v, min, max into the strings that are used for byte comparison.
+ For each string not more sizeof(ulonglong) first bytes are taken
+ from the result of conversion. Then these bytes are interpreted as the
+ big-endian representation of an ulonglong integer. The values of these
+ integer numbers obtained for the strings v, min, max are used to calculate
+ the position of v in [min,max] in the same way is it's done for numeric
+ fields (see Field::pos_in_interval_val_real).
+
+ @todo
+ Improve the procedure for the case when min and max have the same
+ beginning
+
+ @param min value of the left end of the interval
+ @param max value of the right end of the interval
+
+ @return
+ relative position of the field value in the string interval [min,max]
+*/
+
+double Field::pos_in_interval_val_str(Field *min, Field *max, uint data_offset)
+{
+ uchar mp_prefix[sizeof(ulonglong)];
+ uchar minp_prefix[sizeof(ulonglong)];
+ uchar maxp_prefix[sizeof(ulonglong)];
+ ulonglong mp, minp, maxp;
+ my_strnxfrm(charset(), mp_prefix, sizeof(mp),
+ ptr + data_offset,
+ data_length());
+ my_strnxfrm(charset(), minp_prefix, sizeof(minp),
+ min->ptr + data_offset,
+ min->data_length());
+ my_strnxfrm(charset(), maxp_prefix, sizeof(maxp),
+ max->ptr + data_offset,
+ max->data_length());
+ mp= char_prefix_to_ulonglong(mp_prefix);
+ minp= char_prefix_to_ulonglong(minp_prefix);
+ maxp= char_prefix_to_ulonglong(maxp_prefix);
+ double n, d;
+ n= safe_substract(mp, minp);
+ if (n < 0)
+ return 0.0;
+ d= safe_substract(maxp, minp);
+ if (d <= 0)
+ return 1.0;
+ return MY_MIN(n/d, 1.0);
+}
+
+
+/**
Numeric fields base class constructor.
*/
Field_num::Field_num(uchar *ptr_arg,uint32 len_arg, uchar *null_ptr_arg,
@@ -1195,17 +1307,13 @@ int Field_num::check_int(CHARSET_INFO *cs, const char *str, int length,
if (str == int_end || error == MY_ERRNO_EDOM)
{
ErrConvString err(str, length, cs);
- push_warning_printf(table->in_use, MYSQL_ERROR::WARN_LEVEL_WARN,
- ER_TRUNCATED_WRONG_VALUE_FOR_FIELD,
- ER(ER_TRUNCATED_WRONG_VALUE_FOR_FIELD),
- "integer", err.ptr(), field_name,
- (ulong) table->in_use->warning_info->current_row_for_warning());
+ set_warning_truncated_wrong_value("integer", err.ptr());
return 1;
}
/* Test if we have garbage at the end of the given string. */
if (test_if_important_data(cs, int_end, str + length))
{
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1);
+ set_warning(WARN_DATA_TRUNCATED, 1);
return 2;
}
return 0;
@@ -1268,16 +1376,17 @@ bool Field_num::get_int(CHARSET_INFO *cs, const char *from, uint len,
goto out_of_range;
}
}
- if (table->in_use->count_cuted_fields &&
+ if (get_thd()->count_cuted_fields &&
check_int(cs, from, len, end, error))
return 1;
return 0;
out_of_range:
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
return 1;
}
+
/**
Process decimal library return codes and issue warnings for overflow and
truncation.
@@ -1294,12 +1403,12 @@ int Field::warn_if_overflow(int op_result)
{
if (op_result == E_DEC_OVERFLOW)
{
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
return 1;
}
if (op_result == E_DEC_TRUNCATED)
{
- set_warning(MYSQL_ERROR::WARN_LEVEL_NOTE, WARN_DATA_TRUNCATED, 1);
+ set_note(WARN_DATA_TRUNCATED, 1);
/* We return 0 here as this is not a critical issue */
}
return 0;
@@ -1339,13 +1448,18 @@ Field::Field(uchar *ptr_arg,uint32 length_arg,uchar *null_ptr_arg,
option_struct(0), key_start(0), part_of_key(0),
part_of_key_not_clustered(0), part_of_sortkey(0),
unireg_check(unireg_check_arg), field_length(length_arg),
- null_bit(null_bit_arg), is_created_from_null_item(FALSE), vcol_info(0),
+ null_bit(null_bit_arg), is_created_from_null_item(FALSE),
+ read_stats(NULL), collected_stats(0),
+ vcol_info(0),
stored_in_db(TRUE)
{
flags=null_ptr ? 0: NOT_NULL_FLAG;
comment.str= (char*) "";
comment.length=0;
- field_index= 0;
+ field_index= 0;
+ is_stat_field= FALSE;
+ cond_selectivity= 1.0;
+ next_equal_field= NULL;
}
@@ -1445,10 +1559,11 @@ int Field::store(const char *to, uint length, CHARSET_INFO *cs,
enum_check_fields check_level)
{
int res;
- enum_check_fields old_check_level= table->in_use->count_cuted_fields;
- table->in_use->count_cuted_fields= check_level;
+ THD *thd= get_thd();
+ enum_check_fields old_check_level= thd->count_cuted_fields;
+ thd->count_cuted_fields= check_level;
res= store(to, length, cs);
- table->in_use->count_cuted_fields= old_check_level;
+ thd->count_cuted_fields= old_check_level;
return res;
}
@@ -1619,7 +1734,7 @@ longlong Field::convert_decimal2longlong(const my_decimal *val,
{
if (val->sign())
{
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
i= 0;
*err= 1;
}
@@ -1663,7 +1778,7 @@ int Field_num::store_decimal(const my_decimal *val)
ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
int err= 0;
longlong i= convert_decimal2longlong(val, unsigned_flag, &err);
- return test(err | store(i, unsigned_flag));
+ return MY_TEST(err | store(i, unsigned_flag));
}
@@ -1759,7 +1874,7 @@ uint Field::fill_cache_field(CACHE_FIELD *copy)
if (flags & BLOB_FLAG)
{
copy->type= CACHE_BLOB;
- copy->length-= table->s->blob_ptr_size;
+ copy->length-= portable_sizeof_char_ptr;
return copy->length;
}
else if (!zero_pack() &&
@@ -1790,7 +1905,7 @@ bool Field::get_date(MYSQL_TIME *ltime,ulonglong fuzzydate)
String tmp(buff,sizeof(buff),&my_charset_bin),*res;
if (!(res=val_str(&tmp)) ||
str_to_datetime_with_warn(res->charset(), res->ptr(), res->length(),
- ltime, fuzzydate) <= MYSQL_TIMESTAMP_ERROR)
+ ltime, fuzzydate))
return 1;
return 0;
}
@@ -1815,7 +1930,7 @@ int Field::store_time_dec(MYSQL_TIME *ltime, uint dec)
bool Field::optimize_range(uint idx, uint part)
{
- return test(table->file->index_flags(idx, part, 1) & HA_READ_RANGE);
+ return MY_TEST(table->file->index_flags(idx, part, 1) & HA_READ_RANGE);
}
@@ -1832,6 +1947,10 @@ Field *Field::new_field(MEM_ROOT *root, TABLE *new_table,
tmp->key_start.init(0);
tmp->part_of_key.init(0);
tmp->part_of_sortkey.init(0);
+ /*
+ TODO: it is not clear why this method needs to reset unireg_check.
+ Try not to reset it, or explain why it needs to be reset.
+ */
tmp->unireg_check= Field::NONE;
tmp->flags&= (NOT_NULL_FLAG | BLOB_FLAG | UNSIGNED_FLAG |
ZEROFILL_FLAG | BINARY_FLAG | ENUM_FLAG | SET_FLAG);
@@ -1841,8 +1960,8 @@ Field *Field::new_field(MEM_ROOT *root, TABLE *new_table,
Field *Field::new_key_field(MEM_ROOT *root, TABLE *new_table,
- uchar *new_ptr, uchar *new_null_ptr,
- uint new_null_bit)
+ uchar *new_ptr, uint32 length,
+ uchar *new_null_ptr, uint new_null_bit)
{
Field *tmp;
if ((tmp= new_field(root, new_table, table == new_table)))
@@ -1870,6 +1989,32 @@ Field *Field::clone(MEM_ROOT *root, TABLE *new_table)
}
+
+Field *Field::clone(MEM_ROOT *root, TABLE *new_table, my_ptrdiff_t diff,
+ bool stat_flag)
+{
+ Field *tmp;
+ if ((tmp= (Field*) memdup_root(root,(char*) this,size_of())))
+ {
+ tmp->init(new_table);
+ tmp->move_field_offset(diff);
+ }
+ tmp->is_stat_field= stat_flag;
+ return tmp;
+}
+
+
+Field *Field::clone(MEM_ROOT *root, my_ptrdiff_t diff)
+{
+ Field *tmp;
+ if ((tmp= (Field*) memdup_root(root,(char*) this,size_of())))
+ {
+ tmp->move_field_offset(diff);
+ }
+ return tmp;
+}
+
+
/****************************************************************************
Field_null, a field that always return NULL
****************************************************************************/
@@ -1897,7 +2042,7 @@ void Field_decimal::overflow(bool negative)
uint len=field_length;
uchar *to=ptr, filler= '9';
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
if (negative)
{
if (!unsigned_flag)
@@ -1984,7 +2129,7 @@ int Field_decimal::store(const char *from_arg, uint len, CHARSET_INFO *cs)
uchar *left_wall,*right_wall;
uchar tmp_char;
/*
- To remember if table->in_use->cuted_fields has already been incremented,
+ To remember if get_thd()->cuted_fields has already been incremented,
to do that only once
*/
bool is_cuted_fields_incr=0;
@@ -2005,7 +2150,7 @@ int Field_decimal::store(const char *from_arg, uint len, CHARSET_INFO *cs)
from++;
if (from == end)
{
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1);
+ set_warning(WARN_DATA_TRUNCATED, 1);
is_cuted_fields_incr=1;
}
else if (*from == '+' || *from == '-') // Found some sign ?
@@ -2075,13 +2220,13 @@ int Field_decimal::store(const char *from_arg, uint len, CHARSET_INFO *cs)
it makes the code easer to read.
*/
- if (table->in_use->count_cuted_fields)
+ if (get_thd()->count_cuted_fields)
{
// Skip end spaces
for (;from != end && my_isspace(&my_charset_bin, *from); from++) ;
if (from != end) // If still something left, warn
{
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1);
+ set_warning(WARN_DATA_TRUNCATED, 1);
is_cuted_fields_incr=1;
}
}
@@ -2118,7 +2263,7 @@ int Field_decimal::store(const char *from_arg, uint len, CHARSET_INFO *cs)
tmp_uint=tmp_dec+(uint)(int_digits_end-int_digits_from);
else if (expo_sign_char == '-')
{
- tmp_uint=min(exponent,(uint)(int_digits_end-int_digits_from));
+ tmp_uint=MY_MIN(exponent,(uint)(int_digits_end-int_digits_from));
frac_digits_added_zeros=exponent-tmp_uint;
int_digits_end -= tmp_uint;
frac_digits_head_end=int_digits_end+tmp_uint;
@@ -2126,7 +2271,7 @@ int Field_decimal::store(const char *from_arg, uint len, CHARSET_INFO *cs)
}
else // (expo_sign_char=='+')
{
- tmp_uint=min(exponent,(uint)(frac_digits_end-frac_digits_from));
+ tmp_uint=MY_MIN(exponent,(uint)(frac_digits_end-frac_digits_from));
int_digits_added_zeros=exponent-tmp_uint;
int_digits_tail_from=frac_digits_from;
frac_digits_from=frac_digits_from+tmp_uint;
@@ -2227,7 +2372,7 @@ int Field_decimal::store(const char *from_arg, uint len, CHARSET_INFO *cs)
/*
Write digits of the frac_% parts ;
- Depending on table->in_use->count_cutted_fields, we may also want
+ Depending on get_thd()->count_cutted_fields, we may also want
to know if some non-zero tail of these parts will
be truncated (for example, 0.002->0.00 will generate a warning,
while 0.000->0.00 will not)
@@ -2245,7 +2390,7 @@ int Field_decimal::store(const char *from_arg, uint len, CHARSET_INFO *cs)
{
if (pos == right_wall)
{
- if (table->in_use->count_cuted_fields && !is_cuted_fields_incr)
+ if (get_thd()->count_cuted_fields && !is_cuted_fields_incr)
break; // Go on below to see if we lose non zero digits
return 0;
}
@@ -2259,8 +2404,7 @@ int Field_decimal::store(const char *from_arg, uint len, CHARSET_INFO *cs)
if (tmp_char != '0') // Losing a non zero digit ?
{
if (!is_cuted_fields_incr)
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
- WARN_DATA_TRUNCATED, 1);
+ set_warning(WARN_DATA_TRUNCATED, 1);
return 0;
}
continue;
@@ -2282,7 +2426,7 @@ int Field_decimal::store(const char *from_arg, uint len, CHARSET_INFO *cs)
This is a note, not a warning, as we don't want to abort
when we cut decimals in strict mode
*/
- set_warning(MYSQL_ERROR::WARN_LEVEL_NOTE, WARN_DATA_TRUNCATED, 1);
+ set_note(WARN_DATA_TRUNCATED, 1);
}
return 0;
}
@@ -2545,7 +2689,7 @@ Field *Field_new_decimal::create_from_item (Item *item)
{
signed int overflow;
- dec= min(dec, DECIMAL_MAX_SCALE);
+ dec= MY_MIN(dec, DECIMAL_MAX_SCALE);
/*
If the value still overflows the field with the corrected dec,
@@ -2561,7 +2705,7 @@ Field *Field_new_decimal::create_from_item (Item *item)
overflow= required_length - len;
if (overflow > 0)
- dec= max(0, dec - overflow); // too long, discard fract
+ dec= MY_MAX(0, dec - overflow); // too long, discard fract
else
/* Corrected value fits. */
len= required_length;
@@ -2632,7 +2776,7 @@ bool Field_new_decimal::store_value(const my_decimal *decimal_value)
if (unsigned_flag && decimal_value->sign())
{
DBUG_PRINT("info", ("unsigned overflow"));
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
decimal_value= &decimal_zero;
}
@@ -2666,41 +2810,32 @@ int Field_new_decimal::store(const char *from, uint length,
ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
int err;
my_decimal decimal_value;
+ THD *thd= get_thd();
DBUG_ENTER("Field_new_decimal::store(char*)");
if ((err= str2my_decimal(E_DEC_FATAL_ERROR &
~(E_DEC_OVERFLOW | E_DEC_BAD_NUM),
from, length, charset_arg,
&decimal_value)) &&
- table->in_use->abort_on_warning)
+ thd->abort_on_warning)
{
- ErrConvString errmsg(from, length, &my_charset_bin);
- push_warning_printf(table->in_use, MYSQL_ERROR::WARN_LEVEL_WARN,
- ER_TRUNCATED_WRONG_VALUE_FOR_FIELD,
- ER(ER_TRUNCATED_WRONG_VALUE_FOR_FIELD),
- "decimal", errmsg.ptr(), field_name,
- (ulong) table->in_use->warning_info->current_row_for_warning());
-
+ ErrConvString errmsg(from, length, charset_arg);
+ set_warning_truncated_wrong_value("decimal", errmsg.ptr());
DBUG_RETURN(err);
}
switch (err) {
case E_DEC_TRUNCATED:
- set_warning(MYSQL_ERROR::WARN_LEVEL_NOTE, WARN_DATA_TRUNCATED, 1);
+ set_note(WARN_DATA_TRUNCATED, 1);
break;
case E_DEC_OVERFLOW:
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
set_value_on_overflow(&decimal_value, decimal_value.sign());
break;
case E_DEC_BAD_NUM:
{
- ErrConvString errmsg(from, length, &my_charset_bin);
- push_warning_printf(table->in_use, MYSQL_ERROR::WARN_LEVEL_WARN,
- ER_TRUNCATED_WRONG_VALUE_FOR_FIELD,
- ER(ER_TRUNCATED_WRONG_VALUE_FOR_FIELD),
- "decimal", errmsg.ptr(), field_name,
- (ulong) table->in_use->warning_info->
- current_row_for_warning());
+ ErrConvString errmsg(from, length, charset_arg);
+ set_warning_truncated_wrong_value("decimal", errmsg.ptr());
my_decimal_set_zero(&decimal_value);
break;
}
@@ -2727,6 +2862,7 @@ int Field_new_decimal::store(double nr)
ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
my_decimal decimal_value;
int err;
+ THD *thd= get_thd();
DBUG_ENTER("Field_new_decimal::store(double)");
err= double2my_decimal(E_DEC_FATAL_ERROR & ~E_DEC_OVERFLOW, nr,
@@ -2736,11 +2872,11 @@ int Field_new_decimal::store(double nr)
if (check_overflow(err))
set_value_on_overflow(&decimal_value, decimal_value.sign());
/* Only issue a warning if store_value doesn't issue an warning */
- table->in_use->got_warning= 0;
+ thd->got_warning= 0;
}
if (store_value(&decimal_value))
err= 1;
- else if (err && !table->in_use->got_warning)
+ else if (err && !thd->got_warning)
err= warn_if_overflow(err);
DBUG_RETURN(err);
}
@@ -2758,11 +2894,11 @@ int Field_new_decimal::store(longlong nr, bool unsigned_val)
if (check_overflow(err))
set_value_on_overflow(&decimal_value, decimal_value.sign());
/* Only issue a warning if store_value doesn't issue an warning */
- table->in_use->got_warning= 0;
+ get_thd()->got_warning= 0;
}
if (store_value(&decimal_value))
err= 1;
- else if (err && !table->in_use->got_warning)
+ else if (err && !get_thd()->got_warning)
err= warn_if_overflow(err);
return err;
}
@@ -3006,13 +3142,13 @@ int Field_tiny::store(double nr)
if (nr < 0.0)
{
*ptr=0;
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
else if (nr > 255.0)
{
*ptr= (uchar) 255;
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
else
@@ -3023,13 +3159,13 @@ int Field_tiny::store(double nr)
if (nr < -128.0)
{
*ptr= (uchar) -128;
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
else if (nr > 127.0)
{
*ptr=127;
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
else
@@ -3049,13 +3185,13 @@ int Field_tiny::store(longlong nr, bool unsigned_val)
if (nr < 0 && !unsigned_val)
{
*ptr= 0;
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
else if ((ulonglong) nr > (ulonglong) 255)
{
*ptr= (char) 255;
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
else
@@ -3068,13 +3204,13 @@ int Field_tiny::store(longlong nr, bool unsigned_val)
if (nr < -128)
{
*ptr= (char) -128;
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
else if (nr > 127)
{
*ptr=127;
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
else
@@ -3108,7 +3244,7 @@ String *Field_tiny::val_str(String *val_buffer,
ASSERT_COLUMN_MARKED_FOR_READ;
CHARSET_INFO *cs= &my_charset_numeric;
uint length;
- uint mlength=max(field_length+1,5*cs->mbmaxlen);
+ uint mlength=MY_MAX(field_length+1,5*cs->mbmaxlen);
val_buffer->alloc(mlength);
char *to=(char*) val_buffer->ptr();
@@ -3185,13 +3321,13 @@ int Field_short::store(double nr)
if (nr < 0)
{
res=0;
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
else if (nr > (double) UINT_MAX16)
{
res=(int16) UINT_MAX16;
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
else
@@ -3202,13 +3338,13 @@ int Field_short::store(double nr)
if (nr < (double) INT_MIN16)
{
res=INT_MIN16;
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
else if (nr > (double) INT_MAX16)
{
res=INT_MAX16;
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
else
@@ -3230,13 +3366,13 @@ int Field_short::store(longlong nr, bool unsigned_val)
if (nr < 0L && !unsigned_val)
{
res=0;
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
else if ((ulonglong) nr > (ulonglong) UINT_MAX16)
{
res=(int16) UINT_MAX16;
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
else
@@ -3250,13 +3386,13 @@ int Field_short::store(longlong nr, bool unsigned_val)
if (nr < INT_MIN16)
{
res=INT_MIN16;
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
else if (nr > (longlong) INT_MAX16)
{
res=INT_MAX16;
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
else
@@ -3290,7 +3426,7 @@ String *Field_short::val_str(String *val_buffer,
ASSERT_COLUMN_MARKED_FOR_READ;
CHARSET_INFO *cs= &my_charset_numeric;
uint length;
- uint mlength=max(field_length+1,7*cs->mbmaxlen);
+ uint mlength=MY_MAX(field_length+1,7*cs->mbmaxlen);
val_buffer->alloc(mlength);
char *to=(char*) val_buffer->ptr();
short j;
@@ -3373,14 +3509,14 @@ int Field_medium::store(double nr)
if (nr < 0)
{
int3store(ptr,0);
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
else if (nr >= (double) (long) (1L << 24))
{
uint32 tmp=(uint32) (1L << 24)-1L;
int3store(ptr,tmp);
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
else
@@ -3392,14 +3528,14 @@ int Field_medium::store(double nr)
{
long tmp=(long) INT_MIN24;
int3store(ptr,tmp);
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
else if (nr > (double) INT_MAX24)
{
long tmp=(long) INT_MAX24;
int3store(ptr,tmp);
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
else
@@ -3419,14 +3555,14 @@ int Field_medium::store(longlong nr, bool unsigned_val)
if (nr < 0 && !unsigned_val)
{
int3store(ptr,0);
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
else if ((ulonglong) nr >= (ulonglong) (long) (1L << 24))
{
long tmp= (long) (1L << 24)-1L;
int3store(ptr,tmp);
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
else
@@ -3441,14 +3577,14 @@ int Field_medium::store(longlong nr, bool unsigned_val)
{
long tmp= (long) INT_MIN24;
int3store(ptr,tmp);
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
else if (nr > (longlong) INT_MAX24)
{
long tmp=(long) INT_MAX24;
int3store(ptr,tmp);
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
else
@@ -3480,7 +3616,7 @@ String *Field_medium::val_str(String *val_buffer,
ASSERT_COLUMN_MARKED_FOR_READ;
CHARSET_INFO *cs= &my_charset_numeric;
uint length;
- uint mlength=max(field_length+1,10*cs->mbmaxlen);
+ uint mlength=MY_MAX(field_length+1,10*cs->mbmaxlen);
val_buffer->alloc(mlength);
char *to=(char*) val_buffer->ptr();
long j= unsigned_flag ? (long) uint3korr(ptr) : sint3korr(ptr);
@@ -3570,7 +3706,7 @@ int Field_long::store(double nr)
else if (nr > (double) UINT_MAX32)
{
res= UINT_MAX32;
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
else
@@ -3592,7 +3728,7 @@ int Field_long::store(double nr)
res=(int32) (longlong) nr;
}
if (error)
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
int4store(ptr,res);
return error;
@@ -3612,7 +3748,7 @@ int Field_long::store(longlong nr, bool unsigned_val)
res=0;
error= 1;
}
- else if ((ulonglong) nr >= (LL(1) << 32))
+ else if ((ulonglong) nr >= (1LL << 32))
{
res=(int32) (uint32) ~0L;
error= 1;
@@ -3638,7 +3774,7 @@ int Field_long::store(longlong nr, bool unsigned_val)
res=(int32) nr;
}
if (error)
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
int4store(ptr,res);
return error;
@@ -3658,7 +3794,7 @@ longlong Field_long::val_int(void)
ASSERT_COLUMN_MARKED_FOR_READ;
int32 j;
/* See the comment in Field_long::store(long long) */
- DBUG_ASSERT(table->in_use == current_thd);
+ DBUG_ASSERT(!table || table->in_use == current_thd);
j=sint4korr(ptr);
return unsigned_flag ? (longlong) (uint32) j : (longlong) j;
}
@@ -3669,7 +3805,7 @@ String *Field_long::val_str(String *val_buffer,
ASSERT_COLUMN_MARKED_FOR_READ;
CHARSET_INFO *cs= &my_charset_numeric;
uint length;
- uint mlength=max(field_length+1,12*cs->mbmaxlen);
+ uint mlength=MY_MAX(field_length+1,12*cs->mbmaxlen);
val_buffer->alloc(mlength);
char *to=(char*) val_buffer->ptr();
int32 j;
@@ -3737,10 +3873,10 @@ int Field_longlong::store(const char *from,uint len,CHARSET_INFO *cs)
tmp= cs->cset->strntoull10rnd(cs,from,len,unsigned_flag,&end,&error);
if (error == MY_ERRNO_ERANGE)
{
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
- else if (table->in_use->count_cuted_fields &&
+ else if (get_thd()->count_cuted_fields &&
check_int(cs, from, len, end, error))
error= 1;
else
@@ -3759,7 +3895,7 @@ int Field_longlong::store(double nr)
res= double_to_longlong(nr, unsigned_flag, &error);
if (error)
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
int8store(ptr,res);
return error;
@@ -3780,7 +3916,7 @@ int Field_longlong::store(longlong nr, bool unsigned_val)
if (unsigned_flag != unsigned_val)
{
nr= unsigned_flag ? (ulonglong) 0 : (ulonglong) LONGLONG_MAX;
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
}
@@ -3819,7 +3955,7 @@ String *Field_longlong::val_str(String *val_buffer,
{
CHARSET_INFO *cs= &my_charset_numeric;
uint length;
- uint mlength=max(field_length+1,22*cs->mbmaxlen);
+ uint mlength=MY_MAX(field_length+1,22*cs->mbmaxlen);
val_buffer->alloc(mlength);
char *to=(char*) val_buffer->ptr();
longlong j;
@@ -3892,10 +4028,9 @@ int Field_float::store(const char *from,uint len,CHARSET_INFO *cs)
char *end;
double nr= my_strntod(cs,(char*) from,len,&end,&error);
if (error || (!len || ((uint) (end-from) != len &&
- table->in_use->count_cuted_fields)))
+ get_thd()->count_cuted_fields)))
{
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
- (error ? ER_WARN_DATA_OUT_OF_RANGE : WARN_DATA_TRUNCATED), 1);
+ set_warning(error ? ER_WARN_DATA_OUT_OF_RANGE : WARN_DATA_TRUNCATED, 1);
error= error ? 1 : 2;
}
Field_float::store(nr);
@@ -3911,7 +4046,7 @@ int Field_float::store(double nr)
unsigned_flag, FLT_MAX);
if (error)
{
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
if (error < 0) // Wrong double value
{
error= 1;
@@ -4080,10 +4215,9 @@ int Field_double::store(const char *from,uint len,CHARSET_INFO *cs)
char *end;
double nr= my_strntod(cs,(char*) from, len, &end, &error);
if (error || (!len || ((uint) (end-from) != len &&
- table->in_use->count_cuted_fields)))
+ get_thd()->count_cuted_fields)))
{
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
- (error ? ER_WARN_DATA_OUT_OF_RANGE : WARN_DATA_TRUNCATED), 1);
+ set_warning(error ? ER_WARN_DATA_OUT_OF_RANGE : WARN_DATA_TRUNCATED, 1);
error= error ? 1 : 2;
}
Field_double::store(nr);
@@ -4099,7 +4233,7 @@ int Field_double::store(double nr)
unsigned_flag, DBL_MAX);
if (error)
{
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
if (error < 0) // Wrong double value
{
error= 1;
@@ -4264,7 +4398,7 @@ longlong Field_double::val_int(void)
if (error)
{
ErrConvDouble err(j);
- push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
ER_TRUNCATED_WRONG_VALUE,
ER(ER_TRUNCATED_WRONG_VALUE), "INTEGER",
err.ptr());
@@ -4384,16 +4518,10 @@ void Field_double::sql_type(String &res) const
2038-01-01 00:00:00 UTC stored as number of seconds since Unix
Epoch in UTC.
- Up to one of timestamps columns in the table can be automatically
- set on row update and/or have NOW() as default value.
- TABLE::timestamp_field points to Field object for such timestamp with
- auto-set-on-update. TABLE::time_stamp holds offset in record + 1 for this
- field, and is used by handler code which performs updates required.
-
Actually SQL-99 says that we should allow niladic functions (like NOW())
- as defaults for any field. Current limitations (only NOW() and only
- for one TIMESTAMP field) are because of restricted binary .frm format
- and should go away in the future.
+ as defaults for any field. The current limitation (only NOW() and only
+ for TIMESTAMP and DATETIME fields) are because of restricted binary .frm
+ format and should go away in the future.
Also because of this limitation of binary .frm format we use 5 different
unireg_check values with TIMESTAMP field to distinguish various cases of
@@ -4427,17 +4555,18 @@ Field_timestamp::Field_timestamp(uchar *ptr_arg, uint32 len_arg,
uchar *null_ptr_arg, uchar null_bit_arg,
enum utype unireg_check_arg,
const char *field_name_arg,
- TABLE_SHARE *share,
- CHARSET_INFO *cs)
- :Field_str(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
- unireg_check_arg, field_name_arg, cs)
+ TABLE_SHARE *share)
+ :Field_temporal(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
+ unireg_check_arg, field_name_arg)
{
/* For 4.0 MYD and 4.0 InnoDB compatibility */
- flags|= UNSIGNED_FLAG | BINARY_FLAG;
- if (unireg_check != NONE && !share->timestamp_field)
+ flags|= UNSIGNED_FLAG;
+ if (unireg_check != NONE)
{
- /* This timestamp has auto-update */
- share->timestamp_field= this;
+ /*
+ We mark the flag with TIMESTAMP_FLAG to indicate to the client that
+ this field will be automaticly updated on insert.
+ */
flags|= TIMESTAMP_FLAG;
if (unireg_check != TIMESTAMP_DN_FIELD)
flags|= ON_UPDATE_NOW_FLAG;
@@ -4445,40 +4574,6 @@ Field_timestamp::Field_timestamp(uchar *ptr_arg, uint32 len_arg,
}
-/**
- Get auto-set type for TIMESTAMP field.
-
- Returns value indicating during which operations this TIMESTAMP field
- should be auto-set to current timestamp.
-*/
-timestamp_auto_set_type Field_timestamp::get_auto_set_type() const
-{
- switch (unireg_check)
- {
- case TIMESTAMP_DN_FIELD:
- return TIMESTAMP_AUTO_SET_ON_INSERT;
- case TIMESTAMP_UN_FIELD:
- return TIMESTAMP_AUTO_SET_ON_UPDATE;
- case TIMESTAMP_OLD_FIELD:
- /*
- Although we can have several such columns in legacy tables this
- function should be called only for first of them (i.e. the one
- having auto-set property).
- */
- DBUG_ASSERT(table->timestamp_field == this);
- /* Fall-through */
- case TIMESTAMP_DNUN_FIELD:
- return TIMESTAMP_AUTO_SET_ON_BOTH;
- default:
- /*
- Normally this function should not be called for TIMESTAMPs without
- auto-set property.
- */
- DBUG_ASSERT(0);
- return TIMESTAMP_NO_AUTO_SET;
- }
-}
-
my_time_t Field_timestamp::get_timestamp(ulong *sec_part) const
{
ASSERT_COLUMN_MARKED_FOR_READ;
@@ -4489,17 +4584,23 @@ my_time_t Field_timestamp::get_timestamp(ulong *sec_part) const
int Field_timestamp::store_TIME_with_warning(THD *thd, MYSQL_TIME *l_time,
const ErrConv *str,
- bool was_cut,
+ int was_cut,
bool have_smth_to_conv)
{
ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
uint error = 0;
my_time_t timestamp;
- if (was_cut || !have_smth_to_conv)
+ if (MYSQL_TIME_WARN_HAVE_WARNINGS(was_cut) || !have_smth_to_conv)
{
error= 1;
- set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED,
+ set_datetime_warning(WARN_DATA_TRUNCATED,
+ str, MYSQL_TIMESTAMP_DATETIME, 1);
+ }
+ else if (MYSQL_TIME_WARN_HAVE_NOTES(was_cut))
+ {
+ error= 3;
+ set_datetime_warning(Sql_condition::WARN_LEVEL_NOTE, WARN_DATA_TRUNCATED,
str, MYSQL_TIMESTAMP_DATETIME, 1);
}
/* Only convert a correct date (not a zero date) */
@@ -4511,7 +4612,7 @@ int Field_timestamp::store_TIME_with_warning(THD *thd, MYSQL_TIME *l_time,
conversion_error= ER_WARN_DATA_OUT_OF_RANGE;
if (conversion_error)
{
- set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, conversion_error,
+ set_datetime_warning(conversion_error,
str, MYSQL_TIMESTAMP_DATETIME, !error);
error= 1;
}
@@ -4526,13 +4627,24 @@ int Field_timestamp::store_TIME_with_warning(THD *thd, MYSQL_TIME *l_time,
}
+static bool
+copy_or_convert_to_datetime(THD *thd, const MYSQL_TIME *from, MYSQL_TIME *to)
+{
+ if (from->time_type == MYSQL_TIMESTAMP_TIME)
+ return time_to_datetime(thd, from, to);
+ *to= *from;
+ return false;
+}
+
+
int Field_timestamp::store_time_dec(MYSQL_TIME *ltime, uint dec)
{
- THD *thd= table->in_use;
int unused;
- MYSQL_TIME l_time= *ltime;
ErrConvTime str(ltime);
- bool valid= !check_date(&l_time, pack_time(&l_time) != 0,
+ THD *thd= get_thd();
+ MYSQL_TIME l_time;
+ bool valid= !copy_or_convert_to_datetime(thd, ltime, &l_time) &&
+ !check_date(&l_time, pack_time(&l_time) != 0,
(thd->variables.sql_mode & MODE_NO_ZERO_DATE) |
MODE_NO_ZERO_IN_DATE, &unused);
@@ -4543,18 +4655,18 @@ int Field_timestamp::store_time_dec(MYSQL_TIME *ltime, uint dec)
int Field_timestamp::store(const char *from,uint len,CHARSET_INFO *cs)
{
MYSQL_TIME l_time;
- int error;
- int have_smth_to_conv;
+ MYSQL_TIME_STATUS status;
+ bool have_smth_to_conv;
ErrConvString str(from, len, cs);
- THD *thd= table->in_use;
+ THD *thd= get_thd();
/* We don't want to store invalid or fuzzy datetime values in TIMESTAMP */
- have_smth_to_conv= (str_to_datetime(cs, from, len, &l_time,
+ have_smth_to_conv= !str_to_datetime(cs, from, len, &l_time,
(thd->variables.sql_mode &
MODE_NO_ZERO_DATE) |
- MODE_NO_ZERO_IN_DATE, &error) >
- MYSQL_TIMESTAMP_ERROR);
- return store_TIME_with_warning(thd, &l_time, &str, error, have_smth_to_conv);
+ MODE_NO_ZERO_IN_DATE, &status);
+ return store_TIME_with_warning(thd, &l_time, &str,
+ status.warnings, have_smth_to_conv);
}
@@ -4563,7 +4675,7 @@ int Field_timestamp::store(double nr)
MYSQL_TIME l_time;
int error;
ErrConvDouble str(nr);
- THD *thd= table->in_use;
+ THD *thd= get_thd();
longlong tmp= double_to_datetime(nr, &l_time, (thd->variables.sql_mode &
MODE_NO_ZERO_DATE) |
@@ -4577,13 +4689,13 @@ int Field_timestamp::store(longlong nr, bool unsigned_val)
MYSQL_TIME l_time;
int error;
ErrConvInteger str(nr, unsigned_val);
- THD *thd= table->in_use;
+ THD *thd= get_thd();
/* We don't want to store invalid or fuzzy datetime values in TIMESTAMP */
longlong tmp= number_to_datetime(nr, 0, &l_time, (thd->variables.sql_mode &
MODE_NO_ZERO_DATE) |
MODE_NO_ZERO_IN_DATE, &error);
- return store_TIME_with_warning(thd, &l_time, &str, error, tmp != LL(-1));
+ return store_TIME_with_warning(thd, &l_time, &str, error, tmp != -1);
}
@@ -4609,6 +4721,7 @@ String *Field_timestamp::val_str(String *val_buffer, String *val_ptr)
{
MYSQL_TIME ltime;
uint32 temp, temp2;
+ uint dec;
char *to;
val_buffer->alloc(field_length+1);
@@ -4663,13 +4776,23 @@ String *Field_timestamp::val_str(String *val_buffer, String *val_ptr)
*to++= (char) ('0'+(char) (temp));
*to= 0;
val_buffer->set_charset(&my_charset_numeric);
+
+ if ((dec= decimals()))
+ {
+ ulong sec_part= (ulong) sec_part_shift(ltime.second_part, dec);
+ char *buf= const_cast<char*>(val_buffer->ptr() + MAX_DATETIME_WIDTH);
+ for (int i= dec; i > 0; i--, sec_part/= 10)
+ buf[i]= (char)(sec_part % 10) + '0';
+ buf[0]= '.';
+ buf[dec + 1]= 0;
+ }
return val_buffer;
}
bool Field_timestamp::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
{
- THD *thd= table->in_use;
+ THD *thd= get_thd();
thd->time_zone_used= 1;
ulong sec_part;
my_time_t temp= get_timestamp(&sec_part);
@@ -4716,23 +4839,51 @@ void Field_timestamp::sort_string(uchar *to,uint length __attribute__((unused)))
void Field_timestamp::sql_type(String &res) const
{
- res.set_ascii(STRING_WITH_LEN("timestamp"));
+ if (!decimals())
+ {
+ res.set_ascii(STRING_WITH_LEN("timestamp"));
+ return;
+ }
+ CHARSET_INFO *cs=res.charset();
+ res.length(cs->cset->snprintf(cs, (char*) res.ptr(), res.alloced_length(),
+ "timestamp(%u)", decimals()));
}
int Field_timestamp::set_time()
{
- THD *thd= table->in_use;
+ THD *thd= get_thd();
set_notnull();
store_TIME(thd->query_start(), 0);
return 0;
}
-void Field_timestamp_hires::sql_type(String &res) const
+/**
+ Mark the field as having an explicit default value.
+
+ @param value if available, the value that the field is being set to
+
+ @note
+ Fields that have an explicit default value should not be updated
+ automatically via the DEFAULT or ON UPDATE functions. The functions
+ that deal with data change functionality (INSERT/UPDATE/LOAD),
+ determine if there is an explicit value for each field before performing
+ the data change, and call this method to mark the field.
+
+ For timestamp columns, the only case where a column is not marked
+ as been given a value are:
+ - It's explicitly assigned with DEFAULT
+ - We assign NULL to a timestamp field that is defined as NOT NULL.
+ This is how MySQL has worked since it's start.
+*/
+
+void Field_timestamp::set_explicit_default(Item *value)
{
- CHARSET_INFO *cs=res.charset();
- res.length(cs->cset->snprintf(cs, (char*) res.ptr(), res.alloced_length(),
- "timestamp(%u)", dec));
+ if (((value->type() == Item::DEFAULT_VALUE_ITEM &&
+ !((Item_default_value*)value)->arg) ||
+ (!maybe_null() && value->null_value)))
+ return;
+ set_has_explicit_value();
}
#ifdef NOT_USED
@@ -4828,7 +4979,7 @@ my_time_t Field_timestamp_hires::get_timestamp(ulong *sec_part) const
return mi_uint4korr(ptr);
}
-double Field_timestamp_hires::val_real(void)
+double Field_timestamp_with_dec::val_real(void)
{
MYSQL_TIME ltime;
if (get_date(&ltime, TIME_NO_ZERO_DATE))
@@ -4839,39 +4990,21 @@ double Field_timestamp_hires::val_real(void)
ltime.minute * 1e2 + ltime.second + ltime.second_part*1e-6;
}
-String *Field_timestamp_hires::val_str(String *val_buffer, String *val_ptr)
-{
- String *tmp= Field_timestamp::val_str(val_buffer, val_ptr);
- ulong sec_part= (ulong)read_bigendian(ptr+4, sec_part_bytes[dec]);
-
- if (tmp->ptr() == zero_timestamp)
- return tmp;
-
- char *buf= const_cast<char*>(tmp->ptr() + MAX_DATETIME_WIDTH);
- for (int i=dec; i>0; i--, sec_part/=10)
- buf[i]= (char)(sec_part % 10) + '0';
- buf[0]= '.';
- buf[dec+1]= 0;
- return tmp;
-}
-
-
-my_decimal *Field_timestamp_hires::val_decimal(my_decimal *d)
+my_decimal *Field_timestamp_with_dec::val_decimal(my_decimal *d)
{
MYSQL_TIME ltime;
get_date(&ltime, 0);
- longlong intg= TIME_to_ulonglong(&ltime);
- return seconds2my_decimal(ltime.neg, intg, ltime.second_part, d);
+ return TIME_to_my_decimal(&ltime, d);
}
-int Field_timestamp_hires::store_decimal(const my_decimal *d)
+int Field_timestamp::store_decimal(const my_decimal *d)
{
ulonglong nr;
ulong sec_part;
int error;
MYSQL_TIME ltime;
longlong tmp;
- THD *thd= table->in_use;
+ THD *thd= get_thd();
ErrConvDecimal str(d);
if (my_decimal2seconds(d, &nr, &sec_part))
@@ -4887,15 +5020,15 @@ int Field_timestamp_hires::store_decimal(const my_decimal *d)
return store_TIME_with_warning(thd, &ltime, &str, error, tmp != -1);
}
-int Field_timestamp_hires::set_time()
+int Field_timestamp_with_dec::set_time()
{
- THD *thd= table->in_use;
+ THD *thd= get_thd();
set_notnull();
store_TIME(thd->query_start(), thd->query_start_sec_part());
return 0;
}
-bool Field_timestamp_hires::send_binary(Protocol *protocol)
+bool Field_timestamp_with_dec::send_binary(Protocol *protocol)
{
MYSQL_TIME ltime;
Field_timestamp::get_date(&ltime, 0);
@@ -4916,23 +5049,72 @@ int Field_timestamp_hires::cmp(const uchar *a_ptr, const uchar *b_ptr)
}
-void Field_timestamp_hires::sort_string(uchar *to,uint length)
-{
- DBUG_ASSERT(length == Field_timestamp_hires::pack_length());
- memcpy(to, ptr, length);
-}
-
uint32 Field_timestamp_hires::pack_length() const
{
return 4 + sec_part_bytes[dec];
}
-void Field_timestamp_hires::make_field(Send_field *field)
+void Field_timestamp_with_dec::make_field(Send_field *field)
{
Field::make_field(field);
field->decimals= dec;
}
+
+/*************************************************************
+** MySQL-5.6 compatible TIMESTAMP(N)
+**************************************************************/
+
+void Field_timestampf::store_TIME(my_time_t timestamp, ulong sec_part)
+{
+ struct timeval tm;
+ tm.tv_sec= timestamp;
+ tm.tv_usec= sec_part;
+ my_timeval_trunc(&tm, dec);
+ my_timestamp_to_binary(&tm, ptr, dec);
+}
+
+
+my_time_t Field_timestampf::get_timestamp(ulong *sec_part) const
+{
+ struct timeval tm;
+ my_timestamp_from_binary(&tm, ptr, dec);
+ *sec_part= tm.tv_usec;
+ return tm.tv_sec;
+}
+
+
+/*************************************************************/
+uint Field_temporal::is_equal(Create_field *new_field)
+{
+ return new_field->sql_type == real_type() &&
+ new_field->length == max_display_length();
+}
+
+
+void Field_temporal::set_warnings(Sql_condition::enum_warning_level trunc_level,
+ const ErrConv *str, int was_cut,
+ timestamp_type ts_type)
+{
+ /*
+ error code logic:
+ MYSQL_TIME_WARN_TRUNCATED means that the value was not a date/time at all.
+ it will be stored as zero date/time.
+ MYSQL_TIME_WARN_OUT_OF_RANGE means that the value was a date/time,
+ that is, it was parsed as such, but the value was invalid.
+
+ Also, MYSQL_TIME_WARN_TRUNCATED is used when storing a DATETIME in
+ a DATE field and non-zero time part is thrown away.
+ */
+ if (was_cut & MYSQL_TIME_WARN_TRUNCATED)
+ set_datetime_warning(trunc_level, WARN_DATA_TRUNCATED,
+ str, mysql_type_to_time_type(type()), 1);
+ if (was_cut & MYSQL_TIME_WARN_OUT_OF_RANGE)
+ set_datetime_warning(ER_WARN_DATA_OUT_OF_RANGE,
+ str, mysql_type_to_time_type(type()), 1);
+}
+
+
/*
Store string into a date/time field
@@ -4943,147 +5125,104 @@ void Field_timestamp_hires::make_field(Send_field *field)
3 Datetime value that was cut (warning level NOTE)
This is used by opt_range.cc:get_mm_leaf().
*/
-int Field_temporal::store_TIME_with_warning(MYSQL_TIME *ltime,
- const ErrConv *str,
- int was_cut, int have_smth_to_conv)
+int Field_temporal_with_date::store_TIME_with_warning(MYSQL_TIME *ltime,
+ const ErrConv *str,
+ int was_cut,
+ int have_smth_to_conv)
{
- MYSQL_ERROR::enum_warning_level trunc_level= MYSQL_ERROR::WARN_LEVEL_WARN;
+ Sql_condition::enum_warning_level trunc_level= Sql_condition::WARN_LEVEL_WARN;
int ret= 2;
ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
-#if MARIADB_VERSION_ID < 1000000
- /*
- Check if the YYYYMMDD part was truncated.
- Translate a note into a warning.
- In MariaDB-10.0 we have a better warnings/notes handling,
- so this code is not needed.
- */
- if (was_cut & MYSQL_TIME_NOTE_TRUNCATED)
- was_cut|= MYSQL_TIME_WARN_TRUNCATED;
-#endif
-
- if (was_cut == 0 &&
- have_smth_to_conv == 0 &&
- mysql_type_to_time_type(type()) != MYSQL_TIMESTAMP_TIME) // special case: zero date
+ if (was_cut == 0 && have_smth_to_conv == 0) // special case: zero date
+ {
was_cut= MYSQL_TIME_WARN_OUT_OF_RANGE;
- else
- if (!have_smth_to_conv)
+ }
+ else if (!have_smth_to_conv)
{
bzero(ltime, sizeof(*ltime));
was_cut= MYSQL_TIME_WARN_TRUNCATED;
ret= 1;
}
- else if (!(was_cut & MYSQL_TIME_WARN_TRUNCATED) &&
- mysql_type_to_time_type(type()) == MYSQL_TIMESTAMP_DATE &&
- (ltime->hour || ltime->minute || ltime->second || ltime->second_part))
+ else if (!MYSQL_TIME_WARN_HAVE_WARNINGS(was_cut) &&
+ (MYSQL_TIME_WARN_HAVE_NOTES(was_cut) ||
+ (mysql_type_to_time_type(type()) == MYSQL_TIMESTAMP_DATE &&
+ (ltime->hour || ltime->minute || ltime->second || ltime->second_part))))
{
- trunc_level= MYSQL_ERROR::WARN_LEVEL_NOTE;
+ trunc_level= Sql_condition::WARN_LEVEL_NOTE;
was_cut|= MYSQL_TIME_WARN_TRUNCATED;
ret= 3;
}
- else if (!(was_cut & MYSQL_TIME_WARN_TRUNCATED) &&
- mysql_type_to_time_type(type()) == MYSQL_TIMESTAMP_TIME &&
- (ltime->year || ltime->month))
- {
- ltime->year= ltime->month= ltime->day= 0;
- trunc_level= MYSQL_ERROR::WARN_LEVEL_NOTE;
- was_cut|= MYSQL_TIME_WARN_TRUNCATED;
- ret= 3;
- }
-
- /*
- error code logic:
- MYSQL_TIME_WARN_TRUNCATED means that the value was not a date/time at all.
- it will be stored as zero date/time.
- MYSQL_TIME_WARN_OUT_OF_RANGE means that the value was a date/time,
- that is, it was parsed as such, but the value was invalid.
-
- Also, MYSQL_TIME_WARN_TRUNCATED is used when storing a DATETIME in
- a DATE field and non-zero time part is thrown away.
- */
- if (was_cut & MYSQL_TIME_WARN_TRUNCATED)
- set_datetime_warning(trunc_level, WARN_DATA_TRUNCATED,
- str, mysql_type_to_time_type(type()), 1);
- if (was_cut & MYSQL_TIME_WARN_OUT_OF_RANGE)
- set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE,
- str, mysql_type_to_time_type(type()), 1);
-
+ set_warnings(trunc_level, str, was_cut, mysql_type_to_time_type(type()));
store_TIME(ltime);
return was_cut ? ret : 0;
}
-int Field_temporal::store(const char *from,uint len,CHARSET_INFO *cs)
+int Field_temporal_with_date::store(const char *from, uint len, CHARSET_INFO *cs)
{
MYSQL_TIME ltime;
- int error;
- enum enum_mysql_timestamp_type func_res;
- THD *thd= table->in_use;
+ MYSQL_TIME_STATUS status;
+ THD *thd= get_thd();
ErrConvString str(from, len, cs);
-
- func_res= str_to_datetime(cs, from, len, &ltime,
- (thd->variables.sql_mode &
- (MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE |
- MODE_INVALID_DATES)),
- &error);
- return store_TIME_with_warning(&ltime, &str, error, func_res > MYSQL_TIMESTAMP_ERROR);
+ bool func_res= !str_to_datetime(cs, from, len, &ltime,
+ sql_mode_for_dates(thd),
+ &status);
+ return store_TIME_with_warning(&ltime, &str, status.warnings, func_res);
}
-int Field_temporal::store(double nr)
+int Field_temporal_with_date::store(double nr)
{
int error= 0;
MYSQL_TIME ltime;
- THD *thd= table->in_use;
+ THD *thd= get_thd();
ErrConvDouble str(nr);
longlong tmp= double_to_datetime(nr, &ltime,
- (thd->variables.sql_mode &
- (MODE_NO_ZERO_IN_DATE |
- MODE_NO_ZERO_DATE |
- MODE_INVALID_DATES)), &error);
+ sql_mode_for_dates(thd), &error);
return store_TIME_with_warning(&ltime, &str, error, tmp != -1);
}
-int Field_temporal::store(longlong nr, bool unsigned_val)
+int Field_temporal_with_date::store(longlong nr, bool unsigned_val)
{
int error;
MYSQL_TIME ltime;
longlong tmp;
- THD *thd= table->in_use;
+ THD *thd= get_thd();
ErrConvInteger str(nr, unsigned_val);
- tmp= number_to_datetime(nr, 0, &ltime, (thd->variables.sql_mode &
- (MODE_NO_ZERO_IN_DATE |
- MODE_NO_ZERO_DATE |
- MODE_INVALID_DATES)), &error);
+ tmp= number_to_datetime(nr, 0, &ltime, sql_mode_for_dates(thd), &error);
return store_TIME_with_warning(&ltime, &str, error, tmp != -1);
}
-int Field_temporal::store_time_dec(MYSQL_TIME *ltime, uint dec)
+int Field_temporal_with_date::store_time_dec(MYSQL_TIME *ltime, uint dec)
{
- int error = 0, have_smth_to_conv= 1;
- MYSQL_TIME l_time= *ltime;
+ int error= 0, have_smth_to_conv= 1;
ErrConvTime str(ltime);
+ MYSQL_TIME l_time;
- if (l_time.time_type == MYSQL_TIMESTAMP_TIME && time_to_datetime(&l_time))
+ if (copy_or_convert_to_datetime(get_thd(), ltime, &l_time))
{
- have_smth_to_conv= 0;
- error= 1;
+ /*
+ Set have_smth_to_conv and error in a way to have
+ store_TIME_with_warning do bzero().
+ */
+ have_smth_to_conv= false;
+ error= MYSQL_TIME_WARN_OUT_OF_RANGE;
goto store;
}
+
/*
We don't perform range checking here since values stored in TIME
structure always fit into DATETIME range.
*/
have_smth_to_conv= !check_date(&l_time, pack_time(&l_time) != 0,
- (current_thd->variables.sql_mode &
- (MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE |
- MODE_INVALID_DATES)), &error);
+ sql_mode_for_dates(current_thd), &error);
store:
return store_TIME_with_warning(&l_time, &str, error, have_smth_to_conv);
}
@@ -5096,8 +5235,7 @@ my_decimal *Field_temporal::val_decimal(my_decimal *d)
bzero(&ltime, sizeof(ltime));
ltime.time_type= mysql_type_to_time_type(type());
}
- longlong intg= TIME_to_ulonglong(&ltime);
- return seconds2my_decimal(ltime.neg, intg, ltime.second_part, d);
+ return TIME_to_my_decimal(&ltime, d);
}
/****************************************************************************
@@ -5106,6 +5244,36 @@ my_decimal *Field_temporal::val_decimal(my_decimal *d)
** In number context: HHMMSS
** Stored as a 3 byte unsigned int
****************************************************************************/
+int Field_time::store_TIME_with_warning(MYSQL_TIME *ltime,
+ const ErrConv *str,
+ int was_cut,
+ int have_smth_to_conv)
+{
+ Sql_condition::enum_warning_level trunc_level= Sql_condition::WARN_LEVEL_WARN;
+ int ret= 2;
+ ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED;
+
+ if (!have_smth_to_conv)
+ {
+ bzero(ltime, sizeof(*ltime));
+ was_cut= MYSQL_TIME_WARN_TRUNCATED;
+ ret= 1;
+ }
+ else if (!MYSQL_TIME_WARN_HAVE_WARNINGS(was_cut) &&
+ ((ltime->year || ltime->month) ||
+ MYSQL_TIME_WARN_HAVE_NOTES(was_cut)))
+ {
+ if (ltime->year || ltime->month)
+ ltime->year= ltime->month= ltime->day= 0;
+ trunc_level= Sql_condition::WARN_LEVEL_NOTE;
+ was_cut|= MYSQL_TIME_WARN_TRUNCATED;
+ ret= 3;
+ }
+ set_warnings(trunc_level, str, was_cut, MYSQL_TIMESTAMP_TIME);
+ store_TIME(ltime);
+ return was_cut ? ret : 0;
+}
+
void Field_time::store_TIME(MYSQL_TIME *ltime)
{
@@ -5119,16 +5287,40 @@ void Field_time::store_TIME(MYSQL_TIME *ltime)
int Field_time::store(const char *from,uint len,CHARSET_INFO *cs)
{
MYSQL_TIME ltime;
+ MYSQL_TIME_STATUS status;
ErrConvString str(from, len, cs);
- int was_cut;
- int have_smth_to_conv=
- str_to_time(cs, from, len, &ltime,
- table->in_use->variables.sql_mode &
- (MODE_NO_ZERO_DATE | MODE_NO_ZERO_IN_DATE |
- MODE_INVALID_DATES),
- &was_cut) > MYSQL_TIMESTAMP_ERROR;
+ bool have_smth_to_conv=
+ !str_to_time(cs, from, len, &ltime, sql_mode_for_dates(get_thd()),
+ &status);
- return store_TIME_with_warning(&ltime, &str, was_cut, have_smth_to_conv);
+ return store_TIME_with_warning(&ltime, &str,
+ status.warnings, have_smth_to_conv);
+}
+
+
+/**
+ subtract a given number of days from DATETIME, return TIME
+
+ optimized version of calc_time_diff()
+
+ @note it might generate TIME values outside of the valid TIME range!
+*/
+static void calc_datetime_days_diff(MYSQL_TIME *ltime, long days)
+{
+ long daydiff= calc_daynr(ltime->year, ltime->month, ltime->day) - days;
+ ltime->year= ltime->month= 0;
+ if (daydiff >=0 )
+ ltime->day= daydiff;
+ else
+ {
+ longlong timediff= ((((daydiff * 24LL +
+ ltime->hour) * 60LL +
+ ltime->minute) * 60LL +
+ ltime->second) * 1000000LL +
+ ltime->second_part);
+ unpack_time(timediff, ltime);
+ }
+ ltime->time_type= MYSQL_TIMESTAMP_TIME;
}
@@ -5138,6 +5330,9 @@ int Field_time::store_time_dec(MYSQL_TIME *ltime, uint dec)
ErrConvTime str(ltime);
int was_cut= 0;
+ if (curdays && l_time.time_type != MYSQL_TIMESTAMP_TIME)
+ calc_datetime_days_diff(&l_time, curdays);
+
int have_smth_to_conv= !check_time_range(&l_time, decimals(), &was_cut);
return store_TIME_with_warning(&l_time, &str, was_cut, have_smth_to_conv);
}
@@ -5172,8 +5367,30 @@ int Field_time::store(longlong nr, bool unsigned_val)
return store_TIME_with_warning(&ltime, &str, was_cut, have_smth_to_conv);
}
-
-
+
+
+void Field_time::set_curdays(THD *thd)
+{
+ MYSQL_TIME ltime;
+ set_current_date(thd, &ltime);
+ curdays= calc_daynr(ltime.year, ltime.month, ltime.day);
+}
+
+
+Field *Field_time::new_key_field(MEM_ROOT *root, TABLE *new_table,
+ uchar *new_ptr, uint32 length,
+ uchar *new_null_ptr, uint new_null_bit)
+{
+ THD *thd= get_thd();
+ Field_time *res=
+ (Field_time*) Field::new_key_field(root, new_table, new_ptr, length,
+ new_null_ptr, new_null_bit);
+ if (!(thd->variables.old_behavior & OLD_MODE_ZERO_DATE_TIME_CAST) && res)
+ res->set_curdays(thd);
+ return res;
+}
+
+
double Field_time::val_real(void)
{
ASSERT_COLUMN_MARKED_FOR_READ;
@@ -5194,32 +5411,31 @@ longlong Field_time::val_int(void)
my_charset_bin
*/
-String *Field_time::val_str(String *val_buffer,
- String *val_ptr __attribute__((unused)))
+String *Field_time::val_str(String *str,
+ String *unused __attribute__((unused)))
{
ASSERT_COLUMN_MARKED_FOR_READ;
MYSQL_TIME ltime;
- long tmp=(long) sint3korr(ptr);
- ltime.neg= 0;
- if (tmp < 0)
- {
- tmp= -tmp;
- ltime.neg= 1;
- }
- ltime.year= ltime.month= 0;
- ltime.day= (uint) 0;
- ltime.hour= (uint) (tmp/10000);
- ltime.minute= (uint) (tmp/100 % 100);
- ltime.second= (uint) (tmp % 100);
- ltime.second_part= 0;
+ get_date(&ltime, TIME_TIME_ONLY);
+ str->alloc(field_length + 1);
+ str->length(my_time_to_str(&ltime, const_cast<char*>(str->ptr()), decimals()));
+ str->set_charset(&my_charset_numeric);
+ return str;
+}
- val_buffer->alloc(MAX_DATE_STRING_REP_LENGTH);
- uint length= (uint) my_time_to_str(&ltime,
- const_cast<char*>(val_buffer->ptr()), 0);
- val_buffer->length(length);
- val_buffer->set_charset(&my_charset_numeric);
- return val_buffer;
+bool Field_time::check_zero_in_date_with_warn(ulonglong fuzzydate)
+{
+ if (!(fuzzydate & TIME_TIME_ONLY) && (fuzzydate & TIME_NO_ZERO_IN_DATE))
+ {
+ THD *thd= get_thd();
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
+ ER_WARN_DATA_OUT_OF_RANGE,
+ ER(ER_WARN_DATA_OUT_OF_RANGE), field_name,
+ thd->get_stmt_da()->current_row_for_warning());
+ return true;
+ }
+ return false;
}
@@ -5232,16 +5448,8 @@ String *Field_time::val_str(String *val_buffer,
bool Field_time::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
{
- THD *thd= table->in_use;
- if (!(fuzzydate & TIME_TIME_ONLY) &&
- (fuzzydate & TIME_NO_ZERO_IN_DATE))
- {
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
- ER_WARN_DATA_OUT_OF_RANGE,
- ER(ER_WARN_DATA_OUT_OF_RANGE), field_name,
- thd->warning_info->current_row_for_warning());
- return 1;
- }
+ if (check_zero_in_date_with_warn(fuzzydate))
+ return true;
long tmp=(long) sint3korr(ptr);
ltime->neg=0;
if (tmp < 0)
@@ -5263,8 +5471,8 @@ bool Field_time::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
bool Field_time::send_binary(Protocol *protocol)
{
MYSQL_TIME ltime;
- Field_time::get_date(&ltime, TIME_TIME_ONLY);
- return protocol->store_time(&ltime, 0);
+ get_date(&ltime, TIME_TIME_ONLY);
+ return protocol->store_time(&ltime, decimals());
}
@@ -5285,7 +5493,14 @@ void Field_time::sort_string(uchar *to,uint length __attribute__((unused)))
void Field_time::sql_type(String &res) const
{
- res.set_ascii(STRING_WITH_LEN("time"));
+ if (decimals() == 0)
+ {
+ res.set_ascii(STRING_WITH_LEN("time"));
+ return;
+ }
+ const CHARSET_INFO *cs= res.charset();
+ res.length(cs->cset->snprintf(cs, (char*) res.ptr(), res.alloced_length(),
+ "time(%d)", decimals()));
}
int Field_time_hires::reset()
@@ -5301,7 +5516,7 @@ void Field_time_hires::store_TIME(MYSQL_TIME *ltime)
store_bigendian(packed, ptr, Field_time_hires::pack_length());
}
-int Field_time_hires::store_decimal(const my_decimal *d)
+int Field_time::store_decimal(const my_decimal *d)
{
ulonglong nr;
ulong sec_part;
@@ -5320,37 +5535,27 @@ uint32 Field_time_hires::pack_length() const
return time_hires_bytes[dec];
}
-longlong Field_time_hires::val_int(void)
+longlong Field_time_with_dec::val_int(void)
{
ASSERT_COLUMN_MARKED_FOR_READ;
MYSQL_TIME ltime;
- Field_time_hires::get_date(&ltime, TIME_TIME_ONLY);
+ get_date(&ltime, TIME_TIME_ONLY);
longlong val= TIME_to_ulonglong_time(&ltime);
return ltime.neg ? -val : val;
}
-double Field_time_hires::val_real(void)
+double Field_time_with_dec::val_real(void)
{
ASSERT_COLUMN_MARKED_FOR_READ;
MYSQL_TIME ltime;
- Field_time_hires::get_date(&ltime, TIME_TIME_ONLY);
+ get_date(&ltime, TIME_TIME_ONLY);
return TIME_to_double(&ltime);
}
-String *Field_time_hires::val_str(String *str,
- String *unused __attribute__((unused)))
-{
- ASSERT_COLUMN_MARKED_FOR_READ;
- MYSQL_TIME ltime;
- Field_time_hires::get_date(&ltime, TIME_TIME_ONLY);
- str->alloc(field_length+1);
- str->length(my_time_to_str(&ltime, (char*) str->ptr(), dec));
- str->set_charset(&my_charset_bin);
- return str;
-}
-
bool Field_time_hires::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
{
+ if (check_zero_in_date_with_warn(fuzzydate))
+ return true;
uint32 len= pack_length();
longlong packed= read_bigendian(ptr, len);
@@ -5364,15 +5569,7 @@ bool Field_time_hires::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
ltime->time_type= MYSQL_TIMESTAMP_TIME;
ltime->hour+= (ltime->month*32+ltime->day)*24;
ltime->month= ltime->day= 0;
- return !(fuzzydate & TIME_TIME_ONLY) && (fuzzydate & TIME_NO_ZERO_IN_DATE);
-}
-
-
-bool Field_time_hires::send_binary(Protocol *protocol)
-{
- MYSQL_TIME ltime;
- Field_time_hires::get_date(&ltime, TIME_TIME_ONLY);
- return protocol->store_time(&ltime, dec);
+ return false;
}
@@ -5390,17 +5587,38 @@ void Field_time_hires::sort_string(uchar *to,uint length __attribute__((unused))
to[0]^= 128;
}
-void Field_time_hires::sql_type(String &res) const
+void Field_time_with_dec::make_field(Send_field *field)
{
- CHARSET_INFO *cs=res.charset();
- res.length(cs->cset->snprintf(cs, (char*) res.ptr(), res.alloced_length(),
- "time(%u)", dec));
+ Field::make_field(field);
+ field->decimals= dec;
}
-void Field_time_hires::make_field(Send_field *field)
+/****************************************************************************
+** time type with fsp (MySQL-5.6 version)
+** In string context: HH:MM:SS.FFFFFF
+** In number context: HHMMSS.FFFFFF
+****************************************************************************/
+
+int Field_timef::reset()
{
- Field::make_field(field);
- field->decimals= dec;
+ my_time_packed_to_binary(0, ptr, dec);
+ return 0;
+}
+
+void Field_timef::store_TIME(MYSQL_TIME *ltime)
+{
+ my_time_trunc(ltime, decimals());
+ longlong tmp= TIME_to_longlong_time_packed(ltime);
+ my_time_packed_to_binary(tmp, ptr, dec);
+}
+
+bool Field_timef::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
+{
+ if (check_zero_in_date_with_warn(fuzzydate))
+ return true;
+ longlong tmp= my_time_packed_from_binary(ptr, dec);
+ TIME_from_longlong_time_packed(ltime, tmp);
+ return false;
}
/****************************************************************************
@@ -5420,10 +5638,10 @@ int Field_year::store(const char *from, uint len,CHARSET_INFO *cs)
error == MY_ERRNO_ERANGE)
{
*ptr=0;
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
return 1;
}
- if (table->in_use->count_cuted_fields &&
+ if (get_thd()->count_cuted_fields &&
(error= check_int(cs, from, len, end, error)))
{
if (error == 1) /* empty or incorrect string */
@@ -5463,7 +5681,7 @@ int Field_year::store(longlong nr, bool unsigned_val)
if (nr < 0 || (nr >= 100 && nr <= 1900) || nr > 2155)
{
*ptr= 0;
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
return 1;
}
if (nr != 0 || field_length != 4) // 0000 -> 0; 00 -> 2000
@@ -5484,8 +5702,7 @@ int Field_year::store_time_dec(MYSQL_TIME *ltime, uint dec)
if (Field_year::store(ltime->year, 0))
return 1;
- set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED,
- &str, ltime->time_type, 1);
+ set_datetime_warning(WARN_DATA_TRUNCATED, &str, ltime->time_type, 1);
return 0;
}
@@ -5759,8 +5976,8 @@ bool Field_datetime::send_binary(Protocol *protocol)
Field_datetime::get_date(&tm, 0);
return protocol->store(&tm, 0);
}
-
-
+
+
double Field_datetime::val_real(void)
{
return (double) Field_datetime::val_int();
@@ -5793,8 +6010,8 @@ String *Field_datetime::val_str(String *val_buffer,
Avoid problem with slow longlong arithmetic and sprintf
*/
- part1=(long) (tmp/LL(1000000));
- part2=(long) (tmp - (ulonglong) part1*LL(1000000));
+ part1=(long) (tmp/1000000LL);
+ part2=(long) (tmp - (ulonglong) part1*1000000LL);
pos=(char*) val_buffer->ptr() + MAX_DATETIME_WIDTH;
*pos--=0;
@@ -5825,8 +6042,8 @@ bool Field_datetime::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
{
longlong tmp=Field_datetime::val_int();
uint32 part1,part2;
- part1=(uint32) (tmp/LL(1000000));
- part2=(uint32) (tmp - (ulonglong) part1*LL(1000000));
+ part1=(uint32) (tmp/1000000LL);
+ part2=(uint32) (tmp - (ulonglong) part1*1000000LL);
ltime->time_type= MYSQL_TIMESTAMP_DATETIME;
ltime->neg= 0;
@@ -5868,23 +6085,44 @@ void Field_datetime::sort_string(uchar *to,uint length __attribute__((unused)))
void Field_datetime::sql_type(String &res) const
{
- res.set_ascii(STRING_WITH_LEN("datetime"));
+ if (decimals() == 0)
+ {
+ res.set_ascii(STRING_WITH_LEN("datetime"));
+ return;
+ }
+ CHARSET_INFO *cs= res.charset();
+ res.length(cs->cset->snprintf(cs, (char*) res.ptr(), res.alloced_length(),
+ "datetime(%u)", decimals()));
}
+
+int Field_datetime::set_time()
+{
+ THD *thd= table->in_use;
+ MYSQL_TIME now_time;
+ thd->variables.time_zone->gmt_sec_to_TIME(&now_time, thd->query_start());
+ now_time.second_part= thd->query_start_sec_part();
+ set_notnull();
+ store_TIME(&now_time);
+ thd->time_zone_used= 1;
+ return 0;
+}
+
+
void Field_datetime_hires::store_TIME(MYSQL_TIME *ltime)
{
ulonglong packed= sec_part_shift(pack_time(ltime), dec);
store_bigendian(packed, ptr, Field_datetime_hires::pack_length());
}
-int Field_datetime_hires::store_decimal(const my_decimal *d)
+int Field_temporal_with_date::store_decimal(const my_decimal *d)
{
ulonglong nr;
ulong sec_part;
int error;
MYSQL_TIME ltime;
longlong tmp;
- THD *thd= table->in_use;
+ THD *thd= get_thd();
ErrConvDecimal str(d);
if (my_decimal2seconds(d, &nr, &sec_part))
@@ -5893,46 +6131,44 @@ int Field_datetime_hires::store_decimal(const my_decimal *d)
error= 2;
}
else
- tmp= number_to_datetime(nr, sec_part, &ltime, (thd->variables.sql_mode &
- (MODE_NO_ZERO_IN_DATE |
- MODE_NO_ZERO_DATE |
- MODE_INVALID_DATES)), &error);
+ tmp= number_to_datetime(nr, sec_part, &ltime, sql_mode_for_dates(thd),
+ &error);
return store_TIME_with_warning(&ltime, &str, error, tmp != -1);
}
-bool Field_datetime_hires::send_binary(Protocol *protocol)
+bool Field_datetime_with_dec::send_binary(Protocol *protocol)
{
MYSQL_TIME ltime;
- Field_datetime_hires::get_date(&ltime, 0);
+ get_date(&ltime, 0);
return protocol->store(&ltime, dec);
}
-double Field_datetime_hires::val_real(void)
+double Field_datetime_with_dec::val_real(void)
{
MYSQL_TIME ltime;
- Field_datetime_hires::get_date(&ltime, 0);
+ get_date(&ltime, 0);
return TIME_to_double(&ltime);
}
-longlong Field_datetime_hires::val_int(void)
+longlong Field_datetime_with_dec::val_int(void)
{
MYSQL_TIME ltime;
- Field_datetime_hires::get_date(&ltime, 0);
+ get_date(&ltime, 0);
return TIME_to_ulonglong_datetime(&ltime);
}
-String *Field_datetime_hires::val_str(String *str,
- String *unused __attribute__((unused)))
+String *Field_datetime_with_dec::val_str(String *str,
+ String *unused __attribute__((unused)))
{
MYSQL_TIME ltime;
- Field_datetime_hires::get_date(&ltime, 0);
+ get_date(&ltime, 0);
str->alloc(field_length+1);
str->length(field_length);
my_datetime_to_str(&ltime, (char*) str->ptr(), dec);
- str->set_charset(&my_charset_bin);
+ str->set_charset(&my_charset_numeric);
return str;
}
@@ -5959,27 +6195,42 @@ int Field_datetime_hires::cmp(const uchar *a_ptr, const uchar *b_ptr)
return a < b ? -1 : a > b ? 1 : 0;
}
-void Field_datetime_hires::sort_string(uchar *to,
- uint length __attribute__((unused)))
+void Field_datetime_with_dec::make_field(Send_field *field)
{
- DBUG_ASSERT(length == Field_datetime_hires::pack_length());
- memcpy(to, ptr, length);
+ Field::make_field(field);
+ field->decimals= dec;
}
-void Field_datetime_hires::sql_type(String &res) const
+/****************************************************************************
+** MySQL-5.6 compatible DATETIME(N)
+**
+****************************************************************************/
+int Field_datetimef::reset()
{
- CHARSET_INFO *cs=res.charset();
- res.length(cs->cset->snprintf(cs, (char*) res.ptr(), res.alloced_length(),
- "datetime(%u)", dec));
+ my_datetime_packed_to_binary(0, ptr, dec);
+ return 0;
}
-void Field_datetime_hires::make_field(Send_field *field)
+void Field_datetimef::store_TIME(MYSQL_TIME *ltime)
{
- Field::make_field(field);
- field->decimals= dec;
+ my_time_trunc(ltime, decimals());
+ longlong tmp= TIME_to_longlong_datetime_packed(ltime);
+ my_datetime_packed_to_binary(tmp, ptr, dec);
+}
+
+bool Field_datetimef::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate)
+{
+ longlong tmp= my_datetime_packed_from_binary(ptr, dec);
+ TIME_from_longlong_datetime_packed(ltime, tmp);
+ if (!tmp)
+ return fuzzydate & TIME_NO_ZERO_DATE;
+ if (!ltime->month || !ltime->day)
+ return fuzzydate & TIME_NO_ZERO_IN_DATE;
+ return false;
}
+
/****************************************************************************
** string type
** A string may be varchar or binary
@@ -6011,29 +6262,21 @@ void Field_datetime_hires::make_field(Send_field *field)
TRUE - If an error happened
*/
-static bool
-check_string_copy_error(Field_str *field,
- const char *well_formed_error_pos,
- const char *cannot_convert_error_pos,
- const char *end,
- CHARSET_INFO *cs)
+bool
+Field_longstr::check_string_copy_error(const char *well_formed_error_pos,
+ const char *cannot_convert_error_pos,
+ const char *end,
+ CHARSET_INFO *cs)
{
const char *pos;
char tmp[32];
- THD *thd= field->table->in_use;
if (!(pos= well_formed_error_pos) &&
!(pos= cannot_convert_error_pos))
return FALSE;
convert_to_printable(tmp, sizeof(tmp), pos, (end - pos), cs, 6);
-
- push_warning_printf(thd,
- MYSQL_ERROR::WARN_LEVEL_WARN,
- ER_TRUNCATED_WRONG_VALUE_FOR_FIELD,
- ER(ER_TRUNCATED_WRONG_VALUE_FOR_FIELD),
- "string", tmp, field->field_name,
- thd->warning_info->current_row_for_warning());
+ set_warning_truncated_wrong_value("string", tmp);
return TRUE;
}
@@ -6062,19 +6305,20 @@ int
Field_longstr::report_if_important_data(const char *pstr, const char *end,
bool count_spaces)
{
- if ((pstr < end) && table->in_use->count_cuted_fields)
+ THD *thd= get_thd();
+ if ((pstr < end) && thd->count_cuted_fields)
{
if (test_if_important_data(field_charset, pstr, end))
{
- if (table->in_use->abort_on_warning)
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_DATA_TOO_LONG, 1);
+ if (thd->abort_on_warning)
+ set_warning(ER_DATA_TOO_LONG, 1);
else
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1);
+ set_warning(WARN_DATA_TRUNCATED, 1);
return 2;
}
else if (count_spaces)
{ /* If we lost only spaces then produce a NOTE, not a WARNING */
- set_warning(MYSQL_ERROR::WARN_LEVEL_NOTE, WARN_DATA_TRUNCATED, 1);
+ set_note(WARN_DATA_TRUNCATED, 1);
return 2;
}
}
@@ -6093,7 +6337,7 @@ int Field_string::store(const char *from,uint length,CHARSET_INFO *cs)
const char *from_end_pos;
/* See the comment for Field_long::store(long long) */
- DBUG_ASSERT(table->in_use == current_thd);
+ DBUG_ASSERT(!table || table->in_use == current_thd);
copy_length= well_formed_copy_nchars(field_charset,
(char*) ptr, field_length,
@@ -6109,7 +6353,7 @@ int Field_string::store(const char *from,uint length,CHARSET_INFO *cs)
field_length-copy_length,
field_charset->pad_char);
- if (check_string_copy_error(this, well_formed_error_pos,
+ if (check_string_copy_error(well_formed_error_pos,
cannot_convert_error_pos, from + length, cs))
return 2;
@@ -6139,15 +6383,14 @@ int Field_str::store(double nr)
if (error)
{
- if (table->in_use->abort_on_warning)
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_DATA_TOO_LONG, 1);
+ if (get_thd()->abort_on_warning)
+ set_warning(ER_DATA_TOO_LONG, 1);
else
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1);
+ set_warning(WARN_DATA_TRUNCATED, 1);
}
return store(buff, length, &my_charset_numeric);
}
-
uint Field::is_equal(Create_field *new_field)
{
return (new_field->sql_type == real_type());
@@ -6199,13 +6442,13 @@ double Field_string::val_real(void)
double result;
result= my_strntod(cs,(char*) ptr,field_length,&end,&error);
- if (!table->in_use->no_errors &&
+ if (!get_thd()->no_errors &&
(error || (field_length != (uint32)(end - (char*) ptr) &&
!check_if_only_end_space(cs, end,
(char*) ptr + field_length))))
{
ErrConvString err((char*) ptr, field_length, cs);
- push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
ER_TRUNCATED_WRONG_VALUE,
ER(ER_TRUNCATED_WRONG_VALUE), "DOUBLE",
err.ptr());
@@ -6223,13 +6466,13 @@ longlong Field_string::val_int(void)
longlong result;
result= my_strntoll(cs, (char*) ptr,field_length,10,&end,&error);
- if (!table->in_use->no_errors &&
+ if (!get_thd()->no_errors &&
(error || (field_length != (uint32)(end - (char*) ptr) &&
!check_if_only_end_space(cs, end,
(char*) ptr + field_length))))
{
ErrConvString err((char*) ptr, field_length, cs);
- push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
ER_TRUNCATED_WRONG_VALUE,
ER(ER_TRUNCATED_WRONG_VALUE),
"INTEGER", err.ptr());
@@ -6243,9 +6486,9 @@ String *Field_string::val_str(String *val_buffer __attribute__((unused)),
{
ASSERT_COLUMN_MARKED_FOR_READ;
/* See the comment for Field_long::store(long long) */
- DBUG_ASSERT(table->in_use == current_thd);
+ DBUG_ASSERT(!table || table->in_use == current_thd);
uint length;
- if (table->in_use->variables.sql_mode &
+ if (get_thd()->variables.sql_mode &
MODE_PAD_CHAR_TO_FULL_LENGTH)
length= my_charpos(field_charset, ptr, ptr + field_length,
field_length / field_charset->mbmaxlen);
@@ -6257,24 +6500,32 @@ String *Field_string::val_str(String *val_buffer __attribute__((unused)),
}
-my_decimal *Field_string::val_decimal(my_decimal *decimal_value)
+my_decimal *Field_longstr::val_decimal_from_str(const char *str,
+ uint length,
+ CHARSET_INFO *cs,
+ my_decimal *decimal_value)
{
- ASSERT_COLUMN_MARKED_FOR_READ;
- int err= str2my_decimal(E_DEC_FATAL_ERROR, (char*) ptr, field_length,
- charset(), decimal_value);
- if (!table->in_use->no_errors && err)
+ int err= str2my_decimal(E_DEC_FATAL_ERROR, str, length, cs, decimal_value);
+ if (!get_thd()->no_errors && err)
{
- ErrConvString errmsg((char*) ptr, field_length, charset());
- push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ ErrConvString errmsg(str, length, cs);
+ push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
ER_TRUNCATED_WRONG_VALUE,
ER(ER_TRUNCATED_WRONG_VALUE),
"DECIMAL", errmsg.ptr());
}
-
return decimal_value;
}
+my_decimal *Field_string::val_decimal(my_decimal *decimal_value)
+{
+ ASSERT_COLUMN_MARKED_FOR_READ;
+ return val_decimal_from_str((const char *) ptr, field_length,
+ Field_string::charset(), decimal_value);
+}
+
+
struct Check_field_param {
Field *field;
};
@@ -6335,7 +6586,13 @@ int Field_string::cmp(const uchar *a_ptr, const uchar *b_ptr)
void Field_string::sort_string(uchar *to,uint length)
{
uint tmp __attribute__((unused))=
- my_strnxfrm(field_charset, to, length, ptr, field_length);
+ field_charset->coll->strnxfrm(field_charset,
+ to, length,
+ char_length() *
+ field_charset->strxfrm_multiply,
+ ptr, field_length,
+ MY_STRXFRM_PAD_WITH_SPACE |
+ MY_STRXFRM_PAD_TO_MAXLEN);
DBUG_ASSERT(tmp == length);
}
@@ -6361,7 +6618,7 @@ void Field_string::sql_type(String &res) const
uchar *Field_string::pack(uchar *to, const uchar *from, uint max_length)
{
- uint length= min(field_length,max_length);
+ uint length= MY_MIN(field_length,max_length);
uint local_char_length= max_length/field_charset->mbmaxlen;
DBUG_PRINT("debug", ("Packing field '%s' - length: %u ", field_name, length));
@@ -6613,7 +6870,7 @@ int Field_varstring::store(const char *from,uint length,CHARSET_INFO *cs)
else
int2store(ptr, copy_length);
- if (check_string_copy_error(this, well_formed_error_pos,
+ if (check_string_copy_error(well_formed_error_pos,
cannot_convert_error_pos, from + length, cs))
return 2;
@@ -6646,7 +6903,7 @@ double Field_varstring::val_real(void)
uint length= length_bytes == 1 ? (uint) *ptr : uint2korr(ptr);
result= my_strntod(cs, (char*)ptr+length_bytes, length, &end, &error);
- if (!table->in_use->no_errors &&
+ if (!get_thd()->no_errors &&
(error || (length != (uint)(end - (char*)ptr+length_bytes) &&
!check_if_only_end_space(cs, end, (char*)ptr+length_bytes+length))))
{
@@ -6669,7 +6926,7 @@ longlong Field_varstring::val_int(void)
longlong result= my_strntoll(cs, (char*) ptr+length_bytes, length, 10,
&end, &error);
- if (!table->in_use->no_errors &&
+ if (!get_thd()->no_errors &&
(error || (length != (uint)(end - (char*)ptr+length_bytes) &&
!check_if_only_end_space(cs, end, (char*)ptr+length_bytes+length))))
{
@@ -6693,18 +6950,9 @@ String *Field_varstring::val_str(String *val_buffer __attribute__((unused)),
my_decimal *Field_varstring::val_decimal(my_decimal *decimal_value)
{
ASSERT_COLUMN_MARKED_FOR_READ;
- CHARSET_INFO *cs= charset();
uint length= length_bytes == 1 ? (uint) *ptr : uint2korr(ptr);
- int error= str2my_decimal(E_DEC_FATAL_ERROR, (char*) ptr+length_bytes, length,
- cs, decimal_value);
-
- if (!table->in_use->no_errors && error)
- {
- push_numerical_conversion_warning(current_thd, (char*)ptr+length_bytes,
- length, cs, "DECIMAL",
- ER_TRUNCATED_WRONG_VALUE);
- }
- return decimal_value;
+ return val_decimal_from_str((const char *) ptr + length_bytes, length,
+ Field_varstring::charset(), decimal_value);
}
@@ -6792,9 +7040,13 @@ void Field_varstring::sort_string(uchar *to,uint length)
length-= length_bytes;
}
- tot_length= my_strnxfrm(field_charset,
- to, length, ptr + length_bytes,
- tot_length);
+ tot_length= field_charset->coll->strnxfrm(field_charset,
+ to, length,
+ char_length() *
+ field_charset->strxfrm_multiply,
+ ptr + length_bytes, tot_length,
+ MY_STRXFRM_PAD_WITH_SPACE |
+ MY_STRXFRM_PAD_TO_MAXLEN);
DBUG_ASSERT(tot_length == length);
}
@@ -6984,17 +7236,14 @@ Field *Field_varstring::new_field(MEM_ROOT *root, TABLE *new_table,
}
-Field *Field_varstring::new_key_field(MEM_ROOT *root,
- TABLE *new_table,
- uchar *new_ptr, uchar *new_null_ptr,
- uint new_null_bit)
+Field *Field_varstring::new_key_field(MEM_ROOT *root, TABLE *new_table,
+ uchar *new_ptr, uint32 length,
+ uchar *new_null_ptr, uint new_null_bit)
{
Field_varstring *res;
- if ((res= (Field_varstring*) Field::new_key_field(root,
- new_table,
- new_ptr,
- new_null_ptr,
- new_null_bit)))
+ if ((res= (Field_varstring*) Field::new_key_field(root, new_table,
+ new_ptr, length,
+ new_null_ptr, new_null_bit)))
{
/* Keys length prefixes are always packed with 2 bytes */
res->length_bytes= 2;
@@ -7002,7 +7251,6 @@ Field *Field_varstring::new_key_field(MEM_ROOT *root,
return res;
}
-
uint Field_varstring::is_equal(Create_field *new_field)
{
if (new_field->sql_type == real_type() &&
@@ -7136,7 +7384,7 @@ int Field_blob::store(const char *from,uint length,CHARSET_INFO *cs)
from= tmpstr.ptr();
}
- new_length= min(max_data_length(), field_charset->mbmaxlen * length);
+ new_length= MY_MIN(max_data_length(), field_charset->mbmaxlen * length);
if (value.alloc(new_length))
goto oom_error;
@@ -7168,7 +7416,7 @@ int Field_blob::store(const char *from,uint length,CHARSET_INFO *cs)
tmp= value.ptr();
bmove(ptr+packlength,(uchar*) &tmp,sizeof(char*));
- if (check_string_copy_error(this, well_formed_error_pos,
+ if (check_string_copy_error(well_formed_error_pos,
cannot_convert_error_pos, from + length, cs))
return 2;
@@ -7254,9 +7502,8 @@ my_decimal *Field_blob::val_decimal(my_decimal *decimal_value)
else
length= get_length(ptr);
- str2my_decimal(E_DEC_FATAL_ERROR, blob, length, charset(),
- decimal_value);
- return decimal_value;
+ return val_decimal_from_str(blob, length,
+ Field_blob::charset(), decimal_value);
}
@@ -7296,7 +7543,7 @@ int Field_blob::cmp_binary(const uchar *a_ptr, const uchar *b_ptr,
b_length=get_length(b_ptr);
if (b_length > max_length)
b_length=max_length;
- diff=memcmp(a,b,min(a_length,b_length));
+ diff=memcmp(a,b,MY_MIN(a_length,b_length));
return diff ? diff : (int) (a_length - b_length);
}
@@ -7388,6 +7635,18 @@ int Field_blob::key_cmp(const uchar *a,const uchar *b)
}
+Field *Field_blob::new_key_field(MEM_ROOT *root, TABLE *new_table,
+ uchar *new_ptr, uint32 length,
+ uchar *new_null_ptr, uint new_null_bit)
+{
+ Field_varstring *res= new (root) Field_varstring(new_ptr, length, 2,
+ new_null_ptr, new_null_bit, Field::NONE,
+ field_name, table->s, charset());
+ res->init(new_table);
+ return res;
+}
+
+
/**
Save the field metadata for blob fields.
@@ -7437,8 +7696,11 @@ void Field_blob::sort_string(uchar *to,uint length)
}
memcpy(&blob, ptr+packlength, sizeof(char*));
- blob_length=my_strnxfrm(field_charset,
- to, length, blob, blob_length);
+ blob_length= field_charset->coll->strnxfrm(field_charset,
+ to, length, length,
+ blob, blob_length,
+ MY_STRXFRM_PAD_WITH_SPACE |
+ MY_STRXFRM_PAD_TO_MAXLEN);
DBUG_ASSERT(blob_length == length);
}
}
@@ -7474,7 +7736,7 @@ uchar *Field_blob::pack(uchar *to, const uchar *from, uint max_length)
length given is smaller than the actual length of the blob, we
just store the initial bytes of the blob.
*/
- store_length(to, packlength, min(length, max_length));
+ store_length(to, packlength, MY_MIN(length, max_length));
/*
Store the actual blob data, which will occupy 'length' bytes.
@@ -7636,7 +7898,7 @@ int Field_geom::store(const char *from, uint length, CHARSET_INFO *cs)
ER(ER_TRUNCATED_WRONG_VALUE_FOR_FIELD), MYF(0),
Geometry::ci_collection[geom_type]->m_name.str,
Geometry::ci_collection[wkb_type]->m_name.str, field_name,
- (ulong) table->in_use->warning_info->current_row_for_warning());
+ (ulong) table->in_use->get_stmt_da()->current_row_for_warning());
goto err_exit;
}
@@ -7727,13 +7989,13 @@ int Field_enum::store(const char *from,uint length,CHARSET_INFO *cs)
if (err || end != from+length || tmp > typelib->count)
{
tmp=0;
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1);
+ set_warning(WARN_DATA_TRUNCATED, 1);
}
- if (!table->in_use->count_cuted_fields)
+ if (!get_thd()->count_cuted_fields)
err= 0;
}
else
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1);
+ set_warning(WARN_DATA_TRUNCATED, 1);
}
store_type((ulonglong) tmp);
return err;
@@ -7752,8 +8014,8 @@ int Field_enum::store(longlong nr, bool unsigned_val)
int error= 0;
if ((ulonglong) nr > typelib->count || nr == 0)
{
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1);
- if (nr != 0 || table->in_use->count_cuted_fields)
+ set_warning(WARN_DATA_TRUNCATED, 1);
+ if (nr != 0 || get_thd()->count_cuted_fields)
{
nr= 0;
error= 1;
@@ -7905,11 +8167,11 @@ int Field_set::store(const char *from,uint length,CHARSET_INFO *cs)
tmp > (ulonglong) (((longlong) 1 << typelib->count) - (longlong) 1))
{
tmp=0;
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1);
+ set_warning(WARN_DATA_TRUNCATED, 1);
}
}
else if (got_warning)
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1);
+ set_warning(WARN_DATA_TRUNCATED, 1);
store_type(tmp);
return err;
}
@@ -7924,12 +8186,12 @@ int Field_set::store(longlong nr, bool unsigned_val)
if (sizeof(ulonglong)*8 <= typelib->count)
max_nr= ULONGLONG_MAX;
else
- max_nr= (ULL(1) << typelib->count) - 1;
+ max_nr= (1ULL << typelib->count) - 1;
if ((ulonglong) nr > max_nr)
{
nr&= max_nr;
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1);
+ set_warning(WARN_DATA_TRUNCATED, 1);
error=1;
}
store_type((ulonglong) nr);
@@ -8240,15 +8502,13 @@ Field_bit::do_last_null_byte() const
}
-Field *Field_bit::new_key_field(MEM_ROOT *root,
- TABLE *new_table,
- uchar *new_ptr, uchar *new_null_ptr,
- uint new_null_bit)
+Field *Field_bit::new_key_field(MEM_ROOT *root, TABLE *new_table,
+ uchar *new_ptr, uint32 length,
+ uchar *new_null_ptr, uint new_null_bit)
{
Field_bit *res;
- if ((res= (Field_bit*) Field::new_key_field(root, new_table,
- new_ptr, new_null_ptr,
- new_null_bit)))
+ if ((res= (Field_bit*) Field::new_key_field(root, new_table, new_ptr, length,
+ new_null_ptr, new_null_bit)))
{
/* Move bits normally stored in null_pointer to new_ptr */
res->bit_ptr= new_ptr;
@@ -8282,10 +8542,10 @@ int Field_bit::store(const char *from, uint length, CHARSET_INFO *cs)
{
set_rec_bits((1 << bit_len) - 1, bit_ptr, bit_ofs, bit_len);
memset(ptr, 0xff, bytes_in_rec);
- if (table->in_use->really_abort_on_warning())
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_DATA_TOO_LONG, 1);
+ if (get_thd()->really_abort_on_warning())
+ set_warning(ER_DATA_TOO_LONG, 1);
else
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
return 1;
}
/* delta is >= -1 here */
@@ -8334,7 +8594,7 @@ int Field_bit::store_decimal(const my_decimal *val)
{
int err= 0;
longlong i= convert_decimal2longlong(val, 1, &err);
- return test(err | store(i, TRUE));
+ return MY_TEST(err | store(i, TRUE));
}
@@ -8373,7 +8633,7 @@ String *Field_bit::val_str(String *val_buffer,
{
ASSERT_COLUMN_MARKED_FOR_READ;
char buff[sizeof(longlong)];
- uint length= min(pack_length(), sizeof(longlong));
+ uint length= MY_MIN(pack_length(), sizeof(longlong));
ulonglong bits= val_int();
mi_int8store(buff,bits);
@@ -8418,7 +8678,9 @@ int Field_bit::cmp_max(const uchar *a, const uchar *b, uint max_len)
if ((flag= (int) (bits_a - bits_b)))
return flag;
}
- return memcmp(a, b, field_length);
+ if (!bytes_in_rec)
+ return 0;
+ return memcmp(a, b, bytes_in_rec);
}
@@ -8459,7 +8721,7 @@ uint Field_bit::get_key_image(uchar *buff, uint length, imagetype type_arg)
*buff++= bits;
length--;
}
- uint data_length = min(length, bytes_in_rec);
+ uint data_length = MY_MIN(length, bytes_in_rec);
memcpy(buff, ptr, data_length);
return data_length + 1;
}
@@ -8583,7 +8845,7 @@ Field_bit::pack(uchar *to, const uchar *from, uint max_length)
uchar bits= get_rec_bits(bit_ptr + (from - ptr), bit_ofs, bit_len);
*to++= bits;
}
- length= min(bytes_in_rec, max_length - (bit_len > 0));
+ length= MY_MIN(bytes_in_rec, max_length - (bit_len > 0));
memcpy(to, from, length);
return to + length;
}
@@ -8621,7 +8883,7 @@ Field_bit::unpack(uchar *to, const uchar *from, const uchar *from_end,
if (param_data == 0 ||
((from_bit_len == bit_len) && (from_len == bytes_in_rec)))
{
- if (from + bytes_in_rec + test(bit_len) > from_end)
+ if (from + bytes_in_rec + MY_TEST(bit_len) > from_end)
return 0; // Error in data
if (bit_len > 0)
@@ -8717,10 +8979,10 @@ int Field_bit_as_char::store(const char *from, uint length, CHARSET_INFO *cs)
memset(ptr, 0xff, bytes_in_rec);
if (bits)
*ptr&= ((1 << bits) - 1); /* set first uchar */
- if (table->in_use->really_abort_on_warning())
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_DATA_TOO_LONG, 1);
+ if (get_thd()->really_abort_on_warning())
+ set_warning(ER_DATA_TOO_LONG, 1);
else
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
+ set_warning(ER_WARN_DATA_OUT_OF_RANGE, 1);
return 1;
}
bzero(ptr, delta);
@@ -8776,7 +9038,7 @@ void Create_field::create_length_to_internal_length(void)
{
pack_length= length / 8;
/* We need one extra byte to store the bits we save among the null bits */
- key_length= pack_length + test(length & 7);
+ key_length= pack_length + MY_TEST(length & 7);
}
break;
case MYSQL_TYPE_NEWDECIMAL:
@@ -8899,6 +9161,7 @@ void Create_field::init_for_tmp_table(enum_field_types sql_type_arg,
FLAGSTR(pack_flag, FIELDFLAG_DECIMAL),
f_packtype(pack_flag)));
vcol_info= 0;
+ create_if_not_exists= FALSE;
stored_in_db= TRUE;
DBUG_VOID_RETURN;
@@ -8936,20 +9199,41 @@ bool Create_field::init(THD *thd, char *fld_name, enum_field_types fld_type,
char *fld_change, List<String> *fld_interval_list,
CHARSET_INFO *fld_charset, uint fld_geom_type,
Virtual_column_info *fld_vcol_info,
- engine_option_value *create_opt)
+ engine_option_value *create_opt, bool check_exists)
{
uint sign_len, allowed_type_modifier= 0;
ulong max_field_charlength= MAX_FIELD_CHARLENGTH;
+ const bool on_update_is_function=
+ (fld_on_update_value != NULL &&
+ fld_on_update_value->type() == Item::FUNC_ITEM);
DBUG_ENTER("Create_field::init()");
field= 0;
field_name= fld_name;
- def= fld_default_value;
flags= fld_type_modifier;
option_list= create_opt;
- unireg_check= (fld_type_modifier & AUTO_INCREMENT_FLAG ?
- Field::NEXT_NUMBER : Field::NONE);
+
+ if (fld_default_value != NULL && fld_default_value->type() == Item::FUNC_ITEM)
+ {
+ /* There is a function default for insertions. */
+ def= NULL;
+ unireg_check= (on_update_is_function ?
+ Field::TIMESTAMP_DNUN_FIELD : // for insertions and for updates.
+ Field::TIMESTAMP_DN_FIELD); // only for insertions.
+ }
+ else
+ {
+ /* No function default for insertions. Either NULL or a constant. */
+ def= fld_default_value;
+ if (on_update_is_function)
+ unireg_check= Field::TIMESTAMP_UN_FIELD; // function default for updates
+ else
+ unireg_check= ((fld_type_modifier & AUTO_INCREMENT_FLAG) != 0 ?
+ Field::NEXT_NUMBER : // Automatic increment.
+ Field::NONE);
+ }
+
decimals= fld_decimals ? (uint)atoi(fld_decimals) : 0;
if (decimals >= NOT_FIXED_DEC)
{
@@ -8969,6 +9253,7 @@ bool Create_field::init(THD *thd, char *fld_name, enum_field_types fld_type,
comment= *fld_comment;
vcol_info= fld_vcol_info;
+ create_if_not_exists= check_exists;
stored_in_db= TRUE;
/* Initialize data for a computed field */
@@ -9000,7 +9285,7 @@ bool Create_field::init(THD *thd, char *fld_name, enum_field_types fld_type,
it is NOT NULL, not an AUTO_INCREMENT field and not a TIMESTAMP.
*/
if (!fld_default_value && !(fld_type_modifier & AUTO_INCREMENT_FLAG) &&
- (fld_type_modifier & NOT_NULL_FLAG) && fld_type != MYSQL_TYPE_TIMESTAMP)
+ (fld_type_modifier & NOT_NULL_FLAG) && !is_timestamp_type(fld_type))
flags|= NO_DEFAULT_VALUE_FLAG;
if (fld_length != NULL)
@@ -9089,9 +9374,7 @@ bool Create_field::init(THD *thd, char *fld_name, enum_field_types fld_type,
A default other than '' is always an error, and any non-NULL
specified default is an error in strict mode.
*/
- if (res->length() || (thd->variables.sql_mode &
- (MODE_STRICT_TRANS_TABLES |
- MODE_STRICT_ALL_TABLES)))
+ if (res->length() || thd->is_strict_mode())
{
my_error(ER_BLOB_CANT_HAVE_DEFAULT, MYF(0),
fld_name); /* purecov: inspected */
@@ -9102,7 +9385,7 @@ bool Create_field::init(THD *thd, char *fld_name, enum_field_types fld_type,
/*
Otherwise a default of '' is just a warning.
*/
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_BLOB_CANT_HAVE_DEFAULT,
ER(ER_BLOB_CANT_HAVE_DEFAULT),
fld_name);
@@ -9164,6 +9447,7 @@ bool Create_field::init(THD *thd, char *fld_name, enum_field_types fld_type,
}
break;
case MYSQL_TYPE_TIMESTAMP:
+ case MYSQL_TYPE_TIMESTAMP2:
if (length > MAX_DATETIME_PRECISION)
{
my_error(ER_TOO_BIG_PRECISION, MYF(0), length, fld_name,
@@ -9172,44 +9456,6 @@ bool Create_field::init(THD *thd, char *fld_name, enum_field_types fld_type,
}
length+= MAX_DATETIME_WIDTH + (length ? 1 : 0);
flags|= UNSIGNED_FLAG;
-
- if (fld_default_value)
- {
- /* Grammar allows only NOW() value for ON UPDATE clause */
- if (fld_default_value->type() == Item::FUNC_ITEM &&
- ((Item_func*)fld_default_value)->functype() == Item_func::NOW_FUNC)
- {
- unireg_check= (fld_on_update_value ? Field::TIMESTAMP_DNUN_FIELD:
- Field::TIMESTAMP_DN_FIELD);
- /*
- We don't need default value any longer moreover it is dangerous.
- Everything handled by unireg_check further.
- */
- def= 0;
- }
- else
- unireg_check= (fld_on_update_value ? Field::TIMESTAMP_UN_FIELD:
- Field::NONE);
- }
- else
- {
- /*
- If we have default TIMESTAMP NOT NULL column without explicit DEFAULT
- or ON UPDATE values then for the sake of compatiblity we should treat
- this column as having DEFAULT NOW() ON UPDATE NOW() (when we don't
- have another TIMESTAMP column with auto-set option before this one)
- or DEFAULT 0 (in other cases).
- So here we are setting TIMESTAMP_OLD_FIELD only temporary, and will
- replace this value by TIMESTAMP_DNUN_FIELD or NONE later when
- information about all TIMESTAMP fields in table will be availiable.
-
- If we have TIMESTAMP NULL column without explicit DEFAULT value
- we treat it as having DEFAULT NULL attribute.
- */
- unireg_check= (fld_on_update_value ? Field::TIMESTAMP_UN_FIELD :
- (flags & NOT_NULL_FLAG ? Field::TIMESTAMP_OLD_FIELD :
- Field::NONE));
- }
break;
case MYSQL_TYPE_DATE:
/* We don't support creation of MYSQL_TYPE_DATE anymore */
@@ -9219,6 +9465,7 @@ bool Create_field::init(THD *thd, char *fld_name, enum_field_types fld_type,
length= MAX_DATE_WIDTH;
break;
case MYSQL_TYPE_TIME:
+ case MYSQL_TYPE_TIME2:
if (length > MAX_DATETIME_PRECISION)
{
my_error(ER_TOO_BIG_PRECISION, MYF(0), length, fld_name,
@@ -9228,6 +9475,7 @@ bool Create_field::init(THD *thd, char *fld_name, enum_field_types fld_type,
length+= MIN_TIME_WIDTH + (length ? 1 : 0);
break;
case MYSQL_TYPE_DATETIME:
+ case MYSQL_TYPE_DATETIME2:
if (length > MAX_DATETIME_PRECISION)
{
my_error(ER_TOO_BIG_PRECISION, MYF(0), length, fld_name,
@@ -9309,17 +9557,6 @@ bool Create_field::init(THD *thd, char *fld_name, enum_field_types fld_type,
DBUG_RETURN(TRUE);
}
- switch (fld_type) {
- case MYSQL_TYPE_DATE:
- case MYSQL_TYPE_NEWDATE:
- case MYSQL_TYPE_TIME:
- case MYSQL_TYPE_DATETIME:
- case MYSQL_TYPE_TIMESTAMP:
- charset= &my_charset_numeric;
- flags|= BINARY_FLAG;
- default: break;
- }
-
DBUG_RETURN(FALSE); /* success */
}
@@ -9358,10 +9595,16 @@ uint32 calc_pack_length(enum_field_types type,uint32 length)
case MYSQL_TYPE_TIME: return length > MIN_TIME_WIDTH
? time_hires_bytes[length - 1 - MIN_TIME_WIDTH]
: 3;
+ case MYSQL_TYPE_TIME2:
+ return length > MIN_TIME_WIDTH ?
+ my_time_binary_length(length - MIN_TIME_WIDTH - 1) : 3;
case MYSQL_TYPE_TIMESTAMP:
return length > MAX_DATETIME_WIDTH
? 4 + sec_part_bytes[length - 1 - MAX_DATETIME_WIDTH]
: 4;
+ case MYSQL_TYPE_TIMESTAMP2:
+ return length > MAX_DATETIME_WIDTH ?
+ my_timestamp_binary_length(length - MAX_DATETIME_WIDTH - 1) : 4;
case MYSQL_TYPE_DATE:
case MYSQL_TYPE_LONG : return 4;
case MYSQL_TYPE_FLOAT : return sizeof(float);
@@ -9370,6 +9613,9 @@ uint32 calc_pack_length(enum_field_types type,uint32 length)
return length > MAX_DATETIME_WIDTH
? datetime_hires_bytes[length - 1 - MAX_DATETIME_WIDTH]
: 8;
+ case MYSQL_TYPE_DATETIME2:
+ return length > MAX_DATETIME_WIDTH ?
+ my_datetime_binary_length(length - MAX_DATETIME_WIDTH - 1) : 5;
case MYSQL_TYPE_LONGLONG: return 8; /* Don't crash if no longlong */
case MYSQL_TYPE_NULL : return 0;
case MYSQL_TYPE_TINY_BLOB: return 1+portable_sizeof_char_ptr;
@@ -9434,16 +9680,6 @@ Field *make_field(TABLE_SHARE *share, uchar *ptr, uint32 field_length,
null_bit= ((uchar) 1) << null_bit;
}
- switch (field_type) {
- case MYSQL_TYPE_DATE:
- case MYSQL_TYPE_NEWDATE:
- case MYSQL_TYPE_TIME:
- case MYSQL_TYPE_DATETIME:
- case MYSQL_TYPE_TIMESTAMP:
- field_charset= &my_charset_numeric;
- default: break;
- }
-
DBUG_PRINT("debug", ("field_type: %d, field_length: %u, interval: %p, pack_flag: %s%s%s%s%s",
field_type, field_length, interval,
FLAGSTR(pack_flag, FIELDFLAG_BINARY),
@@ -9557,30 +9793,51 @@ Field *make_field(TABLE_SHARE *share, uchar *ptr, uint32 field_length,
uint dec= field_length > MAX_DATETIME_WIDTH ?
field_length - MAX_DATETIME_WIDTH - 1: 0;
return new_Field_timestamp(ptr, null_pos, null_bit, unireg_check,
- field_name, share, dec, field_charset);
+ field_name, share, dec);
+ }
+ case MYSQL_TYPE_TIMESTAMP2:
+ {
+ uint dec= field_length > MAX_DATETIME_WIDTH ?
+ field_length - MAX_DATETIME_WIDTH - 1: 0;
+ return new Field_timestampf(ptr, null_pos, null_bit, unireg_check,
+ field_name, share, dec);
}
case MYSQL_TYPE_YEAR:
return new Field_year(ptr,field_length,null_pos,null_bit,
unireg_check, field_name);
case MYSQL_TYPE_DATE:
return new Field_date(ptr,null_pos,null_bit,
- unireg_check, field_name, field_charset);
+ unireg_check, field_name);
case MYSQL_TYPE_NEWDATE:
return new Field_newdate(ptr,null_pos,null_bit,
- unireg_check, field_name, field_charset);
+ unireg_check, field_name);
case MYSQL_TYPE_TIME:
{
uint dec= field_length > MIN_TIME_WIDTH ?
field_length - MIN_TIME_WIDTH - 1: 0;
return new_Field_time(ptr, null_pos, null_bit, unireg_check,
- field_name, dec, field_charset);
+ field_name, dec);
+ }
+ case MYSQL_TYPE_TIME2:
+ {
+ uint dec= field_length > MIN_TIME_WIDTH ?
+ field_length - MIN_TIME_WIDTH - 1: 0;
+ return new Field_timef(ptr, null_pos, null_bit, unireg_check,
+ field_name, dec);
}
case MYSQL_TYPE_DATETIME:
{
uint dec= field_length > MAX_DATETIME_WIDTH ?
field_length - MAX_DATETIME_WIDTH - 1: 0;
return new_Field_datetime(ptr, null_pos, null_bit, unireg_check,
- field_name, dec, field_charset);
+ field_name, dec);
+ }
+ case MYSQL_TYPE_DATETIME2:
+ {
+ uint dec= field_length > MAX_DATETIME_WIDTH ?
+ field_length - MAX_DATETIME_WIDTH - 1: 0;
+ return new Field_datetimef(ptr, null_pos, null_bit, unireg_check,
+ field_name, dec);
}
case MYSQL_TYPE_NULL:
return new Field_null(ptr, field_length, unireg_check, field_name,
@@ -9615,15 +9872,11 @@ Create_field::Create_field(Field *old_field,Field *orig_field)
comment= old_field->comment;
decimals= old_field->decimals();
vcol_info= old_field->vcol_info;
+ create_if_not_exists= FALSE;
stored_in_db= old_field->stored_in_db;
option_list= old_field->option_list;
option_struct= old_field->option_struct;
- /* Fix if the original table had 4 byte pointer blobs */
- if (flags & BLOB_FLAG)
- pack_length= (pack_length- old_field->table->s->blob_ptr_size +
- portable_sizeof_char_ptr);
-
switch (sql_type) {
case MYSQL_TYPE_BLOB:
switch (pack_length - portable_sizeof_char_ptr) {
@@ -9658,7 +9911,7 @@ Create_field::Create_field(Field *old_field,Field *orig_field)
{
char buff[sizeof("YEAR()") + MY_INT64_NUM_DECIMAL_DIGITS + 1];
my_snprintf(buff, sizeof(buff), "YEAR(%lu)", length);
- push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
+ push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_NOTE,
ER_WARN_DEPRECATED_SYNTAX,
ER(ER_WARN_DEPRECATED_SYNTAX),
buff, "YEAR(4)");
@@ -9675,29 +9928,53 @@ Create_field::Create_field(Field *old_field,Field *orig_field)
def=0;
char_length= length;
+ /*
+ Copy the default (constant/function) from the column object orig_field, if
+ supplied. We do this if all these conditions are met:
+
+ - The column allows a default.
+
+ - The column type is not a BLOB type.
+
+ - The original column (old_field) was properly initialized with a record
+ buffer pointer.
+ */
if (!(flags & (NO_DEFAULT_VALUE_FLAG | BLOB_FLAG)) &&
- old_field->ptr && orig_field &&
- (sql_type != MYSQL_TYPE_TIMESTAMP || /* set def only if */
- old_field->table->timestamp_field != old_field || /* timestamp field */
- unireg_check == Field::TIMESTAMP_UN_FIELD)) /* has default val */
- {
- char buff[MAX_FIELD_WIDTH];
- String tmp(buff,sizeof(buff), charset);
- my_ptrdiff_t diff;
-
- /* Get the value from default_values */
- diff= (my_ptrdiff_t) (orig_field->table->s->default_values-
- orig_field->table->record[0]);
- orig_field->move_field_offset(diff); // Points now at default_values
- if (!orig_field->is_real_null())
+ old_field->ptr != NULL &&
+ orig_field != NULL)
+ {
+ bool default_now= false;
+ if (real_type_with_now_as_default(sql_type))
+ {
+ // The SQL type of the new field allows a function default:
+ default_now= orig_field->has_insert_default_function();
+ bool update_now= orig_field->has_update_default_function();
+
+ if (default_now && update_now)
+ unireg_check= Field::TIMESTAMP_DNUN_FIELD;
+ else if (default_now)
+ unireg_check= Field::TIMESTAMP_DN_FIELD;
+ else if (update_now)
+ unireg_check= Field::TIMESTAMP_UN_FIELD;
+ }
+ if (!default_now) // Give a constant default
{
- char buff[MAX_FIELD_WIDTH], *pos;
- String tmp(buff, sizeof(buff), charset), *res;
- res= orig_field->val_str(&tmp);
- pos= (char*) sql_strmake(res->ptr(), res->length());
- def= new Item_string(pos, res->length(), charset);
+ char buff[MAX_FIELD_WIDTH];
+ String tmp(buff,sizeof(buff), charset);
+
+ /* Get the value from default_values */
+ my_ptrdiff_t diff= orig_field->table->default_values_offset();
+ orig_field->move_field_offset(diff); // Points now at default_values
+ if (!orig_field->is_real_null())
+ {
+ char buff[MAX_FIELD_WIDTH], *pos;
+ String tmp(buff, sizeof(buff), charset), *res;
+ res= orig_field->val_str(&tmp);
+ pos= (char*) sql_strmake(res->ptr(), res->length());
+ def= new Item_string(pos, res->length(), charset);
+ }
+ orig_field->move_field_offset(-diff); // Back to record[0]
}
- orig_field->move_field_offset(-diff); // Back to record[0]
}
}
@@ -9779,11 +10056,11 @@ uint32 Field_blob::max_display_length()
*****************************************************************************/
/**
- Produce warning or note about data saved into field.
+* Produce warning or note about data saved into field.
@param level - level of message (Note/Warning/Error)
@param code - error code of message to be produced
- @param cuted_increment - whenever we should increase cut fields count or not
+ @param cut_increment - whenever we should increase cut fields count
@note
This function won't produce warning and increase cut fields counter
@@ -9791,11 +10068,16 @@ uint32 Field_blob::max_display_length()
if count_cuted_fields == CHECK_FIELD_IGNORE then we ignore notes.
This allows us to avoid notes in optimisation, like convert_constant_item().
+
+ @retval
+ 1 if count_cuted_fields == CHECK_FIELD_IGNORE and error level is not NOTE
+ @retval
+ 0 otherwise
*/
-void
-Field::set_warning(MYSQL_ERROR::enum_warning_level level, uint code,
- int cuted_increment)
+bool
+Field::set_warning(Sql_condition::enum_warning_level level, uint code,
+ int cut_increment) const
{
/*
If this field was created only for type conversion purposes it
@@ -9804,10 +10086,12 @@ Field::set_warning(MYSQL_ERROR::enum_warning_level level, uint code,
THD *thd= table ? table->in_use : current_thd;
if (thd->count_cuted_fields)
{
- thd->cuted_fields+= cuted_increment;
+ thd->cuted_fields+= cut_increment;
push_warning_printf(thd, level, code, ER(code), field_name,
- thd->warning_info->current_row_for_warning());
+ thd->get_stmt_da()->current_row_for_warning());
+ return 0;
}
+ return level >= Sql_condition::WARN_LEVEL_WARN;
}
@@ -9829,18 +10113,31 @@ Field::set_warning(MYSQL_ERROR::enum_warning_level level, uint code,
*/
-void Field::set_datetime_warning(MYSQL_ERROR::enum_warning_level level,
+void Field::set_datetime_warning(Sql_condition::enum_warning_level level,
uint code, const ErrConv *str,
timestamp_type ts_type, int cuted_increment)
{
- THD *thd= table->in_use;
- if (thd->really_abort_on_warning() && level >= MYSQL_ERROR::WARN_LEVEL_WARN)
+ THD *thd= get_thd();
+ if (thd->really_abort_on_warning() && level >= Sql_condition::WARN_LEVEL_WARN)
make_truncated_value_warning(thd, level, str, ts_type, field_name);
else
set_warning(level, code, cuted_increment);
}
+void Field::set_warning_truncated_wrong_value(const char *type,
+ const char *value)
+{
+ THD *thd= get_thd();
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
+ ER_TRUNCATED_WRONG_VALUE_FOR_FIELD,
+ ER(ER_TRUNCATED_WRONG_VALUE_FOR_FIELD),
+ type, value, field_name,
+ static_cast<ulong>(thd->get_stmt_da()->
+ current_row_for_warning()));
+}
+
+
/*
@brief
Return possible keys for a field
@@ -9863,3 +10160,29 @@ key_map Field::get_possible_keys()
return (table->pos_in_table_list->is_materialized_derived() ?
part_of_key : key_start);
}
+
+
+/**
+ Mark the field as having an explicit default value.
+
+ @param value if available, the value that the field is being set to
+
+ @note
+ Fields that have an explicit default value should not be updated
+ automatically via the DEFAULT or ON UPDATE functions. The functions
+ that deal with data change functionality (INSERT/UPDATE/LOAD),
+ determine if there is an explicit value for each field before performing
+ the data change, and call this method to mark the field.
+
+ If the 'value' parameter is NULL, then the field is marked unconditionally
+ as having an explicit value. If 'value' is not NULL, then it can be further
+ analyzed to check if it really should count as a value.
+*/
+
+void Field::set_explicit_default(Item *value)
+{
+ if (value->type() == Item::DEFAULT_VALUE_ITEM &&
+ !((Item_default_value*)value)->arg)
+ return;
+ set_has_explicit_value();
+}
diff --git a/sql/field.h b/sql/field.h
index f761aa8d3ea..81b16606e8b 100644
--- a/sql/field.h
+++ b/sql/field.h
@@ -29,13 +29,16 @@
#include "table.h" /* TABLE */
#include "sql_string.h" /* String */
#include "my_decimal.h" /* my_decimal */
-#include "sql_error.h" /* MYSQL_ERROR */
+#include "sql_error.h" /* Sql_condition */
+#include "compat56.h"
class Send_field;
class Protocol;
class Create_field;
class Relay_log_info;
class Field;
+class Column_statistics;
+class Column_statistics_collected;
enum enum_check_fields
{
@@ -64,12 +67,16 @@ enum Derivation
#define MY_REPERTOIRE_NUMERIC MY_REPERTOIRE_ASCII
/* The length of the header part for each virtual column in the .frm file */
-#define FRM_VCOL_HEADER_SIZE(b) (3 + test(b))
+#define FRM_VCOL_HEADER_SIZE(b) (3 + MY_TEST(b))
+
+class Count_distinct_field;
struct ha_field_option_struct;
struct st_cache_field;
int field_conv(Field *to,Field *from);
+int field_conv_incompatible(Field *to,Field *from);
+bool memcpy_field_possible(Field *to, Field *from);
int truncate_double(double *nr, uint field_length, uint dec,
bool unsigned_flag, double max_value);
longlong double_to_longlong(double nr, bool unsigned_flag, bool *error);
@@ -86,12 +93,89 @@ inline uint get_set_pack_length(int elements)
}
+/**
+ Tests if field type is temporal and has date part,
+ i.e. represents DATE, DATETIME or TIMESTAMP types in SQL.
+
+ @param type Field type, as returned by field->type().
+ @retval true If field type is temporal type with date part.
+ @retval false If field type is not temporal type with date part.
+*/
+inline bool is_temporal_type_with_date(enum_field_types type)
+{
+ switch (type)
+ {
+ case MYSQL_TYPE_DATE:
+ case MYSQL_TYPE_DATETIME:
+ case MYSQL_TYPE_TIMESTAMP:
+ return true;
+ case MYSQL_TYPE_DATETIME2:
+ case MYSQL_TYPE_TIMESTAMP2:
+ DBUG_ASSERT(0); // field->real_type() should not get to here.
+ default:
+ return false;
+ }
+}
+
+
+/**
+ Tests if a field real type can have "DEFAULT CURRENT_TIMESTAMP"
+
+ @param type Field type, as returned by field->real_type().
+ @retval true If field real type can have "DEFAULT CURRENT_TIMESTAMP".
+ @retval false If field real type can not have "DEFAULT CURRENT_TIMESTAMP".
+*/
+inline bool real_type_with_now_as_default(enum_field_types type)
+{
+ return type == MYSQL_TYPE_TIMESTAMP || type == MYSQL_TYPE_TIMESTAMP2 ||
+ type == MYSQL_TYPE_DATETIME || type == MYSQL_TYPE_DATETIME2;
+}
+
+
+/**
+ Recognizer for concrete data type (called real_type for some reason),
+ returning true if it is one of the TIMESTAMP types.
+*/
+inline bool is_timestamp_type(enum_field_types type)
+{
+ return type == MYSQL_TYPE_TIMESTAMP || type == MYSQL_TYPE_TIMESTAMP2;
+}
+
+
+/**
+ Convert temporal real types as retuned by field->real_type()
+ to field type as returned by field->type().
+
+ @param real_type Real type.
+ @retval Field type.
+*/
+inline enum_field_types real_type_to_type(enum_field_types real_type)
+{
+ switch (real_type)
+ {
+ case MYSQL_TYPE_TIME2:
+ return MYSQL_TYPE_TIME;
+ case MYSQL_TYPE_DATETIME2:
+ return MYSQL_TYPE_DATETIME;
+ case MYSQL_TYPE_TIMESTAMP2:
+ return MYSQL_TYPE_TIMESTAMP;
+ case MYSQL_TYPE_NEWDATE:
+ return MYSQL_TYPE_DATE;
+ /* Note: NEWDECIMAL is a type, not only a real_type */
+ default: return real_type;
+ }
+}
+
+
static inline enum enum_mysql_timestamp_type
mysql_type_to_time_type(enum enum_field_types mysql_type)
{
switch(mysql_type) {
+ case MYSQL_TYPE_TIME2:
case MYSQL_TYPE_TIME: return MYSQL_TIMESTAMP_TIME;
+ case MYSQL_TYPE_TIMESTAMP2:
case MYSQL_TYPE_TIMESTAMP:
+ case MYSQL_TYPE_DATETIME2:
case MYSQL_TYPE_DATETIME: return MYSQL_TIMESTAMP_DATETIME;
case MYSQL_TYPE_NEWDATE:
case MYSQL_TYPE_DATE: return MYSQL_TIMESTAMP_DATE;
@@ -199,6 +283,13 @@ public:
{
in_partitioning_expr= TRUE;
}
+ bool is_equal(Virtual_column_info* vcol)
+ {
+ return field_type == vcol->get_real_type()
+ && stored_in_db == vcol->is_stored()
+ && expr_str.length == vcol->expr_str.length
+ && memcmp(expr_str.str, vcol->expr_str.str, expr_str.length) == 0;
+ }
};
class Field
@@ -206,9 +297,13 @@ class Field
Field(const Item &); /* Prevent use of these */
void operator=(Field &);
public:
+ static void *operator new(size_t size, MEM_ROOT *mem_root) throw ()
+ { return alloc_root(mem_root, size); }
static void *operator new(size_t size) throw ()
{ return sql_alloc(size); }
static void operator delete(void *ptr_arg, size_t size) { TRASH(ptr_arg, size); }
+ static void operator delete(void *ptr, MEM_ROOT *mem_root)
+ { DBUG_ASSERT(0); }
uchar *ptr; // Position to field in record
/**
@@ -267,6 +362,35 @@ public:
*/
bool is_created_from_null_item;
+ /* TRUE in Field objects created for column min/max values */
+ bool is_stat_field;
+
+ /*
+ Selectivity of the range condition over this field.
+ When calculating this selectivity a range predicate
+ is taken into account only if:
+ - it is extracted from the WHERE clause
+ - it depends only on the table the field belongs to
+ */
+ double cond_selectivity;
+
+ /*
+ The next field in the class of equal fields at the top AND level
+ of the WHERE clause
+ */
+ Field *next_equal_field;
+
+ /*
+ This structure is used for statistical data on the column
+ that has been read from the statistical table column_stat
+ */
+ Column_statistics *read_stats;
+ /*
+ This structure is used for statistical data on the column that
+ is collected by the function collect_statistics_for_table
+ */
+ Column_statistics_collected *collected_stats;
+
/*
This is additional data provided for any computed(virtual) field.
In particular it includes a pointer to the item by which this field
@@ -359,6 +483,26 @@ public:
virtual uint32 data_length() { return pack_length(); }
virtual uint32 sort_length() const { return pack_length(); }
+ /*
+ Get the number bytes occupied by the value in the field.
+ CHAR values are stripped of trailing spaces.
+ Flexible values are stripped of their length.
+ */
+ virtual uint32 value_length()
+ {
+ uint len;
+ if (!zero_pack() &&
+ (type() == MYSQL_TYPE_STRING &&
+ (len= pack_length()) >= 4 && len < 256))
+ {
+ uchar *str, *end;
+ for (str= ptr, end= str+len; end > str && end[-1] == ' '; end--) {}
+ len=(uint) (end-str);
+ return len;
+ }
+ return data_length();
+ }
+
/**
Get the maximum size of the data in packed format.
@@ -380,12 +524,100 @@ public:
*null_ptr= ((*null_ptr & (uchar) ~null_bit) |
(null_ptr[l_offset] & null_bit));
}
+
+ bool has_insert_default_function() const
+ {
+ return unireg_check == TIMESTAMP_DN_FIELD ||
+ unireg_check == TIMESTAMP_DNUN_FIELD;
+ }
+
+ bool has_update_default_function() const
+ {
+ return unireg_check == TIMESTAMP_UN_FIELD ||
+ unireg_check == TIMESTAMP_DNUN_FIELD;
+ }
+
+ /*
+ Mark the field as having a value supplied by the client, thus it should
+ not be auto-updated.
+ */
+ void set_has_explicit_value()
+ {
+ flags|= HAS_EXPLICIT_VALUE;
+ }
+
+ virtual void set_explicit_default(Item *value);
+
+ /**
+ Evaluates the @c INSERT default function and stores the result in the
+ field. If no such function exists for the column, or the function is not
+ valid for the column's data type, invoking this function has no effect.
+ */
+ virtual int evaluate_insert_default_function() { return 0; }
+
+
+ /**
+ Evaluates the @c UPDATE default function, if one exists, and stores the
+ result in the record buffer. If no such function exists for the column,
+ or the function is not valid for the column's data type, invoking this
+ function has no effect.
+ */
+ virtual int evaluate_update_default_function() { return 0; }
+
virtual bool binary() const { return 1; }
virtual bool zero_pack() const { return 1; }
virtual enum ha_base_keytype key_type() const { return HA_KEYTYPE_BINARY; }
virtual uint32 key_length() const { return pack_length(); }
virtual enum_field_types type() const =0;
virtual enum_field_types real_type() const { return type(); }
+ virtual enum_field_types binlog_type() const
+ {
+ /*
+ Binlog stores field->type() as type code by default. For example,
+ it puts MYSQL_TYPE_STRING in case of CHAR, VARCHAR, SET and ENUM,
+ with extra data type details put into metadata.
+
+ Binlog behaviour slightly differs between various MySQL and MariaDB
+ versions for the temporal data types TIME, DATETIME and TIMESTAMP.
+
+ MySQL prior to 5.6 uses MYSQL_TYPE_TIME, MYSQL_TYPE_DATETIME
+ and MYSQL_TYPE_TIMESTAMP type codes in binlog and stores no
+ additional metadata.
+
+ MariaDB-5.3 implements new versions for TIME, DATATIME, TIMESTAMP
+ with fractional second precision, but uses the old format for the
+ types TIME(0), DATETIME(0), TIMESTAMP(0), and it still stores
+ MYSQL_TYPE_TIME, MYSQL_TYPE_DATETIME and MYSQL_TYPE_TIMESTAMP in binlog,
+ with no additional metadata.
+ So row-based replication between temporal data types of
+ different precision is not possible in MariaDB.
+
+ MySQL-5.6 also implements a new version of TIME, DATETIME, TIMESTAMP
+ which support fractional second precision 0..6, and use the new
+ format even for the types TIME(0), DATETIME(0), TIMESTAMP(0).
+ For these new data types, MySQL-5.6 stores new type codes
+ MYSQL_TYPE_TIME2, MYSQL_TYPE_DATETIME2, MYSQL_TYPE_TIMESTAMP2 in binlog,
+ with fractional precision 0..6 put into metadata.
+ This makes it in theory possible to do row-based replication between
+ columns of different fractional precision (e.g. from TIME(1) on master
+ to TIME(6) on slave). However, it's not currently fully implemented yet.
+ MySQL-5.6 can only do row-based replication from the old types
+ TIME, DATETIME, TIMESTAMP (represented by MYSQL_TYPE_TIME,
+ MYSQL_TYPE_DATETIME and MYSQL_TYPE_TIMESTAMP type codes in binlog)
+ to the new corresponding types TIME(0), DATETIME(0), TIMESTAMP(0).
+
+ Note: MariaDB starting from the version 10.0 understands the new
+ MySQL-5.6 type codes MYSQL_TYPE_TIME2, MYSQL_TYPE_DATETIME2,
+ MYSQL_TYPE_TIMESTAMP2. When started over MySQL-5.6 tables both on
+ master and on slave, MariaDB-10.0 can also do row-based replication
+ from the old types TIME, DATETIME, TIMESTAMP to the new MySQL-5.6
+ types TIME(0), DATETIME(0), TIMESTAMP(0).
+
+ Note: perhaps binlog should eventually be modified to store
+ real_type() instead of type() for all column types.
+ */
+ return type();
+ }
inline int cmp(const uchar *str) { return cmp(ptr,str); }
virtual int cmp_max(const uchar *a, const uchar *b, uint max_len)
{ return cmp(a, b); }
@@ -400,6 +632,40 @@ public:
{ return cmp(a, b); }
virtual int key_cmp(const uchar *str, uint length)
{ return cmp(ptr,str); }
+ /*
+ Update the value m of the 'min_val' field with the current value v
+ of this field if force_update is set to TRUE or if v < m.
+ Return TRUE if the value has been updated.
+ */
+ virtual bool update_min(Field *min_val, bool force_update)
+ {
+ bool update_fl= force_update || cmp(ptr, min_val->ptr) < 0;
+ if (update_fl)
+ {
+ min_val->set_notnull();
+ memcpy(min_val->ptr, ptr, pack_length());
+ }
+ return update_fl;
+ }
+ /*
+ Update the value m of the 'max_val' field with the current value v
+ of this field if force_update is set to TRUE or if v > m.
+ Return TRUE if the value has been updated.
+ */
+ virtual bool update_max(Field *max_val, bool force_update)
+ {
+ bool update_fl= force_update || cmp(ptr, max_val->ptr) > 0;
+ if (update_fl)
+ {
+ max_val->set_notnull();
+ memcpy(max_val->ptr, ptr, pack_length());
+ }
+ return update_fl;
+ }
+ virtual void store_field_value(uchar *val, uint len)
+ {
+ memcpy(ptr, val, len);
+ }
virtual uint decimals() const { return 0; }
/*
Caller beware: sql_type can change str.Ptr, so check
@@ -408,32 +674,57 @@ public:
*/
virtual void sql_type(String &str) const =0;
virtual uint size_of() const =0; // For new field
- inline bool is_null(my_ptrdiff_t row_offset= 0)
- { return null_ptr ? (null_ptr[row_offset] & null_bit ? 1 : 0) : table->null_row; }
- inline bool is_real_null(my_ptrdiff_t row_offset= 0)
- { return null_ptr ? (null_ptr[row_offset] & null_bit ? 1 : 0) : 0; }
- inline bool is_null_in_record(const uchar *record)
- {
- if (!null_ptr)
- return 0;
- return test(record[(uint) (null_ptr -table->record[0])] &
- null_bit);
- }
- inline bool is_null_in_record_with_offset(my_ptrdiff_t col_offset)
+ inline bool is_null(my_ptrdiff_t row_offset= 0) const
+ {
+ /*
+ The table may have been marked as containing only NULL values
+ for all fields if it is a NULL-complemented row of an OUTER JOIN
+ or if the query is an implicitly grouped query (has aggregate
+ functions but no GROUP BY clause) with no qualifying rows. If
+ this is the case (in which TABLE::null_row is true), the field
+ is considered to be NULL.
+
+ Note that if a table->null_row is set then also all null_bits are
+ set for the row.
+
+ In the case of the 'result_field' for GROUP BY, table->null_row might
+ refer to the *next* row in the table (when the algorithm is: read the
+ next row, see if any of group column values have changed, send the
+ result - grouped - row to the client if yes). So, table->null_row might
+ be wrong, but such a result_field is always nullable (that's defined by
+ original_field->maybe_null()) and we trust its null bit.
+ */
+ return null_ptr ? null_ptr[row_offset] & null_bit : table->null_row;
+ }
+ inline bool is_real_null(my_ptrdiff_t row_offset= 0) const
+ { return null_ptr && (null_ptr[row_offset] & null_bit); }
+ inline bool is_null_in_record(const uchar *record) const
{
if (!null_ptr)
return 0;
- return test(null_ptr[col_offset] & null_bit);
+ return record[(uint) (null_ptr - table->record[0])] & null_bit;
}
inline void set_null(my_ptrdiff_t row_offset= 0)
{ if (null_ptr) null_ptr[row_offset]|= null_bit; }
inline void set_notnull(my_ptrdiff_t row_offset= 0)
{ if (null_ptr) null_ptr[row_offset]&= (uchar) ~null_bit; }
- inline bool maybe_null(void) { return null_ptr != 0 || table->maybe_null; }
- /**
- Signals that this field is NULL-able.
- */
- inline bool real_maybe_null(void) { return null_ptr != 0; }
+ inline bool maybe_null(void) const
+ { return null_ptr != 0 || table->maybe_null; }
+
+ /* @return true if this field is NULL-able, false otherwise. */
+ inline bool real_maybe_null(void) const { return null_ptr != 0; }
+ uint null_offset(const uchar *record) const
+ { return (uint) (null_ptr - record); }
+
+ uint null_offset() const
+ { return null_offset(table->record[0]); }
+ void set_null_ptr(uchar *p_null_ptr, uint p_null_bit)
+ {
+ null_ptr= p_null_ptr;
+ null_bit= p_null_bit;
+ }
+
+ inline THD *get_thd() { return table ? table->in_use : current_thd; }
enum {
LAST_NULL_BYTE_UNDEF= 0
@@ -469,9 +760,12 @@ public:
virtual Field *new_field(MEM_ROOT *root, TABLE *new_table,
bool keep_type);
virtual Field *new_key_field(MEM_ROOT *root, TABLE *new_table,
- uchar *new_ptr, uchar *new_null_ptr,
- uint new_null_bit);
+ uchar *new_ptr, uint32 length,
+ uchar *new_null_ptr, uint new_null_bit);
Field *clone(MEM_ROOT *mem_root, TABLE *new_table);
+ Field *clone(MEM_ROOT *mem_root, TABLE *new_table, my_ptrdiff_t diff,
+ bool stat_flag= FALSE);
+ Field *clone(MEM_ROOT *mem_root, my_ptrdiff_t diff);
inline void move_field(uchar *ptr_arg,uchar *null_ptr_arg,uchar null_bit_arg)
{
ptr=ptr_arg; null_ptr=null_ptr_arg; null_bit=null_bit_arg;
@@ -580,22 +874,50 @@ public:
{ return binary() ? &my_charset_bin : charset(); }
virtual CHARSET_INFO *sort_charset(void) const { return charset(); }
virtual bool has_charset(void) const { return FALSE; }
+ /*
+ match_collation_to_optimize_range() is to distinguish in
+ range optimizer (see opt_range.cc) between real string types:
+ CHAR, VARCHAR, TEXT
+ and the other string-alike types with result_type() == STRING_RESULT:
+ DATE, TIME, DATETIME, TIMESTAMP
+ We need it to decide whether to test if collation of the operation
+ matches collation of the field (needed only for real string types).
+ */
+ virtual bool match_collation_to_optimize_range() const { return false; }
virtual void set_charset(CHARSET_INFO *charset_arg) { }
virtual enum Derivation derivation(void) const
{ return DERIVATION_IMPLICIT; }
virtual uint repertoire(void) const { return MY_REPERTOIRE_UNICODE30; }
virtual void set_derivation(enum Derivation derivation_arg) { }
virtual int set_time() { return 1; }
- void set_warning(MYSQL_ERROR::enum_warning_level, unsigned int code,
- int cuted_increment);
- void set_datetime_warning(MYSQL_ERROR::enum_warning_level, uint code,
+ bool set_warning(Sql_condition::enum_warning_level, unsigned int code,
+ int cuted_increment) const;
+protected:
+ bool set_warning(unsigned int code, int cuted_increment) const
+ {
+ return set_warning(Sql_condition::WARN_LEVEL_WARN, code, cuted_increment);
+ }
+ bool set_note(unsigned int code, int cuted_increment) const
+ {
+ return set_warning(Sql_condition::WARN_LEVEL_NOTE, code, cuted_increment);
+ }
+ void set_datetime_warning(Sql_condition::enum_warning_level, uint code,
const ErrConv *str, timestamp_type ts_type,
int cuted_increment);
+ void set_datetime_warning(uint code,
+ const ErrConv *str, timestamp_type ts_type,
+ int cuted_increment)
+ {
+ set_datetime_warning(Sql_condition::WARN_LEVEL_WARN, code, str, ts_type,
+ cuted_increment);
+ }
+ void set_warning_truncated_wrong_value(const char *type, const char *value);
inline bool check_overflow(int op_result)
{
return (op_result == E_DEC_OVERFLOW);
}
int warn_if_overflow(int op_result);
+public:
void set_table_name(String *alias)
{
table_name= &alias->Ptr;
@@ -633,6 +955,30 @@ public:
return GEOM_GEOMETRY;
}
+ ha_storage_media field_storage_type() const
+ {
+ return (ha_storage_media)
+ ((flags >> FIELD_FLAGS_STORAGE_MEDIA) & 3);
+ }
+
+ void set_storage_type(ha_storage_media storage_type_arg)
+ {
+ DBUG_ASSERT(field_storage_type() == HA_SM_DEFAULT);
+ flags |= (storage_type_arg << FIELD_FLAGS_STORAGE_MEDIA);
+ }
+
+ column_format_type column_format() const
+ {
+ return (column_format_type)
+ ((flags >> FIELD_FLAGS_COLUMN_FORMAT) & 3);
+ }
+
+ void set_column_format(column_format_type column_format_arg)
+ {
+ DBUG_ASSERT(column_format() == COLUMN_FORMAT_TYPE_DEFAULT);
+ flags |= (column_format_arg << FIELD_FLAGS_COLUMN_FORMAT);
+ }
+
key_map get_possible_keys();
/* Hash value */
@@ -652,6 +998,12 @@ public:
virtual bool hash_join_is_possible() { return TRUE; }
virtual bool eq_cmp_as_binary() { return TRUE; }
+ /* Position of the field value within the interval of [min, max] */
+ virtual double pos_in_interval(Field *min, Field *max)
+ {
+ return (double) 0.5;
+ }
+
friend int cre_myisam(char * name, register TABLE *form, uint options,
ulonglong auto_increment_value);
friend class Copy_field;
@@ -731,7 +1083,8 @@ protected:
{
return (flags & (BINCMP_FLAG | BINARY_FLAG)) != 0;
}
-
+ double pos_in_interval_val_real(Field *min, Field *max);
+ double pos_in_interval_val_str(Field *min, Field *max, uint data_offset);
};
@@ -770,6 +1123,10 @@ public:
bool get_int(CHARSET_INFO *cs, const char *from, uint len,
longlong *rnd, ulonglong unsigned_max,
longlong signed_min, longlong signed_max);
+ double pos_in_interval(Field *min, Field *max)
+ {
+ return pos_in_interval_val_real(min, max);
+ }
};
@@ -782,23 +1139,11 @@ public:
uchar null_bit_arg, utype unireg_check_arg,
const char *field_name_arg, CHARSET_INFO *charset);
Item_result result_type () const { return STRING_RESULT; }
- /*
- match_collation_to_optimize_range() is to distinguish in
- range optimizer (see opt_range.cc) between real string types:
- CHAR, VARCHAR, TEXT
- and the other string-alike types with result_type() == STRING_RESULT:
- DATE, TIME, DATETIME, TIMESTAMP
- We need it to decide whether to test if collation of the operation
- matches collation of the field (needed only for real string types).
- QQ: shouldn't DATE/TIME types have their own XXX_RESULT types eventually?
- */
- virtual bool match_collation_to_optimize_range() const=0;
uint decimals() const { return NOT_FIXED_DEC; }
int store(double nr);
int store(longlong nr, bool unsigned_val)=0;
int store_decimal(const my_decimal *);
int store(const char *to,uint length,CHARSET_INFO *cs)=0;
- uint size_of() const { return sizeof(*this); }
uint repertoire(void) const
{
return my_charset_repertoire(field_charset);
@@ -814,7 +1159,12 @@ public:
my_decimal *val_decimal(my_decimal *);
virtual bool str_needs_quotes() { return TRUE; }
uint is_equal(Create_field *new_field);
- bool eq_cmp_as_binary() { return test(flags & BINARY_FLAG); }
+ bool eq_cmp_as_binary() { return MY_TEST(flags & BINARY_FLAG); }
+ virtual uint length_size() { return 0; }
+ double pos_in_interval(Field *min, Field *max)
+ {
+ return pos_in_interval_val_str(min, max, length_size());
+ }
};
/* base class for Field_string, Field_varstring and Field_blob */
@@ -824,6 +1174,13 @@ class Field_longstr :public Field_str
protected:
int report_if_important_data(const char *ptr, const char *end,
bool count_spaces);
+ bool check_string_copy_error(const char *well_formed_error_pos,
+ const char *cannot_convert_error_pos,
+ const char *end,
+ CHARSET_INFO *cs);
+ my_decimal *val_decimal_from_str(const char *str, uint length,
+ CHARSET_INFO *cs,
+ my_decimal *decimal_value);
public:
Field_longstr(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
uchar null_bit_arg, utype unireg_check_arg,
@@ -839,6 +1196,7 @@ public:
DBUG_ASSERT(table && table->write_set);
return bitmap_is_set(table->write_set, field_index);
}
+ bool match_collation_to_optimize_range() const { return true; }
};
/* base class for float and double and decimal (old one) */
@@ -1248,7 +1606,6 @@ public:
unireg_check_arg, field_name_arg, cs)
{}
enum_field_types type() const { return MYSQL_TYPE_NULL;}
- bool match_collation_to_optimize_range() const { return FALSE; }
int store(const char *to, uint length, CHARSET_INFO *cs)
{ null[0]=1; return 0; }
int store(double nr) { null[0]=1; return 0; }
@@ -1270,29 +1627,82 @@ public:
};
-class Field_timestamp :public Field_str {
+class Field_temporal: public Field {
+public:
+ Field_temporal(uchar *ptr_arg,uint32 len_arg, uchar *null_ptr_arg,
+ uchar null_bit_arg, utype unireg_check_arg,
+ const char *field_name_arg)
+ :Field(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, unireg_check_arg,
+ field_name_arg)
+ { flags|= BINARY_FLAG; }
+ Item_result result_type () const { return STRING_RESULT; }
+ uint32 max_display_length() { return field_length; }
+ bool str_needs_quotes() { return TRUE; }
+ enum Derivation derivation(void) const { return DERIVATION_NUMERIC; }
+ uint repertoire(void) const { return MY_REPERTOIRE_NUMERIC; }
+ CHARSET_INFO *charset(void) const { return &my_charset_numeric; }
+ const CHARSET_INFO *sort_charset(void) const { return &my_charset_bin; }
+ bool binary() const { return true; }
+ enum Item_result cmp_type () const { return TIME_RESULT; }
+ uint is_equal(Create_field *new_field);
+ bool eq_def(Field *field)
+ {
+ return (Field::eq_def(field) && decimals() == field->decimals());
+ }
+ my_decimal *val_decimal(my_decimal*);
+ void set_warnings(Sql_condition::enum_warning_level trunc_level,
+ const ErrConv *str, int was_cut, timestamp_type ts_type);
+ double pos_in_interval(Field *min, Field *max)
+ {
+ return pos_in_interval_val_real(min, max);
+ }
+};
+
+
+/**
+ Abstract class for:
+ - DATE
+ - DATETIME
+ - DATETIME(1..6)
+ - DATETIME(0..6) - MySQL56 version
+*/
+class Field_temporal_with_date: public Field_temporal {
+protected:
+ int store_TIME_with_warning(MYSQL_TIME *ltime, const ErrConv *str,
+ int was_cut, int have_smth_to_conv);
+ virtual void store_TIME(MYSQL_TIME *ltime) = 0;
+public:
+ Field_temporal_with_date(uchar *ptr_arg, uint32 len_arg,
+ uchar *null_ptr_arg, uchar null_bit_arg,
+ utype unireg_check_arg,
+ const char *field_name_arg)
+ :Field_temporal(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
+ unireg_check_arg, field_name_arg)
+ {}
+ int store(const char *to, uint length, CHARSET_INFO *charset);
+ int store(double nr);
+ int store(longlong nr, bool unsigned_val);
+ int store_time_dec(MYSQL_TIME *ltime, uint dec);
+ int store_decimal(const my_decimal *);
+};
+
+
+class Field_timestamp :public Field_temporal {
protected:
int store_TIME_with_warning(THD *, MYSQL_TIME *, const ErrConv *,
- bool, bool);
+ int warnings, bool have_smth_to_conv);
public:
Field_timestamp(uchar *ptr_arg, uint32 len_arg,
uchar *null_ptr_arg, uchar null_bit_arg,
enum utype unireg_check_arg, const char *field_name_arg,
- TABLE_SHARE *share, CHARSET_INFO *cs);
- Field_timestamp(bool maybe_null_arg, const char *field_name_arg,
- CHARSET_INFO *cs);
+ TABLE_SHARE *share);
enum_field_types type() const { return MYSQL_TYPE_TIMESTAMP;}
- bool match_collation_to_optimize_range() const { return FALSE; }
enum ha_base_keytype key_type() const { return HA_KEYTYPE_ULONG_INT; }
- enum Item_result cmp_type () const { return TIME_RESULT; }
- enum Derivation derivation(void) const { return DERIVATION_NUMERIC; }
- uint repertoire(void) const { return MY_REPERTOIRE_NUMERIC; }
- CHARSET_INFO *charset(void) const { return &my_charset_numeric; }
- bool binary() const { return 1; }
int store(const char *to,uint length,CHARSET_INFO *charset);
int store(double nr);
int store(longlong nr, bool unsigned_val);
int store_time_dec(MYSQL_TIME *ltime, uint dec);
+ int store_decimal(const my_decimal *);
double val_real(void);
longlong val_int(void);
String *val_str(String*,String *);
@@ -1302,16 +1712,29 @@ public:
uint32 pack_length() const { return 4; }
void sql_type(String &str) const;
bool zero_pack() const { return 0; }
- uint decimals() const { return 0; }
virtual int set_time();
virtual void set_default()
{
- if (table->timestamp_field == this &&
- unireg_check != TIMESTAMP_UN_FIELD)
+ if (has_insert_default_function())
set_time();
else
Field::set_default();
}
+ virtual void set_explicit_default(Item *value);
+ virtual int evaluate_insert_default_function()
+ {
+ int res= 0;
+ if (has_insert_default_function())
+ res= set_time();
+ return res;
+ }
+ virtual int evaluate_update_default_function()
+ {
+ int res= 0;
+ if (has_update_default_function())
+ res= set_time();
+ return res;
+ }
/* Get TIMESTAMP field value as seconds since begging of Unix Epoch */
virtual my_time_t get_timestamp(ulong *sec_part) const;
virtual void store_TIME(my_time_t timestamp, ulong sec_part)
@@ -1319,7 +1742,6 @@ public:
int4store(ptr,timestamp);
}
bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate);
- timestamp_auto_set_type get_auto_set_type() const;
uchar *pack(uchar *to, const uchar *from,
uint max_length __attribute__((unused)))
{
@@ -1330,46 +1752,109 @@ public:
{
return unpack_int32(to, from, from_end);
}
+ uint size_of() const { return sizeof(*this); }
};
-class Field_timestamp_hires :public Field_timestamp {
+/**
+ Abstract class for:
+ - TIMESTAMP(1..6)
+ - TIMESTAMP(0..6) - MySQL56 version
+*/
+class Field_timestamp_with_dec :public Field_timestamp {
+protected:
uint dec;
public:
- Field_timestamp_hires(uchar *ptr_arg,
- uchar *null_ptr_arg, uchar null_bit_arg,
- enum utype unireg_check_arg, const char *field_name_arg,
- TABLE_SHARE *share, uint dec_arg, CHARSET_INFO *cs) :
- Field_timestamp(ptr_arg, MAX_DATETIME_WIDTH + dec_arg + 1, null_ptr_arg,
- null_bit_arg, unireg_check_arg, field_name_arg, share, cs),
+ Field_timestamp_with_dec(uchar *ptr_arg,
+ uchar *null_ptr_arg, uchar null_bit_arg,
+ enum utype unireg_check_arg,
+ const char *field_name_arg,
+ TABLE_SHARE *share, uint dec_arg) :
+ Field_timestamp(ptr_arg,
+ MAX_DATETIME_WIDTH + dec_arg + MY_TEST(dec_arg), null_ptr_arg,
+ null_bit_arg, unireg_check_arg, field_name_arg, share),
dec(dec_arg)
{
- DBUG_ASSERT(dec);
DBUG_ASSERT(dec <= TIME_SECOND_PART_DIGITS);
}
- void sql_type(String &str) const;
- my_time_t get_timestamp(ulong *sec_part) const;
- void store_TIME(my_time_t timestamp, ulong sec_part);
- int store_decimal(const my_decimal *d);
- double val_real(void);
- String *val_str(String*,String *);
- my_decimal* val_decimal(my_decimal*);
- bool send_binary(Protocol *protocol);
- int cmp(const uchar *,const uchar *);
- void sort_string(uchar *buff,uint length);
uint decimals() const { return dec; }
- int set_time();
enum ha_base_keytype key_type() const { return HA_KEYTYPE_BINARY; }
- void make_field(Send_field *field);
- uint32 pack_length() const;
uchar *pack(uchar *to, const uchar *from, uint max_length)
{ return Field::pack(to, from, max_length); }
const uchar *unpack(uchar* to, const uchar *from, const uchar *from_end,
uint param_data)
{ return Field::unpack(to, from, from_end, param_data); }
+ void make_field(Send_field *field);
+ void sort_string(uchar *to, uint length)
+ {
+ DBUG_ASSERT(length == pack_length());
+ memcpy(to, ptr, length);
+ }
+ bool send_binary(Protocol *protocol);
+ double val_real(void);
+ my_decimal* val_decimal(my_decimal*);
+ int set_time();
+};
+
+
+class Field_timestamp_hires :public Field_timestamp_with_dec {
+public:
+ Field_timestamp_hires(uchar *ptr_arg,
+ uchar *null_ptr_arg, uchar null_bit_arg,
+ enum utype unireg_check_arg,
+ const char *field_name_arg,
+ TABLE_SHARE *share, uint dec_arg) :
+ Field_timestamp_with_dec(ptr_arg, null_ptr_arg, null_bit_arg,
+ unireg_check_arg, field_name_arg, share, dec_arg)
+ {
+ DBUG_ASSERT(dec);
+ }
+ my_time_t get_timestamp(ulong *sec_part) const;
+ void store_TIME(my_time_t timestamp, ulong sec_part);
+ int cmp(const uchar *,const uchar *);
+ uint32 pack_length() const;
+ uint size_of() const { return sizeof(*this); }
+};
+
+
+/**
+ TIMESTAMP(0..6) - MySQL56 version
+*/
+class Field_timestampf :public Field_timestamp_with_dec {
+ int do_save_field_metadata(uchar *metadata_ptr)
+ {
+ *metadata_ptr= decimals();
+ return 1;
+ }
+public:
+ Field_timestampf(uchar *ptr_arg,
+ uchar *null_ptr_arg, uchar null_bit_arg,
+ enum utype unireg_check_arg,
+ const char *field_name_arg,
+ TABLE_SHARE *share, uint dec_arg) :
+ Field_timestamp_with_dec(ptr_arg, null_ptr_arg, null_bit_arg,
+ unireg_check_arg, field_name_arg, share, dec_arg)
+ {}
+ enum_field_types real_type() const { return MYSQL_TYPE_TIMESTAMP2; }
+ enum_field_types binlog_type() const { return MYSQL_TYPE_TIMESTAMP2; }
+ uint32 pack_length() const
+ {
+ return my_timestamp_binary_length(dec);
+ }
+ uint row_pack_length() const { return pack_length(); }
+ uint pack_length_from_metadata(uint field_metadata)
+ {
+ DBUG_ENTER("Field_timestampf::pack_length_from_metadata");
+ uint tmp= my_timestamp_binary_length(field_metadata);
+ DBUG_RETURN(tmp);
+ }
+ int cmp(const uchar *a_ptr,const uchar *b_ptr)
+ {
+ return memcmp(a_ptr, b_ptr, pack_length());
+ }
+ void store_TIME(my_time_t timestamp, ulong sec_part);
+ my_time_t get_timestamp(ulong *sec_part) const;
uint size_of() const { return sizeof(*this); }
- bool eq_def(Field *field)
- { return Field_str::eq_def(field) && dec == field->decimals(); }
};
@@ -1396,56 +1881,24 @@ public:
};
-class Field_temporal: public Field_str {
-protected:
- int store_TIME_with_warning(MYSQL_TIME *ltime, const ErrConv *str,
- int was_cut, int have_smth_to_conv);
- virtual void store_TIME(MYSQL_TIME *ltime) = 0;
-public:
- Field_temporal(uchar *ptr_arg,uint32 len_arg, uchar *null_ptr_arg,
- uchar null_bit_arg, utype unireg_check_arg,
- const char *field_name_arg, CHARSET_INFO *charset_arg)
- :Field_str(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, unireg_check_arg,
- field_name_arg, charset_arg)
- { flags|= BINARY_FLAG; }
- enum Derivation derivation(void) const { return DERIVATION_NUMERIC; }
- uint repertoire(void) const { return MY_REPERTOIRE_NUMERIC; }
- CHARSET_INFO *charset(void) const { return &my_charset_numeric; }
- bool binary() const { return 1; }
- bool match_collation_to_optimize_range() const { return FALSE; }
- enum Item_result cmp_type () const { return TIME_RESULT; }
- int store(const char *to,uint length,CHARSET_INFO *charset);
- int store(double nr);
- int store(longlong nr, bool unsigned_val);
- int store_time_dec(MYSQL_TIME *ltime, uint dec);
- my_decimal *val_decimal(my_decimal*);
- bool eq_def(Field *field)
- {
- return (Field_str::eq_def(field) && decimals() == field->decimals());
- }
-};
-
-class Field_date :public Field_temporal {
+class Field_date :public Field_temporal_with_date {
void store_TIME(MYSQL_TIME *ltime);
public:
Field_date(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg,
- enum utype unireg_check_arg, const char *field_name_arg,
- CHARSET_INFO *cs)
- :Field_temporal(ptr_arg, MAX_DATE_WIDTH, null_ptr_arg, null_bit_arg,
- unireg_check_arg, field_name_arg, cs) {}
+ enum utype unireg_check_arg, const char *field_name_arg)
+ :Field_temporal_with_date(ptr_arg, MAX_DATE_WIDTH, null_ptr_arg, null_bit_arg,
+ unireg_check_arg, field_name_arg) {}
enum_field_types type() const { return MYSQL_TYPE_DATE;}
enum ha_base_keytype key_type() const { return HA_KEYTYPE_ULONG_INT; }
int reset(void) { ptr[0]=ptr[1]=ptr[2]=ptr[3]=0; return 0; }
double val_real(void);
longlong val_int(void);
String *val_str(String*,String *);
- uint decimals() const { return 0; }
bool send_binary(Protocol *protocol);
int cmp(const uchar *,const uchar *);
void sort_string(uchar *buff,uint length);
uint32 pack_length() const { return 4; }
void sql_type(String &str) const;
- bool zero_pack() const { return 1; }
uchar *pack(uchar* to, const uchar *from,
uint max_length __attribute__((unused)))
{
@@ -1456,23 +1909,22 @@ public:
{
return unpack_int32(to, from, from_end);
}
+ uint size_of() const { return sizeof(*this); }
};
-class Field_newdate :public Field_temporal {
+class Field_newdate :public Field_temporal_with_date {
void store_TIME(MYSQL_TIME *ltime);
public:
Field_newdate(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg,
- enum utype unireg_check_arg, const char *field_name_arg,
- CHARSET_INFO *cs)
- :Field_temporal(ptr_arg, MAX_DATE_WIDTH, null_ptr_arg, null_bit_arg,
- unireg_check_arg, field_name_arg, cs)
+ enum utype unireg_check_arg, const char *field_name_arg)
+ :Field_temporal_with_date(ptr_arg, MAX_DATE_WIDTH, null_ptr_arg, null_bit_arg,
+ unireg_check_arg, field_name_arg)
{}
enum_field_types type() const { return MYSQL_TYPE_DATE;}
enum_field_types real_type() const { return MYSQL_TYPE_NEWDATE; }
enum ha_base_keytype key_type() const { return HA_KEYTYPE_UINT24; }
int reset(void) { ptr[0]=ptr[1]=ptr[2]=0; return 0; }
- uint decimals() const { return 0; }
double val_real(void);
longlong val_int(void);
String *val_str(String*,String *);
@@ -1481,19 +1933,29 @@ public:
void sort_string(uchar *buff,uint length);
uint32 pack_length() const { return 3; }
void sql_type(String &str) const;
- bool zero_pack() const { return 1; }
bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate);
+ uint size_of() const { return sizeof(*this); }
};
class Field_time :public Field_temporal {
- void store_TIME(MYSQL_TIME *ltime);
+ /*
+ when this Field_time instance is used for storing values for index lookups
+ (see class store_key, Field::new_key_field(), etc), the following
+ might be set to TO_DAYS(CURDATE()). See also Field_time::store_time_dec()
+ */
+ long curdays;
+protected:
+ virtual void store_TIME(MYSQL_TIME *ltime);
+ int store_TIME_with_warning(MYSQL_TIME *ltime, const ErrConv *str,
+ int was_cut, int have_smth_to_conv);
+ bool check_zero_in_date_with_warn(ulonglong fuzzydate);
public:
Field_time(uchar *ptr_arg, uint length_arg, uchar *null_ptr_arg,
uchar null_bit_arg, enum utype unireg_check_arg,
- const char *field_name_arg, CHARSET_INFO *cs)
+ const char *field_name_arg)
:Field_temporal(ptr_arg, length_arg, null_ptr_arg, null_bit_arg,
- unireg_check_arg, field_name_arg, cs)
+ unireg_check_arg, field_name_arg), curdays(0)
{}
enum_field_types type() const { return MYSQL_TYPE_TIME;}
enum ha_base_keytype key_type() const { return HA_KEYTYPE_INT24; }
@@ -1501,7 +1963,7 @@ public:
int store(const char *to,uint length,CHARSET_INFO *charset);
int store(double nr);
int store(longlong nr, bool unsigned_val);
- uint decimals() const { return 0; }
+ int store_decimal(const my_decimal *);
double val_real(void);
longlong val_int(void);
String *val_str(String*,String *);
@@ -1511,55 +1973,126 @@ public:
void sort_string(uchar *buff,uint length);
uint32 pack_length() const { return 3; }
void sql_type(String &str) const;
- bool zero_pack() const { return 1; }
+ uint size_of() const { return sizeof(*this); }
+ void set_curdays(THD *thd);
+ Field *new_key_field(MEM_ROOT *root, TABLE *new_table,
+ uchar *new_ptr, uint32 length,
+ uchar *new_null_ptr, uint new_null_bit);
};
-class Field_time_hires :public Field_time {
+
+/**
+ Abstract class for:
+ - TIME(1..6)
+ - TIME(0..6) - MySQL56 version
+*/
+class Field_time_with_dec :public Field_time {
+protected:
uint dec;
+public:
+ Field_time_with_dec(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg,
+ enum utype unireg_check_arg, const char *field_name_arg,
+ uint dec_arg)
+ :Field_time(ptr_arg, MIN_TIME_WIDTH + dec_arg + MY_TEST(dec_arg),
+ null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg),
+ dec(dec_arg)
+ {
+ DBUG_ASSERT(dec <= TIME_SECOND_PART_DIGITS);
+ }
+ uint decimals() const { return dec; }
+ enum ha_base_keytype key_type() const { return HA_KEYTYPE_BINARY; }
+ longlong val_int(void);
+ double val_real(void);
+ void make_field(Send_field *);
+};
+
+
+/**
+ TIME(1..6)
+*/
+class Field_time_hires :public Field_time_with_dec {
longlong zero_point;
void store_TIME(MYSQL_TIME *ltime);
public:
Field_time_hires(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg,
enum utype unireg_check_arg, const char *field_name_arg,
- uint dec_arg, CHARSET_INFO *cs)
- :Field_time(ptr_arg, MIN_TIME_WIDTH + dec_arg + 1, null_ptr_arg,
- null_bit_arg, unireg_check_arg, field_name_arg, cs),
- dec(dec_arg)
+ uint dec_arg)
+ :Field_time_with_dec(ptr_arg, null_ptr_arg,
+ null_bit_arg, unireg_check_arg, field_name_arg,
+ dec_arg)
{
DBUG_ASSERT(dec);
- DBUG_ASSERT(dec <= TIME_SECOND_PART_DIGITS);
zero_point= sec_part_shift(
((TIME_MAX_VALUE_SECONDS+1LL)*TIME_SECOND_PART_FACTOR), dec);
}
- enum ha_base_keytype key_type() const { return HA_KEYTYPE_BINARY; }
- uint decimals() const { return dec; }
- int store_decimal(const my_decimal *d);
- longlong val_int(void);
- double val_real(void);
- String *val_str(String*,String *);
int reset(void);
bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate);
- bool send_binary(Protocol *protocol);
int cmp(const uchar *,const uchar *);
void sort_string(uchar *buff,uint length);
uint32 pack_length() const;
- void sql_type(String &str) const;
- void make_field(Send_field *);
uint size_of() const { return sizeof(*this); }
};
-class Field_datetime :public Field_temporal {
+
+/**
+ TIME(0..6) - MySQL56 version
+*/
+class Field_timef :public Field_time_with_dec {
+ void store_TIME(MYSQL_TIME *ltime);
+ int do_save_field_metadata(uchar *metadata_ptr)
+ {
+ *metadata_ptr= decimals();
+ return 1;
+ }
+public:
+ Field_timef(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg,
+ enum utype unireg_check_arg, const char *field_name_arg,
+ uint dec_arg)
+ :Field_time_with_dec(ptr_arg, null_ptr_arg,
+ null_bit_arg, unireg_check_arg, field_name_arg,
+ dec_arg)
+ {
+ DBUG_ASSERT(dec <= TIME_SECOND_PART_DIGITS);
+ }
+ enum_field_types real_type() const { return MYSQL_TYPE_TIME2; }
+ enum_field_types binlog_type() const { return MYSQL_TYPE_TIME2; }
+ uint32 pack_length() const
+ {
+ return my_time_binary_length(dec);
+ }
+ uint row_pack_length() const { return pack_length(); }
+ uint pack_length_from_metadata(uint field_metadata)
+ {
+ DBUG_ENTER("Field_timef::pack_length_from_metadata");
+ uint tmp= my_time_binary_length(field_metadata);
+ DBUG_RETURN(tmp);
+ }
+ void sort_string(uchar *to, uint length)
+ {
+ DBUG_ASSERT(length == Field_timef::pack_length());
+ memcpy(to, ptr, length);
+ }
+ int cmp(const uchar *a_ptr, const uchar *b_ptr)
+ {
+ return memcmp(a_ptr, b_ptr, pack_length());
+ }
+ int reset();
+ bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate);
+ uint size_of() const { return sizeof(*this); }
+};
+
+
+class Field_datetime :public Field_temporal_with_date {
void store_TIME(MYSQL_TIME *ltime);
public:
Field_datetime(uchar *ptr_arg, uint length_arg, uchar *null_ptr_arg,
uchar null_bit_arg, enum utype unireg_check_arg,
- const char *field_name_arg, CHARSET_INFO *cs)
- :Field_temporal(ptr_arg, length_arg, null_ptr_arg, null_bit_arg,
- unireg_check_arg, field_name_arg, cs)
+ const char *field_name_arg)
+ :Field_temporal_with_date(ptr_arg, length_arg, null_ptr_arg, null_bit_arg,
+ unireg_check_arg, field_name_arg)
{}
enum_field_types type() const { return MYSQL_TYPE_DATETIME;}
enum ha_base_keytype key_type() const { return HA_KEYTYPE_ULONGLONG; }
- uint decimals() const { return 0; }
double val_real(void);
longlong val_int(void);
String *val_str(String*,String *);
@@ -1568,8 +2101,29 @@ public:
void sort_string(uchar *buff,uint length);
uint32 pack_length() const { return 8; }
void sql_type(String &str) const;
- bool zero_pack() const { return 1; }
bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate);
+ virtual int set_time();
+ virtual void set_default()
+ {
+ if (has_insert_default_function())
+ set_time();
+ else
+ Field::set_default();
+ }
+ virtual int evaluate_insert_default_function()
+ {
+ int res= 0;
+ if (has_insert_default_function())
+ res= set_time();
+ return res;
+ }
+ virtual int evaluate_update_default_function()
+ {
+ int res= 0;
+ if (has_update_default_function())
+ res= set_time();
+ return res;
+ }
uchar *pack(uchar* to, const uchar *from,
uint max_length __attribute__((unused)))
{
@@ -1580,85 +2134,149 @@ public:
{
return unpack_int64(to, from, from_end);
}
+ uint size_of() const { return sizeof(*this); }
};
-class Field_datetime_hires :public Field_datetime {
- void store_TIME(MYSQL_TIME *ltime);
+/**
+ Abstract class for:
+ - DATETIME(1..6)
+ - DATETIME(0..6) - MySQL56 version
+*/
+class Field_datetime_with_dec :public Field_datetime {
+protected:
uint dec;
public:
- Field_datetime_hires(uchar *ptr_arg, uchar *null_ptr_arg,
- uchar null_bit_arg, enum utype unireg_check_arg,
- const char *field_name_arg, uint dec_arg,
- CHARSET_INFO *cs)
- :Field_datetime(ptr_arg, MAX_DATETIME_WIDTH + dec_arg + 1,
+ Field_datetime_with_dec(uchar *ptr_arg, uchar *null_ptr_arg,
+ uchar null_bit_arg, enum utype unireg_check_arg,
+ const char *field_name_arg, uint dec_arg)
+ :Field_datetime(ptr_arg, MAX_DATETIME_WIDTH + dec_arg + MY_TEST(dec_arg),
null_ptr_arg, null_bit_arg, unireg_check_arg,
- field_name_arg, cs), dec(dec_arg)
+ field_name_arg), dec(dec_arg)
{
- DBUG_ASSERT(dec);
DBUG_ASSERT(dec <= TIME_SECOND_PART_DIGITS);
}
- enum ha_base_keytype key_type() const { return HA_KEYTYPE_BINARY; }
uint decimals() const { return dec; }
+ enum ha_base_keytype key_type() const { return HA_KEYTYPE_BINARY; }
void make_field(Send_field *field);
- int store_decimal(const my_decimal *d);
- double val_real(void);
- longlong val_int(void);
- String *val_str(String*,String *);
bool send_binary(Protocol *protocol);
- int cmp(const uchar *,const uchar *);
- void sort_string(uchar *buff,uint length);
- uint32 pack_length() const;
- void sql_type(String &str) const;
- bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate);
uchar *pack(uchar *to, const uchar *from, uint max_length)
{ return Field::pack(to, from, max_length); }
const uchar *unpack(uchar* to, const uchar *from, const uchar *from_end,
uint param_data)
{ return Field::unpack(to, from, from_end, param_data); }
+ void sort_string(uchar *to, uint length)
+ {
+ DBUG_ASSERT(length == pack_length());
+ memcpy(to, ptr, length);
+ }
+ double val_real(void);
+ longlong val_int(void);
+ String *val_str(String*,String *);
+};
+
+
+/**
+ DATETIME(1..6)
+*/
+class Field_datetime_hires :public Field_datetime_with_dec {
+ void store_TIME(MYSQL_TIME *ltime);
+public:
+ Field_datetime_hires(uchar *ptr_arg, uchar *null_ptr_arg,
+ uchar null_bit_arg, enum utype unireg_check_arg,
+ const char *field_name_arg, uint dec_arg)
+ :Field_datetime_with_dec(ptr_arg, null_ptr_arg, null_bit_arg,
+ unireg_check_arg, field_name_arg, dec_arg)
+ {
+ DBUG_ASSERT(dec);
+ }
+ int cmp(const uchar *,const uchar *);
+ uint32 pack_length() const;
+ bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate);
uint size_of() const { return sizeof(*this); }
};
+
+/**
+ DATETIME(0..6) - MySQL56 version
+*/
+class Field_datetimef :public Field_datetime_with_dec {
+ void store_TIME(MYSQL_TIME *ltime);
+ int do_save_field_metadata(uchar *metadata_ptr)
+ {
+ *metadata_ptr= decimals();
+ return 1;
+ }
+public:
+ Field_datetimef(uchar *ptr_arg, uchar *null_ptr_arg,
+ uchar null_bit_arg, enum utype unireg_check_arg,
+ const char *field_name_arg, uint dec_arg)
+ :Field_datetime_with_dec(ptr_arg, null_ptr_arg, null_bit_arg,
+ unireg_check_arg, field_name_arg, dec_arg)
+ {}
+ enum_field_types real_type() const { return MYSQL_TYPE_DATETIME2; }
+ enum_field_types binlog_type() const { return MYSQL_TYPE_DATETIME2; }
+ uint32 pack_length() const
+ {
+ return my_datetime_binary_length(dec);
+ }
+ uint row_pack_length() const { return pack_length(); }
+ uint pack_length_from_metadata(uint field_metadata)
+ {
+ DBUG_ENTER("Field_datetimef::pack_length_from_metadata");
+ uint tmp= my_datetime_binary_length(field_metadata);
+ DBUG_RETURN(tmp);
+ }
+ int cmp(const uchar *a_ptr, const uchar *b_ptr)
+ {
+ return memcmp(a_ptr, b_ptr, pack_length());
+ }
+ int reset();
+ bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate);
+ uint size_of() const { return sizeof(*this); }
+};
+
+
static inline Field_timestamp *
new_Field_timestamp(uchar *ptr, uchar *null_ptr, uchar null_bit,
enum Field::utype unireg_check, const char *field_name,
- TABLE_SHARE *share, uint dec, CHARSET_INFO *cs)
+ TABLE_SHARE *share, uint dec)
{
if (dec==0)
return new Field_timestamp(ptr, MAX_DATETIME_WIDTH, null_ptr, null_bit,
- unireg_check, field_name, share, cs);
+ unireg_check, field_name, share);
if (dec == NOT_FIXED_DEC)
dec= MAX_DATETIME_PRECISION;
return new Field_timestamp_hires(ptr, null_ptr, null_bit, unireg_check,
- field_name, share, dec, cs);
+ field_name, share, dec);
}
static inline Field_time *
new_Field_time(uchar *ptr, uchar *null_ptr, uchar null_bit,
enum Field::utype unireg_check, const char *field_name,
- uint dec, CHARSET_INFO *cs)
+ uint dec)
{
if (dec == 0)
return new Field_time(ptr, MIN_TIME_WIDTH, null_ptr, null_bit,
- unireg_check, field_name, cs);
+ unireg_check, field_name);
if (dec == NOT_FIXED_DEC)
dec= MAX_DATETIME_PRECISION;
return new Field_time_hires(ptr, null_ptr, null_bit,
- unireg_check, field_name, dec, cs);
+ unireg_check, field_name, dec);
}
static inline Field_datetime *
new_Field_datetime(uchar *ptr, uchar *null_ptr, uchar null_bit,
enum Field::utype unireg_check,
- const char *field_name, uint dec, CHARSET_INFO *cs)
+ const char *field_name, uint dec)
{
if (dec == 0)
return new Field_datetime(ptr, MAX_DATETIME_WIDTH, null_ptr, null_bit,
- unireg_check, field_name, cs);
+ unireg_check, field_name);
if (dec == NOT_FIXED_DEC)
dec= MAX_DATETIME_PRECISION;
return new Field_datetime_hires(ptr, null_ptr, null_bit,
- unireg_check, field_name, dec, cs);
+ unireg_check, field_name, dec);
}
class Field_string :public Field_longstr {
@@ -1685,7 +2303,6 @@ public:
orig_table->s->frm_version < FRM_VER_TRUE_VARCHAR ?
MYSQL_TYPE_VAR_STRING : MYSQL_TYPE_STRING);
}
- bool match_collation_to_optimize_range() const { return TRUE; }
enum ha_base_keytype key_type() const
{ return binary() ? HA_KEYTYPE_BINARY : HA_KEYTYPE_TEXT; }
bool zero_pack() const { return 0; }
@@ -1766,7 +2383,6 @@ public:
}
enum_field_types type() const { return MYSQL_TYPE_VARCHAR; }
- bool match_collation_to_optimize_range() const { return TRUE; }
enum ha_base_keytype key_type() const;
uint row_pack_length() const { return field_length; }
bool zero_pack() const { return 0; }
@@ -1809,10 +2425,11 @@ public:
{ return charset() == &my_charset_bin ? FALSE : TRUE; }
Field *new_field(MEM_ROOT *root, TABLE *new_table, bool keep_type);
Field *new_key_field(MEM_ROOT *root, TABLE *new_table,
- uchar *new_ptr, uchar *new_null_ptr,
- uint new_null_bit);
+ uchar *new_ptr, uint32 length,
+ uchar *new_null_ptr, uint new_null_bit);
uint is_equal(Create_field *new_field);
void hash(ulong *nr, ulong *nr2);
+ uint length_size() { return length_bytes; }
private:
int do_save_field_metadata(uchar *first_byte);
};
@@ -1860,8 +2477,8 @@ public:
Field_blob(uint32 packlength_arg)
:Field_longstr((uchar*) 0, 0, (uchar*) "", 0, NONE, "temp", system_charset_info),
packlength(packlength_arg) {}
+ /* Note that the default copy constructor is used, in clone() */
enum_field_types type() const { return MYSQL_TYPE_BLOB;}
- bool match_collation_to_optimize_range() const { return TRUE; }
enum ha_base_keytype key_type() const
{ return binary() ? HA_KEYTYPE_VARBINARY2 : HA_KEYTYPE_VARTEXT2; }
int store(const char *to,uint length,CHARSET_INFO *charset);
@@ -1878,10 +2495,14 @@ public:
int cmp_binary(const uchar *a,const uchar *b, uint32 max_length=~0L);
int key_cmp(const uchar *,const uchar*);
int key_cmp(const uchar *str, uint length);
+ /* Never update the value of min_val for a blob field */
+ bool update_min(Field *min_val, bool force_update) { return FALSE; }
+ /* Never update the value of max_val for a blob field */
+ bool update_max(Field *max_val, bool force_update) { return FALSE; }
uint32 key_length() const { return 0; }
void sort_string(uchar *buff,uint length);
uint32 pack_length() const
- { return (uint32) (packlength+table->s->blob_ptr_size); }
+ { return (uint32) (packlength + portable_sizeof_char_ptr); }
/**
Return the packed length without the pointer size added.
@@ -1895,6 +2516,7 @@ public:
{ return (uint32) (packlength); }
uint row_pack_length() const { return pack_length_no_ptr(); }
uint32 sort_length() const;
+ uint32 value_length() { return get_length(); }
virtual uint32 max_data_length() const
{
return (uint32) (((ulonglong) 1 << (packlength*8)) -1);
@@ -1938,6 +2560,9 @@ public:
int copy_value(Field_blob *from);
uint get_key_image(uchar *buff,uint length, imagetype type);
void set_key_image(const uchar *buff,uint length);
+ Field *new_key_field(MEM_ROOT *root, TABLE *new_table,
+ uchar *new_ptr, uint32 length,
+ uchar *new_null_ptr, uint new_null_bit);
void sql_type(String &str) const;
inline bool copy()
{
@@ -1959,7 +2584,7 @@ public:
uint max_packed_col_length(uint max_length);
void free() { value.free(); }
inline void clear_temporary() { bzero((uchar*) &value,sizeof(value)); }
- friend int field_conv(Field *to,Field *from);
+ friend int field_conv_incompatible(Field *to,Field *from);
uint size_of() const { return sizeof(*this); }
bool has_charset(void) const
{ return charset() == &my_charset_bin ? FALSE : TRUE; }
@@ -1991,7 +2616,7 @@ public:
{ geom_type= geom_type_arg; }
enum ha_base_keytype key_type() const { return HA_KEYTYPE_VARBINARY2; }
enum_field_types type() const { return MYSQL_TYPE_GEOMETRY; }
- bool match_collation_to_optimize_range() const { return FALSE; }
+ bool match_collation_to_optimize_range() const { return false; }
void sql_type(String &str) const;
int store(const char *to, uint length, CHARSET_INFO *charset);
int store(double nr);
@@ -2038,7 +2663,6 @@ public:
}
Field *new_field(MEM_ROOT *root, TABLE *new_table, bool keep_type);
enum_field_types type() const { return MYSQL_TYPE_STRING; }
- bool match_collation_to_optimize_range() const { return FALSE; }
enum Item_result cmp_type () const { return INT_RESULT; }
enum ha_base_keytype key_type() const;
int store(const char *to,uint length,CHARSET_INFO *charset);
@@ -2154,9 +2778,9 @@ public:
{
DBUG_ASSERT(ptr == a || ptr == b);
if (ptr == a)
- return Field_bit::key_cmp(b, bytes_in_rec+test(bit_len));
+ return Field_bit::key_cmp(b, bytes_in_rec + MY_TEST(bit_len));
else
- return Field_bit::key_cmp(a, bytes_in_rec+test(bit_len)) * -1;
+ return Field_bit::key_cmp(a, bytes_in_rec + MY_TEST(bit_len)) * -1;
}
int cmp_binary_offset(uint row_offset)
{ return cmp_offset(row_offset); }
@@ -2165,6 +2789,36 @@ public:
{ return cmp_binary((uchar *) a, (uchar *) b); }
int key_cmp(const uchar *str, uint length);
int cmp_offset(uint row_offset);
+ bool update_min(Field *min_val, bool force_update)
+ {
+ longlong val= val_int();
+ bool update_fl= force_update || val < min_val->val_int();
+ if (update_fl)
+ {
+ min_val->set_notnull();
+ min_val->store(val, FALSE);
+ }
+ return update_fl;
+ }
+ bool update_max(Field *max_val, bool force_update)
+ {
+ longlong val= val_int();
+ bool update_fl= force_update || val > max_val->val_int();
+ if (update_fl)
+ {
+ max_val->set_notnull();
+ max_val->store(val, FALSE);
+ }
+ return update_fl;
+ }
+ void store_field_value(uchar *val, uint len)
+ {
+ store(*((longlong *)val), TRUE);
+ }
+ double pos_in_interval(Field *min, Field *max)
+ {
+ return pos_in_interval_val_real(min, max);
+ }
void get_image(uchar *buff, uint length, CHARSET_INFO *cs)
{ get_key_image(buff, length, itRAW); }
void set_image(const uchar *buff,uint length, CHARSET_INFO *cs)
@@ -2188,8 +2842,8 @@ public:
virtual void set_default();
Field *new_key_field(MEM_ROOT *root, TABLE *new_table,
- uchar *new_ptr, uchar *new_null_ptr,
- uint new_null_bit);
+ uchar *new_ptr, uint32 length,
+ uchar *new_null_ptr, uint new_null_bit);
void set_bit_ptr(uchar *bit_ptr_arg, uchar bit_ofs_arg)
{
bit_ptr= bit_ptr_arg;
@@ -2273,10 +2927,11 @@ public:
/** structure with parsed options (for comparing fields in ALTER TABLE) */
ha_field_option_struct *option_struct;
- uint8 row,col,sc_length,interval_id; // For rea_create_table
+ uint8 interval_id; // For rea_create_table
uint offset,pack_flag;
+ bool create_if_not_exists; // Used in ALTER TABLE IF NOT EXISTS
- /*
+ /*
This is additinal data provided for any computed(virtual) field.
In particular it includes a pointer to the item by which this field
can be computed from other fields.
@@ -2289,7 +2944,8 @@ public:
*/
bool stored_in_db;
- Create_field() :after(0), option_list(NULL), option_struct(NULL)
+ Create_field() :after(0), option_list(NULL), option_struct(NULL),
+ create_if_not_exists(FALSE)
{}
Create_field(Field *field, Field *orig_field);
/* Used to make a clone of this object for ALTER/CREATE TABLE */
@@ -2307,18 +2963,29 @@ public:
Item *on_update_value, LEX_STRING *comment, char *change,
List<String> *interval_list, CHARSET_INFO *cs,
uint uint_geom_type, Virtual_column_info *vcol_info,
- engine_option_value *option_list);
+ engine_option_value *option_list, bool check_exists);
bool field_flags_are_binary()
{
return (flags & (BINCMP_FLAG | BINARY_FLAG)) != 0;
}
+
+ ha_storage_media field_storage_type() const
+ {
+ return (ha_storage_media)
+ ((flags >> FIELD_FLAGS_STORAGE_MEDIA) & 3);
+ }
+
+ column_format_type column_format() const
+ {
+ return (column_format_type)
+ ((flags >> FIELD_FLAGS_COLUMN_FORMAT) & 3);
+ }
+
uint virtual_col_expr_maxlen()
{
return 255 - FRM_VCOL_HEADER_SIZE(interval != NULL);
}
-private:
- const String empty_set_string;
};
diff --git a/sql/field_conv.cc b/sql/field_conv.cc
index 5781f1da576..5006266eaed 100644
--- a/sql/field_conv.cc
+++ b/sql/field_conv.cc
@@ -25,6 +25,7 @@
gives much more speed.
*/
+#include <my_global.h>
#include "sql_priv.h"
#include "sql_class.h" // THD
#include <m_ctype.h>
@@ -131,7 +132,7 @@ set_field_to_null(Field *field)
field->reset();
switch (field->table->in_use->count_cuted_fields) {
case CHECK_FIELD_WARN:
- field->set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1);
+ field->set_warning(Sql_condition::WARN_LEVEL_WARN, WARN_DATA_TRUNCATED, 1);
/* fall through */
case CHECK_FIELD_IGNORE:
return 0;
@@ -200,7 +201,7 @@ set_field_to_null_with_conversions(Field *field, bool no_conversions)
}
switch (field->table->in_use->count_cuted_fields) {
case CHECK_FIELD_WARN:
- field->set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_BAD_NULL_ERROR, 1);
+ field->set_warning(Sql_condition::WARN_LEVEL_WARN, ER_BAD_NULL_ERROR, 1);
/* fall through */
case CHECK_FIELD_IGNORE:
return 0;
@@ -270,7 +271,7 @@ static void do_copy_nullable_row_to_notnull(Copy_field *copy)
if (*copy->null_row ||
(copy->from_null_ptr && (*copy->from_null_ptr & copy->from_bit)))
{
- copy->to_field->set_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
+ copy->to_field->set_warning(Sql_condition::WARN_LEVEL_WARN,
WARN_DATA_TRUNCATED, 1);
copy->to_field->reset();
}
@@ -286,7 +287,7 @@ static void do_copy_not_null(Copy_field *copy)
{
if (*copy->from_null_ptr & copy->from_bit)
{
- copy->to_field->set_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
+ copy->to_field->set_warning(Sql_condition::WARN_LEVEL_WARN,
WARN_DATA_TRUNCATED, 1);
copy->to_field->reset();
}
@@ -395,7 +396,7 @@ static void do_field_int(Copy_field *copy)
{
longlong value= copy->from_field->val_int();
copy->to_field->store(value,
- test(copy->from_field->flags & UNSIGNED_FLAG));
+ MY_TEST(copy->from_field->flags & UNSIGNED_FLAG));
}
static void do_field_real(Copy_field *copy)
@@ -436,7 +437,7 @@ static void do_cut_string(Copy_field *copy)
(char*) copy->from_ptr + copy->from_length,
MY_SEQ_SPACES) < copy->from_length - copy->to_length)
{
- copy->to_field->set_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
+ copy->to_field->set_warning(Sql_condition::WARN_LEVEL_WARN,
WARN_DATA_TRUNCATED, 1);
}
}
@@ -467,7 +468,7 @@ static void do_cut_string_complex(Copy_field *copy)
(char*) from_end,
MY_SEQ_SPACES) < (copy->from_length - copy_length))
{
- copy->to_field->set_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
+ copy->to_field->set_warning(Sql_condition::WARN_LEVEL_WARN,
WARN_DATA_TRUNCATED, 1);
}
@@ -506,7 +507,7 @@ static void do_varstring1(Copy_field *copy)
length=copy->to_length - 1;
if (copy->from_field->table->in_use->count_cuted_fields &&
copy->to_field)
- copy->to_field->set_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
+ copy->to_field->set_warning(Sql_condition::WARN_LEVEL_WARN,
WARN_DATA_TRUNCATED, 1);
}
*(uchar*) copy->to_ptr= (uchar) length;
@@ -527,7 +528,7 @@ static void do_varstring1_mb(Copy_field *copy)
if (length < from_length)
{
if (current_thd->count_cuted_fields)
- copy->to_field->set_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
+ copy->to_field->set_warning(Sql_condition::WARN_LEVEL_WARN,
WARN_DATA_TRUNCATED, 1);
}
*copy->to_ptr= (uchar) length;
@@ -543,7 +544,7 @@ static void do_varstring2(Copy_field *copy)
length=copy->to_length-HA_KEY_BLOB_LENGTH;
if (copy->from_field->table->in_use->count_cuted_fields &&
copy->to_field)
- copy->to_field->set_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
+ copy->to_field->set_warning(Sql_condition::WARN_LEVEL_WARN,
WARN_DATA_TRUNCATED, 1);
}
int2store(copy->to_ptr,length);
@@ -565,7 +566,7 @@ static void do_varstring2_mb(Copy_field *copy)
if (length < from_length)
{
if (current_thd->count_cuted_fields)
- copy->to_field->set_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
+ copy->to_field->set_warning(Sql_condition::WARN_LEVEL_WARN,
WARN_DATA_TRUNCATED, 1);
}
int2store(copy->to_ptr, length);
@@ -782,6 +783,10 @@ Copy_field::get_copy_func(Field *to,Field *from)
else if (to->real_type() != from->real_type() ||
to_length != from_length)
{
+ if ((to->real_type() == MYSQL_TYPE_ENUM ||
+ to->real_type() == MYSQL_TYPE_SET) &&
+ from->real_type() == MYSQL_TYPE_NEWDECIMAL)
+ return do_field_decimal;
if (to->real_type() == MYSQL_TYPE_DECIMAL ||
to->result_type() == STRING_RESULT)
return do_field_string;
@@ -814,43 +819,80 @@ Copy_field::get_copy_func(Field *to,Field *from)
return do_field_eq;
}
+/**
+ Check if it is possible just copy value of the fields
+
+ @param to The field to copy to
+ @param from The field to copy from
+
+ @retval TRUE - it is possible to just copy value of 'from' to 'to'.
+ @retval FALSE - conversion is needed
+*/
+
+bool memcpy_field_possible(Field *to,Field *from)
+{
+ const enum_field_types to_real_type= to->real_type();
+ const enum_field_types from_real_type= from->real_type();
+ /*
+ Warning: Calling from->type() may be unsafe in some (unclear) circumstances
+ related to SPs. See MDEV-6799.
+ */
+ return (to_real_type == from_real_type &&
+ !(to->flags & BLOB_FLAG && to->table->copy_blobs) &&
+ to->pack_length() == from->pack_length() &&
+ !(to->flags & UNSIGNED_FLAG && !(from->flags & UNSIGNED_FLAG)) &&
+ to->decimals() == from->decimals() &&
+ to_real_type != MYSQL_TYPE_ENUM &&
+ to_real_type != MYSQL_TYPE_SET &&
+ to_real_type != MYSQL_TYPE_BIT &&
+ (to_real_type != MYSQL_TYPE_NEWDECIMAL ||
+ to->field_length == from->field_length) &&
+ from->charset() == to->charset() &&
+ (!sql_mode_for_dates(to->table->in_use) ||
+ (from->type()!= MYSQL_TYPE_DATE &&
+ from->type()!= MYSQL_TYPE_DATETIME)) &&
+ (from_real_type != MYSQL_TYPE_VARCHAR ||
+ ((Field_varstring*)from)->length_bytes ==
+ ((Field_varstring*)to)->length_bytes));
+}
+
/** Simple quick field convert that is called on insert. */
int field_conv(Field *to,Field *from)
{
- bool blob_type_dest= to->flags & BLOB_FLAG;
- if (to->real_type() == from->real_type() &&
- !(blob_type_dest && to->table->copy_blobs))
- {
- if (to->pack_length() == from->pack_length() &&
- !(to->flags & UNSIGNED_FLAG && !(from->flags & UNSIGNED_FLAG)) &&
- to->decimals() == from->decimals() &&
- to->real_type() != MYSQL_TYPE_ENUM &&
- to->real_type() != MYSQL_TYPE_SET &&
- to->real_type() != MYSQL_TYPE_BIT &&
- (to->real_type() != MYSQL_TYPE_NEWDECIMAL ||
- to->field_length == from->field_length) &&
- from->charset() == to->charset() &&
- (!(to->table->in_use->variables.sql_mode &
- (MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE | MODE_INVALID_DATES)) ||
- (to->type() != MYSQL_TYPE_DATE &&
- to->type() != MYSQL_TYPE_DATETIME)) &&
- (from->real_type() != MYSQL_TYPE_VARCHAR ||
- ((Field_varstring*)from)->length_bytes ==
- ((Field_varstring*)to)->length_bytes))
- { // Identical fields
- /*
- This may happen if one does 'UPDATE ... SET x=x'
- The test is here mostly for valgrind, but can also be relevant
- if memcpy() is implemented with prefetch-write
- */
- if (to->ptr != from->ptr)
- memcpy(to->ptr,from->ptr,to->pack_length());
- return 0;
- }
+ if (memcpy_field_possible(to, from))
+ { // Identical fields
+ /*
+ This may happen if one does 'UPDATE ... SET x=x'
+ The test is here mostly for valgrind, but can also be relevant
+ if memcpy() is implemented with prefetch-write
+ */
+ if (to->ptr != from->ptr)
+ memcpy(to->ptr, from->ptr, to->pack_length());
+ return 0;
}
- if (blob_type_dest)
+ return field_conv_incompatible(to, from);
+}
+
+
+/**
+ Copy value of the field with conversion.
+
+ @note Impossibility of simple copy should be checked before this call.
+
+ @param to The field to copy to
+ @param from The field to copy from
+
+ @retval TRUE ERROR
+ @retval FALSE OK
+*/
+
+int field_conv_incompatible(Field *to, Field *from)
+{
+ const enum_field_types to_real_type= to->real_type();
+ const enum_field_types from_real_type= from->real_type();
+ if (to->flags & BLOB_FLAG)
{ // Be sure the value is stored
Field_blob *blob=(Field_blob*) to;
from->val_str(&blob->value);
@@ -865,16 +907,17 @@ int field_conv(Field *to,Field *from)
return blob->store(blob->value.ptr(),blob->value.length(),from->charset());
}
- if (from->real_type() == MYSQL_TYPE_ENUM &&
- to->real_type() == MYSQL_TYPE_ENUM &&
+ if (from_real_type == MYSQL_TYPE_ENUM &&
+ to_real_type == MYSQL_TYPE_ENUM &&
from->val_int() == 0)
{
((Field_enum *)(to))->store_type(0);
return 0;
}
- if (from->result_type() == REAL_RESULT)
+ Item_result from_result_type= from->result_type();
+ if (from_result_type == REAL_RESULT)
return to->store(from->val_real());
- if (from->result_type() == DECIMAL_RESULT)
+ if (from_result_type == DECIMAL_RESULT)
{
my_decimal buff;
return to->store_decimal(from->val_decimal(&buff));
@@ -887,10 +930,10 @@ int field_conv(Field *to,Field *from)
else
return to->store_time_dec(&ltime, from->decimals());
}
- if ((from->result_type() == STRING_RESULT &&
+ if ((from_result_type == STRING_RESULT &&
(to->result_type() == STRING_RESULT ||
- (from->real_type() != MYSQL_TYPE_ENUM &&
- from->real_type() != MYSQL_TYPE_SET))) ||
+ (from_real_type != MYSQL_TYPE_ENUM &&
+ from_real_type != MYSQL_TYPE_SET))) ||
to->type() == MYSQL_TYPE_DECIMAL)
{
char buff[MAX_FIELD_WIDTH];
@@ -904,5 +947,5 @@ int field_conv(Field *to,Field *from)
*/
return to->store(result.c_ptr_quick(),result.length(),from->charset());
}
- return to->store(from->val_int(), test(from->flags & UNSIGNED_FLAG));
+ return to->store(from->val_int(), MY_TEST(from->flags & UNSIGNED_FLAG));
}
diff --git a/sql/filesort.cc b/sql/filesort.cc
index 5bb5c64409a..6ad7bee48c6 100644
--- a/sql/filesort.cc
+++ b/sql/filesort.cc
@@ -22,9 +22,9 @@
Sorts a database
*/
+#include <my_global.h>
#include "sql_priv.h"
#include "filesort.h"
-#include "unireg.h" // REQUIRED by other includes
#ifdef HAVE_STDDEF_H
#include <stddef.h> /* for macro offsetof */
#endif
@@ -34,9 +34,11 @@
#include "sql_base.h" // update_virtual_fields
#include "sql_test.h" // TEST_filesort
#include "opt_range.h" // SQL_SELECT
+#include "bounded_queue.h"
+#include "filesort_utils.h"
+#include "sql_select.h"
#include "log_slow.h"
#include "debug_sync.h"
-#include "sql_base.h"
/// How to write record_ref.
#define WRITE_REF(file,from) \
@@ -47,22 +49,63 @@ if (my_b_write((file),(uchar*) (from),param->ref_length)) \
static uchar *read_buffpek_from_file(IO_CACHE *buffer_file, uint count,
uchar *buf);
-static ha_rows find_all_keys(SORTPARAM *param,SQL_SELECT *select,
- uchar * *sort_keys, uchar *sort_keys_buf,
- IO_CACHE *buffer_file, IO_CACHE *tempfile);
-static bool write_keys(SORTPARAM *param,uchar * *sort_keys,
- uint count, IO_CACHE *buffer_file, IO_CACHE *tempfile);
-static void make_sortkey(SORTPARAM *param,uchar *to, uchar *ref_pos);
-static void register_used_fields(SORTPARAM *param);
-static bool save_index(SORTPARAM *param,uchar **sort_keys, uint count,
- FILESORT_INFO *table_sort);
+static ha_rows find_all_keys(Sort_param *param,SQL_SELECT *select,
+ Filesort_info *fs_info,
+ IO_CACHE *buffer_file,
+ IO_CACHE *tempfile,
+ Bounded_queue<uchar, uchar> *pq,
+ ha_rows *found_rows);
+static bool write_keys(Sort_param *param, Filesort_info *fs_info,
+ uint count, IO_CACHE *buffer_file, IO_CACHE *tempfile);
+static void make_sortkey(Sort_param *param, uchar *to, uchar *ref_pos);
+static void register_used_fields(Sort_param *param);
+static bool save_index(Sort_param *param, uint count,
+ Filesort_info *table_sort);
static uint suffix_length(ulong string_length);
static uint sortlength(THD *thd, SORT_FIELD *sortorder, uint s_length,
bool *multi_byte_charset);
-static SORT_ADDON_FIELD *get_addon_fields(THD *thd, Field **ptabfield,
+static SORT_ADDON_FIELD *get_addon_fields(ulong max_length_for_sort_data,
+ Field **ptabfield,
uint sortlength, uint *plength);
static void unpack_addon_fields(struct st_sort_addon_field *addon_field,
uchar *buff, uchar *buff_end);
+static bool check_if_pq_applicable(Sort_param *param, Filesort_info *info,
+ TABLE *table,
+ ha_rows records, ulong memory_available);
+
+
+void Sort_param::init_for_filesort(uint sortlen, TABLE *table,
+ ulong max_length_for_sort_data,
+ ha_rows maxrows, bool sort_positions)
+{
+ sort_length= sortlen;
+ ref_length= table->file->ref_length;
+ if (!(table->file->ha_table_flags() & HA_FAST_KEY_READ) &&
+ !table->fulltext_searched && !sort_positions)
+ {
+ /*
+ Get the descriptors of all fields whose values are appended
+ to sorted fields and get its total length in addon_length.
+ */
+ addon_field= get_addon_fields(max_length_for_sort_data,
+ table->field, sort_length, &addon_length);
+ }
+ if (addon_field)
+ res_length= addon_length;
+ else
+ {
+ res_length= ref_length;
+ /*
+ The reference to the record is considered
+ as an additional sorted field
+ */
+ sort_length+= ref_length;
+ }
+ rec_length= sort_length + addon_length;
+ max_rows= maxrows;
+}
+
+
/**
Sort a table.
Creates a set of pointers that can be used to read the rows
@@ -75,15 +118,17 @@ static void unpack_addon_fields(struct st_sort_addon_field *addon_field,
The result set is stored in table->io_cache or
table->record_pointers.
- @param thd Current thread
- @param table Table to sort
- @param sortorder How to sort the table
- @param s_length Number of elements in sortorder
- @param select condition to apply to the rows
- @param max_rows Return only this many rows
- @param sort_positions Set to 1 if we want to force sorting by position
- (Needed by UPDATE/INSERT or ALTER TABLE)
- @param examined_rows Store number of examined rows here
+ @param thd Current thread
+ @param table Table to sort
+ @param sortorder How to sort the table
+ @param s_length Number of elements in sortorder
+ @param select Condition to apply to the rows
+ @param max_rows Return only this many rows
+ @param sort_positions Set to TRUE if we want to force sorting by position
+ (Needed by UPDATE/INSERT or ALTER TABLE or
+ when rowids are required by executor)
+ @param[out] examined_rows Store number of examined rows here
+ @param[out] found_rows Store the number of found rows here
@note
If we sort by position (like if sort_positions is 1) filesort() will
@@ -93,31 +138,30 @@ static void unpack_addon_fields(struct st_sort_addon_field *addon_field,
HA_POS_ERROR Error
@retval
\# Number of rows
- @retval
- examined_rows will be set to number of examined rows
*/
ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length,
SQL_SELECT *select, ha_rows max_rows,
- bool sort_positions, ha_rows *examined_rows)
+ bool sort_positions,
+ ha_rows *examined_rows,
+ ha_rows *found_rows)
{
int error;
size_t memory_available= thd->variables.sortbuff_size;
- size_t min_sort_memory;
- size_t sort_buff_sz;
uint maxbuffer;
BUFFPEK *buffpek;
ha_rows num_rows= HA_POS_ERROR;
- uchar **sort_keys= 0;
IO_CACHE tempfile, buffpek_pointers, *outfile;
- SORTPARAM param;
+ Sort_param param;
bool multi_byte_charset;
+ Bounded_queue<uchar, uchar> pq;
+
DBUG_ENTER("filesort");
DBUG_EXECUTE("info",TEST_filesort(sortorder,s_length););
#ifdef SKIP_DBUG_IN_FILESORT
DBUG_PUSH(""); /* No DBUG here */
#endif
- FILESORT_INFO table_sort;
+ Filesort_info table_sort= table->sort;
TABLE_LIST *tab= table->pos_in_table_list;
Item_subselect *subselect= tab ? tab->containing_subselect() : 0;
@@ -135,7 +179,6 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length,
QUICK_INDEX_MERGE_SELECT. Work with a copy and put it back at the end
when index_merge select has finished with it.
*/
- memcpy(&table_sort, &table->sort, sizeof(FILESORT_INFO));
table->sort.io_cache= NULL;
DBUG_ASSERT(table_sort.record_pointers == NULL);
@@ -144,80 +187,88 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length,
my_b_clear(&buffpek_pointers);
buffpek=0;
error= 1;
- bzero((char*) &param,sizeof(param));
- param.sort_length= sortlength(thd, sortorder, s_length, &multi_byte_charset);
- param.ref_length= table->file->ref_length;
- if (!(table->file->ha_table_flags() & HA_FAST_KEY_READ) &&
- !table->fulltext_searched && !sort_positions)
- {
- /*
- Get the descriptors of all fields whose values are appended
- to sorted fields and get its total length in param.spack_length.
- */
- param.addon_field= get_addon_fields(thd, table->field,
- param.sort_length,
- &param.addon_length);
- }
+ *found_rows= HA_POS_ERROR;
+
+ param.init_for_filesort(sortlength(thd, sortorder, s_length,
+ &multi_byte_charset),
+ table,
+ thd->variables.max_length_for_sort_data,
+ max_rows, sort_positions);
table_sort.addon_buf= 0;
table_sort.addon_length= param.addon_length;
table_sort.addon_field= param.addon_field;
table_sort.unpack= unpack_addon_fields;
- if (param.addon_field)
- {
- param.res_length= param.addon_length;
- if (!(table_sort.addon_buf= (uchar *) my_malloc(param.addon_length,
- MYF(MY_WME))))
- goto err;
- }
- else
- {
- param.res_length= param.ref_length;
- /*
- The reference to the record is considered
- as an additional sorted field
- */
- param.sort_length+= param.ref_length;
- }
- param.rec_length= param.sort_length+param.addon_length;
- param.max_rows= max_rows;
+ if (param.addon_field &&
+ !(table_sort.addon_buf=
+ (uchar *) my_malloc(param.addon_length, MYF(MY_WME |
+ MY_THREAD_SPECIFIC))))
+ goto err;
if (select && select->quick)
- status_var_increment(thd->status_var.filesort_range_count);
+ thd->inc_status_sort_range();
else
- status_var_increment(thd->status_var.filesort_scan_count);
+ thd->inc_status_sort_scan();
thd->query_plan_flags|= QPLAN_FILESORT;
// If number of rows is not known, use as much of sort buffer as possible.
num_rows= table->file->estimate_rows_upper_bound();
if (multi_byte_charset &&
- !(param.tmp_buffer= (char*) my_malloc(param.sort_length,MYF(MY_WME))))
+ !(param.tmp_buffer= (char*) my_malloc(param.sort_length,
+ MYF(MY_WME | MY_THREAD_SPECIFIC))))
goto err;
- min_sort_memory= max(MIN_SORT_MEMORY, param.sort_length * MERGEBUFF2);
- set_if_bigger(min_sort_memory, sizeof(BUFFPEK*)*MERGEBUFF2);
- if (!table_sort.sort_keys)
+ if (check_if_pq_applicable(&param, &table_sort,
+ table, num_rows, memory_available))
{
- /*
- Cannot depend on num_rows. For external sort, space for upto MERGEBUFF2
- rows is required.
- */
- if (num_rows < MERGEBUFF2)
- num_rows= MERGEBUFF2;
+ DBUG_PRINT("info", ("filesort PQ is applicable"));
+ thd->query_plan_flags|= QPLAN_FILESORT_PRIORITY_QUEUE;
+ status_var_increment(thd->status_var.filesort_pq_sorts_);
+ const size_t compare_length= param.sort_length;
+ if (pq.init(param.max_rows,
+ true, // max_at_top
+ NULL, // compare_function
+ compare_length,
+ &make_sortkey, &param, table_sort.get_sort_keys()))
+ {
+ /*
+ If we fail to init pq, we have to give up:
+ out of memory means my_malloc() will call my_error().
+ */
+ DBUG_PRINT("info", ("failed to allocate PQ"));
+ table_sort.free_sort_buffer();
+ DBUG_ASSERT(thd->is_error());
+ goto err;
+ }
+ // For PQ queries (with limit) we initialize all pointers.
+ table_sort.init_record_pointers();
+ }
+ else
+ {
+ DBUG_PRINT("info", ("filesort PQ is not applicable"));
+ size_t min_sort_memory= MY_MAX(MIN_SORT_MEMORY, param.sort_length*MERGEBUFF2);
+ set_if_bigger(min_sort_memory, sizeof(BUFFPEK*)*MERGEBUFF2);
while (memory_available >= min_sort_memory)
{
ulonglong keys= memory_available / (param.rec_length + sizeof(char*));
- table_sort.keys= (uint) min(num_rows, keys);
- sort_buff_sz= table_sort.keys*(param.rec_length+sizeof(char*));
- set_if_bigger(sort_buff_sz, param.rec_length * MERGEBUFF2);
-
- DBUG_EXECUTE_IF("make_sort_keys_alloc_fail",
- DBUG_SET("+d,simulate_out_of_memory"););
-
- if ((table_sort.sort_keys=
- (uchar**) my_malloc(sort_buff_sz, MYF(0))))
+ param.max_keys_per_buffer= (uint) MY_MIN(num_rows, keys);
+ if (table_sort.get_sort_keys())
+ {
+ // If we have already allocated a buffer, it better have same size!
+ if (!table_sort.check_sort_buffer_properties(param.max_keys_per_buffer,
+ param.rec_length))
+ {
+ /*
+ table->sort will still have a pointer to the same buffer,
+ but that will be overwritten by the assignment below.
+ */
+ table_sort.free_sort_buffer();
+ }
+ }
+ table_sort.alloc_sort_buffer(param.max_keys_per_buffer, param.rec_length);
+ if (table_sort.get_sort_keys())
break;
size_t old_memory_available= memory_available;
memory_available= memory_available/4*3;
@@ -225,40 +276,37 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length,
old_memory_available > min_sort_memory)
memory_available= min_sort_memory;
}
+ if (memory_available < min_sort_memory)
+ {
+ my_error(ER_OUT_OF_SORTMEMORY,MYF(ME_ERROR + ME_FATALERROR));
+ goto err;
+ }
}
- sort_keys= table_sort.sort_keys;
- param.keys= table_sort.keys;
- if (memory_available < min_sort_memory)
- {
- my_error(ER_OUT_OF_SORTMEMORY,MYF(ME_ERROR + ME_FATALERROR));
- goto err;
- }
if (open_cached_file(&buffpek_pointers,mysql_tmpdir,TEMP_PREFIX,
- DISK_BUFFER_SIZE, MYF(ME_ERROR | MY_WME)))
+ DISK_BUFFER_SIZE, MYF(MY_WME)))
goto err;
param.sort_form= table;
param.end=(param.local_sortorder=sortorder)+s_length;
- num_rows= find_all_keys(&param,
- select,
- sort_keys,
- (uchar *)(sort_keys+param.keys),
+ num_rows= find_all_keys(&param, select,
+ &table_sort,
&buffpek_pointers,
- &tempfile);
+ &tempfile,
+ pq.is_initialized() ? &pq : NULL,
+ found_rows);
if (num_rows == HA_POS_ERROR)
goto err;
+
maxbuffer= (uint) (my_b_tell(&buffpek_pointers)/sizeof(*buffpek));
if (maxbuffer == 0) // The whole set is in memory
{
- if (save_index(&param,sort_keys,(uint) num_rows, &table_sort))
+ if (save_index(&param, (uint) num_rows, &table_sort))
goto err;
}
else
{
- thd->query_plan_flags|= QPLAN_FILESORT_DISK;
-
/* filesort cannot handle zero-length records during merge. */
DBUG_ASSERT(param.sort_length != 0);
@@ -277,7 +325,7 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length,
/* Open cached file if it isn't open */
if (! my_b_inited(outfile) &&
open_cached_file(outfile,mysql_tmpdir,TEMP_PREFIX,READ_RECORD_BUFFER,
- MYF(ME_ERROR | MY_WME)))
+ MYF(MY_WME)))
goto err;
if (reinit_io_cache(outfile,WRITE_CACHE,0L,0,0))
goto err;
@@ -286,18 +334,20 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length,
Use also the space previously used by string pointers in sort_buffer
for temporary key storage.
*/
- param.keys=((param.keys *
- (param.rec_length+sizeof(char*))) /
- param.rec_length - 1);
+ param.max_keys_per_buffer=((param.max_keys_per_buffer *
+ (param.rec_length + sizeof(char*))) /
+ param.rec_length - 1);
maxbuffer--; // Offset from 0
- if (merge_many_buff(&param,(uchar*) sort_keys,buffpek,&maxbuffer,
+ if (merge_many_buff(&param,
+ (uchar*) table_sort.get_sort_keys(),
+ buffpek,&maxbuffer,
&tempfile))
goto err;
if (flush_io_cache(&tempfile) ||
reinit_io_cache(&tempfile,READ_CACHE,0L,0,0))
goto err;
if (merge_index(&param,
- (uchar*) sort_keys,
+ (uchar*) table_sort.get_sort_keys(),
buffpek,
maxbuffer,
&tempfile,
@@ -312,12 +362,11 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length,
}
error= 0;
- err:
+ err:
my_free(param.tmp_buffer);
if (!subselect || !subselect->is_uncacheable())
{
- my_free(sort_keys);
- table_sort.sort_keys= 0;
+ table_sort.free_sort_buffer();
my_free(buffpek);
table_sort.buffpek= 0;
table_sort.buffpek_len= 0;
@@ -353,10 +402,11 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length,
MYF(0),
ER_THD(thd, ER_FILSORT_ABORT),
kill_errno ? ER(kill_errno) :
- thd->killed == ABORT_QUERY ? "" : thd->stmt_da->message());
+ thd->killed == ABORT_QUERY ? "" :
+ thd->get_stmt_da()->message());
if (global_system_variables.log_warnings > 1)
- {
+ {
sql_print_warning("%s, host: %s, user: %s, thread: %lu, query: %-.4096s",
ER_THD(thd, ER_FILSORT_ABORT),
thd->security_ctx->host_or_ip,
@@ -366,8 +416,7 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length,
}
}
else
- statistic_add(thd->status_var.filesort_rows,
- (ulong) num_rows, &LOCK_status);
+ thd->inc_status_sort_rows(num_rows);
*examined_rows= param.examined_rows;
#ifdef SKIP_DBUG_IN_FILESORT
DBUG_POP(); /* Ok to DBUG */
@@ -376,8 +425,12 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length,
/* table->sort.io_cache should be free by this time */
DBUG_ASSERT(NULL == table->sort.io_cache);
- memcpy(&table->sort, &table_sort, sizeof(FILESORT_INFO));
- DBUG_PRINT("exit",("num_rows: %ld", (long) num_rows));
+ // Assign the copy back!
+ table->sort= table_sort;
+
+ DBUG_PRINT("exit",
+ ("num_rows: %ld examined_rows: %ld found_rows: %ld",
+ (long) num_rows, (long) *examined_rows, (long) *found_rows));
MYSQL_FILESORT_DONE(error, num_rows);
DBUG_RETURN(error ? HA_POS_ERROR : num_rows);
} /* filesort */
@@ -391,8 +444,7 @@ void filesort_free_buffers(TABLE *table, bool full)
if (full)
{
- my_free(table->sort.sort_keys);
- table->sort.sort_keys= NULL;
+ table->sort.free_sort_buffer();
my_free(table->sort.buffpek);
table->sort.buffpek= NULL;
table->sort.buffpek_len= 0;
@@ -417,7 +469,7 @@ static uchar *read_buffpek_from_file(IO_CACHE *buffpek_pointers, uint count,
if (count > UINT_MAX/sizeof(BUFFPEK))
return 0; /* sizeof(BUFFPEK)*count will overflow */
if (!tmp)
- tmp= (uchar *)my_malloc(length, MYF(MY_WME));
+ tmp= (uchar *)my_malloc(length, MYF(MY_WME | MY_THREAD_SPECIFIC));
if (tmp)
{
if (reinit_io_cache(buffpek_pointers,READ_CACHE,0L,0,0) ||
@@ -560,8 +612,10 @@ static void dbug_print_record(TABLE *table, bool print_rowid)
#endif
+
/**
- Search after sort_keys and write them into tempfile.
+ Search after sort_keys, and write them into tempfile
+ (if we run out of space in the sort_keys buffer).
All produced sequences are guaranteed to be non-empty.
@param param Sorting parameter
@@ -570,19 +624,28 @@ static void dbug_print_record(TABLE *table, bool print_rowid)
@param buffpek_pointers File to write BUFFPEKs describing sorted segments
in tempfile.
@param tempfile File to write sorted sequences of sortkeys to.
+ @param pq If !NULL, use it for keeping top N elements
+ @param [out] found_rows The number of FOUND_ROWS().
+ For a query with LIMIT, this value will typically
+ be larger than the function return value.
@note
Basic idea:
@verbatim
while (get_next_sortkey())
{
- if (no free space in sort_keys buffers)
+ if (using priority queue)
+ push sort key into queue
+ else
{
- sort sort_keys buffer;
- dump sorted sequence to 'tempfile';
- dump BUFFPEK describing sequence location into 'buffpek_pointers';
+ if (no free space in sort_keys buffers)
+ {
+ sort sort_keys buffer;
+ dump sorted sequence to 'tempfile';
+ dump BUFFPEK describing sequence location into 'buffpek_pointers';
+ }
+ put sort key into 'sort_keys';
}
- put sort key into 'sort_keys';
}
if (sort_keys has some elements && dumped at least once)
sort-dump-dump as above;
@@ -596,10 +659,12 @@ static void dbug_print_record(TABLE *table, bool print_rowid)
HA_POS_ERROR on error.
*/
-static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select,
- uchar **sort_keys, uchar *sort_keys_buf,
+static ha_rows find_all_keys(Sort_param *param, SQL_SELECT *select,
+ Filesort_info *fs_info,
IO_CACHE *buffpek_pointers,
- IO_CACHE *tempfile)
+ IO_CACHE *tempfile,
+ Bounded_queue<uchar, uchar> *pq,
+ ha_rows *found_rows)
{
int error,flag,quick_select;
uint idx,indexpos,ref_length;
@@ -607,10 +672,9 @@ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select,
my_off_t record;
TABLE *sort_form;
THD *thd= current_thd;
- volatile killed_state *killed= &thd->killed;
handler *file;
MY_BITMAP *save_read_set, *save_write_set, *save_vcol_set;
- uchar *next_sort_key= sort_keys_buf;
+
DBUG_ENTER("find_all_keys");
DBUG_PRINT("info",("using: %s",
(select ? select->quick ? "ranges" : "where":
@@ -624,10 +688,16 @@ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select,
ref_pos= ref_buff;
quick_select=select && select->quick;
record=0;
+ *found_rows= 0;
flag= ((file->ha_table_flags() & HA_REC_NOT_IN_SEQ) || quick_select);
if (flag)
ref_pos= &file->ref[0];
next_pos=ref_pos;
+
+ DBUG_EXECUTE_IF("show_explain_in_find_all_keys",
+ dbug_serve_apcs(thd, 1);
+ );
+
if (!quick_select)
{
next_pos=(uchar*) 0; /* Find records in sequence */
@@ -639,7 +709,6 @@ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select,
current_thd->variables.read_buff_size);
}
-
/* Remember original bitmaps */
save_read_set= sort_form->read_set;
save_write_set= sort_form->write_set;
@@ -697,7 +766,7 @@ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select,
break;
}
- if (*killed)
+ if (thd->check_killed())
{
DBUG_PRINT("info",("Sort killed by user"));
if (!quick_select)
@@ -742,18 +811,23 @@ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select,
if (write_record)
{
- if (idx == param->keys)
+ ++(*found_rows);
+ if (pq)
{
- if (write_keys(param, sort_keys,
- idx, buffpek_pointers, tempfile))
- DBUG_RETURN(HA_POS_ERROR);
- idx= 0;
- next_sort_key= sort_keys_buf;
- indexpos++;
+ pq->push(ref_pos);
+ idx= pq->num_elements();
+ }
+ else
+ {
+ if (idx == param->max_keys_per_buffer)
+ {
+ if (write_keys(param, fs_info, idx, buffpek_pointers, tempfile))
+ DBUG_RETURN(HA_POS_ERROR);
+ idx= 0;
+ indexpos++;
+ }
+ make_sortkey(param, fs_info->get_record_buffer(idx++), ref_pos);
}
- sort_keys[idx++]= next_sort_key;
- make_sortkey(param, next_sort_key, ref_pos);
- next_sort_key+= param->rec_length;
}
/* It does not make sense to read more keys in case of a fatal error */
@@ -787,12 +861,12 @@ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select,
DBUG_RETURN(HA_POS_ERROR); /* purecov: inspected */
}
if (indexpos && idx &&
- write_keys(param, sort_keys,
- idx, buffpek_pointers, tempfile))
+ write_keys(param, fs_info, idx, buffpek_pointers, tempfile))
DBUG_RETURN(HA_POS_ERROR); /* purecov: inspected */
const ha_rows retval=
my_b_inited(tempfile) ?
(ha_rows) (my_b_tell(tempfile)/param->rec_length) : idx;
+ DBUG_PRINT("info", ("find_all_keys return %u", (uint) retval));
DBUG_RETURN(retval);
} /* find_all_keys */
@@ -820,21 +894,19 @@ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select,
*/
static bool
-write_keys(SORTPARAM *param, register uchar **sort_keys, uint count,
+write_keys(Sort_param *param, Filesort_info *fs_info, uint count,
IO_CACHE *buffpek_pointers, IO_CACHE *tempfile)
{
- size_t sort_length, rec_length;
+ size_t rec_length;
uchar **end;
BUFFPEK buffpek;
DBUG_ENTER("write_keys");
- sort_length= param->sort_length;
rec_length= param->rec_length;
-#ifdef MC68000
- quicksort(sort_keys,count,sort_length);
-#else
- my_string_ptr_sort((uchar*) sort_keys, (uint) count, sort_length);
-#endif
+ uchar **sort_keys= fs_info->get_sort_keys();
+
+ fs_info->sort_buffer(param, count);
+
if (!my_b_inited(tempfile) &&
open_cached_file(tempfile, mysql_tmpdir, TEMP_PREFIX, DISK_BUFFER_SIZE,
MYF(MY_WME)))
@@ -883,8 +955,8 @@ static inline void store_length(uchar *to, uint length, uint pack_length)
/** Make a sort-key from record. */
-static void make_sortkey(register SORTPARAM *param,
- register uchar *to, uchar *ref_pos)
+static void make_sortkey(register Sort_param *param,
+ register uchar *to, uchar *ref_pos)
{
reg3 Field *field;
reg1 SORT_FIELD *sort_field;
@@ -908,7 +980,7 @@ static void make_sortkey(register SORTPARAM *param,
switch (sort_field->result_type) {
case STRING_RESULT:
{
- CHARSET_INFO *cs=item->collation.collation;
+ const CHARSET_INFO *cs=item->collation.collation;
char fill_char= ((cs->state & MY_CS_BINSORT) ? (char) 0 : ' ');
if (maybe_null)
@@ -919,7 +991,7 @@ static void make_sortkey(register SORTPARAM *param,
if (!res)
{
if (maybe_null)
- bzero((char*) to-1,sort_field->length+1);
+ memset(to-1, 0, sort_field->length+1);
else
{
/* purecov: begin deadcode */
@@ -931,7 +1003,7 @@ static void make_sortkey(register SORTPARAM *param,
DBUG_ASSERT(0);
DBUG_PRINT("warning",
("Got null on something that shouldn't be null"));
- bzero((char*) to,sort_field->length); // Avoid crash
+ memset(to, 0, sort_field->length); // Avoid crash
/* purecov: end */
}
break;
@@ -940,8 +1012,12 @@ static void make_sortkey(register SORTPARAM *param,
if (sort_field->need_strxnfrm)
{
uint tmp_length __attribute__((unused));
- tmp_length= my_strnxfrm(cs, to ,sort_field->length,
- (uchar*) res->ptr(), length);
+ tmp_length= cs->coll->strnxfrm(cs, to, sort_field->length,
+ item->max_char_length() *
+ cs->strxfrm_multiply,
+ (uchar*) res->ptr(), length,
+ MY_STRXFRM_PAD_WITH_SPACE |
+ MY_STRXFRM_PAD_TO_MAXLEN);
DBUG_ASSERT(tmp_length == sort_field->length);
}
else
@@ -987,12 +1063,19 @@ static void make_sortkey(register SORTPARAM *param,
}
if (maybe_null)
{
+ *to++=1; /* purecov: inspected */
if (item->null_value)
{
- bzero((char*) to++, sort_field->length+1);
+ if (maybe_null)
+ memset(to-1, 0, sort_field->length+1);
+ else
+ {
+ DBUG_PRINT("warning",
+ ("Got null on something that shouldn't be null"));
+ memset(to, 0, sort_field->length);
+ }
break;
}
- *to++=1; /* purecov: inspected */
}
to[7]= (uchar) value;
to[6]= (uchar) (value >> 8);
@@ -1014,7 +1097,8 @@ static void make_sortkey(register SORTPARAM *param,
{
if (item->null_value)
{
- bzero((char*) to++, sort_field->length+1);
+ memset(to, 0, sort_field->length+1);
+ to++;
break;
}
*to++=1;
@@ -1031,7 +1115,7 @@ static void make_sortkey(register SORTPARAM *param,
{
if (item->null_value)
{
- bzero((char*) to,sort_field->length+1);
+ memset(to, 0, sort_field->length+1);
to++;
break;
}
@@ -1076,7 +1160,7 @@ static void make_sortkey(register SORTPARAM *param,
SORT_ADDON_FIELD *addonf= param->addon_field;
uchar *nulls= to;
DBUG_ASSERT(addonf != 0);
- bzero((char *) nulls, addonf->offset);
+ memset(nulls, 0, addonf->offset);
to+= addonf->offset;
for ( ; (field= addonf->field) ; addonf++)
{
@@ -1115,7 +1199,7 @@ static void make_sortkey(register SORTPARAM *param,
Register fields used by sorting in the sorted table's read set
*/
-static void register_used_fields(SORTPARAM *param)
+static void register_used_fields(Sort_param *param)
{
reg1 SORT_FIELD *sort_field;
TABLE *table=param->sort_form;
@@ -1160,21 +1244,20 @@ static void register_used_fields(SORTPARAM *param)
}
-static bool save_index(SORTPARAM *param, uchar **sort_keys, uint count,
- FILESORT_INFO *table_sort)
+static bool save_index(Sort_param *param, uint count, Filesort_info *table_sort)
{
uint offset,res_length;
uchar *to;
DBUG_ENTER("save_index");
- my_string_ptr_sort((uchar*) sort_keys, (uint) count, param->sort_length);
+ table_sort->sort_buffer(param, count);
res_length= param->res_length;
offset= param->rec_length-res_length;
- if ((ha_rows) count > param->max_rows)
- count=(uint) param->max_rows;
if (!(to= table_sort->record_pointers=
- (uchar*) my_malloc(res_length*count, MYF(MY_WME))))
+ (uchar*) my_malloc(res_length*count,
+ MYF(MY_WME | MY_THREAD_SPECIFIC))))
DBUG_RETURN(1); /* purecov: inspected */
+ uchar **sort_keys= table_sort->get_sort_keys();
for (uchar **end= sort_keys+count ; sort_keys != end ; sort_keys++)
{
memcpy(to, *sort_keys+offset, res_length);
@@ -1184,10 +1267,150 @@ static bool save_index(SORTPARAM *param, uchar **sort_keys, uint count,
}
+/**
+ Test whether priority queue is worth using to get top elements of an
+ ordered result set. If it is, then allocates buffer for required amount of
+ records
+
+ @param param Sort parameters.
+ @param filesort_info Filesort information.
+ @param table Table to sort.
+ @param num_rows Estimate of number of rows in source record set.
+ @param memory_available Memory available for sorting.
+
+ DESCRIPTION
+ Given a query like this:
+ SELECT ... FROM t ORDER BY a1,...,an LIMIT max_rows;
+ This function tests whether a priority queue should be used to keep
+ the result. Necessary conditions are:
+ - estimate that it is actually cheaper than merge-sort
+ - enough memory to store the <max_rows> records.
+
+ If we don't have space for <max_rows> records, but we *do* have
+ space for <max_rows> keys, we may rewrite 'table' to sort with
+ references to records instead of additional data.
+ (again, based on estimates that it will actually be cheaper).
+
+ @retval
+ true - if it's ok to use PQ
+ false - PQ will be slower than merge-sort, or there is not enough memory.
+*/
+
+bool check_if_pq_applicable(Sort_param *param,
+ Filesort_info *filesort_info,
+ TABLE *table, ha_rows num_rows,
+ ulong memory_available)
+{
+ DBUG_ENTER("check_if_pq_applicable");
+
+ /*
+ How much Priority Queue sort is slower than qsort.
+ Measurements (see unit test) indicate that PQ is roughly 3 times slower.
+ */
+ const double PQ_slowness= 3.0;
+
+ if (param->max_rows == HA_POS_ERROR)
+ {
+ DBUG_PRINT("info", ("No LIMIT"));
+ DBUG_RETURN(false);
+ }
+
+ if (param->max_rows + 2 >= UINT_MAX)
+ {
+ DBUG_PRINT("info", ("Too large LIMIT"));
+ DBUG_RETURN(false);
+ }
+
+ ulong num_available_keys=
+ memory_available / (param->rec_length + sizeof(char*));
+ // We need 1 extra record in the buffer, when using PQ.
+ param->max_keys_per_buffer= (uint) param->max_rows + 1;
+
+ if (num_rows < num_available_keys)
+ {
+ // The whole source set fits into memory.
+ if (param->max_rows < num_rows/PQ_slowness )
+ {
+ filesort_info->alloc_sort_buffer(param->max_keys_per_buffer,
+ param->rec_length);
+ DBUG_RETURN(filesort_info->get_sort_keys() != NULL);
+ }
+ else
+ {
+ // PQ will be slower.
+ DBUG_RETURN(false);
+ }
+ }
+
+ // Do we have space for LIMIT rows in memory?
+ if (param->max_keys_per_buffer < num_available_keys)
+ {
+ filesort_info->alloc_sort_buffer(param->max_keys_per_buffer,
+ param->rec_length);
+ DBUG_RETURN(filesort_info->get_sort_keys() != NULL);
+ }
+
+ // Try to strip off addon fields.
+ if (param->addon_field)
+ {
+ const ulong row_length=
+ param->sort_length + param->ref_length + sizeof(char*);
+ num_available_keys= memory_available / row_length;
+
+ // Can we fit all the keys in memory?
+ if (param->max_keys_per_buffer < num_available_keys)
+ {
+ const double sort_merge_cost=
+ get_merge_many_buffs_cost_fast(num_rows,
+ num_available_keys,
+ row_length);
+ /*
+ PQ has cost:
+ (insert + qsort) * log(queue size) / TIME_FOR_COMPARE_ROWID +
+ cost of file lookup afterwards.
+ The lookup cost is a bit pessimistic: we take scan_time and assume
+ that on average we find the row after scanning half of the file.
+ A better estimate would be lookup cost, but note that we are doing
+ random lookups here, rather than sequential scan.
+ */
+ const double pq_cpu_cost=
+ (PQ_slowness * num_rows + param->max_keys_per_buffer) *
+ log((double) param->max_keys_per_buffer) / TIME_FOR_COMPARE_ROWID;
+ const double pq_io_cost=
+ param->max_rows * table->file->scan_time() / 2.0;
+ const double pq_cost= pq_cpu_cost + pq_io_cost;
+
+ if (sort_merge_cost < pq_cost)
+ DBUG_RETURN(false);
+
+ filesort_info->alloc_sort_buffer(param->max_keys_per_buffer,
+ param->sort_length + param->ref_length);
+ if (filesort_info->get_sort_keys())
+ {
+ // Make attached data to be references instead of fields.
+ my_free(filesort_info->addon_buf);
+ my_free(filesort_info->addon_field);
+ filesort_info->addon_buf= NULL;
+ filesort_info->addon_field= NULL;
+ param->addon_field= NULL;
+ param->addon_length= 0;
+
+ param->res_length= param->ref_length;
+ param->sort_length+= param->ref_length;
+ param->rec_length= param->sort_length;
+
+ DBUG_RETURN(true);
+ }
+ }
+ }
+ DBUG_RETURN(false);
+}
+
+
/** Merge buffers to make < MERGEBUFF2 buffers. */
-int merge_many_buff(SORTPARAM *param, uchar *sort_buffer,
- BUFFPEK *buffpek, uint *maxbuffer, IO_CACHE *t_file)
+int merge_many_buff(Sort_param *param, uchar *sort_buffer,
+ BUFFPEK *buffpek, uint *maxbuffer, IO_CACHE *t_file)
{
register uint i;
IO_CACHE t_file2,*from_file,*to_file,*temp;
@@ -1250,7 +1473,7 @@ uint read_to_buffer(IO_CACHE *fromfile, BUFFPEK *buffpek,
register uint count;
uint length;
- if ((count=(uint) min((ha_rows) buffpek->max_keys,buffpek->count)))
+ if ((count=(uint) MY_MIN((ha_rows) buffpek->max_keys,buffpek->count)))
{
if (mysql_file_pread(fromfile->file, (uchar*) buffpek->base,
(length= rec_length*count),
@@ -1318,7 +1541,7 @@ void reuse_freed_buff(QUEUE *queue, BUFFPEK *reuse, uint key_length)
other error
*/
-int merge_buffers(SORTPARAM *param, IO_CACHE *from_file,
+int merge_buffers(Sort_param *param, IO_CACHE *from_file,
IO_CACHE *to_file, uchar *sort_buffer,
BUFFPEK *lastbuff, BUFFPEK *Fb, BUFFPEK *Tb,
int flag)
@@ -1336,18 +1559,13 @@ int merge_buffers(SORTPARAM *param, IO_CACHE *from_file,
void *first_cmp_arg;
element_count dupl_count= 0;
uchar *src;
- killed_state not_killable;
uchar *unique_buff= param->unique_buff;
- volatile killed_state *killed= &current_thd->killed;
+ const bool killable= !param->not_killable;
+ THD* const thd=current_thd;
DBUG_ENTER("merge_buffers");
- status_var_increment(current_thd->status_var.filesort_merge_passes);
- current_thd->query_plan_fsort_passes++;
- if (param->not_killable)
- {
- killed= &not_killable;
- not_killable= NOT_KILLED;
- }
+ thd->inc_status_sort_merge_passes();
+ thd->query_plan_fsort_passes++;
error=0;
rec_length= param->rec_length;
@@ -1360,7 +1578,7 @@ int merge_buffers(SORTPARAM *param, IO_CACHE *from_file,
(flag && min_dupl_count ? sizeof(dupl_count) : 0)-res_length);
uint wr_len= flag ? res_length : rec_length;
uint wr_offset= flag ? offset : 0;
- maxcount= (ulong) (param->keys/((uint) (Tb-Fb) +1));
+ maxcount= (ulong) (param->max_keys_per_buffer/((uint) (Tb-Fb) +1));
to_start_filepos= my_b_tell(to_file);
strpos= sort_buffer;
org_max_rows=max_rows= param->max_rows;
@@ -1424,7 +1642,7 @@ int merge_buffers(SORTPARAM *param, IO_CACHE *from_file,
while (queue.elements > 1)
{
- if (*killed)
+ if (killable && thd->check_killed())
{
error= 1; goto err; /* purecov: inspected */
}
@@ -1500,7 +1718,7 @@ int merge_buffers(SORTPARAM *param, IO_CACHE *from_file,
}
buffpek= (BUFFPEK*) queue_top(&queue);
buffpek->base= (uchar*) sort_buffer;
- buffpek->max_keys= param->keys;
+ buffpek->max_keys= param->max_keys_per_buffer;
/*
As we know all entries in the buffer are unique, we only have to
@@ -1580,7 +1798,7 @@ int merge_buffers(SORTPARAM *param, IO_CACHE *from_file,
!= -1 && error != 0);
end:
- lastbuff->count= min(org_max_rows-max_rows, param->max_rows);
+ lastbuff->count= MY_MIN(org_max_rows-max_rows, param->max_rows);
lastbuff->file_pos= to_start_filepos;
err:
delete_queue(&queue);
@@ -1590,9 +1808,9 @@ err:
/* Do a merge to output-file (save only positions) */
-int merge_index(SORTPARAM *param, uchar *sort_buffer,
- BUFFPEK *buffpek, uint maxbuffer,
- IO_CACHE *tempfile, IO_CACHE *outfile)
+int merge_index(Sort_param *param, uchar *sort_buffer,
+ BUFFPEK *buffpek, uint maxbuffer,
+ IO_CACHE *tempfile, IO_CACHE *outfile)
{
DBUG_ENTER("merge_index");
if (merge_buffers(param,tempfile,outfile,sort_buffer,buffpek,buffpek,
@@ -1638,7 +1856,7 @@ sortlength(THD *thd, SORT_FIELD *sortorder, uint s_length,
bool *multi_byte_charset)
{
reg2 uint length;
- CHARSET_INFO *cs;
+ const CHARSET_INFO *cs;
*multi_byte_charset= 0;
length=0;
@@ -1739,7 +1957,8 @@ sortlength(THD *thd, SORT_FIELD *sortorder, uint s_length,
*/
static SORT_ADDON_FIELD *
-get_addon_fields(THD *thd, Field **ptabfield, uint sortlength, uint *plength)
+get_addon_fields(ulong max_length_for_sort_data,
+ Field **ptabfield, uint sortlength, uint *plength)
{
Field **pfield;
Field *field;
@@ -1776,9 +1995,11 @@ get_addon_fields(THD *thd, Field **ptabfield, uint sortlength, uint *plength)
return 0;
length+= (null_fields+7)/8;
- if (length+sortlength > thd->variables.max_length_for_sort_data ||
+ if (length+sortlength > max_length_for_sort_data ||
!(addonf= (SORT_ADDON_FIELD *) my_malloc(sizeof(SORT_ADDON_FIELD)*
- (fields+1), MYF(MY_WME))))
+ (fields+1),
+ MYF(MY_WME |
+ MY_THREAD_SPECIFIC))))
return 0;
*plength= length;
@@ -1859,7 +2080,7 @@ void change_double_for_sort(double nr,uchar *to)
if (nr == 0.0)
{ /* Change to zero string */
tmp[0]=(uchar) 128;
- bzero((char*) tmp+1,sizeof(nr)-1);
+ memset(tmp+1, 0, sizeof(nr)-1);
}
else
{
diff --git a/sql/filesort.h b/sql/filesort.h
index 8ee8999d055..8960fa6cb66 100644
--- a/sql/filesort.h
+++ b/sql/filesort.h
@@ -29,10 +29,8 @@ typedef struct st_sort_field SORT_FIELD;
ha_rows filesort(THD *thd, TABLE *table, st_sort_field *sortorder,
uint s_length, SQL_SELECT *select,
ha_rows max_rows, bool sort_positions,
- ha_rows *examined_rows);
+ ha_rows *examined_rows, ha_rows *found_rows);
void filesort_free_buffers(TABLE *table, bool full);
-double get_merge_many_buffs_cost(uint *buffer, uint last_n_elems,
- int elem_size);
void change_double_for_sort(double nr,uchar *to);
#endif /* FILESORT_INCLUDED */
diff --git a/sql/filesort_utils.cc b/sql/filesort_utils.cc
new file mode 100644
index 00000000000..1cef30b6a56
--- /dev/null
+++ b/sql/filesort_utils.cc
@@ -0,0 +1,143 @@
+/* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+
+ 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 */
+
+#include "filesort_utils.h"
+#include "sql_const.h"
+#include "sql_sort.h"
+#include "table.h"
+#include "my_sys.h"
+
+
+namespace {
+/**
+ A local helper function. See comments for get_merge_buffers_cost().
+ */
+double get_merge_cost(ha_rows num_elements, ha_rows num_buffers, uint elem_size)
+{
+ return
+ 2.0 * ((double) num_elements * elem_size) / IO_SIZE
+ + (double) num_elements * log((double) num_buffers) /
+ (TIME_FOR_COMPARE_ROWID * M_LN2);
+}
+}
+
+/**
+ This is a simplified, and faster version of @see get_merge_many_buffs_cost().
+ We calculate the cost of merging buffers, by simulating the actions
+ of @see merge_many_buff. For explanations of formulas below,
+ see comments for get_merge_buffers_cost().
+ TODO: Use this function for Unique::get_use_cost().
+*/
+double get_merge_many_buffs_cost_fast(ha_rows num_rows,
+ ha_rows num_keys_per_buffer,
+ uint elem_size)
+{
+ ha_rows num_buffers= num_rows / num_keys_per_buffer;
+ ha_rows last_n_elems= num_rows % num_keys_per_buffer;
+ double total_cost;
+
+ // Calculate CPU cost of sorting buffers.
+ total_cost=
+ ( num_buffers * num_keys_per_buffer * log(1.0 + num_keys_per_buffer) +
+ last_n_elems * log(1.0 + last_n_elems) )
+ / TIME_FOR_COMPARE_ROWID;
+
+ // Simulate behavior of merge_many_buff().
+ while (num_buffers >= MERGEBUFF2)
+ {
+ // Calculate # of calls to merge_buffers().
+ const ha_rows loop_limit= num_buffers - MERGEBUFF*3/2;
+ const ha_rows num_merge_calls= 1 + loop_limit/MERGEBUFF;
+ const ha_rows num_remaining_buffs=
+ num_buffers - num_merge_calls * MERGEBUFF;
+
+ // Cost of merge sort 'num_merge_calls'.
+ total_cost+=
+ num_merge_calls *
+ get_merge_cost(num_keys_per_buffer * MERGEBUFF, MERGEBUFF, elem_size);
+
+ // # of records in remaining buffers.
+ last_n_elems+= num_remaining_buffs * num_keys_per_buffer;
+
+ // Cost of merge sort of remaining buffers.
+ total_cost+=
+ get_merge_cost(last_n_elems, 1 + num_remaining_buffs, elem_size);
+
+ num_buffers= num_merge_calls;
+ num_keys_per_buffer*= MERGEBUFF;
+ }
+
+ // Simulate final merge_buff call.
+ last_n_elems+= num_keys_per_buffer * num_buffers;
+ total_cost+= get_merge_cost(last_n_elems, 1 + num_buffers, elem_size);
+ return total_cost;
+}
+
+uchar **Filesort_buffer::alloc_sort_buffer(uint num_records, uint record_length)
+{
+ ulong sort_buff_sz;
+
+ DBUG_ENTER("alloc_sort_buffer");
+
+ DBUG_EXECUTE_IF("alloc_sort_buffer_fail",
+ DBUG_SET("+d,simulate_out_of_memory"););
+
+ if (m_idx_array.is_null())
+ {
+ sort_buff_sz= num_records * (record_length + sizeof(uchar*));
+ set_if_bigger(sort_buff_sz, record_length * MERGEBUFF2);
+ uchar **sort_keys=
+ (uchar**) my_malloc(sort_buff_sz, MYF(MY_THREAD_SPECIFIC));
+ m_idx_array= Idx_array(sort_keys, num_records);
+ m_record_length= record_length;
+ uchar **start_of_data= m_idx_array.array() + m_idx_array.size();
+ m_start_of_data= reinterpret_cast<uchar*>(start_of_data);
+ }
+ else
+ {
+ DBUG_ASSERT(num_records == m_idx_array.size());
+ DBUG_ASSERT(record_length == m_record_length);
+ }
+ DBUG_RETURN(m_idx_array.array());
+}
+
+
+void Filesort_buffer::free_sort_buffer()
+{
+ my_free(m_idx_array.array());
+ m_idx_array= Idx_array();
+ m_record_length= 0;
+ m_start_of_data= NULL;
+}
+
+
+void Filesort_buffer::sort_buffer(const Sort_param *param, uint count)
+{
+ size_t size= param->sort_length;
+ if (count <= 1 || size == 0)
+ return;
+ uchar **keys= get_sort_keys();
+ uchar **buffer= NULL;
+ if (radixsort_is_appliccable(count, param->sort_length) &&
+ (buffer= (uchar**) my_malloc(count*sizeof(char*),
+ MYF(MY_THREAD_SPECIFIC))))
+ {
+ radixsort_for_str_ptr(keys, count, param->sort_length, buffer);
+ my_free(buffer);
+ return;
+ }
+
+ my_qsort2(keys, count, sizeof(uchar*), get_ptr_compare(size), &size);
+}
diff --git a/sql/filesort_utils.h b/sql/filesort_utils.h
new file mode 100644
index 00000000000..00fa6f2566b
--- /dev/null
+++ b/sql/filesort_utils.h
@@ -0,0 +1,129 @@
+/* Copyright (c) 2010, 2012 Oracle and/or its affiliates. All rights reserved.
+
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#ifndef FILESORT_UTILS_INCLUDED
+#define FILESORT_UTILS_INCLUDED
+
+#include "my_global.h"
+#include "my_base.h"
+#include "sql_array.h"
+
+class Sort_param;
+/*
+ Calculate cost of merge sort
+
+ @param num_rows Total number of rows.
+ @param num_keys_per_buffer Number of keys per buffer.
+ @param elem_size Size of each element.
+
+ Calculates cost of merge sort by simulating call to merge_many_buff().
+
+ @retval
+ Computed cost of merge sort in disk seeks.
+
+ @note
+ Declared here in order to be able to unit test it,
+ since library dependencies have not been sorted out yet.
+
+ See also comments get_merge_many_buffs_cost().
+*/
+
+double get_merge_many_buffs_cost_fast(ha_rows num_rows,
+ ha_rows num_keys_per_buffer,
+ uint elem_size);
+
+
+/**
+ A wrapper class around the buffer used by filesort().
+ The buffer is a contiguous chunk of memory,
+ where the first part is <num_records> pointers to the actual data.
+
+ We wrap the buffer in order to be able to do lazy initialization of the
+ pointers: the buffer is often much larger than what we actually need.
+
+ The buffer must be kept available for multiple executions of the
+ same sort operation, so we have explicit allocate and free functions,
+ rather than doing alloc/free in CTOR/DTOR.
+*/
+class Filesort_buffer
+{
+public:
+ Filesort_buffer() :
+ m_idx_array(), m_record_length(0), m_start_of_data(NULL)
+ {}
+
+ /** Sort me... */
+ void sort_buffer(const Sort_param *param, uint count);
+
+ /// Initializes a record pointer.
+ uchar *get_record_buffer(uint idx)
+ {
+ m_idx_array[idx]= m_start_of_data + (idx * m_record_length);
+ return m_idx_array[idx];
+ }
+
+ /// Initializes all the record pointers.
+ void init_record_pointers()
+ {
+ for (uint ix= 0; ix < m_idx_array.size(); ++ix)
+ (void) get_record_buffer(ix);
+ }
+
+ /// Returns total size: pointer array + record buffers.
+ size_t sort_buffer_size() const
+ {
+ return m_idx_array.size() * (m_record_length + sizeof(uchar*));
+ }
+
+ /// Allocates the buffer, but does *not* initialize pointers.
+ uchar **alloc_sort_buffer(uint num_records, uint record_length);
+
+
+ /// Check <num_records, record_length> for the buffer
+ bool check_sort_buffer_properties(uint num_records, uint record_length)
+ {
+ return (static_cast<uint>(m_idx_array.size()) == num_records &&
+ m_record_length == record_length);
+ }
+
+ /// Frees the buffer.
+ void free_sort_buffer();
+
+ /// Getter, for calling routines which still use the uchar** interface.
+ uchar **get_sort_keys() { return m_idx_array.array(); }
+
+ /**
+ We need an assignment operator, see filesort().
+ This happens to have the same semantics as the one that would be
+ generated by the compiler. We still implement it here, to show shallow
+ assignment explicitly: we have two objects sharing the same array.
+ */
+ Filesort_buffer &operator=(const Filesort_buffer &rhs)
+ {
+ m_idx_array= rhs.m_idx_array;
+ m_record_length= rhs.m_record_length;
+ m_start_of_data= rhs.m_start_of_data;
+ return *this;
+ }
+
+private:
+ typedef Bounds_checked_array<uchar*> Idx_array;
+
+ Idx_array m_idx_array;
+ uint m_record_length;
+ uchar *m_start_of_data;
+};
+
+#endif // FILESORT_UTILS_INCLUDED
diff --git a/sql/frm_crypt.cc b/sql/frm_crypt.cc
deleted file mode 100644
index 5612908aea5..00000000000
--- a/sql/frm_crypt.cc
+++ /dev/null
@@ -1,37 +0,0 @@
-/* Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
-
- 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
-
-
-/*
-** change the following to the output of password('our password')
-** split into 2 parts of 8 characters each.
-** This is done to make it impossible to search after a text string in the
-** mysql binary.
-*/
-
-#include "sql_priv.h"
-#include "frm_crypt.h"
-
-#ifdef HAVE_CRYPTED_FRM
-
-/* password('test') */
-ulong password_seed[2]={0x378b243e, 0x220ca493};
-
-SQL_CRYPT *get_crypt_for_frm(void)
-{
- return new SQL_CRYPT(password_seed);
-}
-
-#endif
diff --git a/sql/frm_crypt.h b/sql/frm_crypt.h
deleted file mode 100644
index 0605644b3e0..00000000000
--- a/sql/frm_crypt.h
+++ /dev/null
@@ -1,23 +0,0 @@
-/* Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
-
- 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
-
-#ifndef FRM_CRYPT_INCLUDED
-#define FRM_CRYPT_INCLUDED
-
-class SQL_CRYPT;
-
-SQL_CRYPT *get_crypt_for_frm(void);
-
-#endif /* FRM_CRYPT_INCLUDED */
diff --git a/sql/gcalc_slicescan.cc b/sql/gcalc_slicescan.cc
index 251869cad03..c5db5053fb9 100644
--- a/sql/gcalc_slicescan.cc
+++ b/sql/gcalc_slicescan.cc
@@ -49,14 +49,14 @@ typedef int (*sc_compare_func)(const void*, const void*);
static Gcalc_scan_iterator::point *eq_sp(const Gcalc_heap::Info *pi)
{
GCALC_DBUG_ASSERT(pi->type == Gcalc_heap::nt_eq_node);
- return (Gcalc_scan_iterator::point *) pi->eq_data;
+ return (Gcalc_scan_iterator::point *) pi->node.eq.data;
}
static Gcalc_scan_iterator::intersection_info *i_data(const Gcalc_heap::Info *pi)
{
GCALC_DBUG_ASSERT(pi->type == Gcalc_heap::nt_intersection);
- return (Gcalc_scan_iterator::intersection_info *) pi->intersection_data;
+ return (Gcalc_scan_iterator::intersection_info *) pi->node.intersection.data;
}
@@ -103,8 +103,8 @@ const char *gcalc_ev_name(int ev)
static int gcalc_pi_str(char *str, const Gcalc_heap::Info *pi, const char *postfix)
{
return sprintf(str, "%s %d %d | %s %d %d%s",
- GCALC_SIGN(pi->ix[0]) ? "-":"", FIRST_DIGIT(pi->ix[0]),pi->ix[1],
- GCALC_SIGN(pi->iy[0]) ? "-":"", FIRST_DIGIT(pi->iy[0]),pi->iy[1],
+ GCALC_SIGN(pi->node.shape.ix[0]) ? "-":"", FIRST_DIGIT(pi->node.shape.ix[0]),pi->node.shape.ix[1],
+ GCALC_SIGN(pi->node.shape.iy[0]) ? "-":"", FIRST_DIGIT(pi->node.shape.iy[0]),pi->node.shape.iy[1],
postfix);
}
@@ -594,8 +594,8 @@ void Gcalc_scan_iterator::intersection_info::do_calc_t()
Gcalc_coord1 a2_a1x, a2_a1y;
Gcalc_coord2 x1y2, x2y1;
- gcalc_sub_coord1(a2_a1x, edge_b->pi->ix, edge_a->pi->ix);
- gcalc_sub_coord1(a2_a1y, edge_b->pi->iy, edge_a->pi->iy);
+ gcalc_sub_coord1(a2_a1x, edge_b->pi->node.shape.ix, edge_a->pi->node.shape.ix);
+ gcalc_sub_coord1(a2_a1y, edge_b->pi->node.shape.iy, edge_a->pi->node.shape.iy);
GCALC_DBUG_ASSERT(!gcalc_is_zero(edge_a->dy, GCALC_COORD_BASE) ||
!gcalc_is_zero(edge_b->dy, GCALC_COORD_BASE));
@@ -619,7 +619,7 @@ void Gcalc_scan_iterator::intersection_info::do_calc_y()
Gcalc_coord3 a_tb, b_ta;
gcalc_mul_coord(a_tb, GCALC_COORD_BASE3,
- t_b, GCALC_COORD_BASE2, edge_a->pi->iy, GCALC_COORD_BASE);
+ t_b, GCALC_COORD_BASE2, edge_a->pi->node.shape.iy, GCALC_COORD_BASE);
gcalc_mul_coord(b_ta, GCALC_COORD_BASE3,
t_a, GCALC_COORD_BASE2, edge_a->dy, GCALC_COORD_BASE);
@@ -635,7 +635,7 @@ void Gcalc_scan_iterator::intersection_info::do_calc_x()
Gcalc_coord3 a_tb, b_ta;
gcalc_mul_coord(a_tb, GCALC_COORD_BASE3,
- t_b, GCALC_COORD_BASE2, edge_a->pi->ix, GCALC_COORD_BASE);
+ t_b, GCALC_COORD_BASE2, edge_a->pi->node.shape.ix, GCALC_COORD_BASE);
gcalc_mul_coord(b_ta, GCALC_COORD_BASE3,
t_a, GCALC_COORD_BASE2, edge_a->dx, GCALC_COORD_BASE);
@@ -656,7 +656,7 @@ static int cmp_node_isc(const Gcalc_heap::Info *node,
inf->calc_y_exp();
gcalc_mul_coord(exp, GCALC_COORD_BASE3,
- inf->t_b, GCALC_COORD_BASE2, node->iy, GCALC_COORD_BASE);
+ inf->t_b, GCALC_COORD_BASE2, node->node.shape.iy, GCALC_COORD_BASE);
result= gcalc_cmp_coord(exp, inf->y_exp, GCALC_COORD_BASE3);
#ifdef GCALC_CHECK_WITH_FLOAT
@@ -664,18 +664,18 @@ static int cmp_node_isc(const Gcalc_heap::Info *node,
isc->calc_xy_ld(&int_x, &int_y);
if (result < 0)
{
- if (!de_check(int_y, node->y) && node->y > int_y)
- GCALC_DBUG_PRINT(("floatcheck cmp_nod_iscy %g < %LG", node->y, int_y));
+ if (!de_check(int_y, node->node.shape.y) && node->node.shape.y > int_y)
+ GCALC_DBUG_PRINT(("floatcheck cmp_nod_iscy %g < %LG", node->node.shape.y, int_y));
}
else if (result > 0)
{
- if (!de_check(int_y, node->y) && node->y < int_y)
- GCALC_DBUG_PRINT(("floatcheck cmp_nod_iscy %g > %LG", node->y, int_y));
+ if (!de_check(int_y, node->node.shape.y) && node->node.shape.y < int_y)
+ GCALC_DBUG_PRINT(("floatcheck cmp_nod_iscy %g > %LG", node->node.shape.y, int_y));
}
else
{
- if (!de_check(int_y, node->y))
- GCALC_DBUG_PRINT(("floatcheck cmp_nod_iscy %g == %LG", node->y, int_y));
+ if (!de_check(int_y, node->node.shape.y))
+ GCALC_DBUG_PRINT(("floatcheck cmp_nod_iscy %g == %LG", node->node.shape.y, int_y));
}
#endif /*GCALC_CHECK_WITH_FLOAT*/
if (result)
@@ -684,27 +684,27 @@ static int cmp_node_isc(const Gcalc_heap::Info *node,
inf->calc_x_exp();
gcalc_mul_coord(exp, GCALC_COORD_BASE3,
- inf->t_b, GCALC_COORD_BASE2, node->ix, GCALC_COORD_BASE);
+ inf->t_b, GCALC_COORD_BASE2, node->node.shape.ix, GCALC_COORD_BASE);
result= gcalc_cmp_coord(exp, inf->x_exp, GCALC_COORD_BASE3);
#ifdef GCALC_CHECK_WITH_FLOAT
if (result < 0)
{
- if (!de_check(int_x, node->x) && node->x > int_x)
+ if (!de_check(int_x, node->node.shape.x) && node->node.shape.x > int_x)
GCALC_DBUG_PRINT(("floatcheck cmp_nod_iscx failed %g < %LG",
- node->x, int_x));
+ node->node.shape.x, int_x));
}
else if (result > 0)
{
- if (!de_check(int_x, node->x) && node->x < int_x)
+ if (!de_check(int_x, node->node.shape.x) && node->node.shape.x < int_x)
GCALC_DBUG_PRINT(("floatcheck cmp_nod_iscx failed %g > %LG",
- node->x, int_x));
+ node->node.shape.x, int_x));
}
else
{
- if (!de_check(int_x, node->x))
+ if (!de_check(int_x, node->node.shape.x))
GCALC_DBUG_PRINT(("floatcheck cmp_nod_iscx failed %g == %LG",
- node->x, int_x));
+ node->node.shape.x, int_x));
}
#endif /*GCALC_CHECK_WITH_FLOAT*/
exit:
@@ -844,13 +844,13 @@ Gcalc_heap::Info *Gcalc_heap::new_point_info(double x, double y,
return NULL;
*m_hook= result;
m_hook= &result->next;
- result->x= x;
- result->y= y;
- result->shape= shape;
- result->top_node= 1;
+ result->node.shape.x= x;
+ result->node.shape.y= y;
+ result->node.shape.shape= shape;
+ result->node.shape.top_node= 1;
result->type= nt_shape_node;
- gcalc_set_double(result->ix, x, coord_extent);
- gcalc_set_double(result->iy, y, coord_extent);
+ gcalc_set_double(result->node.shape.ix, x, coord_extent);
+ gcalc_set_double(result->node.shape.iy, y, coord_extent);
m_n_points++;
return result;
@@ -864,11 +864,11 @@ static Gcalc_heap::Info *new_intersection(
if (!isc)
return 0;
isc->type= Gcalc_heap::nt_intersection;
- isc->p1= ii->edge_a->pi;
- isc->p2= ii->edge_a->next_pi;
- isc->p3= ii->edge_b->pi;
- isc->p4= ii->edge_b->next_pi;
- isc->intersection_data= ii;
+ isc->node.intersection.p1= ii->edge_a->pi;
+ isc->node.intersection.p2= ii->edge_a->next_pi;
+ isc->node.intersection.p3= ii->edge_b->pi;
+ isc->node.intersection.p4= ii->edge_b->next_pi;
+ isc->node.intersection.data= ii;
return isc;
}
@@ -881,46 +881,46 @@ static Gcalc_heap::Info *new_eq_point(
if (!eqp)
return 0;
eqp->type= Gcalc_heap::nt_eq_node;
- eqp->node= p;
- eqp->eq_data= edge;
+ eqp->node.eq.node= p;
+ eqp->node.eq.data= edge;
return eqp;
}
void Gcalc_heap::Info::calc_xy(double *x, double *y) const
{
- double b0_x= p2->x - p1->x;
- double b0_y= p2->y - p1->y;
- double b1_x= p4->x - p3->x;
- double b1_y= p4->y - p3->y;
+ double b0_x= node.intersection.p2->node.shape.x - node.intersection.p1->node.shape.x;
+ double b0_y= node.intersection.p2->node.shape.y - node.intersection.p1->node.shape.y;
+ double b1_x= node.intersection.p4->node.shape.x - node.intersection.p3->node.shape.x;
+ double b1_y= node.intersection.p4->node.shape.y - node.intersection.p3->node.shape.y;
double b0xb1= b0_x * b1_y - b0_y * b1_x;
- double t= (p3->x - p1->x) * b1_y - (p3->y - p1->y) * b1_x;
+ double t= (node.intersection.p3->node.shape.x - node.intersection.p1->node.shape.x) * b1_y - (node.intersection.p3->node.shape.y - node.intersection.p1->node.shape.y) * b1_x;
t/= b0xb1;
- *x= p1->x + b0_x * t;
- *y= p1->y + b0_y * t;
+ *x= node.intersection.p1->node.shape.x + b0_x * t;
+ *y= node.intersection.p1->node.shape.y + b0_y * t;
}
#ifdef GCALC_CHECK_WITH_FLOAT
void Gcalc_heap::Info::calc_xy_ld(long double *x, long double *y) const
{
- long double b0_x= ((long double) p2->x) - p1->x;
- long double b0_y= ((long double) p2->y) - p1->y;
- long double b1_x= ((long double) p4->x) - p3->x;
- long double b1_y= ((long double) p4->y) - p3->y;
+ long double b0_x= ((long double) p2->node.shape.x) - p1->node.shape.x;
+ long double b0_y= ((long double) p2->node.shape.y) - p1->node.shape.y;
+ long double b1_x= ((long double) p4->node.shape.x) - p3->node.shape.x;
+ long double b1_y= ((long double) p4->node.shape.y) - p3->node.shape.y;
long double b0xb1= b0_x * b1_y - b0_y * b1_x;
- long double ax= ((long double) p3->x) - p1->x;
- long double ay= ((long double) p3->y) - p1->y;
+ long double ax= ((long double) p3->node.shape.x) - p1->node.shape.x;
+ long double ay= ((long double) p3->node.shape.y) - p1->node.shape.y;
long double t_a= ax * b1_y - ay * b1_x;
- long double hx= (b0xb1 * (long double) p1->x + b0_x * t_a);
- long double hy= (b0xb1 * (long double) p1->y + b0_y * t_a);
+ long double hx= (b0xb1 * (long double) p1->node.shape.x + b0_x * t_a);
+ long double hy= (b0xb1 * (long double) p1->node.shape.y + b0_y * t_a);
if (fabs(b0xb1) < 1e-15)
{
- *x= p1->x;
- *y= p1->y;
+ *x= p1->node.shape.x;
+ *y= p1->node.shape.y;
return;
}
@@ -933,10 +933,10 @@ void Gcalc_heap::Info::calc_xy_ld(long double *x, long double *y) const
static int cmp_point_info(const Gcalc_heap::Info *i0,
const Gcalc_heap::Info *i1)
{
- int cmp_y= gcalc_cmp_coord1(i0->iy, i1->iy);
+ int cmp_y= gcalc_cmp_coord1(i0->node.shape.iy, i1->node.shape.iy);
if (cmp_y)
return cmp_y;
- return gcalc_cmp_coord1(i0->ix, i1->ix);
+ return gcalc_cmp_coord1(i0->node.shape.ix, i1->node.shape.ix);
}
@@ -944,11 +944,11 @@ static inline void trim_node(Gcalc_heap::Info *node, Gcalc_heap::Info *prev_node
{
if (!node)
return;
- node->top_node= 0;
- GCALC_DBUG_ASSERT((node->left == prev_node) || (node->right == prev_node));
- if (node->left == prev_node)
- node->left= node->right;
- node->right= NULL;
+ node->node.shape.top_node= 0;
+ GCALC_DBUG_ASSERT((node->node.shape.left == prev_node) || (node->node.shape.right == prev_node));
+ if (node->node.shape.left == prev_node)
+ node->node.shape.left= node->node.shape.right;
+ node->node.shape.right= NULL;
GCALC_DBUG_ASSERT(cmp_point_info(node, prev_node));
}
@@ -972,8 +972,8 @@ void Gcalc_heap::prepare_operation()
/* TODO - move this to the 'normal_scan' loop */
for (cur= get_first(); cur; cur= cur->get_next())
{
- trim_node(cur->left, cur);
- trim_node(cur->right, cur);
+ trim_node(cur->node.shape.left, cur);
+ trim_node(cur->node.shape.right, cur);
}
}
@@ -995,7 +995,7 @@ int Gcalc_shape_transporter::int_single_point(gcalc_shape_info Info,
Gcalc_heap::Info *point= m_heap->new_point_info(x, y, Info);
if (!point)
return 1;
- point->left= point->right= 0;
+ point->node.shape.left= point->node.shape.right= 0;
return 0;
}
@@ -1018,9 +1018,9 @@ int Gcalc_shape_transporter::int_add_point(gcalc_shape_info Info,
m_heap->free_point_info(point, hook);
return 0;
}
- GCALC_DBUG_ASSERT(!m_prev || m_prev->x != x || m_prev->y != y);
- m_prev->left= point;
- point->right= m_prev;
+ GCALC_DBUG_ASSERT(!m_prev || m_prev->node.shape.x != x || m_prev->node.shape.y != y);
+ m_prev->node.shape.left= point;
+ point->node.shape.right= m_prev;
}
else
m_first= point;
@@ -1040,16 +1040,16 @@ void Gcalc_shape_transporter::int_complete()
/* simple point */
if (m_first == m_prev)
{
- m_first->right= m_first->left= NULL;
+ m_first->node.shape.right= m_first->node.shape.left= NULL;
return;
}
/* line */
if (m_shape_started == 1)
{
- m_first->right= NULL;
- m_prev->left= m_prev->right;
- m_prev->right= NULL;
+ m_first->node.shape.right= NULL;
+ m_prev->node.shape.left= m_prev->node.shape.right;
+ m_prev->node.shape.right= NULL;
return;
}
@@ -1057,32 +1057,32 @@ void Gcalc_shape_transporter::int_complete()
if (cmp_point_info(m_first, m_prev) == 0)
{
/* Coinciding points, remove the last one from the list */
- m_prev->right->left= m_first;
- m_first->right= m_prev->right;
+ m_prev->node.shape.right->node.shape.left= m_first;
+ m_first->node.shape.right= m_prev->node.shape.right;
m_heap->free_point_info(m_prev, m_prev_hook);
}
else
{
- GCALC_DBUG_ASSERT(m_prev->x != m_first->x || m_prev->y != m_first->y);
- m_first->right= m_prev;
- m_prev->left= m_first;
+ GCALC_DBUG_ASSERT(m_prev->node.shape.x != m_first->node.shape.x || m_prev->node.shape.y != m_first->node.shape.y);
+ m_first->node.shape.right= m_prev;
+ m_prev->node.shape.left= m_first;
}
}
inline void calc_dx_dy(Gcalc_scan_iterator::point *p)
{
- gcalc_sub_coord1(p->dx, p->next_pi->ix, p->pi->ix);
- gcalc_sub_coord1(p->dy, p->next_pi->iy, p->pi->iy);
+ gcalc_sub_coord1(p->dx, p->next_pi->node.shape.ix, p->pi->node.shape.ix);
+ gcalc_sub_coord1(p->dy, p->next_pi->node.shape.iy, p->pi->node.shape.iy);
if (GCALC_SIGN(p->dx[0]))
{
- p->l_border= &p->next_pi->ix;
- p->r_border= &p->pi->ix;
+ p->l_border= &p->next_pi->node.shape.ix;
+ p->r_border= &p->pi->node.shape.ix;
}
else
{
- p->r_border= &p->next_pi->ix;
- p->l_border= &p->pi->ix;
+ p->r_border= &p->next_pi->node.shape.ix;
+ p->l_border= &p->pi->node.shape.ix;
}
}
@@ -1143,10 +1143,10 @@ int Gcalc_scan_iterator::point::cmp_dx_dy(const Gcalc_heap::Info *p1,
const Gcalc_heap::Info *p4)
{
Gcalc_coord1 dx_a, dy_a, dx_b, dy_b;
- gcalc_sub_coord1(dx_a, p2->ix, p1->ix);
- gcalc_sub_coord1(dy_a, p2->iy, p1->iy);
- gcalc_sub_coord1(dx_b, p4->ix, p3->ix);
- gcalc_sub_coord1(dy_b, p4->iy, p3->iy);
+ gcalc_sub_coord1(dx_a, p2->node.shape.ix, p1->node.shape.ix);
+ gcalc_sub_coord1(dy_a, p2->node.shape.iy, p1->node.shape.iy);
+ gcalc_sub_coord1(dx_b, p4->node.shape.ix, p3->node.shape.ix);
+ gcalc_sub_coord1(dy_b, p4->node.shape.iy, p3->node.shape.iy);
return cmp_dx_dy(dx_a, dy_a, dx_b, dy_b);
}
@@ -1168,8 +1168,8 @@ void Gcalc_scan_iterator::point::calc_x(long double *x, long double y,
*x= ix;
}
else
- *x= (ddy * (long double) pi->x + gcalc_get_double(dx, GCALC_COORD_BASE) *
- (y - pi->y)) / ddy;
+ *x= (ddy * (long double) pi->node.shape.x + gcalc_get_double(dx, GCALC_COORD_BASE) *
+ (y - pi->node.shape.y)) / ddy;
}
#endif /*GCALC_CHECK_WITH_FLOAT*/
@@ -1280,7 +1280,7 @@ int Gcalc_scan_iterator::arrange_event(int do_sorting, int n_intersections)
int Gcalc_heap::Info::equal_pi(const Info *pi) const
{
if (type == nt_intersection)
- return equal_intersection;
+ return node.intersection.equal;
if (pi->type == nt_eq_node)
return 1;
if (type == nt_eq_node || pi->type == nt_intersection)
@@ -1322,7 +1322,7 @@ int Gcalc_scan_iterator::step()
#ifndef GCALC_DBUG_OFF
if (m_cur_pi->type == Gcalc_heap::nt_intersection &&
m_cur_pi->get_next()->type == Gcalc_heap::nt_intersection &&
- m_cur_pi->equal_intersection)
+ m_cur_pi->node.intersection.equal)
GCALC_DBUG_ASSERT(cmp_intersections(m_cur_pi, m_cur_pi->get_next()) == 0);
#endif /*GCALC_DBUG_OFF*/
GCALC_DBUG_CHECK_COUNTER();
@@ -1377,23 +1377,23 @@ static int node_on_right(const Gcalc_heap::Info *node,
Gcalc_coord2 ax_by, ay_bx;
int result;
- gcalc_sub_coord1(a_x, node->ix, edge_a->ix);
- gcalc_sub_coord1(a_y, node->iy, edge_a->iy);
- gcalc_sub_coord1(b_x, edge_b->ix, edge_a->ix);
- gcalc_sub_coord1(b_y, edge_b->iy, edge_a->iy);
+ gcalc_sub_coord1(a_x, node->node.shape.ix, edge_a->node.shape.ix);
+ gcalc_sub_coord1(a_y, node->node.shape.iy, edge_a->node.shape.iy);
+ gcalc_sub_coord1(b_x, edge_b->node.shape.ix, edge_a->node.shape.ix);
+ gcalc_sub_coord1(b_y, edge_b->node.shape.iy, edge_a->node.shape.iy);
gcalc_mul_coord1(ax_by, a_x, b_y);
gcalc_mul_coord1(ay_bx, a_y, b_x);
result= gcalc_cmp_coord(ax_by, ay_bx, GCALC_COORD_BASE2);
#ifdef GCALC_CHECK_WITH_FLOAT
{
- long double dx= gcalc_get_double(edge_b->ix, GCALC_COORD_BASE) -
- gcalc_get_double(edge_a->ix, GCALC_COORD_BASE);
- long double dy= gcalc_get_double(edge_b->iy, GCALC_COORD_BASE) -
- gcalc_get_double(edge_a->iy, GCALC_COORD_BASE);
- long double ax= gcalc_get_double(node->ix, GCALC_COORD_BASE) -
- gcalc_get_double(edge_a->ix, GCALC_COORD_BASE);
- long double ay= gcalc_get_double(node->iy, GCALC_COORD_BASE) -
- gcalc_get_double(edge_a->iy, GCALC_COORD_BASE);
+ long double dx= gcalc_get_double(edge_b->node.shape.ix, GCALC_COORD_BASE) -
+ gcalc_get_double(edge_a->node.shape.ix, GCALC_COORD_BASE);
+ long double dy= gcalc_get_double(edge_b->node.shape.iy, GCALC_COORD_BASE) -
+ gcalc_get_double(edge_a->node.shape.iy, GCALC_COORD_BASE);
+ long double ax= gcalc_get_double(node->node.shape.ix, GCALC_COORD_BASE) -
+ gcalc_get_double(edge_a->node.shape.ix, GCALC_COORD_BASE);
+ long double ay= gcalc_get_double(node->node.shape.iy, GCALC_COORD_BASE) -
+ gcalc_get_double(edge_a->node.shape.iy, GCALC_COORD_BASE);
long double d= ax * dy - ay * dx;
if (result == 0)
GCALC_DBUG_ASSERT(de_check(d, 0.0));
@@ -1412,8 +1412,8 @@ static int cmp_tops(const Gcalc_heap::Info *top_node,
{
int cmp_res_a, cmp_res_b;
- cmp_res_a= gcalc_cmp_coord1(edge_a->ix, top_node->ix);
- cmp_res_b= gcalc_cmp_coord1(edge_b->ix, top_node->ix);
+ cmp_res_a= gcalc_cmp_coord1(edge_a->node.shape.ix, top_node->node.shape.ix);
+ cmp_res_b= gcalc_cmp_coord1(edge_b->node.shape.ix, top_node->node.shape.ix);
if (cmp_res_a <= 0 && cmp_res_b > 0)
return -1;
@@ -1438,26 +1438,26 @@ int Gcalc_scan_iterator::insert_top_node()
if (!sp0)
GCALC_DBUG_RETURN(1);
sp0->pi= m_cur_pi;
- sp0->next_pi= m_cur_pi->left;
+ sp0->next_pi= m_cur_pi->node.shape.left;
#ifndef GCALC_DBUG_OFF
sp0->thread= m_cur_thread++;
#endif /*GCALC_DBUG_OFF*/
- if (m_cur_pi->left)
+ if (m_cur_pi->node.shape.left)
{
calc_dx_dy(sp0);
- if (m_cur_pi->right)
+ if (m_cur_pi->node.shape.right)
{
if (!(sp1= new_slice_point()))
GCALC_DBUG_RETURN(1);
sp1->event= sp0->event= scev_two_threads;
sp1->pi= m_cur_pi;
- sp1->next_pi= m_cur_pi->right;
+ sp1->next_pi= m_cur_pi->node.shape.right;
#ifndef GCALC_DBUG_OFF
sp1->thread= m_cur_thread++;
#endif /*GCALC_DBUG_OFF*/
calc_dx_dy(sp1);
/* We have two threads so should decide which one will be first */
- cmp_res= cmp_tops(m_cur_pi, m_cur_pi->left, m_cur_pi->right);
+ cmp_res= cmp_tops(m_cur_pi, m_cur_pi->node.shape.left, m_cur_pi->node.shape.right);
if (cmp_res > 0)
{
point *tmp= sp0;
@@ -1467,7 +1467,7 @@ int Gcalc_scan_iterator::insert_top_node()
else if (cmp_res == 0)
{
/* Exactly same direction of the edges. */
- cmp_res= gcalc_cmp_coord1(m_cur_pi->left->iy, m_cur_pi->right->iy);
+ cmp_res= gcalc_cmp_coord1(m_cur_pi->node.shape.left->node.shape.iy, m_cur_pi->node.shape.right->node.shape.iy);
if (cmp_res != 0)
{
if (cmp_res < 0)
@@ -1483,7 +1483,7 @@ int Gcalc_scan_iterator::insert_top_node()
}
else
{
- cmp_res= gcalc_cmp_coord1(m_cur_pi->left->ix, m_cur_pi->right->ix);
+ cmp_res= gcalc_cmp_coord1(m_cur_pi->node.shape.left->node.shape.ix, m_cur_pi->node.shape.right->node.shape.ix);
if (cmp_res != 0)
{
if (cmp_res < 0)
@@ -1517,7 +1517,7 @@ int Gcalc_scan_iterator::insert_top_node()
/* We need to find the place to insert. */
for (; sp; prev_hook= sp->next_ptr(), sp=sp->get_next())
{
- if (sp->event || gcalc_cmp_coord1(*sp->r_border, m_cur_pi->ix) < 0)
+ if (sp->event || gcalc_cmp_coord1(*sp->r_border, m_cur_pi->node.shape.ix) < 0)
continue;
cmp_res= node_on_right(m_cur_pi, sp->pi, sp->next_pi);
if (cmp_res == 0)
@@ -1743,7 +1743,7 @@ int Gcalc_scan_iterator::node_scan()
GCALC_DBUG_PRINT(("node for %d", sp->thread));
/* Handle the point itself. */
sp->pi= cur_pi;
- sp->next_pi= cur_pi->left;
+ sp->next_pi= cur_pi->node.shape.left;
sp->event= scev_point;
calc_dx_dy(sp);
@@ -1794,7 +1794,7 @@ void Gcalc_scan_iterator::intersection_scan()
ii->edge_a->event= ii->edge_b->event= scev_intersection;
ii->edge_a->ev_pi= ii->edge_b->ev_pi= m_cur_pi;
free_item(ii);
- m_cur_pi->intersection_data= NULL;
+ m_cur_pi->node.intersection.data= NULL;
GCALC_DBUG_VOID_RETURN;
}
@@ -1813,7 +1813,7 @@ int Gcalc_scan_iterator::add_intersection(point *sp_a, point *sp_b,
!(ii= new_intersection(m_heap, i_calc)))
GCALC_DBUG_RETURN(1);
- ii->equal_intersection= 0;
+ ii->node.intersection.equal= 0;
for (;
pi_from->get_next() != sp_a->next_pi &&
@@ -1824,7 +1824,7 @@ int Gcalc_scan_iterator::add_intersection(point *sp_a, point *sp_b,
if (skip_next)
{
if (cur->type == Gcalc_heap::nt_intersection)
- skip_next= cur->equal_intersection;
+ skip_next= cur->node.intersection.equal;
else
skip_next= 0;
continue;
@@ -1832,7 +1832,7 @@ int Gcalc_scan_iterator::add_intersection(point *sp_a, point *sp_b,
if (cur->type == Gcalc_heap::nt_intersection)
{
cmp_res= cmp_intersections(cur, ii);
- skip_next= cur->equal_intersection;
+ skip_next= cur->node.intersection.equal;
}
else if (cur->type == Gcalc_heap::nt_eq_node)
continue;
@@ -1840,7 +1840,7 @@ int Gcalc_scan_iterator::add_intersection(point *sp_a, point *sp_b,
cmp_res= cmp_node_isc(cur, ii);
if (cmp_res == 0)
{
- ii->equal_intersection= 1;
+ ii->node.intersection.equal= 1;
break;
}
else if (cmp_res > 0)
@@ -1881,13 +1881,13 @@ void calc_t(Gcalc_coord2 t_a, Gcalc_coord2 t_b,
Gcalc_coord2 x1y2, x2y1;
Gcalc_coord1 dya, dyb;
- gcalc_sub_coord1(a2_a1x, p3->ix, p1->ix);
- gcalc_sub_coord1(a2_a1y, p3->iy, p1->iy);
+ gcalc_sub_coord1(a2_a1x, p3->node.shape.ix, p1->node.shape.ix);
+ gcalc_sub_coord1(a2_a1y, p3->node.shape.iy, p1->node.shape.iy);
- gcalc_sub_coord1(dxa, p2->ix, p1->ix);
- gcalc_sub_coord1(dya, p2->iy, p1->iy);
- gcalc_sub_coord1(dxb, p4->ix, p3->ix);
- gcalc_sub_coord1(dyb, p4->iy, p3->iy);
+ gcalc_sub_coord1(dxa, p2->node.shape.ix, p1->node.shape.ix);
+ gcalc_sub_coord1(dya, p2->node.shape.iy, p1->node.shape.iy);
+ gcalc_sub_coord1(dxb, p4->node.shape.ix, p3->node.shape.ix);
+ gcalc_sub_coord1(dyb, p4->node.shape.iy, p3->node.shape.iy);
gcalc_mul_coord1(x1y2, dxa, dyb);
gcalc_mul_coord1(x2y1, dya, dxb);
@@ -1908,11 +1908,11 @@ double Gcalc_scan_iterator::get_y() const
Gcalc_coord2 t_a, t_b;
Gcalc_coord3 a_tb, b_ta, y_exp;
calc_t(t_a, t_b, dxa, dya,
- state.pi->p1, state.pi->p2, state.pi->p3, state.pi->p4);
+ state.pi->node.intersection.p1, state.pi->node.intersection.p2, state.pi->node.intersection.p3, state.pi->node.intersection.p4);
gcalc_mul_coord(a_tb, GCALC_COORD_BASE3,
- t_b, GCALC_COORD_BASE2, state.pi->p1->iy, GCALC_COORD_BASE);
+ t_b, GCALC_COORD_BASE2, state.pi->node.intersection.p1->node.shape.iy, GCALC_COORD_BASE);
gcalc_mul_coord(b_ta, GCALC_COORD_BASE3,
t_a, GCALC_COORD_BASE2, dya, GCALC_COORD_BASE);
@@ -1922,7 +1922,7 @@ double Gcalc_scan_iterator::get_y() const
get_pure_double(t_b, GCALC_COORD_BASE2)) / m_heap->coord_extent;
}
else
- return state.pi->y;
+ return state.pi->node.shape.y;
}
@@ -1934,11 +1934,11 @@ double Gcalc_scan_iterator::get_event_x() const
Gcalc_coord2 t_a, t_b;
Gcalc_coord3 a_tb, b_ta, x_exp;
calc_t(t_a, t_b, dxa, dya,
- state.pi->p1, state.pi->p2, state.pi->p3, state.pi->p4);
+ state.pi->node.intersection.p1, state.pi->node.intersection.p2, state.pi->node.intersection.p3, state.pi->node.intersection.p4);
gcalc_mul_coord(a_tb, GCALC_COORD_BASE3,
- t_b, GCALC_COORD_BASE2, state.pi->p1->ix, GCALC_COORD_BASE);
+ t_b, GCALC_COORD_BASE2, state.pi->node.intersection.p1->node.shape.ix, GCALC_COORD_BASE);
gcalc_mul_coord(b_ta, GCALC_COORD_BASE3,
t_a, GCALC_COORD_BASE2, dxa, GCALC_COORD_BASE);
@@ -1948,7 +1948,7 @@ double Gcalc_scan_iterator::get_event_x() const
get_pure_double(t_b, GCALC_COORD_BASE2)) / m_heap->coord_extent;
}
else
- return state.pi->x;
+ return state.pi->node.shape.x;
}
double Gcalc_scan_iterator::get_h() const
@@ -1961,7 +1961,7 @@ double Gcalc_scan_iterator::get_h() const
state.pi->calc_xy(&x, &next_y);
}
else
- next_y= state.pi->y;
+ next_y= state.pi->node.shape.y;
return next_y - cur_y;
}
@@ -1970,11 +1970,11 @@ double Gcalc_scan_iterator::get_sp_x(const point *sp) const
{
double dy;
if (sp->event & (scev_end | scev_two_ends | scev_point))
- return sp->pi->x;
- dy= sp->next_pi->y - sp->pi->y;
+ return sp->pi->node.shape.x;
+ dy= sp->next_pi->node.shape.y - sp->pi->node.shape.y;
if (fabs(dy) < 1e-12)
- return sp->pi->x;
- return (sp->next_pi->x - sp->pi->x) * dy;
+ return sp->pi->node.shape.x;
+ return (sp->next_pi->node.shape.x - sp->pi->node.shape.x) * dy;
}
diff --git a/sql/gcalc_slicescan.h b/sql/gcalc_slicescan.h
index 55de497f1ee..5a0399bc8da 100644
--- a/sql/gcalc_slicescan.h
+++ b/sql/gcalc_slicescan.h
@@ -188,7 +188,7 @@ public:
double x,y;
Gcalc_coord1 ix, iy;
int top_node;
- };
+ } shape;
struct
{
/* nt_intersection */
@@ -197,21 +197,21 @@ public:
const Info *p2;
const Info *p3;
const Info *p4;
- void *intersection_data;
- int equal_intersection;
- };
+ void *data;
+ int equal;
+ } intersection;
struct
{
/* nt_eq_node */
const Info *node;
- void *eq_data;
- };
- };
+ void *data;
+ } eq;
+ } node;
bool is_bottom() const
- { GCALC_DBUG_ASSERT(type == nt_shape_node); return !left; }
+ { GCALC_DBUG_ASSERT(type == nt_shape_node); return !node.shape.left; }
bool is_top() const
- { GCALC_DBUG_ASSERT(type == nt_shape_node); return top_node; }
+ { GCALC_DBUG_ASSERT(type == nt_shape_node); return node.shape.top_node; }
bool is_single_node() const
{ return is_bottom() && is_top(); }
@@ -383,7 +383,7 @@ public:
inline const point *c_get_next() const
{ return (const point *)next; }
inline bool is_bottom() const { return !next_pi; }
- gcalc_shape_info get_shape() const { return pi->shape; }
+ gcalc_shape_info get_shape() const { return pi->node.shape.shape; }
inline point *get_next() { return (point *)next; }
inline const point *get_next() const { return (const point *)next; }
/* Compare the dx_dy parameters regarding the horiz_dir */
diff --git a/sql/gcalc_tools.cc b/sql/gcalc_tools.cc
index 864437401b7..f3c24f9bdf3 100644
--- a/sql/gcalc_tools.cc
+++ b/sql/gcalc_tools.cc
@@ -1243,7 +1243,7 @@ inline int Gcalc_operation_reducer::get_single_result(res_point *res,
GCALC_DBUG_RETURN(1);
}
else
- if (storage->single_point(res->pi->x, res->pi->y))
+ if (storage->single_point(res->pi->node.shape.x, res->pi->node.shape.y))
GCALC_DBUG_RETURN(1);
free_result(res);
GCALC_DBUG_RETURN(0);
@@ -1269,8 +1269,8 @@ int Gcalc_operation_reducer::get_result_thread(res_point *cur,
}
else
{
- x= cur->pi->x;
- y= cur->pi->y;
+ x= cur->pi->node.shape.x;
+ y= cur->pi->node.shape.y;
}
if (storage->add_point(x, y))
GCALC_DBUG_RETURN(1);
diff --git a/sql/gen_lex_token.cc b/sql/gen_lex_token.cc
new file mode 100644
index 00000000000..3b2d37cfd12
--- /dev/null
+++ b/sql/gen_lex_token.cc
@@ -0,0 +1,359 @@
+/*
+ Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
+
+ 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,
+ 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */
+
+#include <my_global.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+/* We only need the tokens here */
+#define YYSTYPE_IS_DECLARED
+#include <sql_yacc.h>
+#include <lex.h>
+
+#include <welcome_copyright_notice.h> /* ORACLE_WELCOME_COPYRIGHT_NOTICE */
+
+/*
+ This is a tool used during build only,
+ so MY_MAX_TOKEN does not need to be exact,
+ only big enough to hold:
+ - 256 character terminal tokens
+ - YYNTOKENS named terminal tokens
+ from bison.
+ See also YYMAXUTOK.
+*/
+#define MY_MAX_TOKEN 1000
+/** Generated token. */
+struct gen_lex_token_string
+{
+ const char *m_token_string;
+ int m_token_length;
+ bool m_append_space;
+ bool m_start_expr;
+};
+
+gen_lex_token_string compiled_token_array[MY_MAX_TOKEN];
+int max_token_seen= 0;
+
+char char_tokens[256];
+
+int tok_generic_value= 0;
+int tok_generic_value_list= 0;
+int tok_row_single_value= 0;
+int tok_row_single_value_list= 0;
+int tok_row_multiple_value= 0;
+int tok_row_multiple_value_list= 0;
+int tok_ident= 0;
+int tok_unused= 0;
+
+void set_token(int tok, const char *str)
+{
+ if (tok <= 0)
+ {
+ fprintf(stderr, "Bad token found\n");
+ exit(1);
+ }
+
+ if (tok > max_token_seen)
+ {
+ max_token_seen= tok;
+ }
+
+ if (max_token_seen >= MY_MAX_TOKEN)
+ {
+ fprintf(stderr, "Added that many new keywords ? Increase MY_MAX_TOKEN\n");
+ exit(1);
+ }
+
+ compiled_token_array[tok].m_token_string= str;
+ compiled_token_array[tok].m_token_length= strlen(str);
+ compiled_token_array[tok].m_append_space= true;
+ compiled_token_array[tok].m_start_expr= false;
+}
+
+void set_start_expr_token(int tok)
+{
+ compiled_token_array[tok].m_start_expr= true;
+}
+
+void compute_tokens()
+{
+ int tok;
+ unsigned int i;
+ char *str;
+
+ /*
+ Default value.
+ */
+ for (tok= 0; tok < MY_MAX_TOKEN; tok++)
+ {
+ compiled_token_array[tok].m_token_string= "(unknown)";
+ compiled_token_array[tok].m_token_length= 9;
+ compiled_token_array[tok].m_append_space= true;
+ compiled_token_array[tok].m_start_expr= false;
+ }
+
+ /*
+ Tokens made of just one terminal character
+ */
+ for (tok=0; tok < 256; tok++)
+ {
+ str= & char_tokens[tok];
+ str[0]= (char) tok;
+ compiled_token_array[tok].m_token_string= str;
+ compiled_token_array[tok].m_token_length= 1;
+ compiled_token_array[tok].m_append_space= true;
+ }
+
+ max_token_seen= 255;
+
+ /*
+ String terminal tokens, used in sql_yacc.yy
+ */
+ set_token(NEG, "~");
+ set_token(TABLE_REF_PRIORITY, "TABLE_REF_PRIORITY");
+
+ /*
+ Tokens hard coded in sql_lex.cc
+ */
+
+ set_token(WITH_CUBE_SYM, "WITH CUBE");
+ set_token(WITH_ROLLUP_SYM, "WITH ROLLUP");
+ set_token(NOT2_SYM, "!");
+ set_token(OR2_SYM, "|");
+ set_token(PARAM_MARKER, "?");
+ set_token(SET_VAR, ":=");
+ set_token(UNDERSCORE_CHARSET, "(_charset)");
+ set_token(END_OF_INPUT, "");
+
+ /*
+ Values.
+ These tokens are all normalized later,
+ so this strings will never be displayed.
+ */
+ set_token(BIN_NUM, "(bin)");
+ set_token(DECIMAL_NUM, "(decimal)");
+ set_token(FLOAT_NUM, "(float)");
+ set_token(HEX_NUM, "(hex)");
+ set_token(LEX_HOSTNAME, "(hostname)");
+ set_token(LONG_NUM, "(long)");
+ set_token(NUM, "(num)");
+ set_token(TEXT_STRING, "(text)");
+ set_token(NCHAR_STRING, "(nchar)");
+ set_token(ULONGLONG_NUM, "(ulonglong)");
+
+ /*
+ Identifiers.
+ */
+ set_token(IDENT, "(id)");
+ set_token(IDENT_QUOTED, "(id_quoted)");
+
+ /*
+ Unused tokens
+ */
+ set_token(LOCATOR_SYM, "LOCATOR");
+ set_token(SERVER_OPTIONS, "SERVER_OPTIONS");
+ set_token(UDF_RETURNS_SYM, "UDF_RETURNS");
+
+ /*
+ See symbols[] in sql/lex.h
+ */
+ for (i= 0; i< sizeof(symbols)/sizeof(symbols[0]); i++)
+ {
+ set_token(symbols[i].tok, symbols[i].name);
+ }
+
+ /*
+ See sql_functions[] in sql/lex.h
+ */
+ for (i= 0; i< sizeof(sql_functions)/sizeof(sql_functions[0]); i++)
+ {
+ set_token(sql_functions[i].tok, sql_functions[i].name);
+ }
+
+ /*
+ Additional FAKE tokens,
+ used internally to normalize a digest text.
+ */
+
+ max_token_seen++;
+ tok_generic_value= max_token_seen;
+ set_token(tok_generic_value, "?");
+
+ max_token_seen++;
+ tok_generic_value_list= max_token_seen;
+ set_token(tok_generic_value_list, "?, ...");
+
+ max_token_seen++;
+ tok_row_single_value= max_token_seen;
+ set_token(tok_row_single_value, "(?)");
+
+ max_token_seen++;
+ tok_row_single_value_list= max_token_seen;
+ set_token(tok_row_single_value_list, "(?) /* , ... */");
+
+ max_token_seen++;
+ tok_row_multiple_value= max_token_seen;
+ set_token(tok_row_multiple_value, "(...)");
+
+ max_token_seen++;
+ tok_row_multiple_value_list= max_token_seen;
+ set_token(tok_row_multiple_value_list, "(...) /* , ... */");
+
+ max_token_seen++;
+ tok_ident= max_token_seen;
+ set_token(tok_ident, "(tok_id)");
+
+ max_token_seen++;
+ tok_unused= max_token_seen;
+ set_token(tok_unused, "UNUSED");
+
+ /*
+ Fix whitespace for some special tokens.
+ */
+
+ /*
+ The lexer parses "@@variable" as '@', '@', 'variable',
+ returning a token for '@' alone.
+
+ This is incorrect, '@' is not really a token,
+ because the syntax "@ @ variable" (with spaces) is not accepted:
+ The lexer keeps some internal state after the '@' fake token.
+
+ To work around this, digest text are printed as "@@variable".
+ */
+ compiled_token_array[(int) '@'].m_append_space= false;
+
+ /*
+ Define additional properties for tokens.
+
+ List all the token that are followed by an expression.
+ This is needed to differentiate unary from binary
+ '+' and '-' operators, because we want to:
+ - reduce <unary +> <NUM> to <?>,
+ - preserve <...> <binary +> <NUM> as is.
+ */
+ set_start_expr_token('(');
+ set_start_expr_token(',');
+ set_start_expr_token(EVERY_SYM);
+ set_start_expr_token(AT_SYM);
+ set_start_expr_token(STARTS_SYM);
+ set_start_expr_token(ENDS_SYM);
+ set_start_expr_token(DEFAULT);
+ set_start_expr_token(RETURN_SYM);
+ set_start_expr_token(IF);
+ set_start_expr_token(ELSEIF_SYM);
+ set_start_expr_token(CASE_SYM);
+ set_start_expr_token(WHEN_SYM);
+ set_start_expr_token(WHILE_SYM);
+ set_start_expr_token(UNTIL_SYM);
+ set_start_expr_token(SELECT_SYM);
+
+ set_start_expr_token(OR_SYM);
+ set_start_expr_token(OR2_SYM);
+ set_start_expr_token(XOR);
+ set_start_expr_token(AND_SYM);
+ set_start_expr_token(AND_AND_SYM);
+ set_start_expr_token(NOT_SYM);
+ set_start_expr_token(BETWEEN_SYM);
+ set_start_expr_token(LIKE);
+ set_start_expr_token(REGEXP);
+
+ set_start_expr_token('|');
+ set_start_expr_token('&');
+ set_start_expr_token(SHIFT_LEFT);
+ set_start_expr_token(SHIFT_RIGHT);
+ set_start_expr_token('+');
+ set_start_expr_token('-');
+ set_start_expr_token(INTERVAL_SYM);
+ set_start_expr_token('*');
+ set_start_expr_token('/');
+ set_start_expr_token('%');
+ set_start_expr_token(DIV_SYM);
+ set_start_expr_token(MOD_SYM);
+ set_start_expr_token('^');
+}
+
+void print_tokens()
+{
+ int tok;
+
+ printf("#ifdef LEX_TOKEN_WITH_DEFINITION\n");
+ printf("lex_token_string lex_token_array[]=\n");
+ printf("{\n");
+ printf("/* PART 1: character tokens. */\n");
+
+ for (tok= 0; tok<256; tok++)
+ {
+ printf("/* %03d */ { \"\\x%02x\", 1, %s, %s},\n",
+ tok,
+ tok,
+ compiled_token_array[tok].m_append_space ? "true" : "false",
+ compiled_token_array[tok].m_start_expr ? "true" : "false");
+ }
+
+ printf("/* PART 2: named tokens. */\n");
+
+ for (tok= 256; tok<= max_token_seen; tok++)
+ {
+ printf("/* %03d */ { \"%s\", %d, %s, %s},\n",
+ tok,
+ compiled_token_array[tok].m_token_string,
+ compiled_token_array[tok].m_token_length,
+ compiled_token_array[tok].m_append_space ? "true" : "false",
+ compiled_token_array[tok].m_start_expr ? "true" : "false");
+ }
+
+ printf("/* DUMMY */ { \"\", 0, false, false}\n");
+ printf("};\n");
+ printf("#endif /* LEX_TOKEN_WITH_DEFINITION */\n");
+
+ printf("/* DIGEST specific tokens. */\n");
+ printf("#define TOK_GENERIC_VALUE %d\n", tok_generic_value);
+ printf("#define TOK_GENERIC_VALUE_LIST %d\n", tok_generic_value_list);
+ printf("#define TOK_ROW_SINGLE_VALUE %d\n", tok_row_single_value);
+ printf("#define TOK_ROW_SINGLE_VALUE_LIST %d\n", tok_row_single_value_list);
+ printf("#define TOK_ROW_MULTIPLE_VALUE %d\n", tok_row_multiple_value);
+ printf("#define TOK_ROW_MULTIPLE_VALUE_LIST %d\n", tok_row_multiple_value_list);
+ printf("#define TOK_IDENT %d\n", tok_ident);
+ printf("#define TOK_UNUSED %d\n", tok_unused);
+}
+
+int main(int argc,char **argv)
+{
+ puts("/*");
+ puts(ORACLE_WELCOME_COPYRIGHT_NOTICE("2011"));
+ puts("*/");
+
+ printf("/*\n");
+ printf(" This file is generated, do not edit.\n");
+ printf(" See file sql/gen_lex_token.cc.\n");
+ printf("*/\n");
+ printf("struct lex_token_string\n");
+ printf("{\n");
+ printf(" const char *m_token_string;\n");
+ printf(" int m_token_length;\n");
+ printf(" bool m_append_space;\n");
+ printf(" bool m_start_expr;\n");
+ printf("};\n");
+ printf("typedef struct lex_token_string lex_token_string;\n");
+
+ compute_tokens();
+ print_tokens();
+
+ return 0;
+}
+
diff --git a/sql/gstream.cc b/sql/gstream.cc
index 3a9e478c376..adb46083621 100644
--- a/sql/gstream.cc
+++ b/sql/gstream.cc
@@ -18,6 +18,7 @@
NOTE: These functions assumes that the string is end \0 terminated!
*/
+#include <my_global.h>
#include "sql_priv.h"
#include "gstream.h"
#include "m_string.h" // LEX_STRING
diff --git a/sql/ha_ndbcluster.cc b/sql/ha_ndbcluster.cc
index 2878f25ed14..ed05521a473 100644
--- a/sql/ha_ndbcluster.cc
+++ b/sql/ha_ndbcluster.cc
@@ -25,6 +25,7 @@
#pragma implementation // gcc: Class implementation
#endif
+#include <my_global.h>
#include "sql_priv.h"
#include "unireg.h" // REQUIRED: for other includes
#include "sql_table.h" // build_table_filename,
@@ -379,11 +380,11 @@ static int ndb_to_mysql_error(const NdbError *ndberr)
- Used by replication to see if the error was temporary
*/
if (ndberr->status == NdbError::TemporaryError)
- push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
ER_GET_TEMPORARY_ERRMSG, ER(ER_GET_TEMPORARY_ERRMSG),
ndberr->code, ndberr->message, "NDB");
else
- push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
ER_GET_ERRMSG, ER(ER_GET_ERRMSG),
ndberr->code, ndberr->message, "NDB");
return error;
@@ -650,7 +651,7 @@ static void set_ndb_err(THD *thd, const NdbError &err)
{
char buf[FN_REFLEN];
ndb_error_string(thd_ndb->m_error_code, buf, sizeof(buf));
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_GET_ERRMSG, ER(ER_GET_ERRMSG),
thd_ndb->m_error_code, buf, "NDB");
}
@@ -930,7 +931,7 @@ int ha_ndbcluster::set_ndb_value(NdbOperation *ndb_op, Field *field,
DBUG_PRINT("value", ("set blob ptr: 0x%lx len: %u",
(long) blob_ptr, blob_len));
- DBUG_DUMP("value", blob_ptr, min(blob_len, 26));
+ DBUG_DUMP("value", blob_ptr, MY_MIN(blob_len, 26));
if (set_blob_value)
*set_blob_value= TRUE;
@@ -1245,8 +1246,8 @@ static int fix_unique_index_attr_order(NDB_INDEX_DATA &data,
}
KEY_PART_INFO* key_part= key_info->key_part;
- KEY_PART_INFO* end= key_part+key_info->key_parts;
- DBUG_ASSERT(key_info->key_parts == sz);
+ KEY_PART_INFO* end= key_part+key_info->user_defined_key_parts;
+ DBUG_ASSERT(key_info->user_defined_key_parts == sz);
for (unsigned i= 0; key_part != end; key_part++, i++)
{
const char *field_name= key_part->field->field_name;
@@ -1576,7 +1577,7 @@ NDB_INDEX_TYPE ha_ndbcluster::get_index_type_from_key(uint inx,
bool ha_ndbcluster::check_index_fields_not_null(KEY* key_info)
{
KEY_PART_INFO* key_part= key_info->key_part;
- KEY_PART_INFO* end= key_part+key_info->key_parts;
+ KEY_PART_INFO* end= key_part+key_info->user_defined_key_parts;
DBUG_ENTER("ha_ndbcluster::check_index_fields_not_null");
for (; key_part != end; key_part++)
@@ -1733,7 +1734,7 @@ int ha_ndbcluster::set_primary_key(NdbOperation *op, const uchar *key)
{
KEY* key_info= table->key_info + table_share->primary_key;
KEY_PART_INFO* key_part= key_info->key_part;
- KEY_PART_INFO* end= key_part+key_info->key_parts;
+ KEY_PART_INFO* end= key_part+key_info->user_defined_key_parts;
DBUG_ENTER("set_primary_key");
for (; key_part != end; key_part++)
@@ -1755,7 +1756,7 @@ int ha_ndbcluster::set_primary_key_from_record(NdbOperation *op, const uchar *re
{
KEY* key_info= table->key_info + table_share->primary_key;
KEY_PART_INFO* key_part= key_info->key_part;
- KEY_PART_INFO* end= key_part+key_info->key_parts;
+ KEY_PART_INFO* end= key_part+key_info->user_defined_key_parts;
DBUG_ENTER("set_primary_key_from_record");
for (; key_part != end; key_part++)
@@ -1772,7 +1773,7 @@ bool ha_ndbcluster::check_index_fields_in_write_set(uint keyno)
{
KEY* key_info= table->key_info + keyno;
KEY_PART_INFO* key_part= key_info->key_part;
- KEY_PART_INFO* end= key_part+key_info->key_parts;
+ KEY_PART_INFO* end= key_part+key_info->user_defined_key_parts;
uint i;
DBUG_ENTER("check_index_fields_in_write_set");
@@ -1793,7 +1794,7 @@ int ha_ndbcluster::set_index_key_from_record(NdbOperation *op,
{
KEY* key_info= table->key_info + keyno;
KEY_PART_INFO* key_part= key_info->key_part;
- KEY_PART_INFO* end= key_part+key_info->key_parts;
+ KEY_PART_INFO* end= key_part+key_info->user_defined_key_parts;
uint i;
DBUG_ENTER("set_index_key_from_record");
@@ -1815,7 +1816,7 @@ ha_ndbcluster::set_index_key(NdbOperation *op,
DBUG_ENTER("ha_ndbcluster::set_index_key");
uint i;
KEY_PART_INFO* key_part= key_info->key_part;
- KEY_PART_INFO* end= key_part+key_info->key_parts;
+ KEY_PART_INFO* end= key_part+key_info->user_defined_key_parts;
for (i= 0; key_part != end; key_part++, i++)
{
@@ -2083,7 +2084,7 @@ check_null_in_record(const KEY* key_info, const uchar *record)
{
KEY_PART_INFO *curr_part, *end_part;
curr_part= key_info->key_part;
- end_part= curr_part + key_info->key_parts;
+ end_part= curr_part + key_info->user_defined_key_parts;
while (curr_part != end_part)
{
@@ -2177,7 +2178,7 @@ int ha_ndbcluster::peek_indexed_rows(const uchar *record,
NdbIndexOperation *iop;
const NDBINDEX *unique_index = m_index[i].unique_index;
key_part= key_info->key_part;
- end= key_part + key_info->key_parts;
+ end= key_part + key_info->user_defined_key_parts;
if (!(iop= trans->getNdbIndexOperation(unique_index, m_table)) ||
iop->readTuple(lm) != 0)
ERR_RETURN(trans->getNdbError());
@@ -2405,7 +2406,7 @@ int ha_ndbcluster::set_bounds(NdbIndexScanOperation *op,
uint range_no)
{
const KEY *const key_info= table->key_info + inx;
- const uint key_parts= key_info->key_parts;
+ const uint key_parts= key_info->user_defined_key_parts;
uint key_tot_len[2];
uint tot_len;
uint i, j;
@@ -2906,8 +2907,6 @@ int ha_ndbcluster::write_row(uchar *record)
}
ha_statistic_increment(&SSV::ha_write_count);
- if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_INSERT)
- table->timestamp_field->set_time();
if (!(op= trans->getNdbOperation(m_table)))
ERR_RETURN(trans->getNdbError());
@@ -3146,11 +3145,6 @@ int ha_ndbcluster::update_row(const uchar *old_data, uchar *new_data)
}
ha_statistic_increment(&SSV::ha_update_count);
- if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_UPDATE)
- {
- table->timestamp_field->set_time();
- bitmap_set_bit(table->write_set, table->timestamp_field->field_index);
- }
if (m_use_partition_function &&
(error= get_parts_for_update(old_data, new_data, table->record[0],
@@ -3213,7 +3207,7 @@ int ha_ndbcluster::update_row(const uchar *old_data, uchar *new_data)
undo_res= write_row((uchar *)old_data);
if (undo_res)
push_warning(current_thd,
- MYSQL_ERROR::WARN_LEVEL_WARN,
+ Sql_condition::WARN_LEVEL_WARN,
undo_res,
"NDB failed undoing delete at primary key update");
m_primary_key_update= FALSE;
@@ -3715,7 +3709,7 @@ check_null_in_key(const KEY* key_info, const uchar *key, uint key_len)
KEY_PART_INFO *curr_part, *end_part;
const uchar* end_ptr= key + key_len;
curr_part= key_info->key_part;
- end_part= curr_part + key_info->key_parts;
+ end_part= curr_part + key_info->user_defined_key_parts;
for (; curr_part != end_part && key < end_ptr; curr_part++)
{
@@ -4086,7 +4080,7 @@ void ha_ndbcluster::position(const uchar *record)
key_length= ref_length;
key_info= table->key_info + table_share->primary_key;
key_part= key_info->key_part;
- end= key_part + key_info->key_parts;
+ end= key_part + key_info->user_defined_key_parts;
buff= ref;
for (; key_part != end; key_part++)
@@ -5423,7 +5417,7 @@ int ha_ndbcluster::create(const char *name,
{
if (create_info->storage_media == HA_SM_MEMORY)
{
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_ILLEGAL_HA_CREATE_OPTION,
ER(ER_ILLEGAL_HA_CREATE_OPTION),
ndbcluster_hton_name,
@@ -5478,7 +5472,7 @@ int ha_ndbcluster::create(const char *name,
case ROW_TYPE_FIXED:
if (field_type_forces_var_part(field->type()))
{
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_ILLEGAL_HA_CREATE_OPTION,
ER(ER_ILLEGAL_HA_CREATE_OPTION),
ndbcluster_hton_name,
@@ -5507,7 +5501,7 @@ int ha_ndbcluster::create(const char *name,
for (i= 0, key_info= form->key_info; i < form->s->keys; i++, key_info++)
{
KEY_PART_INFO *key_part= key_info->key_part;
- KEY_PART_INFO *end= key_part + key_info->key_parts;
+ KEY_PART_INFO *end= key_part + key_info->user_defined_key_parts;
for (; key_part != end; key_part++)
tab.getColumn(key_part->fieldnr-1)->setStorageType(
NdbDictionary::Column::StorageTypeMemory);
@@ -5809,7 +5803,7 @@ int ha_ndbcluster::create_index(const char *name, KEY *key_info,
case UNIQUE_INDEX:
if (check_index_fields_not_null(key_info))
{
- push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
ER_NULL_COLUMN_IN_INDEX,
"Ndb does not support unique index on NULL valued attributes, index access with NULL value will become full table scan");
}
@@ -5818,7 +5812,7 @@ int ha_ndbcluster::create_index(const char *name, KEY *key_info,
case ORDERED_INDEX:
if (key_info->algorithm == HA_KEY_ALG_HASH)
{
- push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
ER_ILLEGAL_HA_CREATE_OPTION,
ER(ER_ILLEGAL_HA_CREATE_OPTION),
ndbcluster_hton_name,
@@ -5867,7 +5861,7 @@ int ha_ndbcluster::create_ndb_index(const char *name,
Ndb *ndb= get_ndb();
NdbDictionary::Dictionary *dict= ndb->getDictionary();
KEY_PART_INFO *key_part= key_info->key_part;
- KEY_PART_INFO *end= key_part + key_info->key_parts;
+ KEY_PART_INFO *end= key_part + key_info->user_defined_key_parts;
DBUG_ENTER("ha_ndbcluster::create_index");
DBUG_PRINT("enter", ("name: %s ", name));
@@ -7291,7 +7285,7 @@ int ndbcluster_find_files(handlerton *hton, THD *thd,
file_name->str));
if (ndb_create_table_from_engine(thd, db, file_name->str))
{
- push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
ER_TABLE_EXISTS_ERROR,
"Discover of table %s.%s failed",
db, file_name->str);
@@ -7317,7 +7311,7 @@ int ndbcluster_find_files(handlerton *hton, THD *thd,
file_name->length);
DBUG_ASSERT(record);
my_hash_delete(&ndb_tables, record);
- push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
ER_TABLE_EXISTS_ERROR,
"Local table %s.%s shadows ndb table",
db, file_name->str);
@@ -8121,23 +8115,33 @@ uint8 ha_ndbcluster::table_cache_type()
}
-uint ndb_get_commitcount(THD *thd, char *dbname, char *tabname,
+/**
+ Retrieve the commit count for the table object.
+
+ @param thd Thread context.
+ @param norm_name Normalized path to the table.
+ @param[out] commit_count Commit count for the table.
+
+ @return 0 on success.
+ @return 1 if an error occured.
+*/
+
+uint ndb_get_commitcount(THD *thd, char *norm_name,
Uint64 *commit_count)
{
- char name[FN_REFLEN + 1];
+ char dbname[NAME_LEN + 1];
NDB_SHARE *share;
DBUG_ENTER("ndb_get_commitcount");
- build_table_filename(name, sizeof(name) - 1,
- dbname, tabname, "", 0);
- DBUG_PRINT("enter", ("name: %s", name));
- mysql_mutex_lock(&ndbcluster_mutex);
+ DBUG_PRINT("enter", ("name: %s", norm_name));
+ pthread_mutex_lock(&ndbcluster_mutex);
if (!(share=(NDB_SHARE*) my_hash_search(&ndbcluster_open_tables,
- (uchar*) name,
- strlen(name))))
+ (const uchar*) norm_name,
+ strlen(norm_name))))
{
- mysql_mutex_unlock(&ndbcluster_mutex);
- DBUG_PRINT("info", ("Table %s not found in ndbcluster_open_tables", name));
+ pthread_mutex_unlock(&ndbcluster_mutex);
+ DBUG_PRINT("info", ("Table %s not found in ndbcluster_open_tables",
+ norm_name));
DBUG_RETURN(1);
}
/* ndb_share reference temporary, free below */
@@ -8169,6 +8173,8 @@ uint ndb_get_commitcount(THD *thd, char *dbname, char *tabname,
Ndb *ndb;
if (!(ndb= check_ndb_in_thd(thd)))
DBUG_RETURN(1);
+
+ ha_ndbcluster::set_dbname(norm_name, dbname);
if (ndb->setDatabaseName(dbname))
{
ERR_RETURN(ndb->getNdbError());
@@ -8178,7 +8184,9 @@ uint ndb_get_commitcount(THD *thd, char *dbname, char *tabname,
struct Ndb_statistics stat;
{
- Ndb_table_guard ndbtab_g(ndb->getDictionary(), tabname);
+ char tblname[NAME_LEN + 1];
+ ha_ndbcluster::set_tabname(norm_name, tblname);
+ Ndb_table_guard ndbtab_g(ndb->getDictionary(), tblname);
if (ndbtab_g.get_table() == 0
|| ndb_get_table_statistics(NULL, FALSE, ndb, ndbtab_g.get_table(), &stat))
{
@@ -8228,10 +8236,9 @@ uint ndb_get_commitcount(THD *thd, char *dbname, char *tabname,
@param thd thread handle
- @param full_name concatenation of database name,
- the null character '\\0', and the table name
- @param full_name_len length of the full name,
- i.e. len(dbname) + len(tablename) + 1
+ @param full_name normalized path to the table in the canonical
+ format.
+ @param full_name_len length of the normalized path to the table.
@param engine_data parameter retrieved when query was first inserted into
the cache. If the value of engine_data is changed,
all queries for this table should be invalidated.
@@ -8250,11 +8257,15 @@ ndbcluster_cache_retrieval_allowed(THD *thd,
ulonglong *engine_data)
{
Uint64 commit_count;
- char *dbname= full_name;
- char *tabname= dbname+strlen(dbname)+1;
+ char dbname[NAME_LEN + 1];
+ char tabname[NAME_LEN + 1];
#ifndef DBUG_OFF
char buff[22], buff2[22];
#endif
+
+ ha_ndbcluster::set_dbname(full_name, dbname);
+ ha_ndbcluster::set_tabname(full_name, tabname);
+
DBUG_ENTER("ndbcluster_cache_retrieval_allowed");
DBUG_PRINT("enter", ("dbname: %s, tabname: %s", dbname, tabname));
@@ -8264,7 +8275,7 @@ ndbcluster_cache_retrieval_allowed(THD *thd,
DBUG_RETURN(FALSE);
}
- if (ndb_get_commitcount(thd, dbname, tabname, &commit_count))
+ if (ndb_get_commitcount(thd, full_name, &commit_count))
{
*engine_data= 0; /* invalidate */
DBUG_PRINT("exit", ("No, could not retrieve commit_count"));
@@ -8299,10 +8310,9 @@ ndbcluster_cache_retrieval_allowed(THD *thd,
the cached query is reused.
@param thd thread handle
- @param full_name concatenation of database name,
- the null character '\\0', and the table name
- @param full_name_len length of the full name,
- i.e. len(dbname) + len(tablename) + 1
+ @param full_name normalized path to the table in the
+ canonical format.
+ @param full_name_len length of the normalized path to the table.
@param engine_callback function to be called before using cache on
this table
@param[out] engine_data commit_count for this table
@@ -8332,7 +8342,7 @@ ha_ndbcluster::register_query_cache_table(THD *thd,
DBUG_RETURN(FALSE);
}
- if (ndb_get_commitcount(thd, m_dbname, m_tabname, &commit_count))
+ if (ndb_get_commitcount(thd, full_name, &commit_count))
{
*engine_data= 0;
DBUG_PRINT("exit", ("Error, could not get commitcount"));
@@ -8656,7 +8666,7 @@ NDB_SHARE *ndbcluster_get_share(const char *key, TABLE *table,
MEM_ROOT **root_ptr=
my_pthread_getspecific_ptr(MEM_ROOT**, THR_MALLOC);
MEM_ROOT *old_root= *root_ptr;
- init_sql_alloc(&share->mem_root, 1024, 0);
+ init_sql_alloc(&share->mem_root, 1024, 0, MYF(0));
*root_ptr= &share->mem_root; // remember to reset before return
share->state= NSS_INITIAL;
/* enough space for key, db, and table_name */
@@ -9500,7 +9510,7 @@ pthread_handler_t ndb_util_thread_func(void *arg __attribute__((unused)))
thd->init_for_queries();
thd->main_security_ctx.host_or_ip= "";
thd->client_capabilities = 0;
- my_net_init(&thd->net, 0);
+ my_net_init(&thd->net, 0, MYF(MY_THREAD_SPECIFIC));
thd->main_security_ctx.master_access= ~0;
thd->main_security_ctx.priv_user[0] = 0;
/* Do not use user-supplied timeout value for system threads. */
@@ -9737,11 +9747,9 @@ next:
mysql_mutex_lock(&LOCK_ndb_util_thread);
ndb_util_thread_end:
- net_end(&thd->net);
ndb_util_thread_fail:
if (share_list)
delete [] share_list;
- thd->cleanup();
delete thd;
/* signal termination */
@@ -9836,11 +9844,11 @@ char* ha_ndbcluster::get_tablespace_name(THD *thd, char* name, uint name_len)
}
err:
if (ndberr.status == NdbError::TemporaryError)
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_GET_TEMPORARY_ERRMSG, ER(ER_GET_TEMPORARY_ERRMSG),
ndberr.code, ndberr.message, "NDB");
else
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_GET_ERRMSG, ER(ER_GET_ERRMSG),
ndberr.code, ndberr.message, "NDB");
return 0;
@@ -9966,7 +9974,7 @@ int ha_ndbcluster::get_default_no_partitions(HA_CREATE_INFO *create_info)
if (adjusted_frag_count(no_fragments, no_nodes, reported_frags))
{
push_warning(current_thd,
- MYSQL_ERROR::WARN_LEVEL_WARN, ER_UNKNOWN_ERROR,
+ Sql_condition::WARN_LEVEL_WARN, ER_UNKNOWN_ERROR,
"Ndb might have problems storing the max amount of rows specified");
}
return (int)reported_frags;
@@ -10155,7 +10163,7 @@ uint ha_ndbcluster::set_up_partition_info(partition_info *part_info,
{
if (!current_thd->variables.new_mode)
{
- push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
ER_ILLEGAL_HA_CREATE_OPTION,
ER(ER_ILLEGAL_HA_CREATE_OPTION),
ndbcluster_hton_name,
diff --git a/sql/ha_ndbcluster_binlog.cc b/sql/ha_ndbcluster_binlog.cc
index ae7bd12f229..64ae31ce231 100644
--- a/sql/ha_ndbcluster_binlog.cc
+++ b/sql/ha_ndbcluster_binlog.cc
@@ -14,6 +14,7 @@
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
+#include <my_global.h>
#include "sql_priv.h"
#include "unireg.h" // REQUIRED: for other includes
#include "sql_show.h"
@@ -289,13 +290,13 @@ static void run_query(THD *thd, char *buf, char *end,
Thd_ndb *thd_ndb= get_thd_ndb(thd);
for (i= 0; no_print_error[i]; i++)
if ((thd_ndb->m_error_code == no_print_error[i]) ||
- (thd->stmt_da->sql_errno() == (unsigned) no_print_error[i]))
+ (thd->get_stmt_da()->sql_errno() == (unsigned) no_print_error[i]))
break;
if (!no_print_error[i])
sql_print_error("NDB: %s: error %s %d(ndb: %d) %d %d",
buf,
- thd->stmt_da->message(),
- thd->stmt_da->sql_errno(),
+ thd->get_stmt_da()->message(),
+ thd->get_stmt_da()->sql_errno(),
thd_ndb->m_error_code,
(int) thd->is_error(), thd->is_slave_error);
}
@@ -309,7 +310,7 @@ static void run_query(THD *thd, char *buf, char *end,
is called from ndbcluster_reset_logs(), which is called from
mysql_flush().
*/
- thd->stmt_da->reset_diagnostics_area();
+ thd->get_stmt_da()->reset_diagnostics_area();
thd->variables.option_bits= save_thd_options;
thd->set_query(save_thd_query, save_thd_query_length);
@@ -375,9 +376,7 @@ ndbcluster_binlog_open_table(THD *thd, NDB_SHARE *share,
free_table_share(table_share);
DBUG_RETURN(error);
}
- mysql_mutex_lock(&LOCK_open);
- assign_new_table_id(table_share);
- mysql_mutex_unlock(&LOCK_open);
+ tdc_assign_new_table_id(table_share);
if (!reopen)
{
@@ -447,7 +446,7 @@ int ndbcluster_binlog_init_share(NDB_SHARE *share, TABLE *_table)
alloc_root(mem_root, no_nodes * sizeof(MY_BITMAP));
for (i= 0; i < no_nodes; i++)
{
- bitmap_init(&share->subscriber_bitmap[i],
+ my_bitmap_init(&share->subscriber_bitmap[i],
(Uint32*)alloc_root(mem_root, max_ndb_nodes/8),
max_ndb_nodes, FALSE);
bitmap_clear_all(&share->subscriber_bitmap[i]);
@@ -983,8 +982,8 @@ static void print_could_not_discover_error(THD *thd,
"my_errno: %d",
schema->db, schema->name, schema->query,
schema->node_id, my_errno);
- List_iterator_fast<MYSQL_ERROR> it(thd->warning_info->warn_list());
- MYSQL_ERROR *err;
+ List_iterator_fast<Sql_condition> it(thd->warning_info->warn_list());
+ Sql_condition *err;
while ((err= it++))
sql_print_warning("NDB Binlog: (%d)%s", err->get_sql_errno(),
err->get_message_text());
@@ -1120,7 +1119,7 @@ ndbcluster_update_slock(THD *thd,
MY_BITMAP slock;
uint32 bitbuf[SCHEMA_SLOCK_SIZE/4];
- bitmap_init(&slock, bitbuf, sizeof(bitbuf)*8, false);
+ my_bitmap_init(&slock, bitbuf, sizeof(bitbuf)*8, false);
if (ndbtab == 0)
{
@@ -1229,7 +1228,7 @@ ndbcluster_update_slock(THD *thd,
char buf[1024];
my_snprintf(buf, sizeof(buf), "Could not release lock on '%s.%s'",
db, table_name);
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_GET_ERRMSG, ER(ER_GET_ERRMSG),
ndb_error->code, ndb_error->message, buf);
}
@@ -1371,7 +1370,7 @@ int ndbcluster_log_schema_op(THD *thd, NDB_SHARE *share,
{
int i, updated= 0;
int no_storage_nodes= g_ndb_cluster_connection->no_db_nodes();
- bitmap_init(&schema_subscribers, bitbuf, sizeof(bitbuf)*8, FALSE);
+ my_bitmap_init(&schema_subscribers, bitbuf, sizeof(bitbuf)*8, FALSE);
bitmap_set_all(&schema_subscribers);
/* begin protect ndb_schema_share */
@@ -1558,7 +1557,7 @@ err:
}
end:
if (ndb_error)
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_GET_ERRMSG, ER(ER_GET_ERRMSG),
ndb_error->code,
ndb_error->message,
@@ -1909,7 +1908,7 @@ ndb_binlog_thread_handle_schema_event(THD *thd, Ndb *ndb,
Cluster_schema *schema= (Cluster_schema *)
sql_alloc(sizeof(Cluster_schema));
MY_BITMAP slock;
- bitmap_init(&slock, schema->slock, 8*SCHEMA_SLOCK_SIZE, FALSE);
+ my_bitmap_init(&slock, schema->slock, 8*SCHEMA_SLOCK_SIZE, FALSE);
uint node_id= g_ndb_cluster_connection->node_id();
{
ndbcluster_get_schema(tmp_share, schema);
@@ -2348,8 +2347,8 @@ static int open_ndb_binlog_index(THD *thd, TABLE **ndb_binlog_index)
sql_print_error("NDB Binlog: Opening ndb_binlog_index: killed");
else
sql_print_error("NDB Binlog: Opening ndb_binlog_index: %d, '%s'",
- thd->stmt_da->sql_errno(),
- thd->stmt_da->message());
+ thd->get_stmt_da()->sql_errno(),
+ thd->get_stmt_da()->message());
thd->proc_info= save_proc_info;
return -1;
}
@@ -2405,9 +2404,9 @@ int ndb_add_ndb_binlog_index(THD *thd, void *_row)
}
add_ndb_binlog_index_err:
- thd->stmt_da->can_overwrite_status= TRUE;
+ thd->get_stmt_da()->set_overwrite_status(true);
thd->is_error() ? trans_rollback_stmt(thd) : trans_commit_stmt(thd);
- thd->stmt_da->can_overwrite_status= FALSE;
+ thd->get_stmt_da()->set_overwrite_status(false);
close_thread_tables(thd);
/*
There should be no need for rolling back transaction due to deadlock
@@ -2736,7 +2735,7 @@ ndbcluster_create_event(Ndb *ndb, const NDBTAB *ndbtab,
"with BLOB attribute and no PK is not supported",
share->key);
if (push_warning)
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_ILLEGAL_HA_CREATE_OPTION,
ER(ER_ILLEGAL_HA_CREATE_OPTION),
ndbcluster_hton_name,
@@ -2780,7 +2779,7 @@ ndbcluster_create_event(Ndb *ndb, const NDBTAB *ndbtab,
failed, print a warning
*/
if (push_warning > 1)
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_GET_ERRMSG, ER(ER_GET_ERRMSG),
dict->getNdbError().code,
dict->getNdbError().message, "NDB");
@@ -2808,7 +2807,7 @@ ndbcluster_create_event(Ndb *ndb, const NDBTAB *ndbtab,
dict->dropEvent(my_event.getName()))
{
if (push_warning > 1)
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_GET_ERRMSG, ER(ER_GET_ERRMSG),
dict->getNdbError().code,
dict->getNdbError().message, "NDB");
@@ -2827,7 +2826,7 @@ ndbcluster_create_event(Ndb *ndb, const NDBTAB *ndbtab,
if (dict->createEvent(my_event))
{
if (push_warning > 1)
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_GET_ERRMSG, ER(ER_GET_ERRMSG),
dict->getNdbError().code,
dict->getNdbError().message, "NDB");
@@ -2840,7 +2839,7 @@ ndbcluster_create_event(Ndb *ndb, const NDBTAB *ndbtab,
DBUG_RETURN(-1);
}
#ifdef NDB_BINLOG_EXTRA_WARNINGS
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_GET_ERRMSG, ER(ER_GET_ERRMSG),
0, "NDB Binlog: Removed trailing event",
"NDB");
@@ -2951,7 +2950,7 @@ ndbcluster_create_event_ops(NDB_SHARE *share, const NDBTAB *ndbtab,
{
sql_print_error("NDB Binlog: Creating NdbEventOperation failed for"
" %s",event_name);
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_GET_ERRMSG, ER(ER_GET_ERRMSG),
ndb->getNdbError().code,
ndb->getNdbError().message,
@@ -3000,7 +2999,7 @@ ndbcluster_create_event_ops(NDB_SHARE *share, const NDBTAB *ndbtab,
sql_print_error("NDB Binlog: Creating NdbEventOperation"
" blob field %u handles failed (code=%d) for %s",
j, op->getNdbError().code, event_name);
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_GET_ERRMSG, ER(ER_GET_ERRMSG),
op->getNdbError().code,
op->getNdbError().message,
@@ -3039,7 +3038,7 @@ ndbcluster_create_event_ops(NDB_SHARE *share, const NDBTAB *ndbtab,
retries= 0;
if (retries == 0)
{
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_GET_ERRMSG, ER(ER_GET_ERRMSG),
op->getNdbError().code, op->getNdbError().message,
"NDB");
@@ -3107,7 +3106,7 @@ ndbcluster_handle_drop_table(Ndb *ndb, const char *event_name,
if (dict->getNdbError().code != 4710)
{
/* drop event failed for some reason, issue a warning */
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_GET_ERRMSG, ER(ER_GET_ERRMSG),
dict->getNdbError().code,
dict->getNdbError().message, "NDB");
@@ -3355,7 +3354,7 @@ ndb_binlog_thread_handle_data_event(Ndb *ndb, NdbEventOperation *pOp,
MY_BITMAP b;
/* Potential buffer for the bitmap */
uint32 bitbuf[128 / (sizeof(uint32) * 8)];
- bitmap_init(&b, n_fields <= sizeof(bitbuf) * 8 ? bitbuf : NULL,
+ my_bitmap_init(&b, n_fields <= sizeof(bitbuf) * 8 ? bitbuf : NULL,
n_fields, FALSE);
bitmap_set_all(&b);
@@ -3575,7 +3574,7 @@ static NDB_SCHEMA_OBJECT *ndb_get_schema_object(const char *key,
break;
}
mysql_mutex_init(key_ndb_schema_object_mutex, &ndb_schema_object->mutex, MY_MUTEX_INIT_FAST);
- bitmap_init(&ndb_schema_object->slock_bitmap, ndb_schema_object->slock,
+ my_bitmap_init(&ndb_schema_object->slock_bitmap, ndb_schema_object->slock,
sizeof(ndb_schema_object->slock)*8, FALSE);
bitmap_clear_all(&ndb_schema_object->slock_bitmap);
break;
@@ -3672,7 +3671,7 @@ pthread_handler_t ndb_binlog_thread_func(void *arg)
thd->system_thread= SYSTEM_THREAD_NDBCLUSTER_BINLOG;
thd->main_security_ctx.host_or_ip= "";
thd->client_capabilities= 0;
- my_net_init(&thd->net, 0);
+ my_net_init(&thd->net, 0, MYF(MY_THREAD_SPECIFIC));
thd->main_security_ctx.master_access= ~0;
thd->main_security_ctx.priv_user[0]= 0;
/* Do not use user-supplied timeout value for system threads. */
@@ -3971,7 +3970,7 @@ restart:
my_pthread_getspecific_ptr(MEM_ROOT**, THR_MALLOC);
MEM_ROOT *old_root= *root_ptr;
MEM_ROOT mem_root;
- init_sql_alloc(&mem_root, 4096, 0);
+ init_sql_alloc(&mem_root, 4096, 0, MYF(0));
List<Cluster_schema> post_epoch_log_list;
List<Cluster_schema> post_epoch_unlock_list;
*root_ptr= &mem_root;
@@ -4283,9 +4282,9 @@ err:
sql_print_information("Stopping Cluster Binlog");
DBUG_PRINT("info",("Shutting down cluster binlog thread"));
thd->proc_info= "Shutting down";
- thd->stmt_da->can_overwrite_status= TRUE;
+ thd->get_stmt_da()->set_overwrite_status(true);
thd->is_error() ? trans_rollback_stmt(thd) : trans_commit_stmt(thd);
- thd->stmt_da->can_overwrite_status= FALSE;
+ thd->get_stmt_da()->set_overwrite_status(false);
close_thread_tables(thd);
thd->mdl_context.release_transactional_locks();
mysql_mutex_lock(&injector_mutex);
@@ -4371,8 +4370,6 @@ err:
my_hash_free(&ndb_schema_objects);
- net_end(&thd->net);
- thd->cleanup();
delete thd;
ndb_binlog_thread_running= -1;
diff --git a/sql/ha_ndbcluster_cond.cc b/sql/ha_ndbcluster_cond.cc
index e1bd6271866..f39e72e1549 100644
--- a/sql/ha_ndbcluster_cond.cc
+++ b/sql/ha_ndbcluster_cond.cc
@@ -22,6 +22,7 @@
#pragma implementation // gcc: Class implementation
#endif
+#include <my_global.h>
#include "sql_priv.h"
#include "sql_class.h" // set_var.h: THD
#include "my_global.h" // WITH_*
@@ -1375,7 +1376,7 @@ ha_ndbcluster_cond::generate_scan_filter(NdbScanOperation *op)
{
// err.message has static storage
DBUG_PRINT("info", ("%s", err.message));
- push_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning(current_thd, Sql_condition::WARN_LEVEL_WARN,
err.code, err.message);
ret=0;
}
@@ -1431,7 +1432,7 @@ int ha_ndbcluster_cond::generate_scan_filter_from_key(NdbScanOperation *op,
uchar *buf)
{
KEY_PART_INFO* key_part= key_info->key_part;
- KEY_PART_INFO* end= key_part+key_info->key_parts;
+ KEY_PART_INFO* end= key_part+key_info->user_defined_key_parts;
NdbScanFilter filter(op, true); // abort on too large
int res;
DBUG_ENTER("generate_scan_filter_from_key");
diff --git a/sql/ha_ndbcluster_cond.h b/sql/ha_ndbcluster_cond.h
index 2387dcaf5ba..952b705bfc2 100644
--- a/sql/ha_ndbcluster_cond.h
+++ b/sql/ha_ndbcluster_cond.h
@@ -350,18 +350,18 @@ class Ndb_cond_traverse_context : public Sql_alloc
skip(0), collation(NULL), rewrite_stack(NULL)
{
// Allocate type checking bitmaps
- bitmap_init(&expect_mask, 0, 512, FALSE);
- bitmap_init(&expect_field_type_mask, 0, 512, FALSE);
- bitmap_init(&expect_field_result_mask, 0, 512, FALSE);
+ my_bitmap_init(&expect_mask, 0, 512, FALSE);
+ my_bitmap_init(&expect_field_type_mask, 0, 512, FALSE);
+ my_bitmap_init(&expect_field_result_mask, 0, 512, FALSE);
if (stack)
cond_ptr= stack->ndb_cond;
};
~Ndb_cond_traverse_context()
{
- bitmap_free(&expect_mask);
- bitmap_free(&expect_field_type_mask);
- bitmap_free(&expect_field_result_mask);
+ my_bitmap_free(&expect_mask);
+ my_bitmap_free(&expect_field_type_mask);
+ my_bitmap_free(&expect_field_result_mask);
if (rewrite_stack) delete rewrite_stack;
}
void expect(Item::Type type)
diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc
index b7580293fe3..85c19da9dda 100644
--- a/sql/ha_partition.cc
+++ b/sql/ha_partition.cc
@@ -1,5 +1,6 @@
/*
Copyright (c) 2005, 2013, Oracle and/or its affiliates.
+ Copyright (c) 2009, 2013, Monty Program Ab & SkySQL 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
@@ -36,10 +37,6 @@
in the execution of queries. This functionality will grow with later
versions of MySQL.
- You can enable it in your buld by doing the following during your build
- process:
- ./configure --with-partition
-
The partition is setup to use table locks. It implements an partition "SHARE"
that is inserted into a hash by table name. You can use this to store
information of state that any partition handler object will be able to see
@@ -49,10 +46,7 @@
if this file.
*/
-#ifdef __GNUC__
-#pragma implementation // gcc: Class implementation
-#endif
-
+#include <my_global.h>
#include "sql_priv.h"
#include "sql_parse.h" // append_file_to_dir
#include "create_options.h"
@@ -62,14 +56,30 @@
#include "sql_table.h" // tablename_to_filename
#include "key.h"
#include "sql_plugin.h"
-#include "table.h" /* HA_DATA_PARTITION */
#include "sql_show.h" // append_identifier
#include "sql_admin.h" // SQL_ADMIN_MSG_TEXT_SIZE
#include "debug_sync.h"
+/* First 4 bytes in the .par file is the number of 32-bit words in the file */
+#define PAR_WORD_SIZE 4
+/* offset to the .par file checksum */
+#define PAR_CHECKSUM_OFFSET 4
+/* offset to the total number of partitions */
+#define PAR_NUM_PARTS_OFFSET 8
+/* offset to the engines array */
+#define PAR_ENGINES_OFFSET 12
+#define PARTITION_ENABLED_TABLE_FLAGS (HA_FILE_BASED | \
+ HA_REC_NOT_IN_SEQ | \
+ HA_CAN_REPAIR)
+#define PARTITION_DISABLED_TABLE_FLAGS (HA_CAN_GEOMETRY | \
+ HA_CAN_FULLTEXT | \
+ HA_DUPLICATE_POS | \
+ HA_CAN_SQL_HANDLER | \
+ HA_CAN_INSERT_DELAYED | \
+ HA_READ_BEFORE_WRITE_REMOVAL)
static const char *ha_par_ext= ".par";
-#define MI_MAX_MSG_BUF MYSQL_ERRMSG_SIZE
+
/****************************************************************************
MODULE create/delete handler object
****************************************************************************/
@@ -83,6 +93,35 @@ static uint alter_table_flags(uint flags);
extern "C" int cmp_key_part_id(void *key_p, uchar *ref1, uchar *ref2);
extern "C" int cmp_key_rowid_part_id(void *ptr, uchar *ref1, uchar *ref2);
+/*
+ If frm_error() is called then we will use this to to find out what file
+ extensions exist for the storage engine. This is also used by the default
+ rename_table and delete_table method in handler.cc.
+*/
+static const char *ha_partition_ext[]=
+{
+ ha_par_ext, NullS
+};
+
+
+#ifdef HAVE_PSI_INTERFACE
+PSI_mutex_key key_partition_auto_inc_mutex;
+
+static PSI_mutex_info all_partition_mutexes[]=
+{
+ { &key_partition_auto_inc_mutex, "Partition_share::auto_inc_mutex", 0}
+};
+
+static void init_partition_psi_keys(void)
+{
+ const char* category= "partition";
+ int count;
+
+ count= array_elements(all_partition_mutexes);
+ mysql_mutex_register(category, all_partition_mutexes, count);
+}
+#endif /* HAVE_PSI_INTERFACE */
+
static int partition_initialize(void *p)
{
@@ -97,10 +136,46 @@ static int partition_initialize(void *p)
partition_hton->flags= HTON_NOT_USER_SELECTABLE |
HTON_HIDDEN |
HTON_TEMPORARY_NOT_SUPPORTED;
+ partition_hton->tablefile_extensions= ha_partition_ext;
+#ifdef HAVE_PSI_INTERFACE
+ init_partition_psi_keys();
+#endif
return 0;
}
+
+/**
+ Initialize and allocate space for partitions shares.
+
+ @param num_parts Number of partitions to allocate storage for.
+
+ @return Operation status.
+ @retval true Failure (out of memory).
+ @retval false Success.
+*/
+
+bool Partition_share::init(uint num_parts)
+{
+ DBUG_ENTER("Partition_share::init");
+ mysql_mutex_init(key_partition_auto_inc_mutex,
+ &auto_inc_mutex,
+ MY_MUTEX_INIT_FAST);
+ auto_inc_initialized= false;
+ partition_name_hash_initialized= false;
+ next_auto_inc_val= 0;
+ partitions_share_refs= new Parts_share_refs;
+ if (!partitions_share_refs)
+ DBUG_RETURN(true);
+ if (partitions_share_refs->init(num_parts))
+ {
+ delete partitions_share_refs;
+ DBUG_RETURN(true);
+ }
+ DBUG_RETURN(false);
+}
+
+
/*
Create new partition handler
@@ -155,7 +230,7 @@ static uint alter_table_flags(uint flags __attribute__((unused)))
HA_FAST_CHANGE_PARTITION);
}
-const uint ha_partition::NO_CURRENT_PART_ID= 0xFFFFFFFF;
+const uint32 ha_partition::NO_CURRENT_PART_ID= NOT_A_PARTITION_ID;
/*
Constructor method
@@ -172,7 +247,7 @@ ha_partition::ha_partition(handlerton *hton, TABLE_SHARE *share)
:handler(hton, share)
{
DBUG_ENTER("ha_partition::ha_partition(table)");
- init_alloc_root(&m_mem_root, 512, 512);
+ init_alloc_root(&m_mem_root, 512, 512, MYF(0));
init_handler_variables();
DBUG_VOID_RETURN;
}
@@ -194,7 +269,7 @@ ha_partition::ha_partition(handlerton *hton, partition_info *part_info)
{
DBUG_ENTER("ha_partition::ha_partition(part_info)");
DBUG_ASSERT(part_info);
- init_alloc_root(&m_mem_root, 512, 512);
+ init_alloc_root(&m_mem_root, 512, 512, MYF(0));
init_handler_variables();
m_part_info= part_info;
m_create_handler= TRUE;
@@ -221,14 +296,16 @@ ha_partition::ha_partition(handlerton *hton, TABLE_SHARE *share,
:handler(hton, share)
{
DBUG_ENTER("ha_partition::ha_partition(clone)");
- init_alloc_root(&m_mem_root, 512, 512);
+ init_alloc_root(&m_mem_root, 512, 512, MYF(0));
init_handler_variables();
m_part_info= part_info_arg;
m_create_handler= TRUE;
m_is_sub_partitioned= m_part_info->is_sub_partitioned();
m_is_clone_of= clone_arg;
m_clone_mem_root= clone_mem_root_arg;
- m_pkey_is_clustered= clone_arg->primary_key_is_clustered();
+ part_share= clone_arg->part_share;
+ m_tot_parts= clone_arg->m_tot_parts;
+ m_pkey_is_clustered= clone_arg->primary_key_is_clustered();
DBUG_VOID_RETURN;
}
@@ -259,7 +336,6 @@ void ha_partition::init_handler_variables()
m_added_file= NULL;
m_tot_parts= 0;
m_pkey_is_clustered= 0;
- m_lock_type= F_UNLCK;
m_part_spec.start_part= NO_CURRENT_PART_ID;
m_scan_value= 2;
m_ref_length= 0;
@@ -295,6 +371,8 @@ void ha_partition::init_handler_variables()
m_is_sub_partitioned= 0;
m_is_clone_of= NULL;
m_clone_mem_root= NULL;
+ part_share= NULL;
+ m_new_partitions_share_refs.empty();
m_part_ids_sorted_by_num_of_records= NULL;
#ifdef DONT_HAVE_TO_BE_INITALIZED
@@ -307,7 +385,7 @@ void ha_partition::init_handler_variables()
const char *ha_partition::table_type() const
{
// we can do this since we only support a single engine type
- return m_file && m_file[0] ? m_file[0]->table_type() : "Unknown";
+ return m_file[0]->table_type();
}
#ifdef WITH_WSREP
int ha_partition::wsrep_db_type() const
@@ -330,6 +408,8 @@ int ha_partition::wsrep_db_type() const
ha_partition::~ha_partition()
{
DBUG_ENTER("ha_partition::~ha_partition()");
+ if (m_new_partitions_share_refs.elements)
+ m_new_partitions_share_refs.delete_elements();
if (m_file != NULL)
{
uint i;
@@ -480,7 +560,7 @@ int ha_partition::delete_table(const char *name)
{
DBUG_ENTER("ha_partition::delete_table");
- DBUG_RETURN(del_ren_cre_table(name, NULL, NULL, NULL));
+ DBUG_RETURN(del_ren_table(name, NULL));
}
@@ -510,7 +590,7 @@ int ha_partition::rename_table(const char *from, const char *to)
{
DBUG_ENTER("ha_partition::rename_table");
- DBUG_RETURN(del_ren_cre_table(from, to, NULL, NULL));
+ DBUG_RETURN(del_ren_table(from, to));
}
@@ -518,7 +598,7 @@ int ha_partition::rename_table(const char *from, const char *to)
Create the handler file (.par-file)
SYNOPSIS
- create_handler_files()
+ create_partitioning_metadata()
name Full path of table name
create_info Create info generated for CREATE TABLE
@@ -527,19 +607,18 @@ int ha_partition::rename_table(const char *from, const char *to)
0 Success
DESCRIPTION
- create_handler_files is called to create any handler specific files
+ create_partitioning_metadata is called to create any handler specific files
before opening the file with openfrm to later call ::create on the
file object.
In the partition handler this is used to store the names of partitions
and types of engines in the partitions.
*/
-int ha_partition::create_handler_files(const char *path,
+int ha_partition::create_partitioning_metadata(const char *path,
const char *old_path,
- int action_flag,
- HA_CREATE_INFO *create_info)
+ int action_flag)
{
- DBUG_ENTER("ha_partition::create_handler_files()");
+ DBUG_ENTER("ha_partition::create_partitioning_metadata()");
/*
We need to update total number of parts since we might write the handler
@@ -600,24 +679,86 @@ int ha_partition::create_handler_files(const char *path,
int ha_partition::create(const char *name, TABLE *table_arg,
HA_CREATE_INFO *create_info)
{
- char t_name[FN_REFLEN];
+ int error;
+ char name_buff[FN_REFLEN], name_lc_buff[FN_REFLEN];
+ char *name_buffer_ptr;
+ const char *path;
+ uint i;
+ List_iterator_fast <partition_element> part_it(m_part_info->partitions);
+ partition_element *part_elem;
+ handler **file, **abort_file;
DBUG_ENTER("ha_partition::create");
- if (create_info->used_fields & HA_CREATE_USED_CONNECTION)
+ DBUG_ASSERT(*fn_rext((char*)name) == '\0');
+
+ /* Not allowed to create temporary partitioned tables */
+ if (create_info && create_info->tmp_table())
{
- my_error(ER_CONNECT_TO_FOREIGN_DATA_SOURCE, MYF(0),
- "CONNECTION not valid for partition");
- DBUG_RETURN(1);
+ my_error(ER_PARTITION_NO_TEMPORARY, MYF(0));
+ DBUG_RETURN(TRUE);
}
- strmov(t_name, name);
- DBUG_ASSERT(*fn_rext((char*)name) == '\0');
- if (del_ren_cre_table(t_name, NULL, table_arg, create_info))
+ if (get_from_handler_file(name, ha_thd()->mem_root, false))
+ DBUG_RETURN(TRUE);
+ DBUG_ASSERT(m_file_buffer);
+ DBUG_PRINT("enter", ("name: (%s)", name));
+ name_buffer_ptr= m_name_buffer_ptr;
+ file= m_file;
+ /*
+ Since ha_partition has HA_FILE_BASED, it must alter underlying table names
+ if they do not have HA_FILE_BASED and lower_case_table_names == 2.
+ See Bug#37402, for Mac OS X.
+ The appended #P#<partname>[#SP#<subpartname>] will remain in current case.
+ Using the first partitions handler, since mixing handlers is not allowed.
+ */
+ path= get_canonical_filename(*file, name, name_lc_buff);
+ for (i= 0; i < m_part_info->num_parts; i++)
{
- handler::delete_table(t_name);
- DBUG_RETURN(1);
+ part_elem= part_it++;
+ if (m_is_sub_partitioned)
+ {
+ uint j;
+ List_iterator_fast <partition_element> sub_it(part_elem->subpartitions);
+ for (j= 0; j < m_part_info->num_subparts; j++)
+ {
+ part_elem= sub_it++;
+ create_partition_name(name_buff, path, name_buffer_ptr,
+ NORMAL_PART_NAME, FALSE);
+ if ((error= set_up_table_before_create(table_arg, name_buff,
+ create_info, part_elem)) ||
+ ((error= (*file)->ha_create(name_buff, table_arg, create_info))))
+ goto create_error;
+
+ name_buffer_ptr= strend(name_buffer_ptr) + 1;
+ file++;
+ }
+ }
+ else
+ {
+ create_partition_name(name_buff, path, name_buffer_ptr,
+ NORMAL_PART_NAME, FALSE);
+ if ((error= set_up_table_before_create(table_arg, name_buff,
+ create_info, part_elem)) ||
+ ((error= (*file)->ha_create(name_buff, table_arg, create_info))))
+ goto create_error;
+
+ name_buffer_ptr= strend(name_buffer_ptr) + 1;
+ file++;
+ }
}
DBUG_RETURN(0);
+
+create_error:
+ name_buffer_ptr= m_name_buffer_ptr;
+ for (abort_file= file, file= m_file; file < abort_file; file++)
+ {
+ create_partition_name(name_buff, path, name_buffer_ptr, NORMAL_PART_NAME,
+ FALSE);
+ (void) (*file)->ha_delete_table((const char*) name_buff);
+ name_buffer_ptr= strend(name_buffer_ptr) + 1;
+ }
+ handler::delete_table(name);
+ DBUG_RETURN(error);
}
@@ -998,7 +1139,8 @@ int ha_partition::repair(THD *thd, HA_CHECK_OPT *check_opt)
{
DBUG_ENTER("ha_partition::repair");
- DBUG_RETURN(handle_opt_partitions(thd, check_opt, REPAIR_PARTS));
+ int res= handle_opt_partitions(thd, check_opt, REPAIR_PARTS);
+ DBUG_RETURN(res);
}
/**
@@ -1104,7 +1246,7 @@ int ha_partition::handle_opt_part(THD *thd, HA_CHECK_OPT *check_opt,
/*
- print a message row formatted for ANALYZE/CHECK/OPTIMIZE/REPAIR TABLE
+ print a message row formatted for ANALYZE/CHECK/OPTIMIZE/REPAIR TABLE
(modelled after mi_check_print_msg)
TODO: move this into the handler, or rewrite mysql_admin_table.
*/
@@ -1122,7 +1264,7 @@ static bool print_admin_msg(THD* thd, uint len,
Protocol *protocol= thd->protocol;
uint length;
uint msg_length;
- char name[SAFE_NAME_LEN*2+2];
+ char name[NAME_LEN*2+2];
char *msgbuf;
bool error= true;
@@ -1203,7 +1345,7 @@ int ha_partition::handle_opt_partitions(THD *thd, HA_CHECK_OPT *check_opt,
when ALTER TABLE <CMD> PARTITION ...
it should only do named partitions, otherwise all partitions
*/
- if (!(thd->lex->alter_info.flags & ALTER_ADMIN_PARTITION) ||
+ if (!(thd->lex->alter_info.flags & Alter_info::ALTER_ADMIN_PARTITION) ||
part_elem->part_state == PART_ADMIN)
{
if (m_is_sub_partitioned)
@@ -1224,7 +1366,7 @@ int ha_partition::handle_opt_partitions(THD *thd, HA_CHECK_OPT *check_opt,
error != HA_ADMIN_ALREADY_DONE &&
error != HA_ADMIN_TRY_ALTER)
{
- print_admin_msg(thd, MI_MAX_MSG_BUF, "error",
+ print_admin_msg(thd, MYSQL_ERRMSG_SIZE, "error",
table_share->db.str, table->alias,
opt_op_name[flag],
"Subpartition %s returned error",
@@ -1251,9 +1393,9 @@ int ha_partition::handle_opt_partitions(THD *thd, HA_CHECK_OPT *check_opt,
error != HA_ADMIN_ALREADY_DONE &&
error != HA_ADMIN_TRY_ALTER)
{
- print_admin_msg(thd, MI_MAX_MSG_BUF, "error",
+ print_admin_msg(thd, MYSQL_ERRMSG_SIZE, "error",
table_share->db.str, table->alias,
- opt_op_name[flag], "Partition %s returned error",
+ opt_op_name[flag], "Partition %s returned error",
part_elem->partition_name);
}
/* reset part_state for the remaining partitions */
@@ -1279,6 +1421,8 @@ int ha_partition::handle_opt_partitions(THD *thd, HA_CHECK_OPT *check_opt,
@retval TRUE Error/Not supported
@retval FALSE Success
+
+ @note Called if open_table_from_share fails and ::is_crashed().
*/
bool ha_partition::check_and_repair(THD *thd)
@@ -1359,9 +1503,25 @@ int ha_partition::prepare_new_partition(TABLE *tbl,
int error;
DBUG_ENTER("prepare_new_partition");
- if ((error= set_up_table_before_create(tbl, part_name, create_info,
- 0, p_elem)))
+ /*
+ This call to set_up_table_before_create() is done for an alter table.
+ So this may be the second time around for this partition_element,
+ depending on how many partitions and subpartitions there were before,
+ and how many there are now.
+ The first time, on the CREATE, data_file_name and index_file_name
+ came from the parser. They did not have the file name attached to
+ the end. But if this partition is less than the total number of
+ previous partitions, it's data_file_name has the filename attached.
+ So we need to take the partition filename off if it exists.
+ That file name may be different from part_name, which will be
+ attached in append_file_to_dir().
+ */
+ truncate_partition_filename(p_elem->data_file_name);
+ truncate_partition_filename(p_elem->index_file_name);
+
+ if ((error= set_up_table_before_create(tbl, part_name, create_info, p_elem)))
goto error_create;
+
tbl->s->connect_string = p_elem->connect_string;
if ((error= file->ha_create(part_name, tbl, create_info)))
{
@@ -1377,7 +1537,8 @@ int ha_partition::prepare_new_partition(TABLE *tbl,
goto error_create;
}
DBUG_PRINT("info", ("partition %s created", part_name));
- if ((error= file->ha_open(tbl, part_name, m_mode, m_open_test_lock)))
+ if ((error= file->ha_open(tbl, part_name, m_mode,
+ m_open_test_lock | HA_OPEN_NO_PSI_CALL)))
goto error_open;
DBUG_PRINT("info", ("partition %s opened", part_name));
@@ -1542,7 +1703,7 @@ int ha_partition::change_partitions(HA_CREATE_INFO *create_info,
(m_reorged_parts + 1))))
{
mem_alloc_error(sizeof(handler*)*(m_reorged_parts+1));
- DBUG_RETURN(ER_OUTOFMEMORY);
+ DBUG_RETURN(HA_ERR_OUT_OF_MEM);
}
/*
@@ -1574,7 +1735,7 @@ int ha_partition::change_partitions(HA_CREATE_INFO *create_info,
(2*(num_remain_partitions + 1)))))
{
mem_alloc_error(sizeof(handler*)*2*(num_remain_partitions+1));
- DBUG_RETURN(ER_OUTOFMEMORY);
+ DBUG_RETURN(HA_ERR_OUT_OF_MEM);
}
m_added_file= &new_file_array[num_remain_partitions + 1];
@@ -1644,15 +1805,33 @@ int ha_partition::change_partitions(HA_CREATE_INFO *create_info,
part_elem->part_state == PART_TO_BE_ADDED)
{
uint j= 0;
+ Parts_share_refs *p_share_refs;
+ /*
+ The Handler_shares for each partition's handler can be allocated
+ within this handler, since there will not be any more instances of the
+ new partitions, until the table is reopened after the ALTER succeeded.
+ */
+ p_share_refs= new Parts_share_refs;
+ if (!p_share_refs)
+ DBUG_RETURN(HA_ERR_OUT_OF_MEM);
+ if (p_share_refs->init(num_subparts))
+ DBUG_RETURN(HA_ERR_OUT_OF_MEM);
+ if (m_new_partitions_share_refs.push_back(p_share_refs))
+ DBUG_RETURN(HA_ERR_OUT_OF_MEM);
do
{
- if (!(new_file_array[part_count++]=
+ handler **new_file= &new_file_array[part_count++];
+ if (!(*new_file=
get_new_handler(table->s,
thd->mem_root,
part_elem->engine_type)))
{
mem_alloc_error(sizeof(handler));
- DBUG_RETURN(ER_OUTOFMEMORY);
+ DBUG_RETURN(HA_ERR_OUT_OF_MEM);
+ }
+ if ((*new_file)->set_ha_share_ref(&p_share_refs->ha_shares[j]))
+ {
+ DBUG_RETURN(HA_ERR_OUT_OF_MEM);
}
} while (++j < num_subparts);
if (part_elem->part_state == PART_CHANGED)
@@ -1820,7 +1999,7 @@ int ha_partition::copy_partitions(ulonglong * const copied,
late_extra_cache(reorg_part);
if ((result= file->ha_rnd_init_with_error(1)))
- goto error;
+ goto init_error;
while (TRUE)
{
if ((result= file->ha_rnd_next(m_rec0)))
@@ -1865,10 +2044,10 @@ int ha_partition::copy_partitions(ulonglong * const copied,
DBUG_RETURN(FALSE);
error:
m_reorged_file[reorg_part]->ha_rnd_end();
+init_error:
DBUG_RETURN(result);
}
-
/*
Update create info as part of ALTER TABLE
@@ -1880,11 +2059,16 @@ error:
NONE
DESCRIPTION
- Method empty so far
+ Forward this handler call to the storage engine foreach
+ partition handler. The data_file_name for each partition may
+ need to be reset if the tablespace was moved. Use a dummy
+ HA_CREATE_INFO structure and transfer necessary data.
*/
void ha_partition::update_create_info(HA_CREATE_INFO *create_info)
{
+ DBUG_ENTER("ha_partition::update_create_info");
+
/*
Fix for bug#38751, some engines needs info-calls in ALTER.
Archive need this since it flushes in ::info.
@@ -1898,13 +2082,129 @@ void ha_partition::update_create_info(HA_CREATE_INFO *create_info)
if (!(create_info->used_fields & HA_CREATE_USED_AUTO))
create_info->auto_increment_value= stats.auto_increment_value;
+ /*
+ DATA DIRECTORY and INDEX DIRECTORY are never applied to the whole
+ partitioned table, only its parts.
+ */
+ my_bool from_alter = (create_info->data_file_name == (const char*) -1);
create_info->data_file_name= create_info->index_file_name = NULL;
- create_info->connect_string.str= NULL;
- create_info->connect_string.length= 0;
- return;
+
+ create_info->connect_string= null_lex_str;
+
+ /*
+ We do not need to update the individual partition DATA DIRECTORY settings
+ since they can be changed by ALTER TABLE ... REORGANIZE PARTITIONS.
+ */
+ if (from_alter)
+ DBUG_VOID_RETURN;
+
+ /*
+ send Handler::update_create_info() to the storage engine for each
+ partition that currently has a handler object. Using a dummy
+ HA_CREATE_INFO structure to collect DATA and INDEX DIRECTORYs.
+ */
+
+ List_iterator<partition_element> part_it(m_part_info->partitions);
+ partition_element *part_elem, *sub_elem;
+ uint num_subparts= m_part_info->num_subparts;
+ uint num_parts = num_subparts ? m_file_tot_parts / num_subparts
+ : m_file_tot_parts;
+ HA_CREATE_INFO dummy_info;
+ memset(&dummy_info, 0, sizeof(dummy_info));
+
+ /*
+ Since update_create_info() can be called from mysql_prepare_alter_table()
+ when not all handlers are set up, we look for that condition first.
+ If all handlers are not available, do not call update_create_info for any.
+ */
+ uint i, j, part;
+ for (i= 0; i < num_parts; i++)
+ {
+ part_elem= part_it++;
+ if (!part_elem)
+ DBUG_VOID_RETURN;
+ if (m_is_sub_partitioned)
+ {
+ List_iterator<partition_element> subpart_it(part_elem->subpartitions);
+ for (j= 0; j < num_subparts; j++)
+ {
+ sub_elem= subpart_it++;
+ if (!sub_elem)
+ DBUG_VOID_RETURN;
+ part= i * num_subparts + j;
+ if (part >= m_file_tot_parts || !m_file[part])
+ DBUG_VOID_RETURN;
+ }
+ }
+ else
+ {
+ if (!m_file[i])
+ DBUG_VOID_RETURN;
+ }
+ }
+ part_it.rewind();
+
+ for (i= 0; i < num_parts; i++)
+ {
+ part_elem= part_it++;
+ DBUG_ASSERT(part_elem);
+ if (m_is_sub_partitioned)
+ {
+ List_iterator<partition_element> subpart_it(part_elem->subpartitions);
+ for (j= 0; j < num_subparts; j++)
+ {
+ sub_elem= subpart_it++;
+ DBUG_ASSERT(sub_elem);
+ part= i * num_subparts + j;
+ DBUG_ASSERT(part < m_file_tot_parts && m_file[part]);
+ if (ha_legacy_type(m_file[part]->ht) == DB_TYPE_INNODB)
+ {
+ dummy_info.data_file_name= dummy_info.index_file_name = NULL;
+ m_file[part]->update_create_info(&dummy_info);
+
+ if (dummy_info.data_file_name || sub_elem->data_file_name)
+ {
+ sub_elem->data_file_name = (char*) dummy_info.data_file_name;
+ }
+ if (dummy_info.index_file_name || sub_elem->index_file_name)
+ {
+ sub_elem->index_file_name = (char*) dummy_info.index_file_name;
+ }
+ }
+ }
+ }
+ else
+ {
+ DBUG_ASSERT(m_file[i]);
+ if (ha_legacy_type(m_file[i]->ht) == DB_TYPE_INNODB)
+ {
+ dummy_info.data_file_name= dummy_info.index_file_name= NULL;
+ m_file[i]->update_create_info(&dummy_info);
+ if (dummy_info.data_file_name || part_elem->data_file_name)
+ {
+ part_elem->data_file_name = (char*) dummy_info.data_file_name;
+ }
+ if (dummy_info.index_file_name || part_elem->index_file_name)
+ {
+ part_elem->index_file_name = (char*) dummy_info.index_file_name;
+ }
+ }
+ }
+ }
+ DBUG_VOID_RETURN;
}
+/**
+ Change the internal TABLE_SHARE pointer
+
+ @param table_arg TABLE object
+ @param share New share to use
+
+ @note Is used in error handling in ha_delete_table.
+ All handlers should exist (lock_partitions should not be used)
+*/
+
void ha_partition::change_table_ptr(TABLE *table_arg, TABLE_SHARE *share)
{
handler **file_array;
@@ -1955,72 +2255,52 @@ char *ha_partition::update_table_comment(const char *comment)
}
+/**
+ Handle delete and rename table
-/*
- Handle delete, rename and create table
-
- SYNOPSIS
- del_ren_cre_table()
- from Full path of old table
- to Full path of new table
- table_arg Table object
- create_info Create info
+ @param from Full path of old table
+ @param to Full path of new table
- RETURN VALUE
- >0 Error
- 0 Success
+ @return Operation status
+ @retval >0 Error
+ @retval 0 Success
- DESCRIPTION
- Common routine to handle delete_table and rename_table.
- The routine uses the partition handler file to get the
- names of the partition instances. Both these routines
- are called after creating the handler without table
- object and thus the file is needed to discover the
- names of the partitions and the underlying storage engines.
+ @note Common routine to handle delete_table and rename_table.
+ The routine uses the partition handler file to get the
+ names of the partition instances. Both these routines
+ are called after creating the handler without table
+ object and thus the file is needed to discover the
+ names of the partitions and the underlying storage engines.
*/
-int ha_partition::del_ren_cre_table(const char *from,
- const char *to,
- TABLE *table_arg,
- HA_CREATE_INFO *create_info)
+uint ha_partition::del_ren_table(const char *from, const char *to)
{
int save_error= 0;
- int error= HA_ERR_INTERNAL_ERROR;
+ int error;
char from_buff[FN_REFLEN], to_buff[FN_REFLEN], from_lc_buff[FN_REFLEN],
- to_lc_buff[FN_REFLEN], buff[FN_REFLEN];
+ to_lc_buff[FN_REFLEN];
char *name_buffer_ptr;
const char *from_path;
const char *to_path= NULL;
uint i;
handler **file, **abort_file;
- DBUG_ENTER("del_ren_cre_table()");
-
- /* Not allowed to create temporary partitioned tables */
- if (create_info && create_info->options & HA_LEX_CREATE_TMP_TABLE)
- {
- my_error(ER_PARTITION_NO_TEMPORARY, MYF(0));
- DBUG_RETURN(error);
- }
-
- fn_format(buff,from, "", ha_par_ext, MY_APPEND_EXT);
- /* Check if the par file exists */
- if (my_access(buff,F_OK))
- {
- /*
- If the .par file does not exist, return HA_ERR_NO_SUCH_TABLE,
- This will signal to the caller that it can remove the .frm
- file.
- */
- error= HA_ERR_NO_SUCH_TABLE;
- DBUG_RETURN(error);
- }
+ DBUG_ENTER("ha_partition::del_ren_table");
if (get_from_handler_file(from, ha_thd()->mem_root, false))
- DBUG_RETURN(error);
+ DBUG_RETURN(TRUE);
DBUG_ASSERT(m_file_buffer);
DBUG_PRINT("enter", ("from: (%s) to: (%s)", from, to ? to : "(nil)"));
name_buffer_ptr= m_name_buffer_ptr;
file= m_file;
+ if (to == NULL)
+ {
+ /*
+ Delete table, start by delete the .par file. If error, break, otherwise
+ delete as much as possible.
+ */
+ if ((error= handler::delete_table(from)))
+ DBUG_RETURN(error);
+ }
/*
Since ha_partition has HA_FILE_BASED, it must alter underlying table names
if they do not have HA_FILE_BASED and lower_case_table_names == 2.
@@ -2038,41 +2318,22 @@ int ha_partition::del_ren_cre_table(const char *from,
NORMAL_PART_NAME, FALSE);
if (to != NULL)
- { // Rename branch
+ { // Rename branch
create_partition_name(to_buff, to_path, name_buffer_ptr,
NORMAL_PART_NAME, FALSE);
error= (*file)->ha_rename_table(from_buff, to_buff);
if (error)
goto rename_error;
}
- else if (table_arg == NULL) // delete branch
- error= (*file)->ha_delete_table(from_buff);
- else
+ else // delete branch
{
- if ((error= set_up_table_before_create(table_arg, from_buff,
- create_info, i, NULL)) ||
- parse_engine_table_options(ha_thd(), (*file)->ht,
- (*file)->table_share) ||
- ((error= (*file)->ha_create(from_buff, table_arg, create_info))))
- goto create_error;
+ error= (*file)->ha_delete_table(from_buff);
}
name_buffer_ptr= strend(name_buffer_ptr) + 1;
if (error)
save_error= error;
i++;
} while (*(++file));
-
- if (to == NULL && table_arg == NULL)
- {
- DBUG_EXECUTE_IF("crash_before_deleting_par_file", DBUG_SUICIDE(););
-
- /* Delete the .par file. If error, break.*/
- if ((error= handler::delete_table(from)))
- DBUG_RETURN(error);
-
- DBUG_EXECUTE_IF("crash_after_deleting_par_file", DBUG_SUICIDE(););
- }
-
if (to != NULL)
{
if ((error= handler::rename_table(from, to)))
@@ -2083,16 +2344,6 @@ int ha_partition::del_ren_cre_table(const char *from,
}
}
DBUG_RETURN(save_error);
-create_error:
- name_buffer_ptr= m_name_buffer_ptr;
- for (abort_file= file, file= m_file; file < abort_file; file++)
- {
- create_partition_name(from_buff, from_path, name_buffer_ptr, NORMAL_PART_NAME,
- FALSE);
- (void) (*file)->ha_delete_table((const char*) from_buff);
- name_buffer_ptr= strend(name_buffer_ptr) + 1;
- }
- DBUG_RETURN(error);
rename_error:
name_buffer_ptr= m_name_buffer_ptr;
for (abort_file= file, file= m_file; file < abort_file; file++)
@@ -2109,47 +2360,6 @@ rename_error:
DBUG_RETURN(error);
}
-/*
- Find partition based on partition id
-
- SYNOPSIS
- find_partition_element()
- part_id Partition id of partition looked for
-
- RETURN VALUE
- >0 Reference to partition_element
- 0 Partition not found
-*/
-
-partition_element *ha_partition::find_partition_element(uint part_id)
-{
- uint i;
- uint curr_part_id= 0;
- List_iterator_fast <partition_element> part_it(m_part_info->partitions);
-
- for (i= 0; i < m_part_info->num_parts; i++)
- {
- partition_element *part_elem;
- part_elem= part_it++;
- if (m_is_sub_partitioned)
- {
- uint j;
- List_iterator_fast <partition_element> sub_it(part_elem->subpartitions);
- for (j= 0; j < m_part_info->num_subparts; j++)
- {
- part_elem= sub_it++;
- if (part_id == curr_part_id++)
- return part_elem;
- }
- }
- else if (part_id == curr_part_id++)
- return part_elem;
- }
- DBUG_ASSERT(0);
- my_error(ER_OUT_OF_RESOURCES, MYF(ME_FATALERROR));
- return NULL;
-}
-
uint ha_partition::count_query_cache_dependant_tables(uint8 *tables_type)
{
DBUG_ENTER("ha_partition::count_query_cache_dependant_tables");
@@ -2166,26 +2376,27 @@ uint ha_partition::count_query_cache_dependant_tables(uint8 *tables_type)
DBUG_RETURN(type == HA_CACHE_TBL_ASKTRANSACT ? m_tot_parts : 0);
}
-my_bool ha_partition::reg_query_cache_dependant_table(THD *thd,
- char *key, uint key_len,
- uint8 type,
- Query_cache *cache,
- Query_cache_block_table **block_table,
- handler *file,
- uint *n)
+my_bool ha_partition::
+reg_query_cache_dependant_table(THD *thd,
+ char *engine_key, uint engine_key_len,
+ char *cache_key, uint cache_key_len,
+ uint8 type,
+ Query_cache *cache,
+ Query_cache_block_table **block_table,
+ handler *file,
+ uint *n)
{
DBUG_ENTER("ha_partition::reg_query_cache_dependant_table");
qc_engine_callback engine_callback;
ulonglong engine_data;
/* ask undelying engine */
- if (!file->register_query_cache_table(thd, key,
- key_len,
+ if (!file->register_query_cache_table(thd, engine_key,
+ engine_key_len,
&engine_callback,
&engine_data))
{
- DBUG_PRINT("qcache", ("Handler does not allow caching for %s.%s",
- key,
- key + table_share->db.length + 1));
+ DBUG_PRINT("qcache", ("Handler does not allow caching for %.*s",
+ engine_key_len, engine_key));
/*
As this can change from call to call, don't reset set
thd->lex->safe_to_cache_query
@@ -2194,9 +2405,11 @@ my_bool ha_partition::reg_query_cache_dependant_table(THD *thd,
DBUG_RETURN(TRUE);
}
(++(*block_table))->n= ++(*n);
- if (!cache->insert_table(key_len,
- key, (*block_table),
+ if (!cache->insert_table(cache_key_len,
+ cache_key, (*block_table),
table_share->db.length,
+ (uint8) (cache_key_len -
+ table_share->table_cache_key.length),
type,
engine_callback, engine_data,
FALSE))
@@ -2205,19 +2418,19 @@ my_bool ha_partition::reg_query_cache_dependant_table(THD *thd,
}
-my_bool ha_partition::register_query_cache_dependant_tables(THD *thd,
- Query_cache *cache,
- Query_cache_block_table **block_table,
- uint *n)
+my_bool ha_partition::
+register_query_cache_dependant_tables(THD *thd,
+ Query_cache *cache,
+ Query_cache_block_table **block_table,
+ uint *n)
{
- char *name;
- uint prefix_length= table_share->table_cache_key.length + 3;
+ char *engine_key_end, *query_cache_key_end;
+ uint i;
uint num_parts= m_part_info->num_parts;
uint num_subparts= m_part_info->num_subparts;
- uint i= 0;
+ int diff_length;
List_iterator<partition_element> part_it(m_part_info->partitions);
- char key[FN_REFLEN];
-
+ char engine_key[FN_REFLEN], query_cache_key[FN_REFLEN];
DBUG_ENTER("ha_partition::register_query_cache_dependant_tables");
/* see ha_partition::count_query_cache_dependant_tables */
@@ -2225,36 +2438,51 @@ my_bool ha_partition::register_query_cache_dependant_tables(THD *thd,
DBUG_RETURN(FALSE); // nothing to register
/* prepare static part of the key */
- memmove(key, table_share->table_cache_key.str,
- table_share->table_cache_key.length);
+ memcpy(engine_key, table_share->normalized_path.str,
+ table_share->normalized_path.length);
+ memcpy(query_cache_key, table_share->table_cache_key.str,
+ table_share->table_cache_key.length);
+
+ diff_length= ((int) table_share->table_cache_key.length -
+ (int) table_share->normalized_path.length -1);
- name= key + table_share->table_cache_key.length - 1;
- name[0]= name[2]= '#';
- name[1]= 'P';
- name+= 3;
+ engine_key_end= engine_key + table_share->normalized_path.length;
+ query_cache_key_end= query_cache_key + table_share->table_cache_key.length -1;
+ engine_key_end[0]= engine_key_end[2]= query_cache_key_end[0]=
+ query_cache_key_end[2]= '#';
+ query_cache_key_end[1]= engine_key_end[1]= 'P';
+ engine_key_end+= 3;
+ query_cache_key_end+= 3;
+
+ i= 0;
do
{
partition_element *part_elem= part_it++;
- uint part_len= strmov(name, part_elem->partition_name) - name;
+ char *engine_pos= strmov(engine_key_end, part_elem->partition_name);
if (m_is_sub_partitioned)
{
List_iterator<partition_element> subpart_it(part_elem->subpartitions);
partition_element *sub_elem;
- char *sname= name + part_len;
uint j= 0, part;
- sname[0]= sname[3]= '#';
- sname[1]= 'S';
- sname[2]= 'P';
- sname += 4;
+ engine_pos[0]= engine_pos[3]= '#';
+ engine_pos[1]= 'S';
+ engine_pos[2]= 'P';
+ engine_pos += 4;
do
{
+ char *end;
+ uint length;
sub_elem= subpart_it++;
part= i * num_subparts + j;
- uint spart_len= strmov(sname, sub_elem->partition_name) - name + 1;
- if (reg_query_cache_dependant_table(thd, key,
- prefix_length + part_len + 4 +
- spart_len,
+ /* we store the end \0 as part of the key */
+ end= strmov(engine_pos, sub_elem->partition_name);
+ length= end - engine_key;
+ /* Copy the suffix also to query cache key */
+ memcpy(query_cache_key_end, engine_key_end, (end - engine_key_end));
+ if (reg_query_cache_dependant_table(thd, engine_key, length,
+ query_cache_key,
+ length + diff_length,
m_file[part]->table_cache_type(),
cache,
block_table, m_file[part],
@@ -2264,8 +2492,13 @@ my_bool ha_partition::register_query_cache_dependant_tables(THD *thd,
}
else
{
- if (reg_query_cache_dependant_table(thd, key,
- prefix_length + part_len + 1,
+ char *end= engine_pos+1; // copy end \0
+ uint length= end - engine_key;
+ /* Copy the suffix also to query cache key */
+ memcpy(query_cache_key_end, engine_key_end, (end - engine_key_end));
+ if (reg_query_cache_dependant_table(thd, engine_key, length,
+ query_cache_key,
+ length + diff_length,
m_file[i]->table_cache_type(),
cache,
block_table, m_file[i],
@@ -2278,31 +2511,28 @@ my_bool ha_partition::register_query_cache_dependant_tables(THD *thd,
}
-/*
- Set up table share object before calling create on underlying handler
-
- SYNOPSIS
- set_up_table_before_create()
- table Table object
- info Create info
- part_id Partition id of partition to set-up
+/**
+ Set up table share object before calling create on underlying handler
- RETURN VALUE
- TRUE Error
- FALSE Success
+ @param table Table object
+ @param info Create info
+ @param part_elem[in,out] Pointer to used partition_element, searched if NULL
- DESCRIPTION
- Set up
- 1) Comment on partition
- 2) MAX_ROWS, MIN_ROWS on partition
- 3) Index file name on partition
- 4) Data file name on partition
+ @return status
+ @retval TRUE Error
+ @retval FALSE Success
+
+ @details
+ Set up
+ 1) Comment on partition
+ 2) MAX_ROWS, MIN_ROWS on partition
+ 3) Index file name on partition
+ 4) Data file name on partition
*/
int ha_partition::set_up_table_before_create(TABLE *tbl,
const char *partition_name_with_path,
HA_CREATE_INFO *info,
- uint part_id,
partition_element *part_elem)
{
int error= 0;
@@ -2310,12 +2540,10 @@ int ha_partition::set_up_table_before_create(TABLE *tbl,
THD *thd= ha_thd();
DBUG_ENTER("set_up_table_before_create");
+ DBUG_ASSERT(part_elem);
+
if (!part_elem)
- {
- part_elem= find_partition_element(part_id);
- if (!part_elem)
- DBUG_RETURN(1); // Fatal error
- }
+ DBUG_RETURN(1);
tbl->s->max_rows= part_elem->part_max_rows;
tbl->s->min_rows= part_elem->part_min_rows;
partition_name= strrchr(partition_name_with_path, FN_LIBCHAR);
@@ -2450,10 +2678,8 @@ bool ha_partition::create_handler_file(const char *name)
/* 4 static words (tot words, checksum, tot partitions, name length) */
tot_len_words= 4 + tot_partition_words + tot_name_words;
tot_len_byte= PAR_WORD_SIZE * tot_len_words;
- file_buffer= (uchar *) my_alloca(tot_len_byte);
- if (!file_buffer)
+ if (!(file_buffer= (uchar *) my_malloc(tot_len_byte, MYF(MY_ZEROFILL))))
DBUG_RETURN(TRUE);
- bzero(file_buffer, tot_len_byte);
engine_array= (file_buffer + PAR_ENGINES_OFFSET);
name_buffer_ptr= (char*) (engine_array + tot_partition_words * PAR_WORD_SIZE
+ PAR_WORD_SIZE);
@@ -2534,7 +2760,7 @@ bool ha_partition::create_handler_file(const char *name)
}
else
result= TRUE;
- my_afree((char*) file_buffer);
+ my_free(file_buffer);
DBUG_RETURN(result);
}
@@ -2578,8 +2804,7 @@ bool ha_partition::create_handlers(MEM_ROOT *mem_root)
for (i= 0; i < m_tot_parts; i++)
{
handlerton *hton= plugin_data(m_engine_array[i], handlerton*);
- if (!(m_file[i]= get_new_handler(table_share, mem_root,
- hton)))
+ if (!(m_file[i]= get_new_handler(table_share, mem_root, hton)))
DBUG_RETURN(TRUE);
DBUG_PRINT("info", ("engine_type: %u", hton->db_type));
}
@@ -2686,9 +2911,10 @@ error_end:
bool ha_partition::read_par_file(const char *name)
{
- char buff[FN_REFLEN], *tot_name_len_offset;
+ char buff[FN_REFLEN];
+ uchar *tot_name_len_offset;
File file;
- char *file_buffer;
+ uchar *file_buffer;
uint i, len_bytes, len_words, tot_partition_words, tot_name_words, chksum;
DBUG_ENTER("ha_partition::read_par_file");
DBUG_PRINT("enter", ("table name: '%s'", name));
@@ -2707,9 +2933,9 @@ bool ha_partition::read_par_file(const char *name)
len_bytes= PAR_WORD_SIZE * len_words;
if (mysql_file_seek(file, 0, MY_SEEK_SET, MYF(0)) == MY_FILEPOS_ERROR)
goto err1;
- if (!(file_buffer= (char*) alloc_root(&m_mem_root, len_bytes)))
+ if (!(file_buffer= (uchar*) alloc_root(&m_mem_root, len_bytes)))
goto err1;
- if (mysql_file_read(file, (uchar *) file_buffer, len_bytes, MYF(MY_NABP)))
+ if (mysql_file_read(file, file_buffer, len_bytes, MYF(MY_NABP)))
goto err2;
chksum= 0;
@@ -2732,7 +2958,7 @@ bool ha_partition::read_par_file(const char *name)
if (len_words != (tot_partition_words + tot_name_words + 4))
goto err2;
m_file_buffer= file_buffer; // Will be freed in clear_handler_file()
- m_name_buffer_ptr= tot_name_len_offset + PAR_WORD_SIZE;
+ m_name_buffer_ptr= (char*) (tot_name_len_offset + PAR_WORD_SIZE);
if (!(m_connect_string= (LEX_STRING*)
alloc_root(&m_mem_root, m_tot_parts * sizeof(LEX_STRING))))
@@ -2782,7 +3008,8 @@ bool ha_partition::setup_engine_array(MEM_ROOT *mem_root)
{
uint i;
uchar *buff;
- handlerton **engine_array;
+ handlerton **engine_array, *first_engine;
+ enum legacy_db_type db_type, first_db_type;
DBUG_ASSERT(!m_file);
DBUG_ENTER("ha_partition::setup_engine_array");
@@ -2791,20 +3018,34 @@ bool ha_partition::setup_engine_array(MEM_ROOT *mem_root)
DBUG_RETURN(true);
buff= (uchar *) (m_file_buffer + PAR_ENGINES_OFFSET);
- for (i= 0; i < m_tot_parts; i++)
- {
- engine_array[i]= ha_resolve_by_legacy_type(ha_thd(),
- (enum legacy_db_type)
- *(buff + i));
- if (!engine_array[i])
- goto err;
- }
+ first_db_type= (enum legacy_db_type) buff[0];
+ first_engine= ha_resolve_by_legacy_type(ha_thd(), first_db_type);
+ if (!first_engine)
+ goto err;
+
if (!(m_engine_array= (plugin_ref*)
alloc_root(&m_mem_root, m_tot_parts * sizeof(plugin_ref))))
goto err;
for (i= 0; i < m_tot_parts; i++)
- m_engine_array[i]= ha_lock_engine(NULL, engine_array[i]);
+ {
+ db_type= (enum legacy_db_type) buff[i];
+ if (db_type != first_db_type)
+ {
+ DBUG_PRINT("error", ("partition %u engine %d is not same as "
+ "first partition %d", i, db_type,
+ (int) first_db_type));
+ DBUG_ASSERT(0);
+ clear_handler_file();
+ goto err;
+ }
+ m_engine_array[i]= ha_lock_engine(NULL, first_engine);
+ if (!m_engine_array[i])
+ {
+ clear_handler_file();
+ goto err;
+ }
+ }
my_afree(engine_array);
@@ -2860,19 +3101,298 @@ bool ha_partition::get_from_handler_file(const char *name, MEM_ROOT *mem_root,
MODULE open/close object
****************************************************************************/
+/**
+ Get the partition name.
+
+ @param part Struct containing name and length
+ @param[out] length Length of the name
+
+ @return Partition name
+*/
+
+static uchar *get_part_name(PART_NAME_DEF *part, size_t *length,
+ my_bool not_used __attribute__((unused)))
+{
+ *length= part->length;
+ return part->partition_name;
+}
+
+
+/**
+ Insert a partition name in the partition_name_hash.
+
+ @param name Name of partition
+ @param part_id Partition id (number)
+ @param is_subpart Set if the name belongs to a subpartition
+
+ @return Operation status
+ @retval true Failure
+ @retval false Sucess
+*/
+
+bool ha_partition::insert_partition_name_in_hash(const char *name, uint part_id,
+ bool is_subpart)
+{
+ PART_NAME_DEF *part_def;
+ uchar *part_name;
+ uint part_name_length;
+ DBUG_ENTER("ha_partition::insert_partition_name_in_hash");
+ /*
+ Calculate and store the length here, to avoid doing it when
+ searching the hash.
+ */
+ part_name_length= strlen(name);
+ /*
+ Must use memory that lives as long as table_share.
+ Freed in the Partition_share destructor.
+ Since we use my_multi_malloc, then my_free(part_def) will also free
+ part_name, as a part of my_hash_free.
+ */
+ if (!my_multi_malloc(MY_WME,
+ &part_def, sizeof(PART_NAME_DEF),
+ &part_name, part_name_length + 1,
+ NULL))
+ DBUG_RETURN(true);
+ memcpy(part_name, name, part_name_length + 1);
+ part_def->partition_name= part_name;
+ part_def->length= part_name_length;
+ part_def->part_id= part_id;
+ part_def->is_subpart= is_subpart;
+ if (my_hash_insert(&part_share->partition_name_hash, (uchar *) part_def))
+ {
+ my_free(part_def);
+ DBUG_RETURN(true);
+ }
+ DBUG_RETURN(false);
+}
+
+
+/**
+ Populate the partition_name_hash in part_share.
+*/
+
+bool ha_partition::populate_partition_name_hash()
+{
+ List_iterator<partition_element> part_it(m_part_info->partitions);
+ uint num_parts= m_part_info->num_parts;
+ uint num_subparts= m_is_sub_partitioned ? m_part_info->num_subparts : 1;
+ uint tot_names;
+ uint i= 0;
+ DBUG_ASSERT(part_share);
+
+ DBUG_ENTER("ha_partition::populate_partition_name_hash");
+
+ /*
+ partition_name_hash is only set once and never changed
+ -> OK to check without locking.
+ */
+
+ if (part_share->partition_name_hash_initialized)
+ DBUG_RETURN(false);
+ lock_shared_ha_data();
+ if (part_share->partition_name_hash_initialized)
+ {
+ unlock_shared_ha_data();
+ DBUG_RETURN(false);
+ }
+ tot_names= m_is_sub_partitioned ? m_tot_parts + num_parts : num_parts;
+ if (my_hash_init(&part_share->partition_name_hash,
+ system_charset_info, tot_names, 0, 0,
+ (my_hash_get_key) get_part_name,
+ my_free, HASH_UNIQUE))
+ {
+ unlock_shared_ha_data();
+ DBUG_RETURN(TRUE);
+ }
+
+ do
+ {
+ partition_element *part_elem= part_it++;
+ DBUG_ASSERT(part_elem->part_state == PART_NORMAL);
+ if (part_elem->part_state == PART_NORMAL)
+ {
+ if (insert_partition_name_in_hash(part_elem->partition_name,
+ i * num_subparts, false))
+ goto err;
+ if (m_is_sub_partitioned)
+ {
+ List_iterator<partition_element>
+ subpart_it(part_elem->subpartitions);
+ partition_element *sub_elem;
+ uint j= 0;
+ do
+ {
+ sub_elem= subpart_it++;
+ if (insert_partition_name_in_hash(sub_elem->partition_name,
+ i * num_subparts + j, true))
+ goto err;
+
+ } while (++j < num_subparts);
+ }
+ }
+ } while (++i < num_parts);
+
+ part_share->partition_name_hash_initialized= true;
+ unlock_shared_ha_data();
+
+ DBUG_RETURN(FALSE);
+err:
+ my_hash_free(&part_share->partition_name_hash);
+ unlock_shared_ha_data();
+
+ DBUG_RETURN(TRUE);
+}
+
+
+/**
+ Set Handler_share pointer and allocate Handler_share pointers
+ for each partition and set those.
+
+ @param ha_share_arg Where to store/retrieve the Partitioning_share pointer
+ to be shared by all instances of the same table.
+
+ @return Operation status
+ @retval true Failure
+ @retval false Sucess
+*/
+
+bool ha_partition::set_ha_share_ref(Handler_share **ha_share_arg)
+{
+ Handler_share **ha_shares;
+ uint i;
+ DBUG_ENTER("ha_partition::set_ha_share_ref");
+
+ DBUG_ASSERT(!part_share);
+ DBUG_ASSERT(table_share);
+ DBUG_ASSERT(!m_is_clone_of);
+ DBUG_ASSERT(m_tot_parts);
+ if (handler::set_ha_share_ref(ha_share_arg))
+ DBUG_RETURN(true);
+ if (!(part_share= get_share()))
+ DBUG_RETURN(true);
+ DBUG_ASSERT(part_share->partitions_share_refs);
+ DBUG_ASSERT(part_share->partitions_share_refs->num_parts >= m_tot_parts);
+ ha_shares= part_share->partitions_share_refs->ha_shares;
+ for (i= 0; i < m_tot_parts; i++)
+ {
+ if (m_file[i]->set_ha_share_ref(&ha_shares[i]))
+ DBUG_RETURN(true);
+ }
+ DBUG_RETURN(false);
+}
+
+
+/**
+ Get the PARTITION_SHARE for the table.
+
+ @return Operation status
+ @retval true Error
+ @retval false Success
+
+ @note Gets or initializes the Partition_share object used by partitioning.
+ The Partition_share is used for handling the auto_increment etc.
+*/
+
+Partition_share *ha_partition::get_share()
+{
+ Partition_share *tmp_share;
+ DBUG_ENTER("ha_partition::get_share");
+ DBUG_ASSERT(table_share);
+
+ lock_shared_ha_data();
+ if (!(tmp_share= static_cast<Partition_share*>(get_ha_share_ptr())))
+ {
+ tmp_share= new Partition_share;
+ if (!tmp_share)
+ goto err;
+ if (tmp_share->init(m_tot_parts))
+ {
+ delete tmp_share;
+ tmp_share= NULL;
+ goto err;
+ }
+ set_ha_share_ptr(static_cast<Handler_share*>(tmp_share));
+ }
+err:
+ unlock_shared_ha_data();
+ DBUG_RETURN(tmp_share);
+}
+
+
/**
- A destructor for partition-specific TABLE_SHARE data.
+ Helper function for freeing all internal bitmaps.
*/
-void ha_data_partition_destroy(HA_DATA_PARTITION* ha_part_data)
+void ha_partition::free_partition_bitmaps()
{
- if (ha_part_data)
+ /* Initialize the bitmap we use to minimize ha_start_bulk_insert calls */
+ my_bitmap_free(&m_bulk_insert_started);
+ my_bitmap_free(&m_locked_partitions);
+ my_bitmap_free(&m_partitions_to_reset);
+ my_bitmap_free(&m_key_not_found_partitions);
+}
+
+
+/**
+ Helper function for initializing all internal bitmaps.
+*/
+
+bool ha_partition::init_partition_bitmaps()
+{
+ DBUG_ENTER("ha_partition::init_partition_bitmaps");
+ /* Initialize the bitmap we use to minimize ha_start_bulk_insert calls */
+ if (my_bitmap_init(&m_bulk_insert_started, NULL, m_tot_parts + 1, FALSE))
+ DBUG_RETURN(true);
+ bitmap_clear_all(&m_bulk_insert_started);
+
+ /* Initialize the bitmap we use to keep track of locked partitions */
+ if (my_bitmap_init(&m_locked_partitions, NULL, m_tot_parts, FALSE))
+ {
+ my_bitmap_free(&m_bulk_insert_started);
+ DBUG_RETURN(true);
+ }
+ bitmap_clear_all(&m_locked_partitions);
+
+ /*
+ Initialize the bitmap we use to keep track of partitions which may have
+ something to reset in ha_reset().
+ */
+ if (my_bitmap_init(&m_partitions_to_reset, NULL, m_tot_parts, FALSE))
+ {
+ my_bitmap_free(&m_bulk_insert_started);
+ my_bitmap_free(&m_locked_partitions);
+ DBUG_RETURN(true);
+ }
+ bitmap_clear_all(&m_partitions_to_reset);
+
+ /*
+ Initialize the bitmap we use to keep track of partitions which returned
+ HA_ERR_KEY_NOT_FOUND from index_read_map.
+ */
+ if (my_bitmap_init(&m_key_not_found_partitions, NULL, m_tot_parts, FALSE))
+ {
+ my_bitmap_free(&m_bulk_insert_started);
+ my_bitmap_free(&m_locked_partitions);
+ my_bitmap_free(&m_partitions_to_reset);
+ DBUG_RETURN(true);
+ }
+ bitmap_clear_all(&m_key_not_found_partitions);
+ m_key_not_found= false;
+ /* Initialize the bitmap for read/lock_partitions */
+ if (!m_is_clone_of)
{
- mysql_mutex_destroy(&ha_part_data->LOCK_auto_inc);
+ DBUG_ASSERT(!m_clone_mem_root);
+ if (m_part_info->set_partition_bitmaps(NULL))
+ {
+ free_partition_bitmaps();
+ DBUG_RETURN(true);
+ }
}
+ DBUG_RETURN(false);
}
+
/*
Open handler object
@@ -2902,7 +3422,6 @@ int ha_partition::open(const char *name, int mode, uint test_if_locked)
int error= HA_ERR_INITIALIZATION;
handler **file;
char name_buff[FN_REFLEN];
- bool is_not_tmp_table= (table_share->tmp_table == NO_TMP_TABLE);
ulonglong check_table_flags;
DBUG_ENTER("ha_partition::open");
@@ -2911,9 +3430,13 @@ int ha_partition::open(const char *name, int mode, uint test_if_locked)
m_mode= mode;
m_open_test_lock= test_if_locked;
m_part_field_array= m_part_info->full_part_field_array;
- if (get_from_handler_file(name, &table->mem_root, test(m_is_clone_of)))
+ if (get_from_handler_file(name, &table->mem_root, MY_TEST(m_is_clone_of)))
DBUG_RETURN(error);
name_buffer_ptr= m_name_buffer_ptr;
+ if (populate_partition_name_hash())
+ {
+ DBUG_RETURN(HA_ERR_INITIALIZATION);
+ }
m_start_key.length= 0;
m_rec0= table->record[0];
m_rec_length= table_share->stored_rec_length;
@@ -2928,32 +3451,10 @@ int ha_partition::open(const char *name, int mode, uint test_if_locked)
m_part_ids_sorted_by_num_of_records[i]= i;
}
- /* Initialize the bitmap we use to minimize ha_start_bulk_insert calls */
- if (bitmap_init(&m_bulk_insert_started, NULL, m_tot_parts + 1, FALSE))
+ if (init_partition_bitmaps())
DBUG_RETURN(error);
- bitmap_clear_all(&m_bulk_insert_started);
- /*
- Initialize the bitmap we use to keep track of partitions which returned
- HA_ERR_KEY_NOT_FOUND from index_read_map.
- */
- if (bitmap_init(&m_key_not_found_partitions, NULL, m_tot_parts, FALSE))
- {
- bitmap_free(&m_bulk_insert_started);
- DBUG_RETURN(error);
- }
- bitmap_clear_all(&m_key_not_found_partitions);
- m_key_not_found= false;
- /* Initialize the bitmap we use to determine what partitions are used */
- if (!m_is_clone_of)
- {
- DBUG_ASSERT(!m_clone_mem_root);
- if (bitmap_init(&(m_part_info->used_partitions), NULL, m_tot_parts, TRUE))
- {
- bitmap_free(&m_bulk_insert_started);
- DBUG_RETURN(error);
- }
- bitmap_set_all(&(m_part_info->used_partitions));
- }
+
+ DBUG_ASSERT(m_part_info);
if (m_is_clone_of)
{
@@ -2962,7 +3463,10 @@ int ha_partition::open(const char *name, int mode, uint test_if_locked)
/* Allocate an array of handler pointers for the partitions handlers. */
alloc_len= (m_tot_parts + 1) * sizeof(handler*);
if (!(m_file= (handler **) alloc_root(m_clone_mem_root, alloc_len)))
+ {
+ error= HA_ERR_INITIALIZATION;
goto err_alloc;
+ }
memset(m_file, 0, alloc_len);
/*
Populate them by cloning the original partitions. This also opens them.
@@ -2973,6 +3477,7 @@ int ha_partition::open(const char *name, int mode, uint test_if_locked)
{
create_partition_name(name_buff, name, name_buffer_ptr, NORMAL_PART_NAME,
FALSE);
+ /* ::clone() will also set ha_share from the original. */
if (!(m_file[i]= file[i]->clone(name_buff, m_clone_mem_root)))
{
error= HA_ERR_INITIALIZATION;
@@ -2990,10 +3495,13 @@ int ha_partition::open(const char *name, int mode, uint test_if_locked)
create_partition_name(name_buff, name, name_buffer_ptr, NORMAL_PART_NAME,
FALSE);
table->s->connect_string = m_connect_string[(uint)(file-m_file)];
- if ((error= (*file)->ha_open(table, name_buff, mode, test_if_locked)))
+ if ((error= (*file)->ha_open(table, name_buff, mode,
+ test_if_locked | HA_OPEN_NO_PSI_CALL)))
goto err_handler;
bzero(&table->s->connect_string, sizeof(LEX_STRING));
- m_num_locks+= (*file)->lock_count();
+ if (m_file == file)
+ m_num_locks= (*file)->lock_count();
+ DBUG_ASSERT(m_num_locks == (*file)->lock_count());
name_buffer_ptr+= strlen(name_buffer_ptr) + 1;
} while (*(++file));
}
@@ -3016,7 +3524,7 @@ int ha_partition::open(const char *name, int mode, uint test_if_locked)
(PARTITION_ENABLED_TABLE_FLAGS)))
{
error= HA_ERR_INITIALIZATION;
- /* set file to last handler, so all of them is closed */
+ /* set file to last handler, so all of them are closed */
file = &m_file[m_tot_parts - 1];
goto err_handler;
}
@@ -3037,34 +3545,6 @@ int ha_partition::open(const char *name, int mode, uint test_if_locked)
clear_handler_file();
/*
- Use table_share->ha_part_data to share auto_increment_value among
- all handlers for the same table.
- */
- if (is_not_tmp_table)
- mysql_mutex_lock(&table_share->LOCK_ha_data);
- if (!table_share->ha_part_data)
- {
- /* currently only needed for auto_increment */
- table_share->ha_part_data= (HA_DATA_PARTITION*)
- alloc_root(&table_share->mem_root,
- sizeof(HA_DATA_PARTITION));
- if (!table_share->ha_part_data)
- {
- if (is_not_tmp_table)
- mysql_mutex_unlock(&table_share->LOCK_ha_data);
- goto err_handler;
- }
- DBUG_PRINT("info", ("table_share->ha_part_data 0x%p",
- table_share->ha_part_data));
- bzero(table_share->ha_part_data, sizeof(HA_DATA_PARTITION));
- table_share->ha_part_data_destroy= ha_data_partition_destroy;
- mysql_mutex_init(key_PARTITION_LOCK_auto_inc,
- &table_share->ha_part_data->LOCK_auto_inc,
- MY_MUTEX_INIT_FAST);
- }
- if (is_not_tmp_table)
- mysql_mutex_unlock(&table_share->LOCK_ha_data);
- /*
Some handlers update statistics as part of the open call. This will in
some cases corrupt the statistics of the partition handler and thus
to ensure we have correct statistics we call info from open after
@@ -3084,15 +3564,49 @@ err_handler:
while (file-- != m_file)
(*file)->ha_close();
err_alloc:
- bitmap_free(&m_bulk_insert_started);
- bitmap_free(&m_key_not_found_partitions);
- if (!m_is_clone_of)
- bitmap_free(&(m_part_info->used_partitions));
+ free_partition_bitmaps();
DBUG_RETURN(error);
}
+/*
+ Disabled since it is not possible to prune yet.
+ without pruning, it need to rebind/unbind every partition in every
+ statement which uses a table from the table cache. Will also use
+ as many PSI_tables as there are partitions.
+*/
+#ifdef HAVE_M_PSI_PER_PARTITION
+void ha_partition::unbind_psi()
+{
+ uint i;
+
+ DBUG_ENTER("ha_partition::unbind_psi");
+ handler::unbind_psi();
+ for (i= 0; i < m_tot_parts; i++)
+ {
+ DBUG_ASSERT(m_file[i] != NULL);
+ m_file[i]->unbind_psi();
+ }
+ DBUG_VOID_RETURN;
+}
+
+void ha_partition::rebind_psi()
+{
+ uint i;
+
+ DBUG_ENTER("ha_partition::rebind_psi");
+ handler::rebind_psi();
+ for (i= 0; i < m_tot_parts; i++)
+ {
+ DBUG_ASSERT(m_file[i] != NULL);
+ m_file[i]->rebind_psi();
+ }
+ DBUG_VOID_RETURN;
+}
+#endif /* HAVE_M_PSI_PER_PARTITION */
+
+
/**
Clone the open and locked partitioning handler.
@@ -3116,22 +3630,35 @@ handler *ha_partition::clone(const char *name, MEM_ROOT *mem_root)
DBUG_ENTER("ha_partition::clone");
new_handler= new (mem_root) ha_partition(ht, table_share, m_part_info,
this, mem_root);
+ if (!new_handler)
+ DBUG_RETURN(NULL);
+
+ /*
+ We will not clone each partition's handler here, it will be done in
+ ha_partition::open() for clones. Also set_ha_share_ref is not needed
+ here, since 1) ha_share is copied in the constructor used above
+ 2) each partition's cloned handler will set it from its original.
+ */
+
/*
Allocate new_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 &&
- !(new_handler->ref= (uchar*) alloc_root(mem_root,
+ if (!(new_handler->ref= (uchar*) alloc_root(mem_root,
ALIGN_SIZE(m_ref_length)*2)))
- new_handler= NULL;
+ goto err;
- if (new_handler &&
- new_handler->ha_open(table, name,
- table->db_stat, HA_OPEN_IGNORE_IF_LOCKED))
- new_handler= NULL;
+ if (new_handler->ha_open(table, name,
+ table->db_stat,
+ HA_OPEN_IGNORE_IF_LOCKED | HA_OPEN_NO_PSI_CALL))
+ goto err;
DBUG_RETURN((handler*) new_handler);
+
+err:
+ delete new_handler;
+ DBUG_RETURN(NULL);
}
@@ -3161,10 +3688,8 @@ int ha_partition::close(void)
DBUG_ASSERT(table->s == table_share);
destroy_record_priority_queue();
- bitmap_free(&m_bulk_insert_started);
- bitmap_free(&m_key_not_found_partitions);
- if (!m_is_clone_of)
- bitmap_free(&(m_part_info->used_partitions));
+ free_partition_bitmaps();
+ DBUG_ASSERT(m_part_info);
file= m_file;
repeat:
@@ -3226,41 +3751,64 @@ repeat:
int ha_partition::external_lock(THD *thd, int lock_type)
{
- bool first= TRUE;
uint error;
- handler **file;
+ uint i, first_used_partition;
+ MY_BITMAP *used_partitions;
DBUG_ENTER("ha_partition::external_lock");
DBUG_ASSERT(!auto_increment_lock && !auto_increment_safe_stmt_log_lock);
- file= m_file;
- m_lock_type= lock_type;
-repeat:
- do
+ if (lock_type == F_UNLCK)
+ used_partitions= &m_locked_partitions;
+ else
+ used_partitions= &(m_part_info->lock_partitions);
+
+ first_used_partition= bitmap_get_first_set(used_partitions);
+
+ for (i= first_used_partition;
+ i < m_tot_parts;
+ i= bitmap_get_next_set(used_partitions, i))
{
- DBUG_PRINT("info", ("external_lock(thd, %d) iteration %d",
- lock_type, (int) (file - m_file)));
- if ((error= (*file)->ha_external_lock(thd, lock_type)))
+ DBUG_PRINT("info", ("external_lock(thd, %d) part %d", lock_type, i));
+ if ((error= m_file[i]->ha_external_lock(thd, lock_type)))
{
- if (F_UNLCK != lock_type)
+ if (lock_type != F_UNLCK)
goto err_handler;
}
- } while (*(++file));
+ DBUG_PRINT("info", ("external_lock part %u lock %d", i, lock_type));
+ if (lock_type != F_UNLCK)
+ bitmap_set_bit(&m_locked_partitions, i);
+ }
+ if (lock_type == F_UNLCK)
+ {
+ bitmap_clear_all(used_partitions);
+ }
+ else
+ {
+ /* Add touched partitions to be included in reset(). */
+ bitmap_union(&m_partitions_to_reset, used_partitions);
+ }
- if (first && m_added_file && m_added_file[0])
+ if (m_added_file && m_added_file[0])
{
+ handler **file= m_added_file;
DBUG_ASSERT(lock_type == F_UNLCK);
- file= m_added_file;
- first= FALSE;
- goto repeat;
+ do
+ {
+ (void) (*file)->ha_external_lock(thd, lock_type);
+ } while (*(++file));
}
DBUG_RETURN(0);
err_handler:
- while (file-- != m_file)
+ uint j;
+ for (j= first_used_partition;
+ j < i;
+ j= bitmap_get_next_set(&m_locked_partitions, j))
{
- (*file)->ha_external_lock(thd, F_UNLCK);
+ (void) m_file[j]->ha_external_lock(thd, F_UNLCK);
}
+ bitmap_clear_all(&m_locked_partitions);
DBUG_RETURN(error);
}
@@ -3315,14 +3863,30 @@ THR_LOCK_DATA **ha_partition::store_lock(THD *thd,
THR_LOCK_DATA **to,
enum thr_lock_type lock_type)
{
- handler **file;
+ uint i;
DBUG_ENTER("ha_partition::store_lock");
- file= m_file;
- do
+ DBUG_ASSERT(thd == current_thd);
+
+ /*
+ This can be called from get_lock_data() in mysql_lock_abort_for_thread(),
+ even when thd != table->in_use. In that case don't use partition pruning,
+ but use all partitions instead to avoid using another threads structures.
+ */
+ if (thd != table->in_use)
{
- DBUG_PRINT("info", ("store lock %d iteration", (int) (file - m_file)));
- to= (*file)->store_lock(thd, to, lock_type);
- } while (*(++file));
+ for (i= 0; i < m_tot_parts; i++)
+ to= m_file[i]->store_lock(thd, to, lock_type);
+ }
+ else
+ {
+ for (i= bitmap_get_first_set(&(m_part_info->lock_partitions));
+ i < m_tot_parts;
+ i= bitmap_get_next_set(&m_part_info->lock_partitions, i))
+ {
+ DBUG_PRINT("info", ("store lock %d iteration", i));
+ to= m_file[i]->store_lock(thd, to, lock_type);
+ }
+ }
DBUG_RETURN(to);
}
@@ -3346,40 +3910,57 @@ THR_LOCK_DATA **ha_partition::store_lock(THD *thd,
int ha_partition::start_stmt(THD *thd, thr_lock_type lock_type)
{
int error= 0;
- handler **file;
+ uint i;
+ /* Assert that read_partitions is included in lock_partitions */
+ DBUG_ASSERT(bitmap_is_subset(&m_part_info->read_partitions,
+ &m_part_info->lock_partitions));
+ /*
+ m_locked_partitions is set in previous external_lock/LOCK TABLES.
+ Current statement's lock requests must not include any partitions
+ not previously locked.
+ */
+ DBUG_ASSERT(bitmap_is_subset(&m_part_info->lock_partitions,
+ &m_locked_partitions));
DBUG_ENTER("ha_partition::start_stmt");
- file= m_file;
- do
+ for (i= bitmap_get_first_set(&(m_part_info->lock_partitions));
+ i < m_tot_parts;
+ i= bitmap_get_next_set(&m_part_info->lock_partitions, i))
{
- if ((error= (*file)->start_stmt(thd, lock_type)))
+ if ((error= m_file[i]->start_stmt(thd, lock_type)))
break;
- } while (*(++file));
+ /* Add partition to be called in reset(). */
+ bitmap_set_bit(&m_partitions_to_reset, i);
+ }
DBUG_RETURN(error);
}
-/*
+/**
Get number of lock objects returned in store_lock
- SYNOPSIS
- lock_count()
-
- RETURN VALUE
- Number of locks returned in call to store_lock
+ @returns Number of locks returned in call to store_lock
- DESCRIPTION
+ @desc
Returns the number of store locks needed in call to store lock.
- We return number of partitions since we call store_lock on each
- underlying handler. Assists the above functions in allocating
+ We return number of partitions we will lock multiplied with number of
+ locks needed by each partition. Assists the above functions in allocating
sufficient space for lock structures.
*/
uint ha_partition::lock_count() const
{
DBUG_ENTER("ha_partition::lock_count");
- DBUG_PRINT("info", ("m_num_locks %d", m_num_locks));
- DBUG_RETURN(m_num_locks);
+ /*
+ The caller want to know the upper bound, to allocate enough memory.
+ There is no performance lost if we simply return maximum number locks
+ needed, only some minor over allocation of memory in get_lock_data().
+
+ Also notice that this may be called for another thread != table->in_use,
+ when mysql_lock_abort_for_thread() is called. So this is more safe, then
+ using number of partitions after pruning.
+ */
+ DBUG_RETURN(m_tot_parts * m_num_locks);
}
@@ -3431,7 +4012,7 @@ bool ha_partition::was_semi_consistent_read()
{
DBUG_ENTER("ha_partition::was_semi_consistent_read");
DBUG_ASSERT(m_last_part < m_tot_parts &&
- bitmap_is_set(&(m_part_info->used_partitions), m_last_part));
+ bitmap_is_set(&(m_part_info->read_partitions), m_last_part));
DBUG_RETURN(m_file[m_last_part]->was_semi_consistent_read());
}
@@ -3456,13 +4037,16 @@ bool ha_partition::was_semi_consistent_read()
*/
void ha_partition::try_semi_consistent_read(bool yes)
{
- handler **file;
+ uint i;
DBUG_ENTER("ha_partition::try_semi_consistent_read");
- for (file= m_file; *file; file++)
+ i= bitmap_get_first_set(&(m_part_info->read_partitions));
+ DBUG_ASSERT(i != MY_BIT_NONE);
+ for (;
+ i < m_tot_parts;
+ i= bitmap_get_next_set(&m_part_info->read_partitions, i))
{
- if (bitmap_is_set(&(m_part_info->used_partitions), (file - m_file)))
- (*file)->try_semi_consistent_read(yes);
+ m_file[i]->try_semi_consistent_read(yes);
}
DBUG_VOID_RETURN;
}
@@ -3505,8 +4089,8 @@ void ha_partition::try_semi_consistent_read(bool yes)
ADDITIONAL INFO:
- We have to set timestamp fields and auto_increment fields, because those
- may be used in determining which partition the row should be written to.
+ We have to set auto_increment fields, because those may be used in
+ determining which partition the row should be written to.
*/
int ha_partition::write_row(uchar * buf)
@@ -3517,27 +4101,18 @@ int ha_partition::write_row(uchar * buf)
bool have_auto_increment= table->next_number_field && buf == table->record[0];
my_bitmap_map *old_map;
THD *thd= ha_thd();
- timestamp_auto_set_type saved_timestamp_type= table->timestamp_field_type;
- ulonglong saved_sql_mode= thd->variables.sql_mode;
+ sql_mode_t saved_sql_mode= thd->variables.sql_mode;
bool saved_auto_inc_field_not_null= table->auto_increment_field_not_null;
-#ifdef NOT_NEEDED
- uchar *rec0= m_rec0;
-#endif
DBUG_ENTER("ha_partition::write_row");
DBUG_ASSERT(buf == m_rec0);
- /* If we have a timestamp column, update it to the current time */
- if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_INSERT)
- table->timestamp_field->set_time();
- table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
-
/*
If we have an auto_increment column and we are writing a changed row
or a new row, then update the auto_increment value in the record.
*/
if (have_auto_increment)
{
- if (!table_share->ha_part_data->auto_inc_initialized &&
+ if (!part_share->auto_inc_initialized &&
!table_share->next_number_keypart)
{
/*
@@ -3574,26 +4149,20 @@ int ha_partition::write_row(uchar * buf)
}
old_map= dbug_tmp_use_all_columns(table, table->read_set);
-#ifdef NOT_NEEDED
- if (likely(buf == rec0))
-#endif
- error= m_part_info->get_partition_id(m_part_info, &part_id,
- &func_value);
-#ifdef NOT_NEEDED
- else
- {
- set_field_ptr(m_part_field_array, buf, rec0);
- error= m_part_info->get_partition_id(m_part_info, &part_id,
- &func_value);
- set_field_ptr(m_part_field_array, rec0, buf);
- }
-#endif
+ error= m_part_info->get_partition_id(m_part_info, &part_id, &func_value);
dbug_tmp_restore_column_map(table->read_set, old_map);
if (unlikely(error))
{
m_part_info->err_value= func_value;
goto exit;
}
+ if (!bitmap_is_set(&(m_part_info->lock_partitions), part_id))
+ {
+ DBUG_PRINT("info", ("Write to non-locked partition %u (func_value: %ld)",
+ part_id, (long) func_value));
+ error= HA_ERR_NOT_IN_LOCK_PARTITIONS;
+ goto exit;
+ }
m_last_part= part_id;
DBUG_PRINT("info", ("Insert in partition %d", part_id));
start_part_bulk_insert(thd, part_id);
@@ -3606,7 +4175,6 @@ int ha_partition::write_row(uchar * buf)
exit:
thd->variables.sql_mode= saved_sql_mode;
table->auto_increment_field_not_null= saved_auto_inc_field_not_null;
- table->timestamp_field_type= saved_timestamp_type;
DBUG_RETURN(error);
}
@@ -3641,19 +4209,12 @@ int ha_partition::update_row(const uchar *old_data, uchar *new_data)
uint32 new_part_id, old_part_id;
int error= 0;
longlong func_value;
- timestamp_auto_set_type orig_timestamp_type= table->timestamp_field_type;
DBUG_ENTER("ha_partition::update_row");
m_err_rec= NULL;
- /*
- We need to set timestamp field once before we calculate
- the partition. Then we disable timestamp calculations
- inside m_file[*]->update_row() methods
- */
- if (orig_timestamp_type & TIMESTAMP_AUTO_SET_ON_UPDATE)
- table->timestamp_field->set_time();
- table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
-
+ // Need to read partition-related columns, to locate the row's partition:
+ DBUG_ASSERT(bitmap_is_subset(&m_part_info->full_part_field_set,
+ table->read_set));
if ((error= get_parts_for_update(old_data, new_data, table->record[0],
m_part_info, &old_part_id, &new_part_id,
&func_value)))
@@ -3661,6 +4222,13 @@ int ha_partition::update_row(const uchar *old_data, uchar *new_data)
m_part_info->err_value= func_value;
goto exit;
}
+ DBUG_ASSERT(bitmap_is_set(&(m_part_info->read_partitions), old_part_id));
+ if (!bitmap_is_set(&(m_part_info->lock_partitions), new_part_id))
+ {
+ error= HA_ERR_NOT_IN_LOCK_PARTITIONS;
+ goto exit;
+ }
+
/*
The protocol for updating a row is:
1) position the handler (cursor) on the row to be updated,
@@ -3674,11 +4242,14 @@ int ha_partition::update_row(const uchar *old_data, uchar *new_data)
between partitions! Since we don't check all rows on read, we return an
error instead of correcting m_last_part, to make the user aware of the
problem!
+
+ Notice that HA_READ_BEFORE_WRITE_REMOVAL does not require this protocol,
+ so this is not supported for this engine.
*/
if (old_part_id != m_last_part)
{
m_err_rec= old_data;
- DBUG_RETURN(HA_ERR_NO_PARTITION_FOUND);
+ DBUG_RETURN(HA_ERR_ROW_IN_WRONG_PARTITION);
}
m_last_part= new_part_id;
@@ -3729,7 +4300,7 @@ int ha_partition::update_row(const uchar *old_data, uchar *new_data)
exit:
/*
if updating an auto_increment column, update
- table_share->ha_part_data->next_auto_inc_val if needed.
+ part_share->next_auto_inc_val if needed.
(not to be used if auto_increment on secondary field in a multi-column
index)
mysql_update does not set table->next_number_field, so we use
@@ -3742,11 +4313,10 @@ exit:
bitmap_is_set(table->write_set,
table->found_next_number_field->field_index))
{
- if (!table_share->ha_part_data->auto_inc_initialized)
+ if (!part_share->auto_inc_initialized)
info(HA_STATUS_AUTO);
set_auto_increment_if_higher(table->found_next_number_field);
}
- table->timestamp_field_type= orig_timestamp_type;
DBUG_RETURN(error);
}
@@ -3787,10 +4357,18 @@ int ha_partition::delete_row(const uchar *buf)
DBUG_ENTER("ha_partition::delete_row");
m_err_rec= NULL;
+ DBUG_ASSERT(bitmap_is_subset(&m_part_info->full_part_field_set,
+ table->read_set));
if ((error= get_part_for_delete(buf, m_rec0, m_part_info, &part_id)))
{
DBUG_RETURN(error);
}
+ /* Should never call delete_row on a partition which is not read */
+ DBUG_ASSERT(bitmap_is_set(&(m_part_info->read_partitions), part_id));
+ DBUG_ASSERT(bitmap_is_set(&(m_part_info->lock_partitions), part_id));
+ if (!bitmap_is_set(&(m_part_info->lock_partitions), part_id))
+ DBUG_RETURN(HA_ERR_NOT_IN_LOCK_PARTITIONS);
+
/*
The protocol for deleting a row is:
1) position the handler (cursor) on the row to be deleted,
@@ -3804,15 +4382,20 @@ int ha_partition::delete_row(const uchar *buf)
between partitions! Since we don't check all rows on read, we return an
error instead of forwarding the delete to the correct (m_last_part)
partition!
+
+ Notice that HA_READ_BEFORE_WRITE_REMOVAL does not require this protocol,
+ so this is not supported for this engine.
+
TODO: change the assert in InnoDB into an error instead and make this one
an assert instead and remove the get_part_for_delete()!
*/
if (part_id != m_last_part)
{
m_err_rec= buf;
- DBUG_RETURN(HA_ERR_NO_PARTITION_FOUND);
+ DBUG_RETURN(HA_ERR_ROW_IN_WRONG_PARTITION);
}
+ m_last_part= part_id;
tmp_disable_binlog(thd);
error= m_file[part_id]->ha_delete_row(buf);
reenable_binlog(thd);
@@ -3838,22 +4421,24 @@ int ha_partition::delete_row(const uchar *buf)
Called from item_sum.cc by Item_func_group_concat::clear(),
Item_sum_count_distinct::clear(), and Item_func_group_concat::clear().
Called from sql_delete.cc by mysql_delete().
- Called from sql_select.cc by JOIN::reinit().
+ Called from sql_select.cc by JOIN::reset().
Called from sql_union.cc by st_select_lex_unit::exec().
*/
int ha_partition::delete_all_rows()
{
int error;
- handler **file;
+ uint i;
DBUG_ENTER("ha_partition::delete_all_rows");
- file= m_file;
- do
+ for (i= bitmap_get_first_set(&m_part_info->read_partitions);
+ i < m_tot_parts;
+ i= bitmap_get_next_set(&m_part_info->read_partitions, i))
{
- if ((error= (*file)->ha_delete_all_rows()))
+ /* Can be pruned, like DELETE FROM t PARTITION (pX) */
+ if ((error= m_file[i]->ha_delete_all_rows()))
DBUG_RETURN(error);
- } while (*(++file));
+ }
DBUG_RETURN(0);
}
@@ -3876,8 +4461,8 @@ int ha_partition::truncate()
it so that it will be initialized again at the next use.
*/
lock_auto_increment();
- table_share->ha_part_data->next_auto_inc_val= 0;
- table_share->ha_part_data->auto_inc_initialized= FALSE;
+ part_share->next_auto_inc_val= 0;
+ part_share->auto_inc_initialized= false;
unlock_auto_increment();
file= m_file;
@@ -3918,8 +4503,8 @@ int ha_partition::truncate_partition(Alter_info *alter_info, bool *binlog_stmt)
it so that it will be initialized again at the next use.
*/
lock_auto_increment();
- table_share->ha_part_data->next_auto_inc_val= 0;
- table_share->ha_part_data->auto_inc_initialized= FALSE;
+ part_share->next_auto_inc_val= 0;
+ part_share->auto_inc_initialized= FALSE;
unlock_auto_increment();
*binlog_stmt= true;
@@ -3933,7 +4518,7 @@ int ha_partition::truncate_partition(Alter_info *alter_info, bool *binlog_stmt)
{
List_iterator<partition_element>
subpart_it(part_elem->subpartitions);
- partition_element *sub_elem __attribute__((unused));
+ partition_element *sub_elem;
uint j= 0, part;
do
{
@@ -3965,6 +4550,7 @@ int ha_partition::truncate_partition(Alter_info *alter_info, bool *binlog_stmt)
SYNOPSIS
start_bulk_insert()
rows Number of rows to insert
+ flags Flags to control index creation
RETURN VALUE
NONE
@@ -3972,7 +4558,7 @@ int ha_partition::truncate_partition(Alter_info *alter_info, bool *binlog_stmt)
DESCRIPTION
rows == 0 means we will probably insert many rows
*/
-void ha_partition::start_bulk_insert(ha_rows rows)
+void ha_partition::start_bulk_insert(ha_rows rows, uint flags)
{
DBUG_ENTER("ha_partition::start_bulk_insert");
@@ -3994,6 +4580,7 @@ void ha_partition::start_part_bulk_insert(THD *thd, uint part_id)
if (!bitmap_is_set(&m_bulk_insert_started, part_id) &&
bitmap_is_set(&m_bulk_insert_started, m_tot_parts))
{
+ DBUG_ASSERT(bitmap_is_set(&(m_part_info->lock_partitions), part_id));
old_buffer_size= thd->variables.read_buff_size;
/* Update read_buffer_size for this partition */
thd->variables.read_buff_size= estimate_read_buffer_size(old_buffer_size);
@@ -4101,11 +4688,12 @@ int ha_partition::end_bulk_insert()
if (!bitmap_is_set(&m_bulk_insert_started, m_tot_parts))
DBUG_RETURN(error);
- for (i= 0; i < m_tot_parts; i++)
+ for (i= bitmap_get_first_set(&m_bulk_insert_started);
+ i < m_tot_parts;
+ i= bitmap_get_next_set(&m_bulk_insert_started, i))
{
int tmp;
- if (bitmap_is_set(&m_bulk_insert_started, i) &&
- (tmp= m_file[i]->ha_end_bulk_insert()))
+ if ((tmp= m_file[i]->ha_end_bulk_insert()))
error= tmp;
}
bitmap_clear_all(&m_bulk_insert_started);
@@ -4153,7 +4741,7 @@ int ha_partition::rnd_init(bool scan)
For operations that may need to change data, we may need to extend
read_set.
*/
- if (m_lock_type == F_WRLCK)
+ if (get_lock_type() == F_WRLCK)
{
/*
If write_set contains any of the fields used in partition and
@@ -4178,9 +4766,9 @@ int ha_partition::rnd_init(bool scan)
}
/* Now we see what the index of our first important partition is */
- DBUG_PRINT("info", ("m_part_info->used_partitions: 0x%lx",
- (long) m_part_info->used_partitions.bitmap));
- part_id= bitmap_get_first_set(&(m_part_info->used_partitions));
+ DBUG_PRINT("info", ("m_part_info->read_partitions: 0x%lx",
+ (long) m_part_info->read_partitions.bitmap));
+ part_id= bitmap_get_first_set(&(m_part_info->read_partitions));
DBUG_PRINT("info", ("m_part_spec.start_part %d", part_id));
if (MY_BIT_NONE == part_id)
@@ -4207,13 +4795,12 @@ int ha_partition::rnd_init(bool scan)
}
else
{
- for (i= part_id; i < m_tot_parts; i++)
+ for (i= part_id;
+ i < m_tot_parts;
+ i= bitmap_get_next_set(&m_part_info->read_partitions, i))
{
- if (bitmap_is_set(&(m_part_info->used_partitions), i))
- {
- if ((error= m_file[i]->ha_rnd_init(scan)))
- goto err;
- }
+ if ((error= m_file[i]->ha_rnd_init(scan)))
+ goto err;
}
}
m_scan_value= scan;
@@ -4223,10 +4810,12 @@ int ha_partition::rnd_init(bool scan)
DBUG_RETURN(0);
err:
- while ((int)--i >= (int)part_id)
+ /* Call rnd_end for all previously inited partitions. */
+ for (;
+ part_id < i;
+ part_id= bitmap_get_next_set(&m_part_info->read_partitions, part_id))
{
- if (bitmap_is_set(&(m_part_info->used_partitions), i))
- m_file[i]->ha_rnd_end();
+ m_file[part_id]->ha_rnd_end();
}
err1:
m_scan_value= 2;
@@ -4248,7 +4837,6 @@ err1:
int ha_partition::rnd_end()
{
- handler **file;
DBUG_ENTER("ha_partition::rnd_end");
switch (m_scan_value) {
case 2: // Error
@@ -4261,12 +4849,13 @@ int ha_partition::rnd_end()
}
break;
case 0:
- file= m_file;
- do
+ uint i;
+ for (i= bitmap_get_first_set(&m_part_info->read_partitions);
+ i < m_tot_parts;
+ i= bitmap_get_next_set(&m_part_info->read_partitions, i))
{
- if (bitmap_is_set(&(m_part_info->used_partitions), (file - m_file)))
- (*file)->ha_rnd_end();
- } while (*(++file));
+ m_file[i]->ha_rnd_end();
+ }
break;
}
m_scan_value= 2;
@@ -4329,7 +4918,7 @@ int ha_partition::rnd_next(uchar *buf)
}
/*
- if we get here, then the current partition rnd_next returned failure
+ if we get here, then the current partition ha_rnd_next returned failure
*/
if (result == HA_ERR_RECORD_DELETED)
continue; // Probably MyISAM
@@ -4344,9 +4933,7 @@ int ha_partition::rnd_next(uchar *buf)
break;
/* Shift to next partition */
- while (++part_id < m_tot_parts &&
- !bitmap_is_set(&(m_part_info->used_partitions), part_id))
- ;
+ part_id= bitmap_get_next_set(&m_part_info->read_partitions, part_id);
if (part_id >= m_tot_parts)
{
result= HA_ERR_END_OF_FILE;
@@ -4398,6 +4985,7 @@ void ha_partition::position(const uchar *record)
{
handler *file= m_file[m_last_part];
uint pad_length;
+ DBUG_ASSERT(bitmap_is_set(&(m_part_info->read_partitions), m_last_part));
DBUG_ENTER("ha_partition::position");
file->position(record);
@@ -4411,14 +4999,6 @@ void ha_partition::position(const uchar *record)
}
-void ha_partition::column_bitmaps_signal()
-{
- handler::column_bitmaps_signal();
- /* Must read all partition fields to make position() call possible */
- bitmap_union(table->read_set, &m_part_info->full_part_field_set);
-}
-
-
/*
Read row using position
@@ -4450,8 +5030,9 @@ int ha_partition::rnd_pos(uchar * buf, uchar *pos)
part_id= uint2korr((const uchar *) pos);
DBUG_ASSERT(part_id < m_tot_parts);
file= m_file[part_id];
+ DBUG_ASSERT(bitmap_is_set(&(m_part_info->read_partitions), part_id));
m_last_part= part_id;
- DBUG_RETURN(file->rnd_pos(buf, (pos + PARTITION_BYTES_IN_POS)));
+ DBUG_RETURN(file->ha_rnd_pos(buf, (pos + PARTITION_BYTES_IN_POS)));
}
@@ -4519,7 +5100,7 @@ bool ha_partition::init_record_priority_queue()
if (!m_ordered_rec_buffer)
{
uint alloc_len;
- uint used_parts= bitmap_bits_set(&m_part_info->used_partitions);
+ uint used_parts= bitmap_bits_set(&m_part_info->read_partitions);
/* Allocate record buffer for each used partition. */
m_priority_queue_rec_len= m_rec_length + PARTITION_BYTES_IN_POS;
if (!m_using_extended_keys)
@@ -4539,16 +5120,15 @@ bool ha_partition::init_record_priority_queue()
setting up the scan.
*/
char *ptr= (char*) m_ordered_rec_buffer;
- uint16 i= 0;
- do
+ uint i;
+ for (i= bitmap_get_first_set(&m_part_info->read_partitions);
+ i < m_tot_parts;
+ i= bitmap_get_next_set(&m_part_info->read_partitions, i))
{
- if (bitmap_is_set(&m_part_info->used_partitions, i))
- {
- DBUG_PRINT("info", ("init rec-buf for part %u", i));
- int2store(ptr, i);
- ptr+= m_priority_queue_rec_len;
- }
- } while (++i < m_tot_parts);
+ DBUG_PRINT("info", ("init rec-buf for part %u", i));
+ int2store(ptr, i);
+ ptr+= m_priority_queue_rec_len;
+ }
m_start_key.key= (const uchar*)ptr;
/* Initialize priority queue, initialized to reading forward. */
@@ -4612,7 +5192,7 @@ void ha_partition::destroy_record_priority_queue()
int ha_partition::index_init(uint inx, bool sorted)
{
int error= 0;
- handler **file;
+ uint i;
DBUG_ENTER("ha_partition::index_init");
DBUG_PRINT("info", ("inx %u sorted %u", inx, sorted));
@@ -4649,7 +5229,7 @@ int ha_partition::index_init(uint inx, bool sorted)
calculate the partition id to place updated and deleted records.
But this is required for operations that may need to change data only.
*/
- if (m_lock_type == F_WRLCK)
+ if (get_lock_type() == F_WRLCK)
bitmap_union(table->read_set, &m_part_info->full_part_field_set);
if (sorted)
{
@@ -4665,25 +5245,40 @@ int ha_partition::index_init(uint inx, bool sorted)
TODO: handle COUNT(*) queries via unordered scan.
*/
- uint i;
KEY **key_info= m_curr_key_info;
do
{
- for (i= 0; i < (*key_info)->key_parts; i++)
+ for (i= 0; i < (*key_info)->user_defined_key_parts; i++)
bitmap_set_bit(table->read_set,
(*key_info)->key_part[i].field->field_index);
} while (*(++key_info));
}
- file= m_file;
- do
+ for (i= bitmap_get_first_set(&m_part_info->read_partitions);
+ i < m_tot_parts;
+ i= bitmap_get_next_set(&m_part_info->read_partitions, i))
{
- if (bitmap_is_set(&(m_part_info->used_partitions), (file - m_file)))
- if ((error= (*file)->ha_index_init(inx, sorted)))
- {
- DBUG_ASSERT(0); // Should never happen
- break;
- }
- } while (*(++file));
+ if ((error= m_file[i]->ha_index_init(inx, sorted)))
+ goto err;
+
+ DBUG_EXECUTE_IF("ha_partition_fail_index_init", {
+ i++;
+ error= HA_ERR_NO_PARTITION_FOUND;
+ goto err;
+ });
+ }
+err:
+ if (error)
+ {
+ /* End the previously initialized indexes. */
+ uint j;
+ for (j= bitmap_get_first_set(&m_part_info->read_partitions);
+ j < i;
+ j= bitmap_get_next_set(&m_part_info->read_partitions, j))
+ {
+ (void) m_file[j]->ha_index_end();
+ }
+ destroy_record_priority_queue();
+ }
DBUG_RETURN(error);
}
@@ -4706,19 +5301,19 @@ int ha_partition::index_init(uint inx, bool sorted)
int ha_partition::index_end()
{
int error= 0;
- handler **file;
+ uint i;
DBUG_ENTER("ha_partition::index_end");
active_index= MAX_KEY;
m_part_spec.start_part= NO_CURRENT_PART_ID;
- file= m_file;
- do
+ for (i= bitmap_get_first_set(&m_part_info->read_partitions);
+ i < m_tot_parts;
+ i= bitmap_get_next_set(&m_part_info->read_partitions, i))
{
int tmp;
- if (bitmap_is_set(&(m_part_info->used_partitions), (file - m_file)))
- if ((tmp= (*file)->ha_index_end()))
- error= tmp;
- } while (*(++file));
+ if ((tmp= m_file[i]->ha_index_end()))
+ error= tmp;
+ }
destroy_record_priority_queue();
DBUG_RETURN(error);
}
@@ -5025,17 +5620,20 @@ int ha_partition::index_read_idx_map(uchar *buf, uint index,
or no matching partitions (start_part > end_part)
*/
DBUG_ASSERT(m_part_spec.start_part >= m_part_spec.end_part);
-
- for (part= m_part_spec.start_part; part <= m_part_spec.end_part; part++)
+ /* The start part is must be marked as used. */
+ DBUG_ASSERT(m_part_spec.start_part > m_part_spec.end_part ||
+ bitmap_is_set(&(m_part_info->read_partitions),
+ m_part_spec.start_part));
+
+ for (part= m_part_spec.start_part;
+ part <= m_part_spec.end_part;
+ part= bitmap_get_next_set(&m_part_info->read_partitions, part))
{
- if (bitmap_is_set(&(m_part_info->used_partitions), part))
- {
- error= m_file[part]->index_read_idx_map(buf, index, key,
- keypart_map, find_flag);
- if (error != HA_ERR_KEY_NOT_FOUND &&
- error != HA_ERR_END_OF_FILE)
- break;
- }
+ error= m_file[part]->ha_index_read_idx_map(buf, index, key,
+ keypart_map, find_flag);
+ if (error != HA_ERR_KEY_NOT_FOUND &&
+ error != HA_ERR_END_OF_FILE)
+ break;
}
if (part <= m_part_spec.end_part)
m_last_part= part;
@@ -5177,15 +5775,7 @@ int ha_partition::read_range_first(const key_range *start_key,
m_ordered= sorted;
eq_range= eq_range_arg;
- end_range= 0;
- if (end_key)
- {
- end_range= &save_end_range;
- save_end_range= *end_key;
- key_compare_result_on_equal=
- ((end_key->flag == HA_READ_BEFORE_KEY) ? 1 :
- (end_key->flag == HA_READ_AFTER_KEY) ? -1 : 0);
- }
+ set_end_range(end_key);
range_key_part= m_curr_key_info[0]->key_part;
if (start_key)
@@ -5194,7 +5784,7 @@ int ha_partition::read_range_first(const key_range *start_key,
m_start_key.key= NULL;
m_index_scan_type= partition_read_range;
- error= common_index_read(m_rec0, test(start_key));
+ error= common_index_read(m_rec0, MY_TEST(start_key));
DBUG_RETURN(error);
}
@@ -5287,7 +5877,7 @@ int ha_partition::partition_scan_set_up(uchar * buf, bool idx_read_flag)
Verify this, also bitmap must have at least one bit set otherwise
the result from this table is the empty set.
*/
- uint start_part= bitmap_get_first_set(&(m_part_info->used_partitions));
+ uint start_part= bitmap_get_first_set(&(m_part_info->read_partitions));
if (start_part == MY_BIT_NONE)
{
DBUG_PRINT("info", ("scan with no partition to scan"));
@@ -5404,18 +5994,21 @@ int ha_partition::handle_unordered_next(uchar *buf, bool is_next_same)
int ha_partition::handle_unordered_scan_next_partition(uchar * buf)
{
- uint i;
+ uint i= m_part_spec.start_part;
int saved_error= HA_ERR_END_OF_FILE;
DBUG_ENTER("ha_partition::handle_unordered_scan_next_partition");
- for (i= m_part_spec.start_part; i <= m_part_spec.end_part; i++)
+ if (i)
+ i= bitmap_get_next_set(&m_part_info->read_partitions, i - 1);
+ else
+ i= bitmap_get_first_set(&m_part_info->read_partitions);
+
+ for (;
+ i <= m_part_spec.end_part;
+ i= bitmap_get_next_set(&m_part_info->read_partitions, i))
{
int error;
- handler *file;
-
- if (!(bitmap_is_set(&(m_part_info->used_partitions), i)))
- continue;
- file= m_file[i];
+ handler *file= m_file[i];
m_part_spec.start_part= i;
switch (m_index_scan_type) {
case partition_read_range:
@@ -5514,6 +6107,8 @@ int ha_partition::handle_ordered_index_scan(uchar *buf, bool reverse_order)
}
m_top_entry= NO_CURRENT_PART_ID;
queue_remove_all(&m_queue);
+ DBUG_ASSERT(bitmap_is_set(&m_part_info->read_partitions,
+ m_part_spec.start_part));
/*
Position part_rec_buf_ptr to point to the first used partition >=
@@ -5521,18 +6116,18 @@ int ha_partition::handle_ordered_index_scan(uchar *buf, bool reverse_order)
but is before start_part. These partitions has allocated record buffers
but is dynamically pruned, so those buffers must be skipped.
*/
- uint first_used_part= bitmap_get_first_set(&m_part_info->used_partitions);
- for (; first_used_part < m_part_spec.start_part; first_used_part++)
+ for (i= bitmap_get_first_set(&m_part_info->read_partitions);
+ i < m_part_spec.start_part;
+ i= bitmap_get_next_set(&m_part_info->read_partitions, i))
{
- if (bitmap_is_set(&(m_part_info->used_partitions), first_used_part))
- part_rec_buf_ptr+= m_priority_queue_rec_len;
+ part_rec_buf_ptr+= m_priority_queue_rec_len;
}
DBUG_PRINT("info", ("m_part_spec.start_part %u first_used_part %u",
- m_part_spec.start_part, first_used_part));
- for (i= first_used_part; i <= m_part_spec.end_part; i++)
+ m_part_spec.start_part, i));
+ for (/* continue from above */ ;
+ i <= m_part_spec.end_part;
+ i= bitmap_get_next_set(&m_part_info->read_partitions, i))
{
- if (!(bitmap_is_set(&(m_part_info->used_partitions), i)))
- continue;
DBUG_PRINT("info", ("reading from part %u (scan_type: %u)",
i, m_index_scan_type));
DBUG_ASSERT(i == uint2korr(part_rec_buf_ptr));
@@ -5540,12 +6135,6 @@ int ha_partition::handle_ordered_index_scan(uchar *buf, bool reverse_order)
int error;
handler *file= m_file[i];
- /*
- Reset null bits (to avoid valgrind warnings) and to give a default
- value for not read null fields.
- */
- bfill(rec_buf_ptr, table->s->null_bytes, 255);
-
switch (m_index_scan_type) {
case partition_index_read:
error= file->ha_index_read_map(rec_buf_ptr,
@@ -5666,11 +6255,10 @@ int ha_partition::handle_ordered_index_scan_key_not_found()
Loop over all used partitions to get the correct offset
into m_ordered_rec_buffer.
*/
- for (i= 0; i < m_tot_parts; i++)
+ for (i= bitmap_get_first_set(&m_part_info->read_partitions);
+ i < m_tot_parts;
+ i= bitmap_get_next_set(&m_part_info->read_partitions, i))
{
- if (!bitmap_is_set(&m_part_info->used_partitions, i))
- continue;
-
if (bitmap_is_set(&m_key_not_found_partitions, i))
{
/*
@@ -5678,7 +6266,7 @@ int ha_partition::handle_ordered_index_scan_key_not_found()
in index_read_map.
*/
curr_rec_buf= part_buf + PARTITION_BYTES_IN_POS;
- error= m_file[i]->index_next(curr_rec_buf);
+ error= m_file[i]->ha_index_next(curr_rec_buf);
/* HA_ERR_KEY_NOT_FOUND is not allowed from index_next! */
DBUG_ASSERT(error != HA_ERR_KEY_NOT_FOUND);
if (!error)
@@ -5944,27 +6532,36 @@ int ha_partition::info(uint flag)
uint extra_var_flag= flag & HA_STATUS_VARIABLE_EXTRA;
DBUG_ENTER("ha_partition::info");
+#ifndef DBUG_OFF
+ if (bitmap_is_set_all(&(m_part_info->read_partitions)))
+ DBUG_PRINT("info", ("All partitions are used"));
+#endif /* DBUG_OFF */
if (flag & HA_STATUS_AUTO)
{
bool auto_inc_is_first_in_idx= (table_share->next_number_keypart == 0);
DBUG_PRINT("info", ("HA_STATUS_AUTO"));
if (!table->found_next_number_field)
stats.auto_increment_value= 0;
- else if (table_share->ha_part_data->auto_inc_initialized)
+ else if (part_share->auto_inc_initialized)
{
lock_auto_increment();
- stats.auto_increment_value= table_share->ha_part_data->next_auto_inc_val;
+ stats.auto_increment_value= part_share->next_auto_inc_val;
unlock_auto_increment();
}
else
{
lock_auto_increment();
/* to avoid two concurrent initializations, check again when locked */
- if (table_share->ha_part_data->auto_inc_initialized)
- stats.auto_increment_value=
- table_share->ha_part_data->next_auto_inc_val;
+ if (part_share->auto_inc_initialized)
+ stats.auto_increment_value= part_share->next_auto_inc_val;
else
{
+ /*
+ The auto-inc mutex in the table_share is locked, so we do not need
+ to have the handlers locked.
+ HA_STATUS_NO_LOCK is not checked, since we cannot skip locking
+ the mutex, because it is initialized.
+ */
handler *file, **file_array;
ulonglong auto_increment_value= 0;
file_array= m_file;
@@ -5982,11 +6579,11 @@ int ha_partition::info(uint flag)
stats.auto_increment_value= auto_increment_value;
if (auto_inc_is_first_in_idx)
{
- set_if_bigger(table_share->ha_part_data->next_auto_inc_val,
+ set_if_bigger(part_share->next_auto_inc_val,
auto_increment_value);
- table_share->ha_part_data->auto_inc_initialized= TRUE;
+ part_share->auto_inc_initialized= true;
DBUG_PRINT("info", ("initializing next_auto_inc_val to %lu",
- (ulong) table_share->ha_part_data->next_auto_inc_val));
+ (ulong) part_share->next_auto_inc_val));
}
}
unlock_auto_increment();
@@ -5994,6 +6591,7 @@ int ha_partition::info(uint flag)
}
if (flag & HA_STATUS_VARIABLE)
{
+ uint i;
DBUG_PRINT("info", ("HA_STATUS_VARIABLE"));
/*
Calculates statistical variables
@@ -6014,29 +6612,27 @@ int ha_partition::info(uint flag)
check_time: Time of last check (only applicable to MyISAM)
We report last time of all underlying handlers
*/
- handler *file, **file_array;
+ handler *file;
stats.records= 0;
stats.deleted= 0;
stats.data_file_length= 0;
stats.index_file_length= 0;
stats.check_time= 0;
stats.delete_length= 0;
- file_array= m_file;
- do
+ for (i= bitmap_get_first_set(&m_part_info->read_partitions);
+ i < m_tot_parts;
+ i= bitmap_get_next_set(&m_part_info->read_partitions, i))
{
- if (bitmap_is_set(&(m_part_info->used_partitions), (file_array - m_file)))
- {
- file= *file_array;
- file->info(HA_STATUS_VARIABLE | no_lock_flag | extra_var_flag);
- stats.records+= file->stats.records;
- stats.deleted+= file->stats.deleted;
- stats.data_file_length+= file->stats.data_file_length;
- stats.index_file_length+= file->stats.index_file_length;
- stats.delete_length+= file->stats.delete_length;
- if (file->stats.check_time > stats.check_time)
- stats.check_time= file->stats.check_time;
- }
- } while (*(++file_array));
+ file= m_file[i];
+ file->info(HA_STATUS_VARIABLE | no_lock_flag | extra_var_flag);
+ stats.records+= file->stats.records;
+ stats.deleted+= file->stats.deleted;
+ stats.data_file_length+= file->stats.data_file_length;
+ stats.index_file_length+= file->stats.index_file_length;
+ stats.delete_length+= file->stats.delete_length;
+ if (file->stats.check_time > stats.check_time)
+ stats.check_time= file->stats.check_time;
+ }
if (stats.records && stats.records < 2 &&
!(m_file[0]->ha_table_flags() & HA_STATS_RECORDS_IS_EXACT))
stats.records= 2;
@@ -6107,7 +6703,7 @@ int ha_partition::info(uint flag)
file= *file_array;
/* Get variables if not already done */
if (!(flag & HA_STATUS_VARIABLE) ||
- !bitmap_is_set(&(m_part_info->used_partitions),
+ !bitmap_is_set(&(m_part_info->read_partitions),
(file_array - m_file)))
file->info(HA_STATUS_VARIABLE | no_lock_flag | extra_var_flag);
if (file->stats.records > max_records)
@@ -6174,6 +6770,7 @@ void ha_partition::get_dynamic_partition_info(PARTITION_STATS *stat_info,
uint part_id)
{
handler *file= m_file[part_id];
+ DBUG_ASSERT(bitmap_is_set(&(m_part_info->read_partitions), part_id));
file->info(HA_STATUS_TIME | HA_STATUS_VARIABLE |
HA_STATUS_VARIABLE_EXTRA | HA_STATUS_NO_LOCK);
@@ -6197,7 +6794,6 @@ void ha_partition::get_dynamic_partition_info(PARTITION_STATS *stat_info,
General function to prepare handler for certain behavior.
@param[in] operation operation to execute
- operation Operation type for extra call
@return status
@retval 0 success
@@ -6260,6 +6856,10 @@ void ha_partition::get_dynamic_partition_info(PARTITION_STATS *stat_info,
ensure disk based tables are flushed at end of query execution.
Currently is never used.
+ HA_EXTRA_FORCE_REOPEN:
+ Only used by MyISAM and Archive, called when altering table,
+ closing tables to enforce a reopen of the table files.
+
2) Operations used by some non-MyISAM handlers
----------------------------------------------
HA_EXTRA_KEYREAD_PRESERVE_FIELDS:
@@ -6384,6 +6984,9 @@ void ha_partition::get_dynamic_partition_info(PARTITION_STATS *stat_info,
HA_EXTRA_PREPARE_FOR_RENAME:
Informs the handler we are about to attempt a rename of the table.
+ For handlers that have share open files (MyISAM key-file and
+ Archive writer) they must close the files before rename is possible
+ on Windows.
HA_EXTRA_READCHECK:
HA_EXTRA_NO_READCHECK:
@@ -6404,10 +7007,6 @@ void ha_partition::get_dynamic_partition_info(PARTITION_STATS *stat_info,
HA_EXTRA_NO_READCHECK=5 No readcheck on update
HA_EXTRA_READCHECK=6 Use readcheck (def)
- HA_EXTRA_FORCE_REOPEN:
- Only used by MyISAM, called when altering table, closing tables to
- enforce a reopen of the table files.
-
4) Operations only used by temporary tables for query processing
----------------------------------------------------------------
HA_EXTRA_RESET_STATE:
@@ -6516,6 +7115,10 @@ int ha_partition::extra(enum ha_extra_function operation)
case HA_EXTRA_FLUSH:
case HA_EXTRA_PREPARE_FOR_FORCED_CLOSE:
DBUG_RETURN(loop_extra(operation));
+ case HA_EXTRA_PREPARE_FOR_RENAME:
+ case HA_EXTRA_FORCE_REOPEN:
+ DBUG_RETURN(loop_extra_alter(operation));
+ break;
/* Category 2), used by non-MyISAM handlers */
case HA_EXTRA_IGNORE_DUP_KEY:
@@ -6528,9 +7131,6 @@ int ha_partition::extra(enum ha_extra_function operation)
}
/* Category 3), used by MyISAM handlers */
- case HA_EXTRA_PREPARE_FOR_RENAME:
- DBUG_RETURN(prepare_for_rename());
- break;
case HA_EXTRA_PREPARE_FOR_UPDATE:
/*
Needs to be run on the first partition in the range now, and
@@ -6547,7 +7147,6 @@ int ha_partition::extra(enum ha_extra_function operation)
break;
case HA_EXTRA_NORMAL:
case HA_EXTRA_QUICK:
- case HA_EXTRA_FORCE_REOPEN:
case HA_EXTRA_PREPARE_FOR_DROP:
case HA_EXTRA_FLUSH_CACHE:
{
@@ -6652,33 +7251,34 @@ int ha_partition::extra(enum ha_extra_function operation)
}
-/*
+/**
Special extra call to reset extra parameters
- SYNOPSIS
- reset()
-
- RETURN VALUE
- >0 Error code
- 0 Success
+ @return Operation status.
+ @retval >0 Error code
+ @retval 0 Success
- DESCRIPTION
- Called at end of each statement to reset buffers
+ @note Called at end of each statement to reset buffers.
+ To avoid excessive calls, the m_partitions_to_reset bitmap keep records
+ of which partitions that have been used in extra(), external_lock() or
+ start_stmt() and is needed to be called.
*/
int ha_partition::reset(void)
{
- int result= 0, tmp;
- handler **file;
+ int result= 0;
+ int tmp;
+ uint i;
DBUG_ENTER("ha_partition::reset");
- if (m_part_info)
- bitmap_set_all(&m_part_info->used_partitions);
- file= m_file;
- do
+
+ for (i= bitmap_get_first_set(&m_partitions_to_reset);
+ i < m_tot_parts;
+ i= bitmap_get_next_set(&m_partitions_to_reset, i))
{
- if ((tmp= (*file)->ha_reset()))
+ if ((tmp= m_file[i]->ha_reset()))
result= tmp;
- } while (*(++file));
+ }
+ bitmap_clear_all(&m_partitions_to_reset);
DBUG_RETURN(result);
}
@@ -6725,41 +7325,48 @@ void ha_partition::prepare_extra_cache(uint cachesize)
m_extra_cache_size= cachesize;
if (m_part_spec.start_part != NO_CURRENT_PART_ID)
{
+ DBUG_ASSERT(bitmap_is_set(&m_partitions_to_reset,
+ m_part_spec.start_part));
+ bitmap_set_bit(&m_partitions_to_reset, m_part_spec.start_part);
late_extra_cache(m_part_spec.start_part);
}
DBUG_VOID_RETURN;
}
-/*
- Prepares our new and reorged handlers for rename or delete
+/**
+ Prepares our new and reorged handlers for rename or delete.
- SYNOPSIS
- prepare_for_delete()
+ @param operation Operation to forward
- RETURN VALUE
- >0 Error code
- 0 Success
+ @return Operation status
+ @retval 0 Success
+ @retval !0 Error
*/
-int ha_partition::prepare_for_rename()
+int ha_partition::loop_extra_alter(enum ha_extra_function operation)
{
int result= 0, tmp;
handler **file;
- DBUG_ENTER("ha_partition::prepare_for_rename()");
-
+ DBUG_ENTER("ha_partition::loop_extra_alter()");
+ DBUG_ASSERT(operation == HA_EXTRA_PREPARE_FOR_RENAME ||
+ operation == HA_EXTRA_FORCE_REOPEN);
+
if (m_new_file != NULL)
{
for (file= m_new_file; *file; file++)
- if ((tmp= (*file)->extra(HA_EXTRA_PREPARE_FOR_RENAME)))
- result= tmp;
+ if ((tmp= (*file)->extra(operation)))
+ result= tmp;
+ }
+ if (m_reorged_file != NULL)
+ {
for (file= m_reorged_file; *file; file++)
- if ((tmp= (*file)->extra(HA_EXTRA_PREPARE_FOR_RENAME)))
- result= tmp;
- DBUG_RETURN(result);
+ if ((tmp= (*file)->extra(operation)))
+ result= tmp;
}
-
- DBUG_RETURN(loop_extra(HA_EXTRA_PREPARE_FOR_RENAME));
+ if ((tmp= loop_extra(operation)))
+ result= tmp;
+ DBUG_RETURN(result);
}
/*
@@ -6777,20 +7384,18 @@ int ha_partition::prepare_for_rename()
int ha_partition::loop_extra(enum ha_extra_function operation)
{
int result= 0, tmp;
- handler **file;
- bool is_select;
+ uint i;
DBUG_ENTER("ha_partition::loop_extra()");
- is_select= (thd_sql_command(ha_thd()) == SQLCOM_SELECT);
- for (file= m_file; *file; file++)
+ for (i= bitmap_get_first_set(&m_part_info->lock_partitions);
+ i < m_tot_parts;
+ i= bitmap_get_next_set(&m_part_info->lock_partitions, i))
{
- if (!is_select ||
- bitmap_is_set(&(m_part_info->used_partitions), file - m_file))
- {
- if ((tmp= (*file)->extra(operation)))
- result= tmp;
- }
+ if ((tmp= m_file[i]->extra(operation)))
+ result= tmp;
}
+ /* Add all used partitions to be called in reset(). */
+ bitmap_union(&m_partitions_to_reset, &m_part_info->lock_partitions);
DBUG_RETURN(result);
}
@@ -6864,20 +7469,18 @@ void ha_partition::late_extra_no_cache(uint partition_id)
MODULE optimiser support
****************************************************************************/
-/*
- Get keys to use for scanning
+/**
+ Get keys to use for scanning.
- SYNOPSIS
- keys_to_use_for_scanning()
+ @return key_map of keys usable for scanning
- RETURN VALUE
- key_map of keys usable for scanning
+ @note No need to use read_partitions here, since it does not depend on
+ which partitions is used, only which storage engine used.
*/
const key_map *ha_partition::keys_to_use_for_scanning()
{
DBUG_ENTER("ha_partition::keys_to_use_for_scanning");
-
DBUG_RETURN(m_file[0]->keys_to_use_for_scanning());
}
@@ -6891,7 +7494,7 @@ ha_rows ha_partition::min_rows_for_estimate()
uint i, max_used_partitions, tot_used_partitions;
DBUG_ENTER("ha_partition::min_rows_for_estimate");
- tot_used_partitions= bitmap_bits_set(&m_part_info->used_partitions);
+ tot_used_partitions= bitmap_bits_set(&m_part_info->read_partitions);
/*
All partitions might have been left as unused during partition pruning
@@ -6954,7 +7557,7 @@ uint ha_partition::get_biggest_used_partition(uint *part_index)
while ((*part_index) < m_tot_parts)
{
part_id= m_part_ids_sorted_by_num_of_records[(*part_index)++];
- if (bitmap_is_set(&m_part_info->used_partitions, part_id))
+ if (bitmap_is_set(&m_part_info->read_partitions, part_id))
return part_id;
}
return NO_CURRENT_PART_ID;
@@ -6974,12 +7577,13 @@ uint ha_partition::get_biggest_used_partition(uint *part_index)
double ha_partition::scan_time()
{
double scan_time= 0;
- handler **file;
+ uint i;
DBUG_ENTER("ha_partition::scan_time");
- for (file= m_file; *file; file++)
- if (bitmap_is_set(&(m_part_info->used_partitions), (file - m_file)))
- scan_time+= (*file)->scan_time();
+ for (i= bitmap_get_first_set(&m_part_info->read_partitions);
+ i < m_tot_parts;
+ i= bitmap_get_next_set(&m_part_info->read_partitions, i))
+ scan_time+= m_file[i]->scan_time();
DBUG_RETURN(scan_time);
}
@@ -7065,7 +7669,7 @@ ha_rows ha_partition::estimate_rows_upper_bound()
do
{
- if (bitmap_is_set(&(m_part_info->used_partitions), (file - m_file)))
+ if (bitmap_is_set(&(m_part_info->read_partitions), (file - m_file)))
{
rows= (*file)->estimate_rows_upper_bound();
if (rows == HA_POS_ERROR)
@@ -7107,27 +7711,24 @@ double ha_partition::read_time(uint index, uint ranges, ha_rows rows)
/**
Number of rows in table. see handler.h
- SYNOPSIS
- records()
-
- RETURN VALUE
- Number of total rows in a partitioned table.
+ @return Number of records in the table (after pruning!)
*/
ha_rows ha_partition::records()
{
ha_rows rows, tot_rows= 0;
- handler **file;
+ uint i;
DBUG_ENTER("ha_partition::records");
- file= m_file;
- do
+ for (i= bitmap_get_first_set(&m_part_info->read_partitions);
+ i < m_tot_parts;
+ i= bitmap_get_next_set(&m_part_info->read_partitions, i))
{
- rows= (*file)->records();
+ rows= m_file[i]->records();
if (rows == HA_POS_ERROR)
DBUG_RETURN(HA_POS_ERROR);
tot_rows+= rows;
- } while (*(++file));
+ }
DBUG_RETURN(tot_rows);
}
@@ -7178,37 +7779,161 @@ uint8 ha_partition::table_cache_type()
}
+/**
+ Calculate hash value for KEY partitioning using an array of fields.
+
+ @param field_array An array of the fields in KEY partitioning
+
+ @return hash_value calculated
+
+ @note Uses the hash function on the character set of the field.
+ Integer and floating point fields use the binary character set by default.
+*/
+
+uint32 ha_partition::calculate_key_hash_value(Field **field_array)
+{
+ ulong nr1= 1;
+ ulong nr2= 4;
+ bool use_51_hash;
+ use_51_hash= MY_TEST((*field_array)->table->part_info->key_algorithm ==
+ partition_info::KEY_ALGORITHM_51);
+
+ do
+ {
+ Field *field= *field_array;
+ if (use_51_hash)
+ {
+ switch (field->real_type()) {
+ case MYSQL_TYPE_TINY:
+ case MYSQL_TYPE_SHORT:
+ case MYSQL_TYPE_LONG:
+ case MYSQL_TYPE_FLOAT:
+ case MYSQL_TYPE_DOUBLE:
+ case MYSQL_TYPE_NEWDECIMAL:
+ case MYSQL_TYPE_TIMESTAMP:
+ case MYSQL_TYPE_LONGLONG:
+ case MYSQL_TYPE_INT24:
+ case MYSQL_TYPE_TIME:
+ case MYSQL_TYPE_DATETIME:
+ case MYSQL_TYPE_YEAR:
+ case MYSQL_TYPE_NEWDATE:
+ {
+ if (field->is_null())
+ {
+ nr1^= (nr1 << 1) | 1;
+ continue;
+ }
+ /* Force this to my_hash_sort_bin, which was used in 5.1! */
+ uint len= field->pack_length();
+ my_charset_bin.coll->hash_sort(&my_charset_bin, field->ptr, len,
+ &nr1, &nr2);
+ /* Done with this field, continue with next one. */
+ continue;
+ }
+ case MYSQL_TYPE_STRING:
+ case MYSQL_TYPE_VARCHAR:
+ case MYSQL_TYPE_BIT:
+ /* Not affected, same in 5.1 and 5.5 */
+ break;
+ /*
+ ENUM/SET uses my_hash_sort_simple in 5.1 (i.e. my_charset_latin1)
+ and my_hash_sort_bin in 5.5!
+ */
+ case MYSQL_TYPE_ENUM:
+ case MYSQL_TYPE_SET:
+ {
+ if (field->is_null())
+ {
+ nr1^= (nr1 << 1) | 1;
+ continue;
+ }
+ /* Force this to my_hash_sort_bin, which was used in 5.1! */
+ uint len= field->pack_length();
+ my_charset_latin1.coll->hash_sort(&my_charset_latin1, field->ptr,
+ len, &nr1, &nr2);
+ continue;
+ }
+ /* New types in mysql-5.6. */
+ case MYSQL_TYPE_DATETIME2:
+ case MYSQL_TYPE_TIME2:
+ case MYSQL_TYPE_TIMESTAMP2:
+ /* Not affected, 5.6+ only! */
+ break;
+
+ /* These types should not be allowed for partitioning! */
+ case MYSQL_TYPE_NULL:
+ case MYSQL_TYPE_DECIMAL:
+ case MYSQL_TYPE_DATE:
+ case MYSQL_TYPE_TINY_BLOB:
+ case MYSQL_TYPE_MEDIUM_BLOB:
+ case MYSQL_TYPE_LONG_BLOB:
+ case MYSQL_TYPE_BLOB:
+ case MYSQL_TYPE_VAR_STRING:
+ case MYSQL_TYPE_GEOMETRY:
+ /* fall through. */
+ default:
+ DBUG_ASSERT(0); // New type?
+ /* Fall through for default hashing (5.5). */
+ }
+ /* fall through, use collation based hashing. */
+ }
+ field->hash(&nr1, &nr2);
+ } while (*(++field_array));
+ return (uint32) nr1;
+}
+
+
/****************************************************************************
MODULE print messages
****************************************************************************/
const char *ha_partition::index_type(uint inx)
{
+ uint first_used_partition;
DBUG_ENTER("ha_partition::index_type");
- DBUG_RETURN(m_file[0]->index_type(inx));
+ first_used_partition= bitmap_get_first_set(&(m_part_info->read_partitions));
+
+ if (first_used_partition == MY_BIT_NONE)
+ {
+ DBUG_ASSERT(0); // How can this happen?
+ DBUG_RETURN(handler::index_type(inx));
+ }
+
+ DBUG_RETURN(m_file[first_used_partition]->index_type(inx));
}
enum row_type ha_partition::get_row_type() const
{
- handler **file;
- enum row_type type= (*m_file)->get_row_type();
+ uint i;
+ enum row_type type;
+ DBUG_ENTER("ha_partition::get_row_type");
- for (file= m_file, file++; *file; file++)
+ i= bitmap_get_first_set(&m_part_info->read_partitions);
+ DBUG_ASSERT(i < m_tot_parts);
+ if (i >= m_tot_parts)
+ DBUG_RETURN(ROW_TYPE_NOT_USED);
+
+ type= m_file[i]->get_row_type();
+ DBUG_PRINT("info", ("partition %u, row_type: %d", i, type));
+
+ for (i= bitmap_get_next_set(&m_part_info->lock_partitions, i);
+ i < m_tot_parts;
+ i= bitmap_get_next_set(&m_part_info->lock_partitions, i))
{
- enum row_type part_type= (*file)->get_row_type();
+ enum row_type part_type= m_file[i]->get_row_type();
+ DBUG_PRINT("info", ("partition %u, row_type: %d", i, type));
if (part_type != type)
- return ROW_TYPE_NOT_USED;
+ DBUG_RETURN(ROW_TYPE_NOT_USED);
}
- return type;
+ DBUG_RETURN(type);
}
void ha_partition::append_row_to_str(String &str)
{
- Field **field_ptr;
const uchar *rec;
bool is_rec0= !m_err_rec || m_err_rec == table->record[0];
if (is_rec0)
@@ -7220,7 +7945,7 @@ void ha_partition::append_row_to_str(String &str)
{
KEY *key= table->key_info + table->s->primary_key;
KEY_PART_INFO *key_part= key->key_part;
- KEY_PART_INFO *key_part_end= key_part + key->key_parts;
+ KEY_PART_INFO *key_part_end= key_part + key->user_defined_key_parts;
if (!is_rec0)
set_key_field_ptr(key, rec, table->record[0]);
for (; key_part != key_part_end; key_part++)
@@ -7236,6 +7961,7 @@ void ha_partition::append_row_to_str(String &str)
}
else
{
+ Field **field_ptr;
if (!is_rec0)
set_field_ptr(m_part_info->full_part_field_array, rec,
table->record[0]);
@@ -7265,58 +7991,54 @@ void ha_partition::print_error(int error, myf errflag)
/* Should probably look for my own errors first */
DBUG_PRINT("enter", ("error: %d", error));
- if (error == HA_ERR_NO_PARTITION_FOUND)
+ if ((error == HA_ERR_NO_PARTITION_FOUND) &&
+ ! (thd->lex->alter_info.flags & Alter_info::ALTER_TRUNCATE_PARTITION))
+ {
+ m_part_info->print_no_partition_found(table, errflag);
+ DBUG_VOID_RETURN;
+ }
+ else if (error == HA_ERR_ROW_IN_WRONG_PARTITION)
{
- switch(thd_sql_command(thd))
+ /* Should only happen on DELETE or UPDATE! */
+ DBUG_ASSERT(thd_sql_command(thd) == SQLCOM_DELETE ||
+ thd_sql_command(thd) == SQLCOM_DELETE_MULTI ||
+ thd_sql_command(thd) == SQLCOM_UPDATE ||
+ thd_sql_command(thd) == SQLCOM_UPDATE_MULTI);
+ DBUG_ASSERT(m_err_rec);
+ if (m_err_rec)
{
- case SQLCOM_DELETE:
- case SQLCOM_DELETE_MULTI:
- case SQLCOM_UPDATE:
- case SQLCOM_UPDATE_MULTI:
- if (m_err_rec)
+ uint max_length;
+ char buf[MAX_KEY_LENGTH];
+ String str(buf,sizeof(buf),system_charset_info);
+ uint32 part_id;
+ str.length(0);
+ str.append("(");
+ str.append_ulonglong(m_last_part);
+ str.append(" != ");
+ if (get_part_for_delete(m_err_rec, m_rec0, m_part_info, &part_id))
+ str.append("?");
+ else
+ str.append_ulonglong(part_id);
+ str.append(")");
+ append_row_to_str(str);
+
+ /* Log this error, so the DBA can notice it and fix it! */
+ sql_print_error("Table '%-192s' corrupted: row in wrong partition: %s\n"
+ "Please REPAIR the table!",
+ table->s->table_name.str,
+ str.c_ptr_safe());
+
+ max_length= (MYSQL_ERRMSG_SIZE - (uint) strlen(ER(ER_ROW_IN_WRONG_PARTITION)));
+ if (str.length() >= max_length)
{
- uint max_length;
- char buf[MAX_KEY_LENGTH];
- const char *msg= "Found a row in wrong partition (";
- String str(buf,sizeof(buf),system_charset_info);
- uint32 part_id;
- /* Should only happen on DELETE or UPDATE! */
- str.length(0);
- str.append_ulonglong(m_last_part);
- str.append(" != ");
- if (!get_part_for_delete(m_err_rec, m_rec0, m_part_info, &part_id))
- {
- str.append_ulonglong(part_id);
- }
- str.append(")");
- append_row_to_str(str);
- /* Log this error, so the DBA can notice it and fix it! */
- sql_print_error("Table '%-192s' corrupted: %s%s\n"
- "Please CHECK and REPAIR the table!",
- table->s->table_name.str, msg, str.c_ptr_safe());
-
- max_length= (MYSQL_ERRMSG_SIZE-
- (uint) strlen(msg));
- if (str.length() >= max_length)
- {
- str.length(max_length-4);
- str.append(STRING_WITH_LEN("..."));
- }
- my_printf_error(ER_NO_PARTITION_FOR_GIVEN_VALUE, "%s%s", MYF(0),
- msg, str.c_ptr_safe());
- m_err_rec= NULL;
- DBUG_VOID_RETURN;
+ str.length(max_length-4);
+ str.append(STRING_WITH_LEN("..."));
}
- default:
- {
- if (!(thd->lex->alter_info.flags & ALTER_TRUNCATE_PARTITION))
- {
- m_part_info->print_no_partition_found(table, errflag);
- DBUG_VOID_RETURN;
- }
- }
- /* fall through to generic error handling. */
+ my_error(ER_ROW_IN_WRONG_PARTITION, MYF(0), str.c_ptr_safe());
+ m_err_rec= NULL;
+ DBUG_VOID_RETURN;
}
+ /* fall through to generic error handling. */
}
/* In case m_file has not been initialized, like in bug#42438 */
@@ -7350,49 +8072,48 @@ bool ha_partition::get_error_message(int error, String *buf)
/****************************************************************************
- MODULE handler characteristics
+ MODULE in-place ALTER
****************************************************************************/
/**
+ Get table flags.
+*/
+
+handler::Table_flags ha_partition::table_flags() const
+{
+ uint first_used_partition= 0;
+ DBUG_ENTER("ha_partition::table_flags");
+ if (m_handler_status < handler_initialized ||
+ m_handler_status >= handler_closed)
+ DBUG_RETURN(PARTITION_ENABLED_TABLE_FLAGS);
+
+ if (get_lock_type() != F_UNLCK)
+ {
+ /*
+ The flags are cached after external_lock, and may depend on isolation
+ level. So we should use a locked partition to get the correct flags.
+ */
+ first_used_partition= bitmap_get_first_set(&m_part_info->lock_partitions);
+ if (first_used_partition == MY_BIT_NONE)
+ first_used_partition= 0;
+ }
+ DBUG_RETURN((m_file[first_used_partition]->ha_table_flags() &
+ ~(PARTITION_DISABLED_TABLE_FLAGS)) |
+ (PARTITION_ENABLED_TABLE_FLAGS));
+}
+
+
+/**
alter_table_flags must be on handler/table level, not on hton level
due to the ha_partition hton does not know what the underlying hton is.
*/
uint ha_partition::alter_table_flags(uint flags)
{
- uint flags_to_return, flags_to_check;
+ uint flags_to_return;
DBUG_ENTER("ha_partition::alter_table_flags");
flags_to_return= ht->alter_table_flags(flags);
- flags_to_return|= m_file[0]->alter_table_flags(flags);
+ flags_to_return|= m_file[0]->alter_table_flags(flags);
- /*
- If one partition fails we must be able to revert the change for the other,
- already altered, partitions. So both ADD and DROP can only be supported in
- pairs.
- */
- flags_to_check= HA_INPLACE_ADD_INDEX_NO_READ_WRITE;
- flags_to_check|= HA_INPLACE_DROP_INDEX_NO_READ_WRITE;
- if ((flags_to_return & flags_to_check) != flags_to_check)
- flags_to_return&= ~flags_to_check;
- flags_to_check= HA_INPLACE_ADD_UNIQUE_INDEX_NO_READ_WRITE;
- flags_to_check|= HA_INPLACE_DROP_UNIQUE_INDEX_NO_READ_WRITE;
- if ((flags_to_return & flags_to_check) != flags_to_check)
- flags_to_return&= ~flags_to_check;
- flags_to_check= HA_INPLACE_ADD_PK_INDEX_NO_READ_WRITE;
- flags_to_check|= HA_INPLACE_DROP_PK_INDEX_NO_READ_WRITE;
- if ((flags_to_return & flags_to_check) != flags_to_check)
- flags_to_return&= ~flags_to_check;
- flags_to_check= HA_INPLACE_ADD_INDEX_NO_WRITE;
- flags_to_check|= HA_INPLACE_DROP_INDEX_NO_WRITE;
- if ((flags_to_return & flags_to_check) != flags_to_check)
- flags_to_return&= ~flags_to_check;
- flags_to_check= HA_INPLACE_ADD_UNIQUE_INDEX_NO_WRITE;
- flags_to_check|= HA_INPLACE_DROP_UNIQUE_INDEX_NO_WRITE;
- if ((flags_to_return & flags_to_check) != flags_to_check)
- flags_to_return&= ~flags_to_check;
- flags_to_check= HA_INPLACE_ADD_PK_INDEX_NO_WRITE;
- flags_to_check|= HA_INPLACE_DROP_PK_INDEX_NO_WRITE;
- if ((flags_to_return & flags_to_check) != flags_to_check)
- flags_to_return&= ~flags_to_check;
DBUG_RETURN(flags_to_return);
}
@@ -7421,244 +8142,260 @@ bool ha_partition::check_if_incompatible_data(HA_CREATE_INFO *create_info,
/**
- Helper class for [final_]add_index, see handler.h
+ Support of in-place alter table.
*/
-class ha_partition_add_index : public handler_add_index
+/**
+ Helper class for in-place alter, see handler.h
+*/
+
+class ha_partition_inplace_ctx : public inplace_alter_handler_ctx
{
public:
- handler_add_index **add_array;
- ha_partition_add_index(TABLE* table_arg, KEY* key_info_arg,
- uint num_of_keys_arg)
- : handler_add_index(table_arg, key_info_arg, num_of_keys_arg)
+ inplace_alter_handler_ctx **handler_ctx_array;
+private:
+ uint m_tot_parts;
+
+public:
+ ha_partition_inplace_ctx(THD *thd, uint tot_parts)
+ : inplace_alter_handler_ctx(),
+ handler_ctx_array(NULL),
+ m_tot_parts(tot_parts)
{}
- ~ha_partition_add_index() {}
+
+ ~ha_partition_inplace_ctx()
+ {
+ if (handler_ctx_array)
+ {
+ for (uint index= 0; index < m_tot_parts; index++)
+ delete handler_ctx_array[index];
+ }
+ }
};
-/**
- Support of in-place add/drop index
+enum_alter_inplace_result
+ha_partition::check_if_supported_inplace_alter(TABLE *altered_table,
+ Alter_inplace_info *ha_alter_info)
+{
+ uint index= 0;
+ enum_alter_inplace_result result= HA_ALTER_INPLACE_NO_LOCK;
+ ha_partition_inplace_ctx *part_inplace_ctx;
+ bool first_is_set= false;
+ THD *thd= ha_thd();
+
+ DBUG_ENTER("ha_partition::check_if_supported_inplace_alter");
+ /*
+ Support inplace change of KEY () -> KEY ALGORITHM = N ().
+ Any other change would set partition_changed in
+ prep_alter_part_table() in mysql_alter_table().
+ */
+ if (ha_alter_info->alter_info->flags == Alter_info::ALTER_PARTITION)
+ DBUG_RETURN(HA_ALTER_INPLACE_NO_LOCK);
- @param table_arg Table to add index to
- @param key_info Struct over the new keys to add
- @param num_of_keys Number of keys to add
- @param[out] add Data to be submitted with final_add_index
+ part_inplace_ctx=
+ new (thd->mem_root) ha_partition_inplace_ctx(thd, m_tot_parts);
+ if (!part_inplace_ctx)
+ DBUG_RETURN(HA_ALTER_ERROR);
- @return Operation status
- @retval 0 Success
- @retval != 0 Failure (error code returned, and all operations rollbacked)
-*/
+ part_inplace_ctx->handler_ctx_array= (inplace_alter_handler_ctx **)
+ thd->alloc(sizeof(inplace_alter_handler_ctx *) * (m_tot_parts + 1));
+ if (!part_inplace_ctx->handler_ctx_array)
+ DBUG_RETURN(HA_ALTER_ERROR);
-int ha_partition::add_index(TABLE *table_arg, KEY *key_info, uint num_of_keys,
- handler_add_index **add)
-{
- uint i;
- int ret= 0;
- THD *thd= ha_thd();
- ha_partition_add_index *part_add_index;
+ /* Set all to NULL, including the terminating one. */
+ for (index= 0; index <= m_tot_parts; index++)
+ part_inplace_ctx->handler_ctx_array[index]= NULL;
+
+ ha_alter_info->handler_flags |= Alter_inplace_info::ALTER_PARTITIONED;
+ for (index= 0; index < m_tot_parts; index++)
+ {
+ enum_alter_inplace_result p_result=
+ m_file[index]->check_if_supported_inplace_alter(altered_table,
+ ha_alter_info);
+ part_inplace_ctx->handler_ctx_array[index]= ha_alter_info->handler_ctx;
+
+ if (index == 0)
+ {
+ first_is_set= (ha_alter_info->handler_ctx != NULL);
+ }
+ else if (first_is_set != (ha_alter_info->handler_ctx != NULL))
+ {
+ /* Either none or all partitions must set handler_ctx! */
+ DBUG_ASSERT(0);
+ DBUG_RETURN(HA_ALTER_ERROR);
+ }
+ if (p_result < result)
+ result= p_result;
+ if (result == HA_ALTER_ERROR)
+ break;
+ }
- DBUG_ENTER("ha_partition::add_index");
+ ha_alter_info->handler_ctx= part_inplace_ctx;
/*
- There has already been a check in fix_partition_func in mysql_alter_table
- before this call, which checks for unique/primary key violations of the
- partitioning function. So no need for extra check here.
+ To indicate for future inplace calls that there are several
+ partitions/handlers that need to be committed together,
+ we set group_commit_ctx to the NULL terminated array of
+ the partitions handlers.
*/
-
+ ha_alter_info->group_commit_ctx= part_inplace_ctx->handler_ctx_array;
+
+ DBUG_RETURN(result);
+}
+
+
+bool ha_partition::prepare_inplace_alter_table(TABLE *altered_table,
+ Alter_inplace_info *ha_alter_info)
+{
+ uint index= 0;
+ bool error= false;
+ ha_partition_inplace_ctx *part_inplace_ctx;
+
+ DBUG_ENTER("ha_partition::prepare_inplace_alter_table");
+
/*
- This will be freed at the end of the statement.
- And destroyed at final_add_index. (Sql_alloc does not free in delete).
+ Changing to similar partitioning, only update metadata.
+ Non allowed changes would be catched in prep_alter_part_table().
*/
- part_add_index= new (thd->mem_root)
- ha_partition_add_index(table_arg, key_info, num_of_keys);
- if (!part_add_index)
- DBUG_RETURN(HA_ERR_OUT_OF_MEM);
- part_add_index->add_array= (handler_add_index **)
- thd->alloc(sizeof(void *) * m_tot_parts);
- if (!part_add_index->add_array)
- {
- delete part_add_index;
- DBUG_RETURN(HA_ERR_OUT_OF_MEM);
- }
+ if (ha_alter_info->alter_info->flags == Alter_info::ALTER_PARTITION)
+ DBUG_RETURN(false);
- for (i= 0; i < m_tot_parts; i++)
- {
- if ((ret= m_file[i]->add_index(table_arg, key_info, num_of_keys,
- &part_add_index->add_array[i])))
- goto err;
- }
- *add= part_add_index;
- DBUG_RETURN(ret);
-err:
- /* Rollback all prepared partitions. i - 1 .. 0 */
- while (i)
+ part_inplace_ctx=
+ static_cast<class ha_partition_inplace_ctx*>(ha_alter_info->handler_ctx);
+
+ for (index= 0; index < m_tot_parts && !error; index++)
{
- i--;
- (void) m_file[i]->final_add_index(part_add_index->add_array[i], false);
+ ha_alter_info->handler_ctx= part_inplace_ctx->handler_ctx_array[index];
+ if (m_file[index]->ha_prepare_inplace_alter_table(altered_table,
+ ha_alter_info))
+ error= true;
+ part_inplace_ctx->handler_ctx_array[index]= ha_alter_info->handler_ctx;
}
- delete part_add_index;
- DBUG_RETURN(ret);
+ ha_alter_info->handler_ctx= part_inplace_ctx;
+
+ DBUG_RETURN(error);
}
-/**
- Second phase of in-place add index.
+bool ha_partition::inplace_alter_table(TABLE *altered_table,
+ Alter_inplace_info *ha_alter_info)
+{
+ uint index= 0;
+ bool error= false;
+ ha_partition_inplace_ctx *part_inplace_ctx;
- @param add Info from add_index
- @param commit Should we commit or rollback the add_index operation
+ DBUG_ENTER("ha_partition::inplace_alter_table");
- @return Operation status
- @retval 0 Success
- @retval != 0 Failure (error code returned)
+ /*
+ Changing to similar partitioning, only update metadata.
+ Non allowed changes would be catched in prep_alter_part_table().
+ */
+ if (ha_alter_info->alter_info->flags == Alter_info::ALTER_PARTITION)
+ DBUG_RETURN(false);
- @note If commit is false, index changes are rolled back by dropping the
- added indexes. If commit is true, nothing is done as the indexes
- were already made active in ::add_index()
-*/
+ part_inplace_ctx=
+ static_cast<class ha_partition_inplace_ctx*>(ha_alter_info->handler_ctx);
-int ha_partition::final_add_index(handler_add_index *add, bool commit)
-{
- ha_partition_add_index *part_add_index;
- uint i;
- int ret= 0;
-
- DBUG_ENTER("ha_partition::final_add_index");
-
- if (!add)
+ for (index= 0; index < m_tot_parts && !error; index++)
{
- DBUG_ASSERT(!commit);
- DBUG_RETURN(0);
+ ha_alter_info->handler_ctx= part_inplace_ctx->handler_ctx_array[index];
+ if (m_file[index]->ha_inplace_alter_table(altered_table,
+ ha_alter_info))
+ error= true;
+ part_inplace_ctx->handler_ctx_array[index]= ha_alter_info->handler_ctx;
}
- part_add_index= static_cast<class ha_partition_add_index*>(add);
+ ha_alter_info->handler_ctx= part_inplace_ctx;
- for (i= 0; i < m_tot_parts; i++)
- {
- if ((ret= m_file[i]->final_add_index(part_add_index->add_array[i], commit)))
- goto err;
- DBUG_EXECUTE_IF("ha_partition_fail_final_add_index", {
- /* Simulate a failure by rollback the second partition */
- if (m_tot_parts > 1)
- {
- i++;
- m_file[i]->final_add_index(part_add_index->add_array[i], false);
- /* Set an error that is specific to ha_partition. */
- ret= HA_ERR_NO_PARTITION_FOUND;
- goto err;
- }
- });
- }
- delete part_add_index;
- DBUG_RETURN(ret);
-err:
- uint j;
- uint *key_numbers= NULL;
- KEY *old_key_info= NULL;
- uint num_of_keys= 0;
- int error;
-
- /* How could this happen? Needed to create a covering test case :) */
- DBUG_ASSERT(ret == HA_ERR_NO_PARTITION_FOUND);
+ DBUG_RETURN(error);
+}
- if (i > 0)
- {
- num_of_keys= part_add_index->num_of_keys;
- key_numbers= (uint*) ha_thd()->alloc(sizeof(uint) * num_of_keys);
- if (!key_numbers)
- {
- sql_print_error("Failed with error handling of adding index:\n"
- "committing index failed, and when trying to revert "
- "already committed partitions we failed allocating\n"
- "memory for the index for table '%s'",
- table_share->table_name.str);
- DBUG_RETURN(HA_ERR_OUT_OF_MEM);
- }
- old_key_info= table->key_info;
- /*
- Use the newly added key_info as table->key_info to remove them.
- Note that this requires the subhandlers to use name lookup of the
- index. They must use given table->key_info[key_number], they cannot
- use their local view of the keys, since table->key_info only include
- the indexes to be removed here.
- */
- for (j= 0; j < num_of_keys; j++)
- key_numbers[j]= j;
- table->key_info= part_add_index->key_info;
- }
- for (j= 0; j < m_tot_parts; j++)
+/*
+ Note that this function will try rollback failed ADD INDEX by
+ executing DROP INDEX for the indexes that were committed (if any)
+ before the error occured. This means that the underlying storage
+ engine must be able to drop index in-place with X-lock held.
+ (As X-lock will be held here if new indexes are to be committed)
+*/
+bool ha_partition::commit_inplace_alter_table(TABLE *altered_table,
+ Alter_inplace_info *ha_alter_info,
+ bool commit)
+{
+ ha_partition_inplace_ctx *part_inplace_ctx;
+ bool error= false;
+
+ DBUG_ENTER("ha_partition::commit_inplace_alter_table");
+
+ /*
+ Changing to similar partitioning, only update metadata.
+ Non allowed changes would be catched in prep_alter_part_table().
+ */
+ if (ha_alter_info->alter_info->flags == Alter_info::ALTER_PARTITION)
+ DBUG_RETURN(false);
+
+ part_inplace_ctx=
+ static_cast<class ha_partition_inplace_ctx*>(ha_alter_info->handler_ctx);
+
+ if (commit)
{
- if (j < i)
+ DBUG_ASSERT(ha_alter_info->group_commit_ctx ==
+ part_inplace_ctx->handler_ctx_array);
+ ha_alter_info->handler_ctx= part_inplace_ctx->handler_ctx_array[0];
+ error= m_file[0]->ha_commit_inplace_alter_table(altered_table,
+ ha_alter_info, commit);
+ if (error)
+ goto end;
+ if (ha_alter_info->group_commit_ctx)
{
- /* Remove the newly added index */
- error= m_file[j]->prepare_drop_index(table, key_numbers, num_of_keys);
- if (error || m_file[j]->final_drop_index(table))
+ /*
+ If ha_alter_info->group_commit_ctx is not set to NULL,
+ then the engine did only commit the first partition!
+ The engine is probably new, since both innodb and the default
+ implementation of handler::commit_inplace_alter_table sets it to NULL
+ and simply return false, since it allows metadata changes only.
+ Loop over all other partitions as to follow the protocol!
+ */
+ uint i;
+ DBUG_ASSERT(0);
+ for (i= 1; i < m_tot_parts; i++)
{
- sql_print_error("Failed with error handling of adding index:\n"
- "committing index failed, and when trying to revert "
- "already committed partitions we failed removing\n"
- "the index for table '%s' partition nr %d",
- table_share->table_name.str, j);
+ ha_alter_info->handler_ctx= part_inplace_ctx->handler_ctx_array[i];
+ error|= m_file[i]->ha_commit_inplace_alter_table(altered_table,
+ ha_alter_info,
+ true);
}
+ }
}
- else if (j > i)
+ else
+ {
+ uint i;
+ for (i= 0; i < m_tot_parts; i++)
{
- /* Rollback non finished partitions */
- if (m_file[j]->final_add_index(part_add_index->add_array[j], false))
- {
- /* How could this happen? */
- sql_print_error("Failed with error handling of adding index:\n"
- "Rollback of add_index failed for table\n"
- "'%s' partition nr %d",
- table_share->table_name.str, j);
+ /* Rollback, commit == false, is done for each partition! */
+ ha_alter_info->handler_ctx= part_inplace_ctx->handler_ctx_array[i];
+ if (m_file[i]->ha_commit_inplace_alter_table(altered_table,
+ ha_alter_info, false))
+ error= true;
}
}
- }
- if (i > 0)
- table->key_info= old_key_info;
- delete part_add_index;
- DBUG_RETURN(ret);
-}
-
-int ha_partition::prepare_drop_index(TABLE *table_arg, uint *key_num,
- uint num_of_keys)
-{
- handler **file;
- int ret= 0;
+end:
+ ha_alter_info->handler_ctx= part_inplace_ctx;
- /*
- DROP INDEX does not affect partitioning.
- */
- for (file= m_file; *file; file++)
- if ((ret= (*file)->prepare_drop_index(table_arg, key_num, num_of_keys)))
- break;
- return ret;
+ DBUG_RETURN(error);
}
-int ha_partition::final_drop_index(TABLE *table_arg)
+void ha_partition::notify_table_changed()
{
handler **file;
- int ret= HA_ERR_WRONG_COMMAND;
-
- for (file= m_file; *file; file++)
- if ((ret= (*file)->final_drop_index(table_arg)))
- break;
- return ret;
-}
+ DBUG_ENTER("ha_partition::notify_table_changed");
-/*
- If frm_error() is called then we will use this to to find out what file
- extensions exist for the storage engine. This is also used by the default
- rename_table and delete_table method in handler.cc.
-*/
-
-static const char *ha_partition_ext[]=
-{
- ha_par_ext, NullS
-};
+ for (file= m_file; *file; file++)
+ (*file)->ha_notify_table_changed();
-const char **ha_partition::bas_ext() const
-{ return ha_partition_ext; }
+ DBUG_VOID_RETURN;
+}
uint ha_partition::min_of_the_max_uint(
@@ -7817,8 +8554,8 @@ int ha_partition::reset_auto_increment(ulonglong value)
int res;
DBUG_ENTER("ha_partition::reset_auto_increment");
lock_auto_increment();
- table_share->ha_part_data->auto_inc_initialized= FALSE;
- table_share->ha_part_data->next_auto_inc_val= 0;
+ part_share->auto_inc_initialized= false;
+ part_share->next_auto_inc_val= 0;
do
{
if ((res= (*file)->ha_reset_auto_increment(value)) != 0)
@@ -7832,7 +8569,7 @@ int ha_partition::reset_auto_increment(ulonglong value)
/**
This method is called by update_auto_increment which in turn is called
by the individual handlers as part of write_row. We use the
- table_share->ha_part_data->next_auto_inc_val, or search all
+ part_share->next_auto_inc_val, or search all
partitions for the highest auto_increment_value if not initialized or
if auto_increment field is a secondary part of a key, we must search
every partition when holding a mutex to be sure of correctness.
@@ -7866,7 +8603,7 @@ void ha_partition::get_auto_increment(ulonglong offset, ulonglong increment,
/* Only nb_desired_values = 1 makes sense */
(*file)->get_auto_increment(offset, increment, 1,
&first_value_part, &nb_reserved_values_part);
- if (first_value_part == ~(ulonglong)(0)) // error in one partition
+ if (first_value_part == ULONGLONG_MAX) // error in one partition
{
*first_value= first_value_part;
/* log that the error was between table/partition handler */
@@ -7885,9 +8622,9 @@ void ha_partition::get_auto_increment(ulonglong offset, ulonglong increment,
/*
This is initialized in the beginning of the first write_row call.
*/
- DBUG_ASSERT(table_share->ha_part_data->auto_inc_initialized);
+ DBUG_ASSERT(part_share->auto_inc_initialized);
/*
- Get a lock for handling the auto_increment in table_share->ha_part_data
+ Get a lock for handling the auto_increment in part_share
for avoiding two concurrent statements getting the same number.
*/
@@ -7914,9 +8651,8 @@ void ha_partition::get_auto_increment(ulonglong offset, ulonglong increment,
}
/* this gets corrected (for offset/increment) in update_auto_increment */
- *first_value= table_share->ha_part_data->next_auto_inc_val;
- table_share->ha_part_data->next_auto_inc_val+=
- nb_desired_values * increment;
+ *first_value= part_share->next_auto_inc_val;
+ part_share->next_auto_inc_val+= nb_desired_values * increment;
unlock_auto_increment();
DBUG_PRINT("info", ("*first_value: %lu", (ulong) *first_value));
@@ -7931,14 +8667,19 @@ void ha_partition::release_auto_increment()
if (table->s->next_number_keypart)
{
- for (uint i= 0; i < m_tot_parts; i++)
+ uint i;
+ for (i= bitmap_get_first_set(&m_part_info->lock_partitions);
+ i < m_tot_parts;
+ i= bitmap_get_next_set(&m_part_info->lock_partitions, i))
+ {
m_file[i]->ha_release_auto_increment();
+ }
}
else if (next_insert_id)
{
ulonglong next_auto_inc_val;
lock_auto_increment();
- next_auto_inc_val= table_share->ha_part_data->next_auto_inc_val;
+ next_auto_inc_val= part_share->next_auto_inc_val;
/*
If the current auto_increment values is lower than the reserved
value, and the reserved value was reserved by this thread,
@@ -7953,10 +8694,10 @@ void ha_partition::release_auto_increment()
with SET INSERT_ID, i.e. forced/non generated values.
*/
if (thd->auto_inc_intervals_forced.maximum() < next_insert_id)
- table_share->ha_part_data->next_auto_inc_val= next_insert_id;
+ part_share->next_auto_inc_val= next_insert_id;
}
- DBUG_PRINT("info", ("table_share->ha_part_data->next_auto_inc_val: %lu",
- (ulong) table_share->ha_part_data->next_auto_inc_val));
+ DBUG_PRINT("info", ("part_share->next_auto_inc_val: %lu",
+ (ulong) part_share->next_auto_inc_val));
/* Unlock the multi row statement lock taken in get_auto_increment */
if (auto_increment_safe_stmt_log_lock)
@@ -7980,6 +8721,27 @@ void ha_partition::init_table_handle_for_HANDLER()
}
+/**
+ Return the checksum of the table (all partitions)
+*/
+
+uint ha_partition::checksum() const
+{
+ ha_checksum sum= 0;
+
+ DBUG_ENTER("ha_partition::checksum");
+ if ((table_flags() & (HA_HAS_OLD_CHECKSUM | HA_HAS_NEW_CHECKSUM)))
+ {
+ handler **file= m_file;
+ do
+ {
+ sum+= (*file)->checksum();
+ } while (*(++file));
+ }
+ DBUG_RETURN(sum);
+}
+
+
/****************************************************************************
MODULE enable/disable indexes
****************************************************************************/
@@ -7999,6 +8761,7 @@ int ha_partition::disable_indexes(uint mode)
handler **file;
int error= 0;
+ DBUG_ASSERT(bitmap_is_set_all(&(m_part_info->lock_partitions)));
for (file= m_file; *file; file++)
{
if ((error= (*file)->ha_disable_indexes(mode)))
@@ -8023,6 +8786,7 @@ int ha_partition::enable_indexes(uint mode)
handler **file;
int error= 0;
+ DBUG_ASSERT(bitmap_is_set_all(&(m_part_info->lock_partitions)));
for (file= m_file; *file; file++)
{
if ((error= (*file)->ha_enable_indexes(mode)))
@@ -8047,6 +8811,7 @@ int ha_partition::indexes_are_disabled(void)
handler **file;
int error= 0;
+ DBUG_ASSERT(bitmap_is_set_all(&(m_part_info->lock_partitions)));
for (file= m_file; *file; file++)
{
if ((error= (*file)->indexes_are_disabled()))
@@ -8095,7 +8860,7 @@ int ha_partition::check_misplaced_rows(uint read_part_id, bool repair)
while (true)
{
- if ((result= m_file[read_part_id]->rnd_next(m_rec0)))
+ if ((result= m_file[read_part_id]->ha_rnd_next(m_rec0)))
{
if (result == HA_ERR_RECORD_DELETED)
continue;
@@ -8104,7 +8869,7 @@ int ha_partition::check_misplaced_rows(uint read_part_id, bool repair)
if (num_misplaced_rows > 0)
{
- print_admin_msg(ha_thd(), MI_MAX_MSG_BUF, "warning",
+ print_admin_msg(ha_thd(), MYSQL_ERRMSG_SIZE, "warning",
table_share->db.str, table->alias,
opt_op_name[REPAIR_PARTS],
"Moved %lld misplaced rows",
@@ -8126,7 +8891,7 @@ int ha_partition::check_misplaced_rows(uint read_part_id, bool repair)
if (!repair)
{
/* Check. */
- print_admin_msg(ha_thd(), MI_MAX_MSG_BUF, "error",
+ print_admin_msg(ha_thd(), MYSQL_ERRMSG_SIZE, "error",
table_share->db.str, table->alias,
opt_op_name[CHECK_PARTS],
"Found a misplaced row");
@@ -8174,7 +8939,7 @@ int ha_partition::check_misplaced_rows(uint read_part_id, bool repair)
correct_part_id,
str.c_ptr_safe());
}
- print_admin_msg(ha_thd(), MI_MAX_MSG_BUF, "error",
+ print_admin_msg(ha_thd(), MYSQL_ERRMSG_SIZE, "error",
table_share->db.str, table->alias,
opt_op_name[REPAIR_PARTS],
"Failed to move/insert a row"
@@ -8222,7 +8987,8 @@ int ha_partition::check_misplaced_rows(uint read_part_id, bool repair)
#define KEY_PARTITIONING_CHANGED_STR \
- "KEY () partitioning changed, please run:\nALTER TABLE %s.%s %s"
+ "KEY () partitioning changed, please run:\n" \
+ "ALTER TABLE %s.%s ALGORITHM = INPLACE %s"
int ha_partition::check_for_upgrade(HA_CHECK_OPT *check_opt)
{
@@ -8316,7 +9082,7 @@ int ha_partition::check_for_upgrade(HA_CHECK_OPT *check_opt)
part_buf))
{
/* Error creating admin message (too long string?). */
- print_admin_msg(thd, MI_MAX_MSG_BUF, "error",
+ print_admin_msg(thd, MYSQL_ERRMSG_SIZE, "error",
table_share->db.str, table->alias,
opt_op_name[CHECK_PARTS],
KEY_PARTITIONING_CHANGED_STR,
diff --git a/sql/ha_partition.h b/sql/ha_partition.h
index c044f9daa9c..33a1a8ab43b 100644
--- a/sql/ha_partition.h
+++ b/sql/ha_partition.h
@@ -2,24 +2,21 @@
#define HA_PARTITION_INCLUDED
/*
- Copyright (c) 2005, 2013, Oracle and/or its affiliates.
+ Copyright (c) 2005, 2012, Oracle and/or its affiliates.
+ Copyright (c) 2009, 2013, Monty Program Ab & SkySQL 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 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.
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
-
-#ifdef __GNUC__
-#pragma interface /* gcc class implementation */
-#endif
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
#include "sql_partition.h" /* part_id_range, partition_element */
#include "queues.h" /* QUEUE */
@@ -30,24 +27,95 @@ enum partition_keywords
PKW_COLUMNS, PKW_ALGORITHM
};
+
#define PARTITION_BYTES_IN_POS 2
-#define PARTITION_ENABLED_TABLE_FLAGS (HA_FILE_BASED | \
- HA_REC_NOT_IN_SEQ | \
- HA_CAN_REPAIR)
-#define PARTITION_DISABLED_TABLE_FLAGS (HA_CAN_GEOMETRY | \
- HA_CAN_FULLTEXT | \
- HA_DUPLICATE_POS | \
- HA_CAN_SQL_HANDLER | \
- HA_CAN_INSERT_DELAYED)
-
-/* First 4 bytes in the .par file is the number of 32-bit words in the file */
-#define PAR_WORD_SIZE 4
-/* offset to the .par file checksum */
-#define PAR_CHECKSUM_OFFSET 4
-/* offset to the total number of partitions */
-#define PAR_NUM_PARTS_OFFSET 8
-/* offset to the engines array */
-#define PAR_ENGINES_OFFSET 12
+
+
+/** Struct used for partition_name_hash */
+typedef struct st_part_name_def
+{
+ uchar *partition_name;
+ uint length;
+ uint32 part_id;
+ my_bool is_subpart;
+} PART_NAME_DEF;
+
+/** class where to save partitions Handler_share's */
+class Parts_share_refs
+{
+public:
+ uint num_parts; /**< Size of ha_share array */
+ Handler_share **ha_shares; /**< Storage for each part */
+ Parts_share_refs()
+ {
+ num_parts= 0;
+ ha_shares= NULL;
+ }
+ ~Parts_share_refs()
+ {
+ uint i;
+ for (i= 0; i < num_parts; i++)
+ if (ha_shares[i])
+ delete ha_shares[i];
+ if (ha_shares)
+ delete [] ha_shares;
+ }
+ bool init(uint arg_num_parts)
+ {
+ DBUG_ASSERT(!num_parts && !ha_shares);
+ num_parts= arg_num_parts;
+ /* Allocate an array of Handler_share pointers */
+ ha_shares= new Handler_share *[num_parts];
+ if (!ha_shares)
+ {
+ num_parts= 0;
+ return true;
+ }
+ memset(ha_shares, 0, sizeof(Handler_share*) * num_parts);
+ return false;
+ }
+};
+
+
+/**
+ Partition specific Handler_share.
+*/
+class Partition_share : public Handler_share
+{
+public:
+ bool auto_inc_initialized;
+ mysql_mutex_t auto_inc_mutex; /**< protecting auto_inc val */
+ ulonglong next_auto_inc_val; /**< first non reserved value */
+ /**
+ Hash of partition names. Initialized in the first ha_partition::open()
+ for the table_share. After that it is read-only, i.e. no locking required.
+ */
+ bool partition_name_hash_initialized;
+ HASH partition_name_hash;
+ /** Storage for each partitions Handler_share */
+ Parts_share_refs *partitions_share_refs;
+ Partition_share() {}
+ ~Partition_share()
+ {
+ DBUG_ENTER("Partition_share::~Partition_share");
+ mysql_mutex_destroy(&auto_inc_mutex);
+ if (partition_name_hash_initialized)
+ my_hash_free(&partition_name_hash);
+ if (partitions_share_refs)
+ delete partitions_share_refs;
+ DBUG_VOID_RETURN;
+ }
+ bool init(uint num_parts);
+ void lock_auto_inc()
+ {
+ mysql_mutex_lock(&auto_inc_mutex);
+ }
+ void unlock_auto_inc()
+ {
+ mysql_mutex_unlock(&auto_inc_mutex);
+ }
+};
+
extern "C" int cmp_key_rowid_part_id(void *ptr, uchar *ref1, uchar *ref2);
@@ -60,13 +128,14 @@ private:
partition_index_first= 1,
partition_index_first_unordered= 2,
partition_index_last= 3,
- partition_read_range = 4,
- partition_no_index_scan= 5
+ partition_index_read_last= 4,
+ partition_read_range = 5,
+ partition_no_index_scan= 6
};
/* Data for the partition handler */
int m_mode; // Open mode
uint m_open_test_lock; // Open test_if_locked
- char *m_file_buffer; // Content of the .par file
+ uchar *m_file_buffer; // Content of the .par file
char *m_name_buffer_ptr; // Pointer to first partition name
MEM_ROOT m_mem_root;
plugin_ref *m_engine_array; // Array of types of the handlers
@@ -126,8 +195,6 @@ private:
uint m_tot_parts; // Total number of partitions;
uint m_num_locks; // For engines like ha_blackhole, which needs no locks
uint m_last_part; // Last file that we update,write,read
- int m_lock_type; // Remembers type of last
- // external_lock
part_id_range m_part_spec; // Which parts to scan
uint m_scan_value; // Value passed in rnd_init
// call
@@ -197,16 +264,25 @@ private:
ha_rows m_bulk_inserted_rows;
/** used for prediction of start_bulk_insert rows */
enum_monotonicity_info m_part_func_monotonicity_info;
+ /** keep track of locked partitions */
+ MY_BITMAP m_locked_partitions;
+ /** Stores shared auto_increment etc. */
+ Partition_share *part_share;
+ /** Temporary storage for new partitions Handler_shares during ALTER */
+ List<Parts_share_refs> m_new_partitions_share_refs;
/** Sorted array of partition ids in descending order of number of rows. */
uint32 *m_part_ids_sorted_by_num_of_records;
/* Compare function for my_qsort2, for reversed order. */
static int compare_number_of_records(ha_partition *me,
const uint32 *a,
const uint32 *b);
+ /** keep track of partitions to call ha_reset */
+ MY_BITMAP m_partitions_to_reset;
/** partitions that returned HA_ERR_KEY_NOT_FOUND. */
MY_BITMAP m_key_not_found_partitions;
bool m_key_not_found;
public:
+ Partition_share *get_part_share() { return part_share; }
handler *clone(const char *name, MEM_ROOT *mem_root);
virtual void set_part_info(partition_info *part_info)
{
@@ -250,7 +326,7 @@ public:
chance for the handler to add any interesting comments to the table
comments not provided by the users comment.
- create_handler_files is called before opening a new handler object
+ create_partitioning_metadata is called before opening a new handler object
with openfrm to call create. It is used to create any local handler
object needed in opening the object in openfrm
-------------------------------------------------------------------------
@@ -259,9 +335,8 @@ public:
virtual int rename_table(const char *from, const char *to);
virtual int create(const char *name, TABLE *form,
HA_CREATE_INFO *create_info);
- virtual int create_handler_files(const char *name,
- const char *old_name, int action_flag,
- HA_CREATE_INFO *create_info);
+ virtual int create_partitioning_metadata(const char *name,
+ const char *old_name, int action_flag);
virtual void update_create_info(HA_CREATE_INFO *create_info);
virtual char *update_table_comment(const char *comment);
virtual int change_partitions(HA_CREATE_INFO *create_info,
@@ -282,7 +357,6 @@ public:
virtual bool check_if_incompatible_data(HA_CREATE_INFO *create_info,
uint table_changes);
private:
- int prepare_for_rename();
int copy_partitions(ulonglong * const copied, ulonglong * const deleted);
void cleanup_new_partition(uint part_count);
int prepare_new_partition(TABLE *table, HA_CREATE_INFO *create_info,
@@ -290,11 +364,10 @@ private:
partition_element *p_elem,
uint disable_non_uniq_indexes);
/*
- delete_table, rename_table and create uses very similar logic which
+ delete_table and rename_table uses very similar logic which
is packed into this routine.
*/
- int del_ren_cre_table(const char *from, const char *to,
- TABLE *table_arg, HA_CREATE_INFO *create_info);
+ uint del_ren_table(const char *from, const char *to);
/*
One method to create the table_name.par file containing the names of the
underlying partitions, their engine and the number of partitions.
@@ -311,9 +384,16 @@ private:
int set_up_table_before_create(TABLE *table_arg,
const char *partition_name_with_path,
HA_CREATE_INFO *info,
- uint part_id,
partition_element *p_elem);
partition_element *find_partition_element(uint part_id);
+ bool insert_partition_name_in_hash(const char *name, uint part_id,
+ bool is_subpart);
+ bool populate_partition_name_hash();
+ Partition_share *get_share();
+ bool set_ha_share_ref(Handler_share **ha_share);
+ void fix_data_dir(char* path);
+ bool init_partition_bitmaps();
+ void free_partition_bitmaps();
public:
@@ -373,6 +453,18 @@ public:
virtual void try_semi_consistent_read(bool);
/*
+ NOTE: due to performance and resource issues with many partitions,
+ we only use the m_psi on the ha_partition handler, excluding all
+ partitions m_psi.
+ */
+#ifdef HAVE_M_PSI_PER_PARTITION
+ /*
+ Bind the table/handler thread to track table i/o.
+ */
+ virtual void unbind_psi();
+ virtual void rebind_psi();
+#endif
+ /*
-------------------------------------------------------------------------
MODULE change record
-------------------------------------------------------------------------
@@ -397,7 +489,7 @@ public:
virtual int delete_row(const uchar * buf);
virtual int delete_all_rows(void);
virtual int truncate();
- virtual void start_bulk_insert(ha_rows rows);
+ virtual void start_bulk_insert(ha_rows rows, uint flags);
virtual int end_bulk_insert();
private:
ha_rows guess_bulk_insert_rows();
@@ -417,10 +509,13 @@ public:
virtual bool is_fatal_error(int error, uint flags)
{
if (!handler::is_fatal_error(error, flags) ||
- error == HA_ERR_NO_PARTITION_FOUND)
+ error == HA_ERR_NO_PARTITION_FOUND ||
+ error == HA_ERR_NOT_IN_LOCK_PARTITIONS)
return FALSE;
return TRUE;
}
+
+
/*
-------------------------------------------------------------------------
MODULE full table scan
@@ -545,7 +640,6 @@ private:
int handle_ordered_next(uchar * buf, bool next_same);
int handle_ordered_prev(uchar * buf);
void return_top_record(uchar * buf);
- void column_bitmaps_signal();
public:
/*
-------------------------------------------------------------------------
@@ -571,13 +665,17 @@ public:
private:
my_bool reg_query_cache_dependant_table(THD *thd,
- char *key, uint key_len, uint8 type,
+ char *engine_key,
+ uint engine_key_len,
+ char *query_key, uint query_key_len,
+ uint8 type,
Query_cache *cache,
Query_cache_block_table
**block_table,
handler *file, uint *n);
static const uint NO_CURRENT_PART_ID;
int loop_extra(enum ha_extra_function operation);
+ int loop_extra_alter(enum ha_extra_function operations);
void late_extra_cache(uint partition_id);
void late_extra_no_cache(uint partition_id);
void prepare_extra_cache(uint cachesize);
@@ -646,6 +744,9 @@ public:
virtual uint8 table_cache_type();
virtual ha_rows records();
+ /* Calculate hash value for PARTITION BY KEY tables. */
+ static uint32 calculate_key_hash_value(Field **field_array);
+
/*
-------------------------------------------------------------------------
MODULE print messages
@@ -825,17 +926,7 @@ public:
HA_CAN_INSERT_DELAYED, HA_PRIMARY_KEY_REQUIRED_FOR_POSITION is disabled
until further investigated.
*/
- virtual Table_flags table_flags() const
- {
- DBUG_ENTER("ha_partition::table_flags");
- if (m_handler_status < handler_initialized ||
- m_handler_status >= handler_closed)
- DBUG_RETURN(PARTITION_ENABLED_TABLE_FLAGS);
-
- DBUG_RETURN((m_file[0]->ha_table_flags() &
- ~(PARTITION_DISABLED_TABLE_FLAGS)) |
- (PARTITION_ENABLED_TABLE_FLAGS));
- }
+ virtual Table_flags table_flags() const;
/*
This is a bitmap of flags that says how the storage engine
@@ -908,10 +999,6 @@ public:
*/
virtual uint alter_table_flags(uint flags);
/*
- extensions of table handler files
- */
- virtual const char **bas_ext() const;
- /*
unireg.cc will call the following to make sure that the storage engine
can handle the data it is about to send.
@@ -980,16 +1067,15 @@ private:
/* lock already taken */
if (auto_increment_safe_stmt_log_lock)
return;
- DBUG_ASSERT(table_share->ha_part_data && !auto_increment_lock);
+ DBUG_ASSERT(!auto_increment_lock);
if(table_share->tmp_table == NO_TMP_TABLE)
{
auto_increment_lock= TRUE;
- mysql_mutex_lock(&table_share->ha_part_data->LOCK_auto_inc);
+ part_share->lock_auto_inc();
}
}
virtual void unlock_auto_increment()
{
- DBUG_ASSERT(table_share->ha_part_data);
/*
If auto_increment_safe_stmt_log_lock is true, we have to keep the lock.
It will be set to false and thus unlocked at the end of the statement by
@@ -997,7 +1083,7 @@ private:
*/
if(auto_increment_lock && !auto_increment_safe_stmt_log_lock)
{
- mysql_mutex_unlock(&table_share->ha_part_data->LOCK_auto_inc);
+ part_share->unlock_auto_inc();
auto_increment_lock= FALSE;
}
}
@@ -1006,10 +1092,10 @@ private:
ulonglong nr= (((Field_num*) field)->unsigned_flag ||
field->val_int() > 0) ? field->val_int() : 0;
lock_auto_increment();
- DBUG_ASSERT(table_share->ha_part_data->auto_inc_initialized == TRUE);
+ DBUG_ASSERT(part_share->auto_inc_initialized);
/* must check when the mutex is taken */
- if (nr >= table_share->ha_part_data->next_auto_inc_val)
- table_share->ha_part_data->next_auto_inc_val= nr + 1;
+ if (nr >= part_share->next_auto_inc_val)
+ part_share->next_auto_inc_val= nr + 1;
unlock_auto_increment();
}
@@ -1075,18 +1161,23 @@ public:
/*
-------------------------------------------------------------------------
- MODULE on-line ALTER TABLE
+ MODULE in-place ALTER TABLE
-------------------------------------------------------------------------
These methods are in the handler interface. (used by innodb-plugin)
- They are used for on-line/fast alter table add/drop index:
+ They are used for in-place alter table:
-------------------------------------------------------------------------
*/
- virtual int add_index(TABLE *table_arg, KEY *key_info, uint num_of_keys,
- handler_add_index **add);
- virtual int final_add_index(handler_add_index *add, bool commit);
- virtual int prepare_drop_index(TABLE *table_arg, uint *key_num,
- uint num_of_keys);
- virtual int final_drop_index(TABLE *table_arg);
+ virtual enum_alter_inplace_result
+ check_if_supported_inplace_alter(TABLE *altered_table,
+ Alter_inplace_info *ha_alter_info);
+ virtual bool prepare_inplace_alter_table(TABLE *altered_table,
+ Alter_inplace_info *ha_alter_info);
+ virtual bool inplace_alter_table(TABLE *altered_table,
+ Alter_inplace_info *ha_alter_info);
+ virtual bool commit_inplace_alter_table(TABLE *altered_table,
+ Alter_inplace_info *ha_alter_info,
+ bool commit);
+ virtual void notify_table_changed();
/*
-------------------------------------------------------------------------
@@ -1130,6 +1221,7 @@ public:
int check_misplaced_rows(uint read_part_id, bool repair);
void append_row_to_str(String &str);
public:
+
/*
-------------------------------------------------------------------------
Admin commands not supported currently (almost purely MyISAM routines)
@@ -1140,8 +1232,8 @@ public:
virtual int restore(THD* thd, HA_CHECK_OPT *check_opt);
virtual int dump(THD* thd, int fd = -1);
virtual int net_read_dump(NET* net);
- virtual uint checksum() const;
*/
+ virtual uint checksum() const;
/* Enabled keycache for performance reasons, WL#4571 */
virtual int assign_to_keycache(THD* thd, HA_CHECK_OPT *check_opt);
virtual int preload_keys(THD* thd, HA_CHECK_OPT* check_opt);
@@ -1190,13 +1282,7 @@ public:
DBUG_ASSERT(h == m_file[i]->ht);
return h;
}
-
#ifdef WITH_WSREP
- void wsrep_reset_files()
- {
- for (uint i=0; i < m_tot_parts; i++)
- m_file[i]->ft_handler= 0;
- }
virtual int wsrep_db_type() const;
#endif /* WITH_WSREP */
diff --git a/sql/handler.cc b/sql/handler.cc
index 2337eaced55..f3b908ceb1e 100644
--- a/sql/handler.cc
+++ b/sql/handler.cc
@@ -11,8 +11,8 @@
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., 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
/** @file handler.cc
@@ -20,10 +20,7 @@
Handler-calling-functions
*/
-#ifdef USE_PRAGMA_IMPLEMENTATION
-#pragma implementation // gcc: Class implementation
-#endif
-
+#include <my_global.h>
#include "sql_priv.h"
#include "unireg.h"
#include "rpl_handler.h"
@@ -34,7 +31,7 @@
#include "sql_parse.h" // check_stack_overrun
#include "sql_acl.h" // SUPER_ACL
#include "sql_base.h" // free_io_cache
-#include "discover.h" // writefrm
+#include "discover.h" // extension_based_table_discovery, etc
#include "log_event.h" // *_rows_log_event
#include "create_options.h"
#include "rpl_filter.h"
@@ -42,9 +39,9 @@
#include "transaction.h"
#include "myisam.h"
#include "probes_mysql.h"
+#include <mysql/psi/mysql_table.h>
#include "debug_sync.h" // DEBUG_SYNC
#include "sql_audit.h"
-#include "../mysys/my_handler_errors.h"
#ifdef WITH_PARTITION_STORAGE_ENGINE
#include "ha_partition.h"
@@ -56,6 +53,7 @@
#ifdef WITH_WSREP
#include "wsrep_mysqld.h"
+#include "wsrep_xid.h"
#endif
/*
While we have legacy_db_type, we have this array to
@@ -69,7 +67,7 @@ static handlerton *installed_htons[128];
#define BITMAP_STACKBUF_SIZE (128/8)
KEY_CREATE_INFO default_key_create_info=
- { HA_KEY_ALG_UNDEF, 0, {NullS, 0}, {NullS, 0} };
+{ HA_KEY_ALG_UNDEF, 0, {NullS, 0}, {NullS, 0}, true };
/* number of entries in handlertons[] */
ulong total_ha= 0;
@@ -104,6 +102,7 @@ uint known_extensions_id= 0;
static int commit_one_phase_2(THD *thd, bool all, THD_TRANS *trans,
bool is_real_trans);
+
static plugin_ref ha_default_plugin(THD *thd)
{
if (thd->variables.table_plugin)
@@ -126,7 +125,7 @@ handlerton *ha_default_handlerton(THD *thd)
{
plugin_ref plugin= ha_default_plugin(thd);
DBUG_ASSERT(plugin);
- handlerton *hton= plugin_data(plugin, handlerton*);
+ handlerton *hton= plugin_hton(plugin);
DBUG_ASSERT(hton);
return hton;
}
@@ -157,7 +156,7 @@ redo:
if ((plugin= my_plugin_lock_by_name(thd, name, MYSQL_STORAGE_ENGINE_PLUGIN)))
{
- handlerton *hton= plugin_data(plugin, handlerton *);
+ handlerton *hton= plugin_hton(plugin);
if (hton && !(hton->flags & HTON_NOT_USER_SELECTABLE))
return plugin;
@@ -205,7 +204,7 @@ handlerton *ha_resolve_by_legacy_type(THD *thd, enum legacy_db_type db_type)
default:
if (db_type > DB_TYPE_UNKNOWN && db_type < DB_TYPE_DEFAULT &&
(plugin= ha_lock_engine(thd, installed_htons[db_type])))
- return plugin_data(plugin, handlerton*);
+ return plugin_hton(plugin);
/* fall through */
case DB_TYPE_UNKNOWN:
return NULL;
@@ -233,14 +232,7 @@ handlerton *ha_checktype(THD *thd, enum legacy_db_type database_type,
return NULL;
}
- RUN_HOOK(transaction, after_rollback, (thd, FALSE));
-
- switch (database_type) {
- case DB_TYPE_MRG_ISAM:
- return ha_resolve_by_legacy_type(thd, DB_TYPE_MRG_MYISAM);
- default:
- break;
- }
+ (void) RUN_HOOK(transaction, after_rollback, (thd, FALSE));
return ha_default_handlerton(thd);
} /* ha_checktype */
@@ -367,8 +359,10 @@ int ha_init_errors(void)
SETMSG(HA_ERR_TOO_MANY_CONCURRENT_TRXS, ER_DEFAULT(ER_TOO_MANY_CONCURRENT_TRXS));
SETMSG(HA_ERR_INDEX_COL_TOO_LONG, ER_DEFAULT(ER_INDEX_COLUMN_TOO_LONG));
SETMSG(HA_ERR_INDEX_CORRUPT, ER_DEFAULT(ER_INDEX_CORRUPT));
+ SETMSG(HA_FTS_INVALID_DOCID, "Invalid InnoDB FTS Doc ID");
SETMSG(HA_ERR_TABLE_IN_FK_CHECK, ER_DEFAULT(ER_TABLE_IN_FK_CHECK));
SETMSG(HA_ERR_DISK_FULL, ER_DEFAULT(ER_DISK_FULL));
+ SETMSG(HA_ERR_FTS_TOO_MANY_WORDS_IN_PHRASE, "Too many words in a FTS phrase or proximity search");
/* Register the error messages for use with my_error(). */
return my_error_register(get_handler_errmsgs, HA_ERR_FIRST, HA_ERR_LAST);
@@ -394,6 +388,38 @@ static int ha_finish_errors(void)
return 0;
}
+static volatile int32 need_full_discover_for_existence= 0;
+static volatile int32 engines_with_discover_table_names= 0;
+static volatile int32 engines_with_discover= 0;
+
+static int full_discover_for_existence(handlerton *, const char *, const char *)
+{ return 0; }
+
+static int ext_based_existence(handlerton *, const char *, const char *)
+{ return 0; }
+
+static int hton_ext_based_table_discovery(handlerton *hton, LEX_STRING *db,
+ MY_DIR *dir, handlerton::discovered_list *result)
+{
+ /*
+ tablefile_extensions[0] is the metadata file, see
+ the comment above tablefile_extensions declaration
+ */
+ return extension_based_table_discovery(dir, hton->tablefile_extensions[0],
+ result);
+}
+
+static void update_discovery_counters(handlerton *hton, int val)
+{
+ if (hton->discover_table_existence == full_discover_for_existence)
+ my_atomic_add32(&need_full_discover_for_existence, val);
+
+ if (hton->discover_table_names)
+ my_atomic_add32(&engines_with_discover_table_names, val);
+
+ if (hton->discover_table)
+ my_atomic_add32(&engines_with_discover, val);
+}
int ha_finalize_handlerton(st_plugin_int *plugin)
{
@@ -431,6 +457,9 @@ int ha_finalize_handlerton(st_plugin_int *plugin)
}
}
+ free_sysvar_table_options(hton);
+ update_discovery_counters(hton, -1);
+
/*
In case a plugin is uninstalled and re-installed later, it should
reuse an array slot. Otherwise the number of uninstall/install
@@ -454,12 +483,12 @@ int ha_finalize_handlerton(st_plugin_int *plugin)
int ha_initialize_handlerton(st_plugin_int *plugin)
{
handlerton *hton;
+ static const char *no_exts[]= { 0 };
DBUG_ENTER("ha_initialize_handlerton");
DBUG_PRINT("plugin", ("initialize plugin: '%s'", plugin->name.str));
hton= (handlerton *)my_malloc(sizeof(handlerton),
MYF(MY_WME | MY_ZEROFILL));
-
if (hton == NULL)
{
sql_print_error("Unable to allocate memory for plugin '%s' handlerton.",
@@ -467,6 +496,9 @@ int ha_initialize_handlerton(st_plugin_int *plugin)
goto err_no_hton_memory;
}
+ hton->tablefile_extensions= no_exts;
+ hton->discover_table_names= hton_ext_based_table_discovery;
+
hton->slot= HA_SLOT_UNDEF;
/* Historical Requirement */
plugin->data= hton; // shortcut for the future
@@ -477,6 +509,21 @@ int ha_initialize_handlerton(st_plugin_int *plugin)
goto err;
}
+ // hton_ext_based_table_discovery() works only when discovery
+ // is supported and the engine if file-based.
+ if (hton->discover_table_names == hton_ext_based_table_discovery &&
+ (!hton->discover_table || !hton->tablefile_extensions[0]))
+ hton->discover_table_names= NULL;
+
+ // default discover_table_existence implementation
+ if (!hton->discover_table_existence && hton->discover_table)
+ {
+ if (hton->tablefile_extensions[0])
+ hton->discover_table_existence= ext_based_existence;
+ else
+ hton->discover_table_existence= full_discover_for_existence;
+ }
+
/*
the switch below and hton->state should be removed when
command-line options for plugins will be implemented
@@ -550,7 +597,7 @@ int ha_initialize_handlerton(st_plugin_int *plugin)
{
total_ha_2pc--;
hton->prepare= 0;
- push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
ER_UNKNOWN_ERROR,
"Cannot enable tc-log at run-time. "
"XA features of %s are disabled",
@@ -584,6 +631,9 @@ int ha_initialize_handlerton(st_plugin_int *plugin)
break;
};
+ resolve_sysvar_table_options(hton);
+ update_discovery_counters(hton, 1);
+
DBUG_RETURN(0);
err_deinit:
@@ -637,7 +687,7 @@ int ha_end()
static my_bool dropdb_handlerton(THD *unused1, plugin_ref plugin,
void *path)
{
- handlerton *hton= plugin_data(plugin, handlerton *);
+ handlerton *hton= plugin_hton(plugin);
if (hton->state == SHOW_OPTION_YES && hton->drop_database)
hton->drop_database(hton, (char *)path);
return FALSE;
@@ -653,7 +703,7 @@ void ha_drop_database(char* path)
static my_bool checkpoint_state_handlerton(THD *unused1, plugin_ref plugin,
void *disable)
{
- handlerton *hton= plugin_data(plugin, handlerton *);
+ handlerton *hton= plugin_hton(plugin);
if (hton->state == SHOW_OPTION_YES && hton->checkpoint_state)
hton->checkpoint_state(hton, (int) *(bool*) disable);
return FALSE;
@@ -666,11 +716,48 @@ void ha_checkpoint_state(bool disable)
}
+struct st_commit_checkpoint_request {
+ void *cookie;
+ void (*pre_hook)(void *);
+};
+
+static my_bool commit_checkpoint_request_handlerton(THD *unused1, plugin_ref plugin,
+ void *data)
+{
+ st_commit_checkpoint_request *st= (st_commit_checkpoint_request *)data;
+ handlerton *hton= plugin_hton(plugin);
+ if (hton->state == SHOW_OPTION_YES && hton->commit_checkpoint_request)
+ {
+ void *cookie= st->cookie;
+ if (st->pre_hook)
+ (*st->pre_hook)(cookie);
+ (*hton->commit_checkpoint_request)(hton, cookie);
+ }
+ return FALSE;
+}
+
+
+/*
+ Invoke commit_checkpoint_request() in all storage engines that implement it.
+
+ If pre_hook is non-NULL, the hook will be called prior to each invocation.
+*/
+void
+ha_commit_checkpoint_request(void *cookie, void (*pre_hook)(void *))
+{
+ st_commit_checkpoint_request st;
+ st.cookie= cookie;
+ st.pre_hook= pre_hook;
+ plugin_foreach(NULL, commit_checkpoint_request_handlerton,
+ MYSQL_STORAGE_ENGINE_PLUGIN, &st);
+}
+
+
static my_bool closecon_handlerton(THD *thd, plugin_ref plugin,
void *unused)
{
- handlerton *hton= plugin_data(plugin, handlerton *);
+ handlerton *hton= plugin_hton(plugin);
/*
there's no need to rollback here as all transactions must
be rolled back already
@@ -697,7 +784,7 @@ void ha_close_connection(THD* thd)
static my_bool kill_handlerton(THD *thd, plugin_ref plugin,
void *level)
{
- handlerton *hton= plugin_data(plugin, handlerton *);
+ handlerton *hton= plugin_hton(plugin);
if (hton->state == SHOW_OPTION_YES && hton->kill_query &&
thd_get_ha_data(thd, hton))
@@ -756,7 +843,7 @@ void ha_kill_query(THD* thd, enum thd_kill_levels level)
end. Such nested transaction was internally referred to as
a "statement transaction" and gave birth to the term.
- <Historical note ends>
+ (Historical note ends)
Since then a statement transaction is started for each statement
that accesses transactional tables or uses the binary log. If
@@ -1030,11 +1117,14 @@ void trans_register_ha(THD *thd, bool all, handlerton *ht_arg)
{
trans= &thd->transaction.all;
thd->server_status|= SERVER_STATUS_IN_TRANS;
+ if (thd->tx_read_only)
+ thd->server_status|= SERVER_STATUS_IN_TRANS_READONLY;
+ DBUG_PRINT("info", ("setting SERVER_STATUS_IN_TRANS"));
}
else
trans= &thd->transaction.stmt;
- ha_info= thd->ha_data[ht_arg->slot].ha_info + static_cast<unsigned>(all);
+ ha_info= thd->ha_data[ht_arg->slot].ha_info + (all ? 1 : 0);
if (ha_info->is_started())
DBUG_VOID_RETURN; /* already registered, return */
@@ -1071,17 +1161,36 @@ int ha_prepare(THD *thd)
{
if ((err= ht->prepare(ht, thd, all)))
{
+#ifdef WITH_WSREP
+ if (WSREP(thd) && ht->db_type== DB_TYPE_WSREP)
+ {
+ error= 1;
+ /* avoid sending error, if we need to replay */
+ if (thd->wsrep_conflict_state!= MUST_REPLAY)
+ {
+ my_error(ER_LOCK_DEADLOCK, MYF(0), err);
+ }
+ }
+ else
+ {
+ /* not wsrep hton, bail to native mysql behavior */
+#endif
my_error(ER_ERROR_DURING_COMMIT, MYF(0), err);
ha_rollback_trans(thd, all);
error=1;
break;
+#ifdef WITH_WSREP
+ }
+#endif
}
}
else
{
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
- ER_ILLEGAL_HA, ER(ER_ILLEGAL_HA),
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
+ ER_GET_ERRNO, ER(ER_GET_ERRNO),
+ HA_ERR_WRONG_COMMAND,
ha_resolve_storage_engine_name(ht));
+
}
}
}
@@ -1179,18 +1288,24 @@ int ha_commit_trans(THD *thd, bool all)
the changes are not durable as they might be rolled back if the
enclosing 'all' transaction is rolled back.
*/
- bool is_real_trans= all || thd->transaction.all.ha_list == 0;
+ bool is_real_trans= ((all || thd->transaction.all.ha_list == 0) &&
+ !(thd->variables.option_bits & OPTION_GTID_BEGIN));
Ha_trx_info *ha_info= trans->ha_list;
bool need_prepare_ordered, need_commit_ordered;
my_xid xid;
DBUG_ENTER("ha_commit_trans");
+ DBUG_PRINT("info",("thd: %p option_bits: %lu all: %d",
+ thd, (ulong) thd->variables.option_bits, all));
/* Just a random warning to test warnings pushed during autocommit. */
DBUG_EXECUTE_IF("warn_during_ha_commit_trans",
- push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning(thd, Sql_condition::WARN_LEVEL_WARN,
ER_WARNING_NOT_COMPLETE_ROLLBACK,
ER(ER_WARNING_NOT_COMPLETE_ROLLBACK)););
+ DBUG_PRINT("info",
+ ("all: %d thd->in_sub_stmt: %d ha_info: %p is_real_trans: %d",
+ all, thd->in_sub_stmt, ha_info, is_real_trans));
/*
We must not commit the normal transaction if a statement
transaction is pending. Otherwise statement transaction
@@ -1227,7 +1342,9 @@ int ha_commit_trans(THD *thd, bool all)
if (!ha_info)
{
- /* Free resources and perform other cleanup even for 'empty' transactions. */
+ /*
+ Free resources and perform other cleanup even for 'empty' transactions.
+ */
if (is_real_trans)
thd->transaction.cleanup();
DBUG_RETURN(0);
@@ -1243,6 +1360,8 @@ int ha_commit_trans(THD *thd, bool all)
/* rw_trans is TRUE when we in a transaction changing data */
bool rw_trans= is_real_trans && (rw_ha_count > 0);
MDL_request mdl_request;
+ DBUG_PRINT("info", ("is_real_trans: %d rw_trans: %d rw_ha_count: %d",
+ is_real_trans, rw_trans, rw_ha_count));
if (rw_trans)
{
@@ -1259,7 +1378,7 @@ int ha_commit_trans(THD *thd, bool all)
#ifdef WITH_WSREP
if (!WSREP(thd) &&
- thd->mdl_context.acquire_lock(&mdl_request,
+ thd->mdl_context.acquire_lock(&mdl_request,
#else
if (thd->mdl_context.acquire_lock(&mdl_request,
#endif /* WITH_WSREP */
@@ -1308,38 +1427,34 @@ int ha_commit_trans(THD *thd, bool all)
*/
err= ht->prepare(ht, thd, all);
status_var_increment(thd->status_var.ha_prepare_count);
-
if (err)
- {
#ifdef WITH_WSREP
- if (WSREP(thd) && ht->db_type== DB_TYPE_WSREP)
- {
- error= 1;
- switch (err)
- {
- case WSREP_TRX_SIZE_EXCEEDED:
- /* give user size exeeded erro from wsrep_api.h */
- my_error(ER_ERROR_DURING_COMMIT, MYF(0), WSREP_SIZE_EXCEEDED);
- break;
- case WSREP_TRX_CERT_FAIL:
- case WSREP_TRX_ERROR:
- /* avoid sending error, if we need to replay */
- if (thd->wsrep_conflict_state!= MUST_REPLAY)
- {
- my_error(ER_LOCK_DEADLOCK, MYF(0), err);
- }
- }
- }
- else
+ {
+ if (WSREP(thd) && ht->db_type== DB_TYPE_WSREP)
+ {
+ error= 1;
+ switch (err)
+ {
+ case WSREP_TRX_SIZE_EXCEEDED:
+ /* give user size exeeded erro from wsrep_api.h */
+ my_error(ER_ERROR_DURING_COMMIT, MYF(0), WSREP_SIZE_EXCEEDED);
+ break;
+ case WSREP_TRX_CERT_FAIL:
+ case WSREP_TRX_ERROR:
+ /* avoid sending error, if we need to replay */
+ if (thd->wsrep_conflict_state!= MUST_REPLAY)
{
- /* not wsrep hton, bail to native mysql behavior */
+ my_error(ER_LOCK_DEADLOCK, MYF(0), err);
+ }
+ }
+ }
+ else
+ /* not wsrep hton, bail to native mysql behavior */
#endif /* WITH_WSREP */
- my_error(ER_ERROR_DURING_COMMIT, MYF(0), err);
- error= 1;
+ my_error(ER_ERROR_DURING_COMMIT, MYF(0), err);
#ifdef WITH_WSREP
- } /* End of else */
-#endif
}
+#endif /* WITH_WSREP */
if (err)
goto err;
@@ -1354,7 +1469,7 @@ int ha_commit_trans(THD *thd, bool all)
if (!error && wsrep_is_wsrep_xid(&thd->transaction.xid_state.xid))
{
// xid was rewritten by wsrep
- xid= wsrep_xid_seqno(&thd->transaction.xid_state.xid);
+ xid= wsrep_xid_seqno(thd->transaction.xid_state.xid);
}
#endif // WITH_WSREP
if (!is_real_trans)
@@ -1363,11 +1478,13 @@ int ha_commit_trans(THD *thd, bool all)
goto done;
}
+ DEBUG_SYNC(thd, "ha_commit_trans_before_log_and_order");
cookie= tc_log->log_and_order(thd, xid, all, need_prepare_ordered,
need_commit_ordered);
if (!cookie)
goto err;
+ DEBUG_SYNC(thd, "ha_commit_trans_after_log_and_order");
DBUG_EXECUTE_IF("crash_commit_after_log", DBUG_SUICIDE(););
error= commit_one_phase_2(thd, all, trans, is_real_trans) ? 2 : 0;
@@ -1387,7 +1504,13 @@ done:
/* Come here if error and we need to rollback. */
err:
error= 1; /* Transaction was rolled back */
- ha_rollback_trans(thd, all);
+ /*
+ In parallel replication, rollback is delayed, as there is extra replication
+ book-keeping to be done before rolling back and allowing a conflicting
+ transaction to continue (MDEV-7458).
+ */
+ if (!(thd->rgi_slave && thd->rgi_slave->is_parallel_exec))
+ ha_rollback_trans(thd, all);
end:
if (rw_trans && mdl_request.ticket)
@@ -1430,9 +1553,17 @@ int ha_commit_one_phase(THD *thd, bool all)
ha_commit_one_phase() can be called with an empty
transaction.all.ha_list, see why in trans_register_ha()).
*/
- bool is_real_trans=all || thd->transaction.all.ha_list == 0;
+ bool is_real_trans= ((all || thd->transaction.all.ha_list == 0) &&
+ !(thd->variables.option_bits & OPTION_GTID_BEGIN));
+ int res;
DBUG_ENTER("ha_commit_one_phase");
- int res= commit_one_phase_2(thd, all, trans, is_real_trans);
+ if (is_real_trans)
+ {
+ DEBUG_SYNC(thd, "ha_commit_one_phase");
+ if ((res= thd->wait_for_prior_commit()))
+ DBUG_RETURN(res);
+ }
+ res= commit_one_phase_2(thd, all, trans, is_real_trans);
DBUG_RETURN(res);
}
@@ -1443,18 +1574,8 @@ commit_one_phase_2(THD *thd, bool all, THD_TRANS *trans, bool is_real_trans)
int error= 0;
Ha_trx_info *ha_info= trans->ha_list, *ha_info_next;
DBUG_ENTER("commit_one_phase_2");
-#ifdef WITH_WSREP
-#ifdef WSREP_PROC_INFO
- char info[64]= { 0, };
- snprintf (info, sizeof(info) - 1, "ha_commit_one_phase(%lld)",
- (long long)wsrep_thd_trx_seqno(thd));
-#else
- const char info[]="ha_commit_one_phase()";
-#endif /* WSREP_PROC_INFO */
- char* tmp_info= NULL;
- if (WSREP(thd)) tmp_info= (char *)thd_proc_info(thd, info);
-#endif /* WITH_WSREP */
-
+ if (is_real_trans)
+ DEBUG_SYNC(thd, "commit_one_phase_2");
if (ha_info)
{
for (; ha_info; ha_info= ha_info_next)
@@ -1483,10 +1604,10 @@ commit_one_phase_2(THD *thd, bool all, THD_TRANS *trans, bool is_real_trans)
}
/* Free resources and perform other cleanup even for 'empty' transactions. */
if (is_real_trans)
- thd->transaction.cleanup();
-#ifdef WITH_WSREP
- if (WSREP(thd)) thd_proc_info(thd, tmp_info);
-#endif /* WITH_WSREP */
+ {
+ thd->has_waiter= false;
+ thd->transaction.cleanup();
+ }
DBUG_RETURN(error);
}
@@ -1520,6 +1641,26 @@ int ha_rollback_trans(THD *thd, bool all)
DBUG_ASSERT(thd->transaction.stmt.ha_list == NULL ||
trans == &thd->transaction.stmt);
+#ifdef HAVE_REPLICATION
+ if (is_real_trans)
+ {
+ /*
+ In parallel replication, if we need to rollback during commit, we must
+ first inform following transactions that we are going to abort our commit
+ attempt. Otherwise those following transactions can run too early, and
+ possibly cause replication to fail. See comments in retry_event_group().
+
+ There were several bugs with this in the past that were very hard to
+ track down (MDEV-7458, MDEV-8302). So we add here an assertion for
+ rollback without signalling following transactions. And in release
+ builds, we explicitly do the signalling before rolling back.
+ */
+ DBUG_ASSERT(!(thd->rgi_slave && thd->rgi_slave->did_mark_start_commit));
+ if (thd->rgi_slave && thd->rgi_slave->did_mark_start_commit)
+ thd->rgi_slave->unmark_start_commit();
+ }
+#endif
+
if (thd->in_sub_stmt)
{
DBUG_ASSERT(0);
@@ -1563,11 +1704,14 @@ int ha_rollback_trans(THD *thd, bool all)
*/
if (is_real_trans && thd->transaction_rollback_request &&
thd->transaction.xid_state.xa_state != XA_NOTR)
- thd->transaction.xid_state.rm_error= thd->stmt_da->sql_errno();
+ thd->transaction.xid_state.rm_error= thd->get_stmt_da()->sql_errno();
/* Always cleanup. Even if nht==0. There may be savepoints. */
if (is_real_trans)
- thd->transaction.cleanup();
+ {
+ thd->has_waiter= false;
+ thd->transaction.cleanup();
+ }
if (all)
thd->transaction_rollback_request= FALSE;
@@ -1586,10 +1730,10 @@ int ha_rollback_trans(THD *thd, bool all)
*/
if (is_real_trans && thd->transaction.all.modified_non_trans_table &&
!thd->slave_thread && thd->killed < KILL_CONNECTION)
- push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning(thd, Sql_condition::WARN_LEVEL_WARN,
ER_WARNING_NOT_COMPLETE_ROLLBACK,
ER(ER_WARNING_NOT_COMPLETE_ROLLBACK));
- RUN_HOOK(transaction, after_rollback, (thd, FALSE));
+ (void) RUN_HOOK(transaction, after_rollback, (thd, FALSE));
DBUG_RETURN(error);
}
@@ -1602,7 +1746,7 @@ struct xahton_st {
static my_bool xacommit_handlerton(THD *unused1, plugin_ref plugin,
void *arg)
{
- handlerton *hton= plugin_data(plugin, handlerton *);
+ handlerton *hton= plugin_hton(plugin);
if (hton->state == SHOW_OPTION_YES && hton->recover)
{
hton->commit_by_xid(hton, ((struct xahton_st *)arg)->xid);
@@ -1614,7 +1758,7 @@ static my_bool xacommit_handlerton(THD *unused1, plugin_ref plugin,
static my_bool xarollback_handlerton(THD *unused1, plugin_ref plugin,
void *arg)
{
- handlerton *hton= plugin_data(plugin, handlerton *);
+ handlerton *hton= plugin_hton(plugin);
if (hton->state == SHOW_OPTION_YES && hton->recover)
{
hton->rollback_by_xid(hton, ((struct xahton_st *)arg)->xid);
@@ -1720,7 +1864,7 @@ struct xarecover_st
static my_bool xarecover_handlerton(THD *unused, plugin_ref plugin,
void *arg)
{
- handlerton *hton= plugin_data(plugin, handlerton *);
+ handlerton *hton= plugin_hton(plugin);
struct xarecover_st *info= (struct xarecover_st *) arg;
int got;
@@ -1734,7 +1878,7 @@ static my_bool xarecover_handlerton(THD *unused, plugin_ref plugin,
{
#ifdef WITH_WSREP
my_xid x=(wsrep_is_wsrep_xid(&info->list[i]) ?
- wsrep_xid_seqno(&info->list[i]) :
+ wsrep_xid_seqno(info->list[i]) :
info->list[i].get_my_xid());
#else
my_xid x=info->list[i].get_my_xid();
@@ -1886,6 +2030,17 @@ bool mysql_xa_recover(THD *thd)
DBUG_RETURN(0);
}
+/*
+ Called by engine to notify TC that a new commit checkpoint has been reached.
+ See comments on handlerton method commit_checkpoint_request() for details.
+*/
+void
+commit_checkpoint_notify_ha(handlerton *hton, void *cookie)
+{
+ tc_log->commit_checkpoint_notify(cookie);
+}
+
+
/**
@details
This function should be called when MySQL sends rows of a SELECT result set
@@ -1924,6 +2079,41 @@ int ha_release_temporary_latches(THD *thd)
return 0;
}
+/**
+ Check if all storage engines used in transaction agree that after
+ rollback to savepoint it is safe to release MDL locks acquired after
+ savepoint creation.
+
+ @param thd The client thread that executes the transaction.
+
+ @return true - It is safe to release MDL locks.
+ false - If it is not.
+*/
+bool ha_rollback_to_savepoint_can_release_mdl(THD *thd)
+{
+ Ha_trx_info *ha_info;
+ THD_TRANS *trans= (thd->in_sub_stmt ? &thd->transaction.stmt :
+ &thd->transaction.all);
+
+ DBUG_ENTER("ha_rollback_to_savepoint_can_release_mdl");
+
+ /**
+ Checking whether it is safe to release metadata locks after rollback to
+ savepoint in all the storage engines that are part of the transaction.
+ */
+ for (ha_info= trans->ha_list; ha_info; ha_info= ha_info->next())
+ {
+ handlerton *ht= ha_info->ht();
+ DBUG_ASSERT(ht);
+
+ if (ht->savepoint_rollback_can_release_mdl == 0 ||
+ ht->savepoint_rollback_can_release_mdl(ht, thd) == false)
+ DBUG_RETURN(false);
+ }
+
+ DBUG_RETURN(true);
+}
+
int ha_rollback_to_savepoint(THD *thd, SAVEPOINT *sv)
{
int error=0;
@@ -2002,7 +2192,7 @@ int ha_savepoint(THD *thd, SAVEPOINT *sv)
}
if ((err= ht->savepoint_set(ht, thd, (uchar *)(sv+1)+ht->savepoint_offset)))
{ // cannot happen
- my_error(ER_GET_ERRNO, MYF(0), err);
+ my_error(ER_GET_ERRNO, MYF(0), err, hton_name(ht)->str);
error=1;
}
status_var_increment(thd->status_var.ha_savepoint_count);
@@ -2033,7 +2223,7 @@ int ha_release_savepoint(THD *thd, SAVEPOINT *sv)
if ((err= ht->savepoint_release(ht, thd,
(uchar *)(sv+1) + ht->savepoint_offset)))
{ // cannot happen
- my_error(ER_GET_ERRNO, MYF(0), err);
+ my_error(ER_GET_ERRNO, MYF(0), err, hton_name(ht)->str);
error=1;
}
}
@@ -2044,7 +2234,7 @@ int ha_release_savepoint(THD *thd, SAVEPOINT *sv)
static my_bool snapshot_handlerton(THD *thd, plugin_ref plugin,
void *arg)
{
- handlerton *hton= plugin_data(plugin, handlerton *);
+ handlerton *hton= plugin_hton(plugin);
if (hton->state == SHOW_OPTION_YES &&
hton->start_consistent_snapshot)
{
@@ -2074,7 +2264,7 @@ int ha_start_consistent_snapshot(THD *thd)
exist:
*/
if (warn)
- push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_UNKNOWN_ERROR,
+ push_warning(thd, Sql_condition::WARN_LEVEL_WARN, ER_UNKNOWN_ERROR,
"This MySQL server does not support any "
"consistent-read capable storage engine");
return 0;
@@ -2084,7 +2274,7 @@ int ha_start_consistent_snapshot(THD *thd)
static my_bool flush_handlerton(THD *thd, plugin_ref plugin,
void *arg)
{
- handlerton *hton= plugin_data(plugin, handlerton *);
+ handlerton *hton= plugin_hton(plugin);
if (hton->state == SHOW_OPTION_YES && hton->flush_logs &&
hton->flush_logs(hton))
return TRUE;
@@ -2170,9 +2360,9 @@ public:
virtual bool handle_condition(THD *thd,
uint sql_errno,
const char* sqlstate,
- MYSQL_ERROR::enum_warning_level level,
+ Sql_condition::enum_warning_level level,
const char* msg,
- MYSQL_ERROR ** cond_hdl);
+ Sql_condition ** cond_hdl);
char buff[MYSQL_ERRMSG_SIZE];
};
@@ -2182,9 +2372,9 @@ Ha_delete_table_error_handler::
handle_condition(THD *,
uint,
const char*,
- MYSQL_ERROR::enum_warning_level,
+ Sql_condition::enum_warning_level,
const char* msg,
- MYSQL_ERROR ** cond_hdl)
+ Sql_condition ** cond_hdl)
{
*cond_hdl= NULL;
/* Grab the error message */
@@ -2193,9 +2383,11 @@ handle_condition(THD *,
}
-/** @brief
- This should return ENOENT if the file doesn't exists.
- The .frm file will be deleted only if we return 0 or ENOENT
+/** delete a table in the engine
+
+ @note
+ ENOENT and HA_ERR_NO_SUCH_TABLE are not considered errors.
+ The .frm file will be deleted only if we return 0.
*/
int ha_delete_table(THD *thd, handlerton *table_type, const char *path,
const char *db, const char *alias, bool generate_warning)
@@ -2207,51 +2399,72 @@ int ha_delete_table(THD *thd, handlerton *table_type, const char *path,
TABLE_SHARE dummy_share;
DBUG_ENTER("ha_delete_table");
+ /* table_type is NULL in ALTER TABLE when renaming only .frm files */
+ if (table_type == NULL || table_type == view_pseudo_hton ||
+ ! (file=get_new_handler((TABLE_SHARE*)0, thd->mem_root, table_type)))
+ DBUG_RETURN(0);
+
bzero((char*) &dummy_table, sizeof(dummy_table));
bzero((char*) &dummy_share, sizeof(dummy_share));
dummy_table.s= &dummy_share;
- /* DB_TYPE_UNKNOWN is used in ALTER TABLE when renaming only .frm files */
- if (table_type == NULL ||
- ! (file=get_new_handler((TABLE_SHARE*)0, thd->mem_root, table_type)))
- DBUG_RETURN(ENOENT);
-
path= get_canonical_filename(file, path, tmp_path);
- if ((error= file->ha_delete_table(path)) && generate_warning)
+ if ((error= file->ha_delete_table(path)))
{
/*
- Because file->print_error() use my_error() to generate the error message
- we use an internal error handler to intercept it and store the text
- in a temporary buffer. Later the message will be presented to user
- as a warning.
+ it's not an error if the table doesn't exist in the engine.
+ warn the user, but still report DROP being a success
*/
- Ha_delete_table_error_handler ha_delete_table_error_handler;
-
- /* Fill up strucutures that print_error may need */
- dummy_share.path.str= (char*) path;
- dummy_share.path.length= strlen(path);
- dummy_share.db.str= (char*) db;
- dummy_share.db.length= strlen(db);
- dummy_share.table_name.str= (char*) alias;
- dummy_share.table_name.length= strlen(alias);
- dummy_table.alias.set(alias, dummy_share.table_name.length,
- table_alias_charset);
-
- file->change_table_ptr(&dummy_table, &dummy_share);
+ bool intercept= error == ENOENT || error == HA_ERR_NO_SUCH_TABLE;
- thd->push_internal_handler(&ha_delete_table_error_handler);
- file->print_error(error, 0);
+ if (!intercept || generate_warning)
+ {
+ /*
+ Because file->print_error() use my_error() to generate the error message
+ we use an internal error handler to intercept it and store the text
+ in a temporary buffer. Later the message will be presented to user
+ as a warning.
+ */
+ Ha_delete_table_error_handler ha_delete_table_error_handler;
+
+ /* Fill up strucutures that print_error may need */
+ dummy_share.path.str= (char*) path;
+ dummy_share.path.length= strlen(path);
+ dummy_share.normalized_path= dummy_share.path;
+ dummy_share.db.str= (char*) db;
+ dummy_share.db.length= strlen(db);
+ dummy_share.table_name.str= (char*) alias;
+ dummy_share.table_name.length= strlen(alias);
+ dummy_table.alias.set(alias, dummy_share.table_name.length,
+ table_alias_charset);
+
+ file->change_table_ptr(&dummy_table, &dummy_share);
+
+#if MYSQL_VERSION_ID > 100105
+ // XXX as an ugly 10.0-only hack we intercept HA_ERR_ROW_IS_REFERENCED,
+ // to report it under the old historical error number.
+#error remove HA_ERR_ROW_IS_REFERENCED, use ME_JUST_WARNING instead of a handler
+#endif
+ if (intercept || error == HA_ERR_ROW_IS_REFERENCED)
+ thd->push_internal_handler(&ha_delete_table_error_handler);
- thd->pop_internal_handler();
+ file->print_error(error, 0);
- /*
- XXX: should we convert *all* errors to warnings here?
- What if the error is fatal?
- */
- push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, error,
- ha_delete_table_error_handler.buff);
+ if (intercept || error == HA_ERR_ROW_IS_REFERENCED)
+ {
+ thd->pop_internal_handler();
+ if (error == HA_ERR_ROW_IS_REFERENCED)
+ my_message(ER_ROW_IS_REFERENCED, ER(ER_ROW_IS_REFERENCED), MYF(0));
+ else
+ push_warning(thd, Sql_condition::WARN_LEVEL_WARN, error,
+ ha_delete_table_error_handler.buff);
+ }
+ }
+ if (intercept)
+ error= 0;
}
delete file;
+
DBUG_RETURN(error);
}
@@ -2261,8 +2474,11 @@ int ha_delete_table(THD *thd, handlerton *table_type, const char *path,
handler *handler::clone(const char *name, MEM_ROOT *mem_root)
{
handler *new_handler= get_new_handler(table->s, mem_root, ht);
- if (! new_handler)
+
+ if (!new_handler)
return NULL;
+ if (new_handler->set_ha_share_ref(ha_share))
+ goto err;
/*
Allocate handler->ref here because otherwise ha_open will allocate it
@@ -2272,7 +2488,7 @@ handler *handler::clone(const char *name, MEM_ROOT *mem_root)
if (!(new_handler->ref= (uchar*) alloc_root(mem_root,
ALIGN_SIZE(ref_length)*2)))
- return NULL;
+ goto err;
/*
TODO: Implement a more efficient way to have more than one index open for
@@ -2283,9 +2499,13 @@ handler *handler::clone(const char *name, MEM_ROOT *mem_root)
*/
if (new_handler->ha_open(table, name, table->db_stat,
HA_OPEN_IGNORE_IF_LOCKED))
- return NULL;
+ goto err;
return new_handler;
+
+err:
+ delete new_handler;
+ return NULL;
}
@@ -2319,9 +2539,28 @@ THD *handler::ha_thd(void) const
return (table && table->in_use) ? table->in_use : current_thd;
}
-PSI_table_share *handler::ha_table_share_psi(const TABLE_SHARE *share) const
+void handler::unbind_psi()
+{
+ /*
+ Notify the instrumentation that this table is not owned
+ by this thread any more.
+ */
+ PSI_CALL_unbind_table(m_psi);
+}
+
+void handler::rebind_psi()
+{
+ /*
+ Notify the instrumentation that this table is now owned
+ by this thread.
+ */
+ m_psi= PSI_CALL_rebind_table(ha_table_share_psi(), this, m_psi);
+}
+
+
+PSI_table_share *handler::ha_table_share_psi() const
{
- return share->m_psi;
+ return table_share->m_psi;
}
/** @brief
@@ -2343,6 +2582,8 @@ int handler::ha_open(TABLE *table_arg, const char *name, int mode,
table= table_arg;
DBUG_ASSERT(table->s == table_share);
+ DBUG_ASSERT(m_lock_type == F_UNLCK);
+ DBUG_PRINT("info", ("old m_lock_type: %d F_UNLCK %d", m_lock_type, F_UNLCK));
DBUG_ASSERT(alloc_root_inited(&table->mem_root));
if ((error=open(name,mode,test_if_locked)))
@@ -2361,6 +2602,18 @@ int handler::ha_open(TABLE *table_arg, const char *name, int mode,
}
else
{
+ DBUG_ASSERT(m_psi == NULL);
+ DBUG_ASSERT(table_share != NULL);
+ /*
+ Do not call this for partitions handlers, since it may take too much
+ resources.
+ So only use the m_psi on table level, not for individual partitions.
+ */
+ if (!(test_if_locked & HA_OPEN_NO_PSI_CALL))
+ {
+ m_psi= PSI_CALL_open_table(ha_table_share_psi(), this);
+ }
+
if (table->s->db_options_in_use & HA_OPTION_READ_ONLY_DATA)
table->db_stat|=HA_READ_ONLY;
(void) extra(HA_EXTRA_NO_READCHECK); // Not needed in SQL
@@ -2369,7 +2622,7 @@ int handler::ha_open(TABLE *table_arg, const char *name, int mode,
if (!ref && !(ref= (uchar*) alloc_root(&table->mem_root,
ALIGN_SIZE(ref_length)*2)))
{
- close();
+ ha_close();
error=HA_ERR_OUT_OF_MEM;
}
else
@@ -2377,11 +2630,11 @@ int handler::ha_open(TABLE *table_arg, const char *name, int mode,
cached_table_flags= table_flags();
}
reset_statistics();
- internal_tmp_table= test(test_if_locked & HA_OPEN_INTERNAL_TABLE);
+ internal_tmp_table= MY_TEST(test_if_locked & HA_OPEN_INTERNAL_TABLE);
DBUG_RETURN(error);
}
-int handler::ha_close()
+int handler::ha_close(void)
{
DBUG_ENTER("ha_close");
/*
@@ -2390,9 +2643,184 @@ int handler::ha_close()
*/
if (table->in_use)
status_var_add(table->in_use->status_var.rows_tmp_read, rows_tmp_read);
+ PSI_CALL_close_table(m_psi);
+ m_psi= NULL; /* instrumentation handle, invalid after close_table() */
+
+ DBUG_ASSERT(m_lock_type == F_UNLCK);
+ DBUG_ASSERT(inited == NONE);
DBUG_RETURN(close());
}
+int handler::ha_rnd_next(uchar *buf)
+{
+ int result;
+ DBUG_ENTER("handler::ha_rnd_next");
+ DBUG_ASSERT(table_share->tmp_table != NO_TMP_TABLE ||
+ m_lock_type != F_UNLCK);
+ DBUG_ASSERT(inited == RND);
+
+ MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_FETCH_ROW, MAX_KEY, 0,
+ { result= rnd_next(buf); })
+ if (!result)
+ {
+ update_rows_read();
+ increment_statistics(&SSV::ha_read_rnd_next_count);
+ }
+ else if (result == HA_ERR_RECORD_DELETED)
+ increment_statistics(&SSV::ha_read_rnd_deleted_count);
+ else
+ increment_statistics(&SSV::ha_read_rnd_next_count);
+
+ table->status=result ? STATUS_NOT_FOUND: 0;
+ DBUG_RETURN(result);
+}
+
+int handler::ha_rnd_pos(uchar *buf, uchar *pos)
+{
+ int result;
+ DBUG_ENTER("handler::ha_rnd_pos");
+ DBUG_ASSERT(table_share->tmp_table != NO_TMP_TABLE ||
+ m_lock_type != F_UNLCK);
+ /* TODO: Find out how to solve ha_rnd_pos when finding duplicate update. */
+ /* DBUG_ASSERT(inited == RND); */
+
+ MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_FETCH_ROW, MAX_KEY, 0,
+ { result= rnd_pos(buf, pos); })
+ increment_statistics(&SSV::ha_read_rnd_count);
+ if (!result)
+ update_rows_read();
+ table->status=result ? STATUS_NOT_FOUND: 0;
+ DBUG_RETURN(result);
+}
+
+int handler::ha_index_read_map(uchar *buf, const uchar *key,
+ key_part_map keypart_map,
+ enum ha_rkey_function find_flag)
+{
+ int result;
+ DBUG_ENTER("handler::ha_index_read_map");
+ DBUG_ASSERT(table_share->tmp_table != NO_TMP_TABLE ||
+ m_lock_type != F_UNLCK);
+ DBUG_ASSERT(inited==INDEX);
+
+ MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_FETCH_ROW, active_index, 0,
+ { result= index_read_map(buf, key, keypart_map, find_flag); })
+ increment_statistics(&SSV::ha_read_key_count);
+ if (!result)
+ update_index_statistics();
+ table->status=result ? STATUS_NOT_FOUND: 0;
+ DBUG_RETURN(result);
+}
+
+/*
+ @note: Other index lookup/navigation functions require prior
+ handler->index_init() call. This function is different, it requires
+ that the scan is not initialized, and accepts "uint index" as an argument.
+*/
+
+int handler::ha_index_read_idx_map(uchar *buf, uint index, const uchar *key,
+ key_part_map keypart_map,
+ enum ha_rkey_function find_flag)
+{
+ int result;
+ DBUG_ASSERT(inited==NONE);
+ DBUG_ASSERT(table_share->tmp_table != NO_TMP_TABLE ||
+ m_lock_type != F_UNLCK);
+ DBUG_ASSERT(end_range == NULL);
+ MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_FETCH_ROW, index, 0,
+ { result= index_read_idx_map(buf, index, key, keypart_map, find_flag); })
+ increment_statistics(&SSV::ha_read_key_count);
+ if (!result)
+ {
+ update_rows_read();
+ index_rows_read[index]++;
+ }
+ table->status=result ? STATUS_NOT_FOUND: 0;
+ return result;
+}
+
+int handler::ha_index_next(uchar * buf)
+{
+ int result;
+ DBUG_ENTER("handler::ha_index_next");
+ DBUG_ASSERT(table_share->tmp_table != NO_TMP_TABLE ||
+ m_lock_type != F_UNLCK);
+ DBUG_ASSERT(inited==INDEX);
+
+ MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_FETCH_ROW, active_index, 0,
+ { result= index_next(buf); })
+ increment_statistics(&SSV::ha_read_next_count);
+ if (!result)
+ update_index_statistics();
+ table->status=result ? STATUS_NOT_FOUND: 0;
+ DBUG_RETURN(result);
+}
+
+int handler::ha_index_prev(uchar * buf)
+{
+ int result;
+ DBUG_ENTER("handler::ha_index_prev");
+ DBUG_ASSERT(table_share->tmp_table != NO_TMP_TABLE ||
+ m_lock_type != F_UNLCK);
+ DBUG_ASSERT(inited==INDEX);
+
+ MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_FETCH_ROW, active_index, 0,
+ { result= index_prev(buf); })
+ increment_statistics(&SSV::ha_read_prev_count);
+ if (!result)
+ update_index_statistics();
+ table->status=result ? STATUS_NOT_FOUND: 0;
+ DBUG_RETURN(result);
+}
+
+int handler::ha_index_first(uchar * buf)
+{
+ int result;
+ DBUG_ASSERT(table_share->tmp_table != NO_TMP_TABLE ||
+ m_lock_type != F_UNLCK);
+ DBUG_ASSERT(inited==INDEX);
+
+ MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_FETCH_ROW, active_index, 0,
+ { result= index_first(buf); })
+ increment_statistics(&SSV::ha_read_first_count);
+ if (!result)
+ update_index_statistics();
+ table->status=result ? STATUS_NOT_FOUND: 0;
+ return result;
+}
+
+int handler::ha_index_last(uchar * buf)
+{
+ int result;
+ DBUG_ASSERT(table_share->tmp_table != NO_TMP_TABLE ||
+ m_lock_type != F_UNLCK);
+ DBUG_ASSERT(inited==INDEX);
+
+ MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_FETCH_ROW, active_index, 0,
+ { result= index_last(buf); })
+ increment_statistics(&SSV::ha_read_last_count);
+ if (!result)
+ update_index_statistics();
+ table->status=result ? STATUS_NOT_FOUND: 0;
+ return result;
+}
+
+int handler::ha_index_next_same(uchar *buf, const uchar *key, uint keylen)
+{
+ int result;
+ DBUG_ASSERT(table_share->tmp_table != NO_TMP_TABLE ||
+ m_lock_type != F_UNLCK);
+ DBUG_ASSERT(inited==INDEX);
+
+ MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_FETCH_ROW, active_index, 0,
+ { result= index_next_same(buf, key, keylen); })
+ increment_statistics(&SSV::ha_read_next_count);
+ if (!result)
+ update_index_statistics();
+ table->status=result ? STATUS_NOT_FOUND: 0;
+ return result;
+}
+
/* Initialize handler for random reading, with error handling */
int handler::ha_rnd_init_with_error(bool scan)
@@ -2737,7 +3165,8 @@ int handler::update_auto_increment()
if (unlikely(nr == ULONGLONG_MAX))
DBUG_RETURN(HA_ERR_AUTOINC_ERANGE);
- DBUG_PRINT("info",("auto_increment: %llu",nr));
+ DBUG_PRINT("info",("auto_increment: %llu nb_reserved_values: %llu",
+ nr, append ? nb_reserved_values : 0));
/* Store field without warning (Warning will be printed by insert) */
save_count_cuted_fields= thd->count_cuted_fields;
@@ -2755,8 +3184,6 @@ int handler::update_auto_increment()
}
if (append)
{
- DBUG_PRINT("info",("nb_reserved_values: %llu",nb_reserved_values));
-
auto_inc_interval_for_cur_row.replace(nr, nb_reserved_values,
variables->auto_increment_increment);
auto_inc_intervals_count++;
@@ -2858,7 +3285,7 @@ void handler::get_auto_increment(ulonglong offset, ulonglong increment,
if (table->s->next_number_keypart == 0)
{ // Autoincrement at key-start
- error=ha_index_last(table->record[1]);
+ error= ha_index_last(table->record[1]);
/*
MySQL implicitely assumes such method does locking (as MySQL decides to
use nr+increment without checking again with the handler, in
@@ -2906,6 +3333,9 @@ void handler::get_auto_increment(ulonglong offset, ulonglong increment,
void handler::ha_release_auto_increment()
{
DBUG_ENTER("ha_release_auto_increment");
+ DBUG_ASSERT(table_share->tmp_table != NO_TMP_TABLE ||
+ m_lock_type != F_UNLCK ||
+ (!next_insert_id && !insert_id_for_cur_row));
release_auto_increment();
insert_id_for_cur_row= 0;
auto_inc_interval_for_cur_row.replace(0, 0, 0);
@@ -2923,13 +3353,25 @@ void handler::ha_release_auto_increment()
}
-void handler::print_keydup_error(uint key_nr, const char *msg, myf errflag)
+/**
+ Construct and emit duplicate key error message using information
+ from table's record buffer.
+
+ @param table TABLE object which record buffer should be used as
+ source for column values.
+ @param key Key description.
+ @param msg Error message template to which key value should be
+ added.
+ @param errflag Flags for my_error() call.
+*/
+
+void print_keydup_error(TABLE *table, KEY *key, const char *msg, myf errflag)
{
/* Write the duplicated key in the error message */
- char key[MAX_KEY_LENGTH];
- String str(key,sizeof(key),system_charset_info);
+ char key_buff[MAX_KEY_LENGTH];
+ String str(key_buff,sizeof(key_buff),system_charset_info);
- if (key_nr == MAX_KEY)
+ if (key == NULL)
{
/* Key is unknown */
str.copy("", 0, system_charset_info);
@@ -2938,18 +3380,29 @@ void handler::print_keydup_error(uint key_nr, const char *msg, myf errflag)
else
{
/* Table is opened and defined at this point */
- key_unpack(&str,table,(uint) key_nr);
+ key_unpack(&str,table, key);
uint max_length=MYSQL_ERRMSG_SIZE-(uint) strlen(msg);
if (str.length() >= max_length)
{
str.length(max_length-4);
str.append(STRING_WITH_LEN("..."));
}
- my_printf_error(ER_DUP_ENTRY, msg,
- errflag, str.c_ptr_safe(), table->key_info[key_nr].name);
+ my_printf_error(ER_DUP_ENTRY, msg, errflag, str.c_ptr_safe(), key->name);
}
}
+/**
+ Construct and emit duplicate key error message using information
+ from table's record buffer.
+
+ @sa print_keydup_error(table, key, msg, errflag).
+*/
+
+void print_keydup_error(TABLE *table, KEY *key, myf errflag)
+{
+ print_keydup_error(table, key, ER(ER_DUP_ENTRY_WITH_KEY_NAME), errflag);
+}
+
/**
Print error that we got from handler function.
@@ -2969,7 +3422,7 @@ void handler::print_error(int error, myf errflag)
DBUG_ENTER("handler::print_error");
DBUG_PRINT("enter",("error: %d",error));
- int textno=ER_GET_ERRNO;
+ int textno= -1; // impossible value
switch (error) {
case EACCES:
textno=ER_OPEN_AS_READONLY;
@@ -3014,7 +3467,9 @@ void handler::print_error(int error, myf errflag)
uint key_nr=get_dup_key(error);
if ((int) key_nr >= 0)
{
- print_keydup_error(key_nr, ER(ER_DUP_ENTRY_WITH_KEY_NAME), errflag);
+ print_keydup_error(table,
+ key_nr == MAX_KEY ? NULL : &table->key_info[key_nr],
+ errflag);
DBUG_VOID_RETURN;
}
}
@@ -3023,44 +3478,31 @@ void handler::print_error(int error, myf errflag)
}
case HA_ERR_FOREIGN_DUPLICATE_KEY:
{
- uint key_nr= get_dup_key(error);
- if ((int) key_nr >= 0)
- {
- uint max_length;
- /* Write the key in the error message */
- char key[MAX_KEY_LENGTH];
- String str(key,sizeof(key),system_charset_info);
- /* Table is opened and defined at this point */
+ char rec_buf[MAX_KEY_LENGTH];
+ String rec(rec_buf, sizeof(rec_buf), system_charset_info);
+ /* Table is opened and defined at this point */
- /*
- Use primary_key instead of key_nr because key_nr is a key
- number in the child FK table, not in our 'table'. See
- Bug#12661768 UPDATE IGNORE CRASHES SERVER IF TABLE IS INNODB
- AND IT IS PARENT FOR OTHER ONE This bug gets a better fix in
- MySQL 5.6, but it is too risky to get that in 5.1 and 5.5
- (extending the handler interface and adding new error message
- codes)
- */
- if (table->s->primary_key < MAX_KEY)
- key_unpack(&str,table,table->s->primary_key);
- else
- {
- LEX_CUSTRING tmp= {USTRING_WITH_LEN("Unknown key value")};
- str.set((const char*) tmp.str, tmp.length, system_charset_info);
- }
- max_length= (MYSQL_ERRMSG_SIZE-
- (uint) strlen(ER(ER_FOREIGN_DUPLICATE_KEY)));
- if (str.length() >= max_length)
- {
- str.length(max_length-4);
- str.append(STRING_WITH_LEN("..."));
+ /*
+ Just print the subset of fields that are part of the first index,
+ printing the whole row from there is not easy.
+ */
+ key_unpack(&rec, table, &table->key_info[0]);
+
+ char child_table_name[NAME_LEN + 1];
+ char child_key_name[NAME_LEN + 1];
+ if (get_foreign_dup_key(child_table_name, sizeof(child_table_name),
+ child_key_name, sizeof(child_key_name)))
+ {
+ my_error(ER_FOREIGN_DUPLICATE_KEY_WITH_CHILD_INFO, errflag,
+ table_share->table_name.str, rec.c_ptr_safe(),
+ child_table_name, child_key_name);
}
- my_error(ER_FOREIGN_DUPLICATE_KEY, errflag, table_share->table_name.str,
- str.c_ptr_safe(), key_nr+1);
- DBUG_VOID_RETURN;
+ else
+ {
+ my_error(ER_FOREIGN_DUPLICATE_KEY_WITHOUT_CHILD_INFO, errflag,
+ table_share->table_name.str, rec.c_ptr_safe());
}
- textno= ER_DUP_KEY;
- break;
+ DBUG_VOID_RETURN;
}
case HA_ERR_NULL_IN_SPATIAL:
my_error(ER_CANT_CREATE_GEOMETRY_OBJECT, errflag);
@@ -3098,7 +3540,9 @@ void handler::print_error(int error, myf errflag)
textno=ER_OUT_OF_RESOURCES;
break;
case HA_ERR_WRONG_COMMAND:
- textno=ER_ILLEGAL_HA;
+ my_error(ER_ILLEGAL_HA, MYF(0), table_type(), table_share->db.str,
+ table_share->table_name.str);
+ DBUG_VOID_RETURN;
break;
case HA_ERR_OLD_FILE:
textno=ER_OLD_KEYFILE;
@@ -3179,7 +3623,7 @@ void handler::print_error(int error, myf errflag)
case HA_ERR_AUTOINC_ERANGE:
textno= error;
my_error(textno, errflag, table->next_number_field->field_name,
- table->in_use->warning_info->current_row_for_warning());
+ table->in_use->get_stmt_da()->current_row_for_warning());
DBUG_VOID_RETURN;
break;
case HA_ERR_TOO_MANY_CONCURRENT_TRXS:
@@ -3188,6 +3632,9 @@ void handler::print_error(int error, myf errflag)
case HA_ERR_INDEX_COL_TOO_LONG:
textno= ER_INDEX_COLUMN_TOO_LONG;
break;
+ case HA_ERR_NOT_IN_LOCK_PARTITIONS:
+ textno=ER_ROW_DOES_NOT_MATCH_GIVEN_PARTITION_SET;
+ break;
case HA_ERR_INDEX_CORRUPT:
textno= ER_INDEX_CORRUPT;
break;
@@ -3216,21 +3663,12 @@ void handler::print_error(int error, myf errflag)
my_error(ER_GET_ERRMSG, errflag, error, str.c_ptr(), engine);
}
}
- else if (error >= HA_ERR_FIRST && error <= HA_ERR_LAST)
- {
- const char* engine= table_type();
- const char *errmsg= handler_error_messages[error - HA_ERR_FIRST];
- my_error(ER_GET_ERRMSG, errflag, error, errmsg, engine);
- SET_FATAL_ERROR;
- }
else
- {
- my_error(ER_GET_ERRNO, errflag,error);
- /* SET_FATAL_ERROR; */
- }
+ my_error(ER_GET_ERRNO, errflag, error, table_type());
DBUG_VOID_RETURN;
}
}
+ DBUG_ASSERT(textno > 0);
if (fatal_error)
{
/* Ensure this becomes a true error */
@@ -3244,7 +3682,17 @@ void handler::print_error(int error, myf errflag)
errflag|= ME_NOREFRESH;
}
}
- my_error(textno, errflag, table_share->table_name.str, error);
+
+ /* if we got an OS error from a file-based engine, specify a path of error */
+ if (error < HA_ERR_FIRST && bas_ext()[0])
+ {
+ char buff[FN_REFLEN];
+ strxnmov(buff, sizeof(buff),
+ table_share->normalized_path.str, bas_ext()[0], NULL);
+ my_error(textno, errflag, buff, error);
+ }
+ else
+ my_error(textno, errflag, table_share->table_name.str, error);
DBUG_VOID_RETURN;
}
@@ -3263,7 +3711,6 @@ bool handler::get_error_message(int error, String* buf)
return FALSE;
}
-
/**
Check for incompatible collation changes.
@@ -3284,7 +3731,7 @@ int handler::check_collation_compatibility()
for (; key < key_end; key++)
{
KEY_PART_INFO *key_part= key->key_part;
- KEY_PART_INFO *key_part_end= key_part + key->key_parts;
+ KEY_PART_INFO *key_part_end= key_part + key->user_defined_key_parts;
for (; key_part < key_part_end; key_part++)
{
if (!key_part->fieldnr)
@@ -3304,9 +3751,10 @@ int handler::check_collation_compatibility()
(cs_number == 33 || /* utf8_general_ci - bug #27877 */
cs_number == 35))) /* ucs2_general_ci - bug #27877 */
return HA_ADMIN_NEEDS_UPGRADE;
- }
- }
- }
+ }
+ }
+ }
+
return 0;
}
@@ -3317,6 +3765,9 @@ int handler::ha_check_for_upgrade(HA_CHECK_OPT *check_opt)
KEY *keyinfo, *keyend;
KEY_PART_INFO *keypart, *keypartend;
+ if (table->s->incompatible_version)
+ return HA_ADMIN_NEEDS_ALTER;
+
if (!table->s->mysql_version)
{
/* check for blob-in-key error */
@@ -3325,7 +3776,7 @@ int handler::ha_check_for_upgrade(HA_CHECK_OPT *check_opt)
for (; keyinfo < keyend; keyinfo++)
{
keypart= keyinfo->key_part;
- keypartend= keypart + keyinfo->key_parts;
+ keypartend= keypart + keyinfo->user_defined_key_parts;
for (; keypart < keypartend; keypart++)
{
if (!keypart->fieldnr)
@@ -3417,6 +3868,8 @@ err:
*/
uint handler::get_dup_key(int error)
{
+ DBUG_ASSERT(table_share->tmp_table != NO_TMP_TABLE ||
+ m_lock_type != F_UNLCK);
DBUG_ENTER("handler::get_dup_key");
table->file->errkey = (uint) -1;
if (error == HA_ERR_FOUND_DUPP_KEY || error == HA_ERR_FOREIGN_DUPLICATE_KEY ||
@@ -3446,9 +3899,14 @@ int handler::delete_table(const char *name)
{
int saved_error= 0;
int error= 0;
- int enoent_or_zero= ENOENT; // Error if no file was deleted
+ int enoent_or_zero;
char buff[FN_REFLEN];
+ if (ht->discover_table)
+ enoent_or_zero= 0; // the table may not exist in the engine, it's ok
+ else
+ enoent_or_zero= ENOENT; // the first file of bas_ext() *must* exist
+
for (const char **ext=bas_ext(); *ext ; ext++)
{
fn_format(buff, name, "", *ext, MY_UNPACK_FILENAME|MY_APPEND_EXT);
@@ -3522,6 +3980,8 @@ void handler::drop_table(const char *name)
int handler::ha_check(THD *thd, HA_CHECK_OPT *check_opt)
{
int error;
+ DBUG_ASSERT(table_share->tmp_table != NO_TMP_TABLE ||
+ m_lock_type != F_UNLCK);
if ((table->s->mysql_version >= MYSQL_VERSION_ID) &&
(check_opt->sql_flags & TT_FOR_UPGRADE))
@@ -3608,6 +4068,8 @@ int
handler::ha_bulk_update_row(const uchar *old_data, uchar *new_data,
uint *dup_key_found)
{
+ DBUG_ASSERT(table_share->tmp_table != NO_TMP_TABLE ||
+ m_lock_type == F_WRLCK);
mark_trx_read_write();
return bulk_update_row(old_data, new_data, dup_key_found);
@@ -3623,6 +4085,8 @@ handler::ha_bulk_update_row(const uchar *old_data, uchar *new_data,
int
handler::ha_delete_all_rows()
{
+ DBUG_ASSERT(table_share->tmp_table != NO_TMP_TABLE ||
+ m_lock_type == F_WRLCK);
mark_trx_read_write();
return delete_all_rows();
@@ -3638,6 +4102,8 @@ handler::ha_delete_all_rows()
int
handler::ha_truncate()
{
+ DBUG_ASSERT(table_share->tmp_table != NO_TMP_TABLE ||
+ m_lock_type == F_WRLCK);
mark_trx_read_write();
return truncate();
@@ -3653,6 +4119,8 @@ handler::ha_truncate()
int
handler::ha_reset_auto_increment(ulonglong value)
{
+ DBUG_ASSERT(table_share->tmp_table != NO_TMP_TABLE ||
+ m_lock_type == F_WRLCK);
mark_trx_read_write();
return reset_auto_increment(value);
@@ -3668,6 +4136,8 @@ handler::ha_reset_auto_increment(ulonglong value)
int
handler::ha_optimize(THD* thd, HA_CHECK_OPT* check_opt)
{
+ DBUG_ASSERT(table_share->tmp_table != NO_TMP_TABLE ||
+ m_lock_type == F_WRLCK);
mark_trx_read_write();
return optimize(thd, check_opt);
@@ -3683,6 +4153,8 @@ handler::ha_optimize(THD* thd, HA_CHECK_OPT* check_opt)
int
handler::ha_analyze(THD* thd, HA_CHECK_OPT* check_opt)
{
+ DBUG_ASSERT(table_share->tmp_table != NO_TMP_TABLE ||
+ m_lock_type != F_UNLCK);
mark_trx_read_write();
return analyze(thd, check_opt);
@@ -3698,6 +4170,8 @@ handler::ha_analyze(THD* thd, HA_CHECK_OPT* check_opt)
bool
handler::ha_check_and_repair(THD *thd)
{
+ DBUG_ASSERT(table_share->tmp_table != NO_TMP_TABLE ||
+ m_lock_type == F_UNLCK);
mark_trx_read_write();
return check_and_repair(thd);
@@ -3713,6 +4187,8 @@ handler::ha_check_and_repair(THD *thd)
int
handler::ha_disable_indexes(uint mode)
{
+ DBUG_ASSERT(table_share->tmp_table != NO_TMP_TABLE ||
+ m_lock_type != F_UNLCK);
mark_trx_read_write();
return disable_indexes(mode);
@@ -3728,6 +4204,8 @@ handler::ha_disable_indexes(uint mode)
int
handler::ha_enable_indexes(uint mode)
{
+ DBUG_ASSERT(table_share->tmp_table != NO_TMP_TABLE ||
+ m_lock_type != F_UNLCK);
mark_trx_read_write();
return enable_indexes(mode);
@@ -3743,26 +4221,117 @@ handler::ha_enable_indexes(uint mode)
int
handler::ha_discard_or_import_tablespace(my_bool discard)
{
+ DBUG_ASSERT(table_share->tmp_table != NO_TMP_TABLE ||
+ m_lock_type == F_WRLCK);
mark_trx_read_write();
return discard_or_import_tablespace(discard);
}
-/**
- Prepare for alter: public interface.
+bool handler::ha_prepare_inplace_alter_table(TABLE *altered_table,
+ Alter_inplace_info *ha_alter_info)
+{
+ DBUG_ASSERT(table_share->tmp_table != NO_TMP_TABLE ||
+ m_lock_type != F_UNLCK);
+ mark_trx_read_write();
+
+ return prepare_inplace_alter_table(altered_table, ha_alter_info);
+}
+
+
+bool handler::ha_commit_inplace_alter_table(TABLE *altered_table,
+ Alter_inplace_info *ha_alter_info,
+ bool commit)
+{
+ /*
+ At this point we should have an exclusive metadata lock on the table.
+ The exception is if we're about to roll back changes (commit= false).
+ In this case, we might be rolling back after a failed lock upgrade,
+ so we could be holding the same lock level as for inplace_alter_table().
+ */
+ DBUG_ASSERT(ha_thd()->mdl_context.is_lock_owner(MDL_key::TABLE,
+ table->s->db.str,
+ table->s->table_name.str,
+ MDL_EXCLUSIVE) ||
+ !commit);
+
+ return commit_inplace_alter_table(altered_table, ha_alter_info, commit);
+}
+
- Called to prepare an *online* ALTER.
+/*
+ Default implementation to support in-place alter table
+ and old online add/drop index API
+*/
+
+enum_alter_inplace_result
+handler::check_if_supported_inplace_alter(TABLE *altered_table,
+ Alter_inplace_info *ha_alter_info)
+{
+ DBUG_ENTER("check_if_supported_alter");
+
+ HA_CREATE_INFO *create_info= ha_alter_info->create_info;
- @sa handler::prepare_for_alter()
+ Alter_inplace_info::HA_ALTER_FLAGS inplace_offline_operations=
+ Alter_inplace_info::ALTER_COLUMN_EQUAL_PACK_LENGTH |
+ Alter_inplace_info::ALTER_COLUMN_NAME |
+ Alter_inplace_info::ALTER_COLUMN_DEFAULT |
+ Alter_inplace_info::ALTER_COLUMN_OPTION |
+ Alter_inplace_info::CHANGE_CREATE_OPTION |
+ Alter_inplace_info::ALTER_RENAME;
+
+ /* Is there at least one operation that requires copy algorithm? */
+ if (ha_alter_info->handler_flags & ~inplace_offline_operations)
+ DBUG_RETURN(HA_ALTER_INPLACE_NOT_SUPPORTED);
+
+ /*
+ ALTER TABLE tbl_name CONVERT TO CHARACTER SET .. and
+ ALTER TABLE table_name DEFAULT CHARSET = .. most likely
+ change column charsets and so not supported in-place through
+ old API.
+
+ Changing of PACK_KEYS, MAX_ROWS and ROW_FORMAT options were
+ not supported as in-place operations in old API either.
+ */
+ if (create_info->used_fields & (HA_CREATE_USED_CHARSET |
+ HA_CREATE_USED_DEFAULT_CHARSET |
+ HA_CREATE_USED_PACK_KEYS |
+ HA_CREATE_USED_MAX_ROWS) ||
+ (table->s->row_type != create_info->row_type))
+ DBUG_RETURN(HA_ALTER_INPLACE_NOT_SUPPORTED);
+
+ uint table_changes= (ha_alter_info->handler_flags &
+ Alter_inplace_info::ALTER_COLUMN_EQUAL_PACK_LENGTH) ?
+ IS_EQUAL_PACK_LENGTH : IS_EQUAL_YES;
+ if (table->file->check_if_incompatible_data(create_info, table_changes)
+ == COMPATIBLE_DATA_YES)
+ DBUG_RETURN(HA_ALTER_INPLACE_NO_LOCK);
+
+ DBUG_RETURN(HA_ALTER_INPLACE_NOT_SUPPORTED);
+}
+
+
+/*
+ Default implementation to support in-place alter table
+ and old online add/drop index API
*/
-void
-handler::ha_prepare_for_alter()
+void handler::notify_table_changed()
{
- mark_trx_read_write();
+ ha_create_partitioning_metadata(table->s->path.str, NULL, CHF_INDEX_FLAG);
+}
- prepare_for_alter();
+
+void Alter_inplace_info::report_unsupported_error(const char *not_supported,
+ const char *try_instead)
+{
+ if (unsupported_reason == NULL)
+ my_error(ER_ALTER_OPERATION_NOT_SUPPORTED, MYF(0),
+ not_supported, try_instead);
+ else
+ my_error(ER_ALTER_OPERATION_NOT_SUPPORTED_REASON, MYF(0),
+ not_supported, unsupported_reason, try_instead);
}
@@ -3775,6 +4344,7 @@ handler::ha_prepare_for_alter()
int
handler::ha_rename_table(const char *from, const char *to)
{
+ DBUG_ASSERT(m_lock_type == F_UNLCK);
mark_trx_read_write();
return rename_table(from, to);
@@ -3807,6 +4377,7 @@ handler::ha_delete_table(const char *name)
void
handler::ha_drop_table(const char *name)
{
+ DBUG_ASSERT(m_lock_type == F_UNLCK);
mark_trx_read_write();
return drop_table(name);
@@ -3822,6 +4393,7 @@ handler::ha_drop_table(const char *name)
int
handler::ha_create(const char *name, TABLE *form, HA_CREATE_INFO *info)
{
+ DBUG_ASSERT(m_lock_type == F_UNLCK);
mark_trx_read_write();
int error= create(name, form, info);
if (!error &&
@@ -3834,16 +4406,23 @@ handler::ha_create(const char *name, TABLE *form, HA_CREATE_INFO *info)
/**
Create handler files for CREATE TABLE: public interface.
- @sa handler::create_handler_files()
+ @sa handler::create_partitioning_metadata()
*/
int
-handler::ha_create_handler_files(const char *name, const char *old_name,
- int action_flag, HA_CREATE_INFO *info)
+handler::ha_create_partitioning_metadata(const char *name, const char *old_name,
+ int action_flag)
{
+ /*
+ Normally this is done when unlocked, but in fast_alter_partition_table,
+ it is done on an already locked handler when preparing to alter/rename
+ partitions.
+ */
+ DBUG_ASSERT(m_lock_type == F_UNLCK ||
+ (!old_name && strcmp(name, table_share->path.str)));
mark_trx_read_write();
- return create_handler_files(name, old_name, action_flag, info);
+ return create_partitioning_metadata(name, old_name, action_flag);
}
@@ -3860,7 +4439,13 @@ handler::ha_change_partitions(HA_CREATE_INFO *create_info,
ulonglong * const deleted,
const uchar *pack_frm_data,
size_t pack_frm_len)
-{
+{ /*
+ Must have at least RDLCK or be a TMP table. Read lock is needed to read
+ from current partitions and write lock will be taken on new partitions.
+ */
+ DBUG_ASSERT(table_share->tmp_table != NO_TMP_TABLE ||
+ m_lock_type != F_UNLCK);
+
mark_trx_read_write();
return change_partitions(create_info, path, copied, deleted,
@@ -3877,6 +4462,8 @@ handler::ha_change_partitions(HA_CREATE_INFO *create_info,
int
handler::ha_drop_partitions(const char *path)
{
+ DBUG_ASSERT(!table->db_stat);
+
mark_trx_read_write();
return drop_partitions(path);
@@ -3892,6 +4479,8 @@ handler::ha_drop_partitions(const char *path)
int
handler::ha_rename_partitions(const char *path)
{
+ DBUG_ASSERT(!table->db_stat);
+
mark_trx_read_write();
return rename_partitions(path);
@@ -3952,7 +4541,7 @@ int handler::index_next_same(uchar *buf, const uchar *key, uint keylen)
table->record[0]= buf;
key_info= table->key_info + active_index;
key_part= key_info->key_part;
- key_part_end= key_part + key_info->key_parts;
+ key_part_end= key_part + key_info->user_defined_key_parts;
for (; key_part < key_part_end; key_part++)
{
DBUG_ASSERT(key_part->field);
@@ -4131,132 +4720,68 @@ end:
*/
int ha_create_table(THD *thd, const char *path,
const char *db, const char *table_name,
- HA_CREATE_INFO *create_info,
- bool update_create_info)
+ HA_CREATE_INFO *create_info, LEX_CUSTRING *frm)
{
int error= 1;
TABLE table;
char name_buff[FN_REFLEN];
const char *name;
TABLE_SHARE share;
+ bool temp_table __attribute__((unused)) =
+ create_info->options & (HA_LEX_CREATE_TMP_TABLE | HA_CREATE_TMP_ALTER);
+
DBUG_ENTER("ha_create_table");
-
+
init_tmp_table_share(thd, &share, db, 0, table_name, path);
- if (open_table_def(thd, &share, 0) ||
- open_table_from_share(thd, &share, "", 0, (uint) READ_ALL, 0, &table,
- TRUE))
- goto err;
- if (update_create_info)
- update_create_info_from_table(create_info, &table);
+ if (frm)
+ {
+ bool write_frm_now= !create_info->db_type->discover_table &&
+ !create_info->tmp_table();
- name= get_canonical_filename(table.file, share.path.str, name_buff);
+ share.frm_image= frm;
- error= table.file->ha_create(name, &table, create_info);
- (void) closefrm(&table, 0);
- if (error)
+ // open an frm image
+ if (share.init_from_binary_frm_image(thd, write_frm_now,
+ frm->str, frm->length))
+ goto err;
+ }
+ else
{
- strxmov(name_buff, db, ".", table_name, NullS);
- my_error(ER_CANT_CREATE_TABLE, MYF(ME_BELL+ME_WAITTANG), name_buff, error);
+ // open an frm file
+ share.db_plugin= ha_lock_engine(thd, create_info->db_type);
+
+ if (open_table_def(thd, &share))
+ goto err;
}
-err:
- free_table_share(&share);
- DBUG_RETURN(error != 0);
-}
-/**
- Try to discover table from engine.
+ share.m_psi= PSI_CALL_get_table_share(temp_table, &share);
- @note
- If found, write the frm file to disk.
+ if (open_table_from_share(thd, &share, "", 0, READ_ALL, 0, &table, true))
+ goto err;
- @retval
- -1 Table did not exists
- @retval
- 0 Table created ok
- @retval
- > 0 Error, table existed but could not be created
-*/
-int ha_create_table_from_engine(THD* thd, const char *db, const char *name)
-{
- int error;
- uchar *frmblob;
- size_t frmlen;
- char path[FN_REFLEN + 1];
- HA_CREATE_INFO create_info;
- TABLE table;
- TABLE_SHARE share;
- DBUG_ENTER("ha_create_table_from_engine");
- DBUG_PRINT("enter", ("name '%s'.'%s'", db, name));
+ update_create_info_from_table(create_info, &table);
- bzero((uchar*) &create_info,sizeof(create_info));
- if ((error= ha_discover(thd, db, name, &frmblob, &frmlen)))
- {
- /* Table could not be discovered and thus not created */
- DBUG_RETURN(error);
- }
+ name= get_canonical_filename(table.file, share.path.str, name_buff);
- /*
- Table exists in handler and could be discovered
- frmblob and frmlen are set, write the frm to disk
- */
+ error= table.file->ha_create(name, &table, create_info);
- build_table_filename(path, sizeof(path) - 1, db, name, "", 0);
- // Save the frm file
- error= writefrm(path, frmblob, frmlen);
- my_free(frmblob);
if (error)
- DBUG_RETURN(2);
-
- init_tmp_table_share(thd, &share, db, 0, name, path);
- if (open_table_def(thd, &share, 0))
- {
- DBUG_RETURN(3);
- }
- if (open_table_from_share(thd, &share, "" ,0, 0, 0, &table, FALSE))
{
- free_table_share(&share);
- DBUG_RETURN(3);
+ if (!thd->is_error())
+ my_error(ER_CANT_CREATE_TABLE, MYF(0), db, table_name, error);
+ table.file->print_error(error, MYF(ME_JUST_WARNING));
+ PSI_CALL_drop_table_share(temp_table, share.db.str, share.db.length,
+ share.table_name.str, share.table_name.length);
}
- update_create_info_from_table(&create_info, &table);
- create_info.table_options|= HA_OPTION_CREATE_FROM_ENGINE;
-
- get_canonical_filename(table.file, path, path);
- error=table.file->ha_create(path, &table, &create_info);
- (void) closefrm(&table, 1);
-
+ (void) closefrm(&table, 0);
+
+err:
+ free_table_share(&share);
DBUG_RETURN(error != 0);
}
-
-/**
- Try to find a table in a storage engine.
-
- @param db Normalized table schema name
- @param name Normalized table name.
- @param[out] exists Only valid if the function succeeded.
-
- @retval TRUE An error is found
- @retval FALSE Success, check *exists
-*/
-
-bool
-ha_check_if_table_exists(THD* thd, const char *db, const char *name,
- bool *exists)
-{
- uchar *frmblob= NULL;
- size_t frmlen;
- DBUG_ENTER("ha_check_if_table_exists");
-
- *exists= ! ha_discover(thd, db, name, &frmblob, &frmlen);
- if (*exists)
- my_free(frmblob);
-
- DBUG_RETURN(FALSE);
-}
-
-
void st_ha_check_opt::init()
{
flags= sql_flags= 0;
@@ -4290,11 +4815,13 @@ int ha_init_key_cache(const char *name, KEY_CACHE *key_cache, void *unused
uint division_limit= (uint)key_cache->param_division_limit;
uint age_threshold= (uint)key_cache->param_age_threshold;
uint partitions= (uint)key_cache->param_partitions;
+ uint changed_blocks_hash_size= (uint)key_cache->changed_blocks_hash_size;
mysql_mutex_unlock(&LOCK_global_system_variables);
DBUG_RETURN(!init_key_cache(key_cache,
tmp_block_size,
tmp_buff_size,
division_limit, age_threshold,
+ changed_blocks_hash_size,
partitions));
}
DBUG_RETURN(0);
@@ -4315,10 +4842,12 @@ int ha_resize_key_cache(KEY_CACHE *key_cache)
long tmp_block_size= (long) key_cache->param_block_size;
uint division_limit= (uint)key_cache->param_division_limit;
uint age_threshold= (uint)key_cache->param_age_threshold;
+ uint changed_blocks_hash_size= (uint)key_cache->changed_blocks_hash_size;
mysql_mutex_unlock(&LOCK_global_system_variables);
DBUG_RETURN(!resize_key_cache(key_cache, tmp_block_size,
tmp_buff_size,
- division_limit, age_threshold));
+ division_limit, age_threshold,
+ changed_blocks_hash_size));
}
DBUG_RETURN(0);
}
@@ -4358,10 +4887,12 @@ int ha_repartition_key_cache(KEY_CACHE *key_cache)
uint division_limit= (uint)key_cache->param_division_limit;
uint age_threshold= (uint)key_cache->param_age_threshold;
uint partitions= (uint)key_cache->param_partitions;
+ uint changed_blocks_hash_size= (uint)key_cache->changed_blocks_hash_size;
mysql_mutex_unlock(&LOCK_global_system_variables);
DBUG_RETURN(!repartition_key_cache(key_cache, tmp_block_size,
tmp_buff_size,
division_limit, age_threshold,
+ changed_blocks_hash_size,
partitions));
}
DBUG_RETURN(0);
@@ -4379,149 +4910,407 @@ int ha_change_key_cache(KEY_CACHE *old_key_cache,
}
-/**
- Try to discover one table from handler(s).
-
- @retval
- -1 Table did not exists
- @retval
- 0 OK. In this case *frmblob and *frmlen are set
- @retval
- >0 error. frmblob and frmlen may not be set
-*/
-struct st_discover_args
-{
- const char *db;
- const char *name;
- uchar **frmblob;
- size_t *frmlen;
-};
-
static my_bool discover_handlerton(THD *thd, plugin_ref plugin,
void *arg)
{
- st_discover_args *vargs= (st_discover_args *)arg;
- handlerton *hton= plugin_data(plugin, handlerton *);
- if (hton->state == SHOW_OPTION_YES && hton->discover &&
- (!(hton->discover(hton, thd, vargs->db, vargs->name,
- vargs->frmblob,
- vargs->frmlen))))
- return TRUE;
+ TABLE_SHARE *share= (TABLE_SHARE *)arg;
+ handlerton *hton= plugin_hton(plugin);
+ if (hton->state == SHOW_OPTION_YES && hton->discover_table)
+ {
+ share->db_plugin= plugin;
+ int error= hton->discover_table(hton, thd, share);
+ if (error != HA_ERR_NO_SUCH_TABLE)
+ {
+ if (error)
+ {
+ DBUG_ASSERT(share->error); // tdc_lock_share needs that
+ /*
+ report an error, unless it is "generic" and a more
+ specific one was already reported
+ */
+ if (error != HA_ERR_GENERIC || !thd->is_error())
+ my_error(ER_GET_ERRNO, MYF(0), error, plugin_name(plugin)->str);
+ share->db_plugin= 0;
+ }
+ else
+ share->error= OPEN_FRM_OK;
- return FALSE;
+ status_var_increment(thd->status_var.ha_discover_count);
+ return TRUE; // abort the search
+ }
+ share->db_plugin= 0;
+ }
+
+ DBUG_ASSERT(share->error == OPEN_FRM_OPEN_ERROR);
+ return FALSE; // continue with the next engine
}
-int ha_discover(THD *thd, const char *db, const char *name,
- uchar **frmblob, size_t *frmlen)
+int ha_discover_table(THD *thd, TABLE_SHARE *share)
{
- int error= -1; // Table does not exist in any handler
- DBUG_ENTER("ha_discover");
- DBUG_PRINT("enter", ("db: %s, name: %s", db, name));
- st_discover_args args= {db, name, frmblob, frmlen};
+ DBUG_ENTER("ha_discover_table");
+ int found;
- if (is_prefix(name,tmp_file_prefix)) /* skip temporary tables */
- DBUG_RETURN(error);
+ DBUG_ASSERT(share->error == OPEN_FRM_OPEN_ERROR); // share is not OK yet
- if (plugin_foreach(thd, discover_handlerton,
- MYSQL_STORAGE_ENGINE_PLUGIN, &args))
- error= 0;
+ if (!engines_with_discover)
+ found= FALSE;
+ else if (share->db_plugin)
+ found= discover_handlerton(thd, share->db_plugin, share);
+ else
+ found= plugin_foreach(thd, discover_handlerton,
+ MYSQL_STORAGE_ENGINE_PLUGIN, share);
+
+ if (!found)
+ open_table_error(share, OPEN_FRM_OPEN_ERROR, ENOENT); // not found
- if (!error)
- status_var_increment(thd->status_var.ha_discover_count);
- DBUG_RETURN(error);
+ DBUG_RETURN(share->error != OPEN_FRM_OK);
}
+static my_bool file_ext_exists(char *path, size_t path_len, const char *ext)
+{
+ strmake(path + path_len, ext, FN_REFLEN - path_len);
+ return !access(path, F_OK);
+}
-/**
- Call this function in order to give the handler the possiblity
- to ask engine if there are any new tables that should be written to disk
- or any dropped tables that need to be removed from disk
-*/
-struct st_find_files_args
+struct st_discover_existence_args
{
- const char *db;
- const char *path;
- const char *wild;
- bool dir;
- List<LEX_STRING> *files;
+ char *path;
+ size_t path_len;
+ const char *db, *table_name;
+ handlerton *hton;
+ bool frm_exists;
};
-static my_bool find_files_handlerton(THD *thd, plugin_ref plugin,
- void *arg)
+static my_bool discover_existence(THD *thd, plugin_ref plugin,
+ void *arg)
{
- st_find_files_args *vargs= (st_find_files_args *)arg;
- handlerton *hton= plugin_data(plugin, handlerton *);
+ st_discover_existence_args *args= (st_discover_existence_args*)arg;
+ handlerton *ht= plugin_hton(plugin);
+ if (ht->state != SHOW_OPTION_YES || !ht->discover_table_existence)
+ return args->frm_exists;
+ args->hton= ht;
- if (hton->state == SHOW_OPTION_YES && hton->find_files)
- if (hton->find_files(hton, thd, vargs->db, vargs->path, vargs->wild,
- vargs->dir, vargs->files))
- return TRUE;
+ if (ht->discover_table_existence == ext_based_existence)
+ return file_ext_exists(args->path, args->path_len,
+ ht->tablefile_extensions[0]);
- return FALSE;
+ return ht->discover_table_existence(ht, args->db, args->table_name);
}
-int
-ha_find_files(THD *thd,const char *db,const char *path,
- const char *wild, bool dir, List<LEX_STRING> *files)
+class Table_exists_error_handler : public Internal_error_handler
{
- int error= 0;
- DBUG_ENTER("ha_find_files");
- DBUG_PRINT("enter", ("db: '%s' path: '%s' wild: '%s' dir: %d",
- db, path, wild, dir));
- st_find_files_args args= {db, path, wild, dir, files};
-
- plugin_foreach(thd, find_files_handlerton,
- MYSQL_STORAGE_ENGINE_PLUGIN, &args);
- /* The return value is not currently used */
- DBUG_RETURN(error);
+public:
+ Table_exists_error_handler()
+ : m_handled_errors(0), m_unhandled_errors(0)
+ {}
+
+ bool handle_condition(THD *thd,
+ uint sql_errno,
+ const char* sqlstate,
+ Sql_condition::enum_warning_level level,
+ const char* msg,
+ Sql_condition ** cond_hdl)
+ {
+ *cond_hdl= NULL;
+ if (sql_errno == ER_NO_SUCH_TABLE ||
+ sql_errno == ER_NO_SUCH_TABLE_IN_ENGINE ||
+ sql_errno == ER_WRONG_OBJECT)
+ {
+ m_handled_errors++;
+ return TRUE;
+ }
+
+ if (level == Sql_condition::WARN_LEVEL_ERROR)
+ m_unhandled_errors++;
+ return FALSE;
+ }
+
+ bool safely_trapped_errors()
+ {
+ return ((m_handled_errors > 0) && (m_unhandled_errors == 0));
+ }
+
+private:
+ int m_handled_errors;
+ int m_unhandled_errors;
+};
+
+/**
+ Check if a given table exists, without doing a full discover, if possible
+
+ If the 'hton' is not NULL, it's set to the handlerton of the storage engine
+ of this table, or to view_pseudo_hton if the frm belongs to a view.
+
+ This function takes discovery correctly into account. If frm is found,
+ it discovers the table to make sure it really exists in the engine.
+ If no frm is found it discovers the table, in case it still exists in
+ the engine.
+
+ While it tries to cut corners (don't open .frm if no discovering engine is
+ enabled, no full discovery if all discovering engines support
+ discover_table_existence, etc), it still *may* be quite expensive
+ and must be used sparingly.
+
+ @retval true Table exists (even if the error occurred, like bad frm)
+ @retval false Table does not exist (one can do CREATE TABLE table_name)
+
+ @note if frm exists and the table in engine doesn't, *hton will be set,
+ but the return value will be false.
+
+ @note if frm file exists, but the table cannot be opened (engine not
+ loaded, frm is invalid), the return value will be true, but
+ *hton will be NULL.
+*/
+bool ha_table_exists(THD *thd, const char *db, const char *table_name,
+ handlerton **hton)
+{
+ handlerton *dummy;
+ DBUG_ENTER("ha_table_exists");
+
+ if (hton)
+ *hton= 0;
+ else if (engines_with_discover)
+ hton= &dummy;
+
+ TABLE_SHARE *share= tdc_lock_share(db, table_name);
+ if (share)
+ {
+ if (hton)
+ *hton= share->db_type();
+ tdc_unlock_share(share);
+ DBUG_RETURN(TRUE);
+ }
+
+ char path[FN_REFLEN + 1];
+ size_t path_len = build_table_filename(path, sizeof(path) - 1,
+ db, table_name, "", 0);
+ st_discover_existence_args args= {path, path_len, db, table_name, 0, true};
+
+ if (file_ext_exists(path, path_len, reg_ext))
+ {
+ bool exists= true;
+ if (hton)
+ {
+ enum legacy_db_type db_type;
+ if (dd_frm_type(thd, path, &db_type) != FRMTYPE_VIEW)
+ {
+ handlerton *ht= ha_resolve_by_legacy_type(thd, db_type);
+ if ((*hton= ht))
+ // verify that the table really exists
+ exists= discover_existence(thd,
+ plugin_int_to_ref(hton2plugin[ht->slot]), &args);
+ }
+ else
+ *hton= view_pseudo_hton;
+ }
+ DBUG_RETURN(exists);
+ }
+
+ args.frm_exists= false;
+ if (plugin_foreach(thd, discover_existence, MYSQL_STORAGE_ENGINE_PLUGIN,
+ &args))
+ {
+ if (hton)
+ *hton= args.hton;
+ DBUG_RETURN(TRUE);
+ }
+
+
+ if (need_full_discover_for_existence)
+ {
+ TABLE_LIST table;
+ uint flags = GTS_TABLE | GTS_VIEW;
+
+ if (!hton)
+ flags|= GTS_NOLOCK;
+
+ Table_exists_error_handler no_such_table_handler;
+ thd->push_internal_handler(&no_such_table_handler);
+ TABLE_SHARE *share= tdc_acquire_share(thd, db, table_name, flags);
+ thd->pop_internal_handler();
+
+ if (hton && share)
+ {
+ *hton= share->db_type();
+ tdc_release_share(share);
+ }
+
+ // the table doesn't exist if we've caught ER_NO_SUCH_TABLE and nothing else
+ DBUG_RETURN(!no_such_table_handler.safely_trapped_errors());
+ }
+
+ DBUG_RETURN(FALSE);
}
/**
- Ask handler if the table exists in engine.
- @retval
- HA_ERR_NO_SUCH_TABLE Table does not exist
- @retval
- HA_ERR_TABLE_EXIST Table exists
- @retval
- \# Error code
+ Discover all table names in a given database
*/
-struct st_table_exists_in_engine_args
+extern "C" {
+
+static int cmp_file_names(const void *a, const void *b)
{
- const char *db;
- const char *name;
- int err;
-};
+ CHARSET_INFO *cs= character_set_filesystem;
+ char *aa= ((FILEINFO *)a)->name;
+ char *bb= ((FILEINFO *)b)->name;
+ return my_strnncoll(cs, (uchar*)aa, strlen(aa), (uchar*)bb, strlen(bb));
+}
-static my_bool table_exists_in_engine_handlerton(THD *thd, plugin_ref plugin,
- void *arg)
+static int cmp_table_names(LEX_STRING * const *a, LEX_STRING * const *b)
{
- st_table_exists_in_engine_args *vargs= (st_table_exists_in_engine_args *)arg;
- handlerton *hton= plugin_data(plugin, handlerton *);
+ return my_strnncoll(&my_charset_bin, (uchar*)((*a)->str), (*a)->length,
+ (uchar*)((*b)->str), (*b)->length);
+}
- int err= HA_ERR_NO_SUCH_TABLE;
+}
- if (hton->state == SHOW_OPTION_YES && hton->table_exists_in_engine)
- err = hton->table_exists_in_engine(hton, thd, vargs->db, vargs->name);
+Discovered_table_list::Discovered_table_list(THD *thd_arg,
+ Dynamic_array<LEX_STRING*> *tables_arg,
+ const LEX_STRING *wild_arg) :
+ thd(thd_arg), with_temps(false), tables(tables_arg)
+{
+ if (wild_arg->str && wild_arg->str[0])
+ {
+ wild= wild_arg->str;
+ wend= wild + wild_arg->length;
+ }
+ else
+ wild= 0;
+}
- vargs->err = err;
- if (vargs->err == HA_ERR_TABLE_EXIST)
- return TRUE;
+bool Discovered_table_list::add_table(const char *tname, size_t tlen)
+{
+ /*
+ TODO Check with_temps and filter out temp tables.
+ Implement the check, when we'll have at least one affected engine (with
+ custom discover_table_names() method, that calls add_table() directly).
+ Note: avoid comparing the same name twice (here and in add_file).
+ */
+ if (wild && my_wildcmp(table_alias_charset, tname, tname + tlen, wild, wend,
+ wild_prefix, wild_one, wild_many))
+ return 0;
- return FALSE;
+ LEX_STRING *name= thd->make_lex_string(tname, tlen);
+ if (!name || tables->append(name))
+ return 1;
+ return 0;
+}
+
+bool Discovered_table_list::add_file(const char *fname)
+{
+ bool is_temp= strncmp(fname, STRING_WITH_LEN(tmp_file_prefix)) == 0;
+
+ if (is_temp && !with_temps)
+ return 0;
+
+ char tname[SAFE_NAME_LEN + 1];
+ size_t tlen= filename_to_tablename(fname, tname, sizeof(tname), is_temp);
+ return add_table(tname, tlen);
+}
+
+
+void Discovered_table_list::sort()
+{
+ tables->sort(cmp_table_names);
}
-int ha_table_exists_in_engine(THD* thd, const char* db, const char* name)
+void Discovered_table_list::remove_duplicates()
{
- DBUG_ENTER("ha_table_exists_in_engine");
- DBUG_PRINT("enter", ("db: %s, name: %s", db, name));
- st_table_exists_in_engine_args args= {db, name, HA_ERR_NO_SUCH_TABLE};
- plugin_foreach(thd, table_exists_in_engine_handlerton,
- MYSQL_STORAGE_ENGINE_PLUGIN, &args);
- DBUG_PRINT("exit", ("error: %d", args.err));
- DBUG_RETURN(args.err);
+ LEX_STRING **src= tables->front();
+ LEX_STRING **dst= src;
+ while (++dst <= tables->back())
+ {
+ LEX_STRING *s= *src, *d= *dst;
+ DBUG_ASSERT(strncmp(s->str, d->str, MY_MIN(s->length, d->length)) <= 0);
+ if ((s->length != d->length || strncmp(s->str, d->str, d->length)))
+ {
+ src++;
+ if (src != dst)
+ *src= *dst;
+ }
+ }
+ tables->elements(src - tables->front() + 1);
+}
+
+struct st_discover_names_args
+{
+ LEX_STRING *db;
+ MY_DIR *dirp;
+ Discovered_table_list *result;
+ uint possible_duplicates;
+};
+
+static my_bool discover_names(THD *thd, plugin_ref plugin,
+ void *arg)
+{
+ st_discover_names_args *args= (st_discover_names_args *)arg;
+ handlerton *ht= plugin_hton(plugin);
+
+ if (ht->state == SHOW_OPTION_YES && ht->discover_table_names)
+ {
+ uint old_elements= args->result->tables->elements();
+ if (ht->discover_table_names(ht, args->db, args->dirp, args->result))
+ return 1;
+
+ /*
+ hton_ext_based_table_discovery never discovers a table that has
+ a corresponding .frm file; but custom engine discover methods might
+ */
+ if (ht->discover_table_names != hton_ext_based_table_discovery)
+ args->possible_duplicates+= args->result->tables->elements() - old_elements;
+ }
+
+ return 0;
}
+/**
+ Return the list of tables
+
+ @param thd
+ @param db database to look into
+ @param dirp list of files in this database (as returned by my_dir())
+ @param result the object to return the list of files in
+ @param reusable if true, on return, 'dirp' will be a valid list of all
+ non-table files. If false, discovery will work much faster,
+ but it will leave 'dirp' corrupted and completely unusable,
+ only good for my_dirend().
+
+ Normally, reusable=false for SHOW and INFORMATION_SCHEMA, and reusable=true
+ for DROP DATABASE (as it needs to know and delete non-table files).
+*/
+
+int ha_discover_table_names(THD *thd, LEX_STRING *db, MY_DIR *dirp,
+ Discovered_table_list *result, bool reusable)
+{
+ int error;
+ DBUG_ENTER("ha_discover_table_names");
+
+ if (engines_with_discover_table_names == 0 && !reusable)
+ {
+ error= ext_table_discovery_simple(dirp, result);
+ result->sort();
+ }
+ else
+ {
+ st_discover_names_args args= {db, dirp, result, 0};
+
+ /* extension_based_table_discovery relies on dirp being sorted */
+ my_qsort(dirp->dir_entry, dirp->number_of_files,
+ sizeof(FILEINFO), cmp_file_names);
+
+ error= extension_based_table_discovery(dirp, reg_ext, result) ||
+ plugin_foreach(thd, discover_names,
+ MYSQL_STORAGE_ENGINE_PLUGIN, &args);
+ result->sort();
+
+ if (args.possible_duplicates > 0)
+ result->remove_duplicates();
+ }
+
+ DBUG_RETURN(error);
+}
+
+
#ifdef HAVE_NDB_BINLOG
/*
TODO: change this into a dynamic struct
@@ -4548,7 +5337,7 @@ struct binlog_func_st
static my_bool binlog_func_list(THD *thd, plugin_ref plugin, void *arg)
{
hton_list_st *hton_list= (hton_list_st *)arg;
- handlerton *hton= plugin_data(plugin, handlerton *);
+ handlerton *hton= plugin_hton(plugin);
if (hton->state == SHOW_OPTION_YES && hton->binlog_func)
{
uint sz= hton_list->sz;
@@ -4638,7 +5427,7 @@ static my_bool binlog_log_query_handlerton(THD *thd,
plugin_ref plugin,
void *args)
{
- return binlog_log_query_handlerton2(thd, plugin_data(plugin, handlerton *), args);
+ return binlog_log_query_handlerton2(thd, plugin_hton(plugin), args);
}
void ha_binlog_log_query(THD *thd, handlerton *hton,
@@ -4688,14 +5477,7 @@ int handler::read_range_first(const key_range *start_key,
DBUG_ENTER("handler::read_range_first");
eq_range= eq_range_arg;
- end_range= 0;
- if (end_key)
- {
- end_range= &save_end_range;
- save_end_range= *end_key;
- key_compare_result_on_equal= ((end_key->flag == HA_READ_BEFORE_KEY) ? 1 :
- (end_key->flag == HA_READ_AFTER_KEY) ? -1 : 0);
- }
+ set_end_range(end_key);
range_key_part= table->key_info[active_index].key_part;
if (!start_key) // Read first record
@@ -4771,12 +5553,26 @@ int handler::read_range_next()
}
+void handler::set_end_range(const key_range *end_key)
+{
+ end_range= 0;
+ if (end_key)
+ {
+ end_range= &save_end_range;
+ save_end_range= *end_key;
+ key_compare_result_on_equal=
+ ((end_key->flag == HA_READ_BEFORE_KEY) ? 1 :
+ (end_key->flag == HA_READ_AFTER_KEY) ? -1 : 0);
+ }
+}
+
+
/**
Compare if found key (in row) is over max-value.
@param range range to compare to row. May be 0 for no range
- @seealso
+ @see also
key.cc::key_cmp()
@return
@@ -4869,27 +5665,21 @@ static my_bool exts_handlerton(THD *unused, plugin_ref plugin,
void *arg)
{
List<char> *found_exts= (List<char> *) arg;
- handlerton *hton= plugin_data(plugin, handlerton *);
- handler *file;
- if (hton->state == SHOW_OPTION_YES && hton->create &&
- (file= hton->create(hton, (TABLE_SHARE*) 0, current_thd->mem_root)))
- {
- List_iterator_fast<char> it(*found_exts);
- const char **ext, *old_ext;
+ handlerton *hton= plugin_hton(plugin);
+ List_iterator_fast<char> it(*found_exts);
+ const char **ext, *old_ext;
- for (ext= file->bas_ext(); *ext; ext++)
+ for (ext= hton->tablefile_extensions; *ext; ext++)
+ {
+ while ((old_ext= it++))
{
- while ((old_ext= it++))
- {
- if (!strcmp(old_ext, *ext))
- break;
- }
- if (!old_ext)
- found_exts->push_back((char *) *ext);
-
- it.rewind();
+ if (!strcmp(old_ext, *ext))
+ break;
}
- delete file;
+ if (!old_ext)
+ found_exts->push_back((char *) *ext);
+
+ it.rewind();
}
return FALSE;
}
@@ -4944,7 +5734,7 @@ static my_bool showstat_handlerton(THD *thd, plugin_ref plugin,
void *arg)
{
enum ha_stat_type stat= *(enum ha_stat_type *) arg;
- handlerton *hton= plugin_data(plugin, handlerton *);
+ handlerton *hton= plugin_hton(plugin);
if (hton->state == SHOW_OPTION_YES && hton->show_status &&
hton->show_status(hton, thd, stat_print, stat))
return TRUE;
@@ -4992,7 +5782,7 @@ bool ha_show_status(THD *thd, handlerton *db_type, enum ha_stat_type stat)
if (!result && !thd->is_error())
my_eof(thd);
else if (!thd->is_error())
- my_error(ER_GET_ERRNO, MYF(0), errno);
+ my_error(ER_GET_ERRNO, MYF(0), errno, hton_name(db_type)->str);
return result;
}
@@ -5014,6 +5804,7 @@ static bool check_table_binlog_row_based(THD *thd, TABLE *table)
if (table->s->cached_row_logging_check == -1)
{
int const check(table->s->tmp_table == NO_TMP_TABLE &&
+ ! table->no_replicate &&
binlog_filter->db_ok(table->s->db.str));
table->s->cached_row_logging_check= check;
}
@@ -5025,7 +5816,9 @@ static bool check_table_binlog_row_based(THD *thd, TABLE *table)
table->s->cached_row_logging_check &&
(thd->variables.option_bits & OPTION_BIN_LOG) &&
#ifdef WITH_WSREP
- ((WSREP(thd) && wsrep_emulate_bin_log) || mysql_bin_log.is_open()));
+ /* applier and replayer should not binlog */
+ ((WSREP_EMULATE_BINLOG(thd) && (thd->wsrep_exec_mode != REPL_RECV)) ||
+ mysql_bin_log.is_open()));
#else
mysql_bin_log.is_open());
#endif
@@ -5125,8 +5918,6 @@ static int binlog_log_row(TABLE* table,
const uchar *after_record,
Log_func *log_func)
{
- if (table->no_replicate)
- return 0;
bool error= 0;
THD *const thd= table->in_use;
@@ -5154,7 +5945,7 @@ static int binlog_log_row(TABLE* table,
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,
+ if (likely(!(error= my_bitmap_init(&cols,
use_bitbuf ? bitbuf : NULL,
(n_fields + 7) & ~7UL,
FALSE))))
@@ -5176,7 +5967,7 @@ static int binlog_log_row(TABLE* table,
before_record, after_record);
}
if (!use_bitbuf)
- bitmap_free(&cols);
+ my_bitmap_free(&cols);
}
}
return error ? HA_ERR_RBR_LOGGING_FAILED : 0;
@@ -5184,6 +5975,7 @@ static int binlog_log_row(TABLE* table,
int handler::ha_external_lock(THD *thd, int lock_type)
{
+ int error;
DBUG_ENTER("handler::ha_external_lock");
/*
Whether this is lock or unlock, this should be true, and is to verify that
@@ -5191,6 +5983,12 @@ int handler::ha_external_lock(THD *thd, int lock_type)
taken a table lock), ha_release_auto_increment() was too.
*/
DBUG_ASSERT(next_insert_id == 0);
+ /* Consecutive calls for lock without unlocking in between is not allowed */
+ DBUG_ASSERT(table_share->tmp_table != NO_TMP_TABLE ||
+ ((lock_type != F_UNLCK && m_lock_type == F_UNLCK) ||
+ lock_type == F_UNLCK));
+ /* SQL HANDLER call locks/unlock while scanning (RND/INDEX). */
+ DBUG_ASSERT(inited == NONE || table->open_by_handler);
if (MYSQL_HANDLER_RDLOCK_START_ENABLED() ||
MYSQL_HANDLER_WRLOCK_START_ENABLED() ||
@@ -5213,14 +6011,18 @@ int handler::ha_external_lock(THD *thd, int lock_type)
}
}
+ ha_statistic_increment(&SSV::ha_external_lock_count);
+
/*
We cache the table flags if the locking succeeded. Otherwise, we
keep them as they were when they were fetched in ha_open().
*/
- int error= external_lock(thd, lock_type);
+ MYSQL_TABLE_LOCK_WAIT(m_psi, PSI_TABLE_EXTERNAL_LOCK, lock_type,
+ { error= external_lock(thd, lock_type); })
if (error == 0)
{
+ m_lock_type= lock_type;
cached_table_flags= table_flags();
if (table_share->tmp_table == NO_TMP_TABLE)
mysql_audit_external_lock(thd, table_share, lock_type);
@@ -5277,16 +6079,18 @@ int handler::ha_write_row(uchar *buf)
{
int error;
Log_func *log_func= Write_rows_log_event::binlog_row_logging_function;
+ DBUG_ASSERT(table_share->tmp_table != NO_TMP_TABLE ||
+ m_lock_type == F_WRLCK);
DBUG_ENTER("handler::ha_write_row");
DEBUG_SYNC_C("ha_write_row_start");
- DBUG_EXECUTE_IF("inject_error_ha_write_row",
- DBUG_RETURN(HA_ERR_INTERNAL_ERROR); );
MYSQL_INSERT_ROW_START(table_share->db.str, table_share->table_name.str);
mark_trx_read_write();
increment_statistics(&SSV::ha_write_count);
- error= write_row(buf);
+ MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,
+ { error= write_row(buf); })
+
MYSQL_INSERT_ROW_DONE(error);
if (unlikely(error))
DBUG_RETURN(error);
@@ -5303,6 +6107,8 @@ 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;
+ DBUG_ASSERT(table_share->tmp_table != NO_TMP_TABLE ||
+ m_lock_type == F_WRLCK);
/*
Some storage engines require that the new record is in record[0]
@@ -5315,7 +6121,9 @@ int handler::ha_update_row(const uchar *old_data, uchar *new_data)
mark_trx_read_write();
increment_statistics(&SSV::ha_update_count);
- error= update_row(old_data, new_data);
+ MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_UPDATE_ROW, active_index, 0,
+ { error= update_row(old_data, new_data);})
+
MYSQL_UPDATE_ROW_DONE(error);
if (unlikely(error))
return error;
@@ -5329,19 +6137,20 @@ int handler::ha_delete_row(const uchar *buf)
{
int error;
Log_func *log_func= Delete_rows_log_event::binlog_row_logging_function;
+ DBUG_ASSERT(table_share->tmp_table != NO_TMP_TABLE ||
+ m_lock_type == F_WRLCK);
/*
Normally table->record[0] is used, but sometimes table->record[1] is used.
*/
DBUG_ASSERT(buf == table->record[0] ||
buf == table->record[1]);
- DBUG_EXECUTE_IF("inject_error_ha_delete_row",
- return HA_ERR_INTERNAL_ERROR; );
MYSQL_DELETE_ROW_START(table_share->db.str, table_share->table_name.str);
mark_trx_read_write();
increment_statistics(&SSV::ha_delete_count);
- error= delete_row(buf);
+ MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_DELETE_ROW, active_index, 0,
+ { error= delete_row(buf);})
MYSQL_DELETE_ROW_DONE(error);
if (unlikely(error))
return error;
@@ -5361,10 +6170,81 @@ int handler::ha_delete_row(const uchar *buf)
void handler::use_hidden_primary_key()
{
/* fallback to use all columns in the table to identify row */
- table->use_all_columns();
+ table->column_bitmaps_set(&table->s->all_set, table->write_set);
}
+/**
+ Get an initialized ha_share.
+
+ @return Initialized ha_share
+ @retval NULL ha_share is not yet initialized.
+ @retval != NULL previous initialized ha_share.
+
+ @note
+ If not a temp table, then LOCK_ha_data must be held.
+*/
+
+Handler_share *handler::get_ha_share_ptr()
+{
+ DBUG_ENTER("handler::get_ha_share_ptr");
+ DBUG_ASSERT(ha_share && table_share);
+
+#ifndef DBUG_OFF
+ if (table_share->tmp_table == NO_TMP_TABLE)
+ mysql_mutex_assert_owner(&table_share->LOCK_ha_data);
+#endif
+
+ DBUG_RETURN(*ha_share);
+}
+
+
+/**
+ Set ha_share to be used by all instances of the same table/partition.
+
+ @param ha_share Handler_share to be shared.
+
+ @note
+ If not a temp table, then LOCK_ha_data must be held.
+*/
+
+void handler::set_ha_share_ptr(Handler_share *arg_ha_share)
+{
+ DBUG_ENTER("handler::set_ha_share_ptr");
+ DBUG_ASSERT(ha_share);
+#ifndef DBUG_OFF
+ if (table_share->tmp_table == NO_TMP_TABLE)
+ mysql_mutex_assert_owner(&table_share->LOCK_ha_data);
+#endif
+
+ *ha_share= arg_ha_share;
+ DBUG_VOID_RETURN;
+}
+
+
+/**
+ Take a lock for protecting shared handler data.
+*/
+
+void handler::lock_shared_ha_data()
+{
+ DBUG_ASSERT(table_share);
+ if (table_share->tmp_table == NO_TMP_TABLE)
+ mysql_mutex_lock(&table_share->LOCK_ha_data);
+}
+
+
+/**
+ Release lock for protecting ha_share.
+*/
+
+void handler::unlock_shared_ha_data()
+{
+ DBUG_ASSERT(table_share);
+ if (table_share->tmp_table == NO_TMP_TABLE)
+ mysql_mutex_unlock(&table_share->LOCK_ha_data);
+}
+
/** @brief
Dummy function which accept information about log files which is not need
by handlers
@@ -5400,7 +6280,7 @@ int ha_wsrep_abort_transaction(THD *bf_thd, THD *victim_thd, my_bool signal)
{
DBUG_ENTER("ha_wsrep_abort_transaction");
if (!WSREP(bf_thd) &&
- !(wsrep_OSU_method_options == WSREP_OSU_RSU &&
+ !(bf_thd->variables.wsrep_OSU_method == WSREP_OSU_RSU &&
bf_thd->wsrep_exec_mode == TOTAL_ORDER)) {
DBUG_RETURN(0);
}
@@ -5547,7 +6427,7 @@ fl_log_iterator_buffer_init(struct handler_iterator *iterator)
/* to be able to make my_free without crash in case of error */
iterator->buffer= 0;
- if (!(dirp = my_dir(fl_dir, MYF(0))))
+ if (!(dirp = my_dir(fl_dir, MYF(MY_THREAD_SPECIFIC))))
{
return HA_ITERATOR_ERROR;
}
@@ -5556,7 +6436,7 @@ fl_log_iterator_buffer_init(struct handler_iterator *iterator)
sizeof(enum log_status) +
+ FN_REFLEN + 1) *
(uint) dirp->number_off_files),
- MYF(0))) == 0)
+ MYF(MY_THREAD_SPECIFIC))) == 0)
{
return HA_ITERATOR_ERROR;
}
@@ -5590,6 +6470,7 @@ fl_log_iterator_buffer_init(struct handler_iterator *iterator)
iterator->buffer= buff;
iterator->next= &fl_log_iterator_next;
iterator->destroy= &fl_log_iterator_destroy;
+ my_dirend(dirp);
return HA_ITERATOR_OK;
}
@@ -5607,3 +6488,22 @@ fl_create_iterator(enum handler_iterator_type type,
}
}
#endif /*TRANS_LOG_MGM_EXAMPLE_CODE*/
+
+
+bool HA_CREATE_INFO::check_conflicting_charset_declarations(CHARSET_INFO *cs)
+{
+ if ((used_fields & HA_CREATE_USED_DEFAULT_CHARSET) &&
+ /* DEFAULT vs explicit, or explicit vs DEFAULT */
+ (((default_table_charset == NULL) != (cs == NULL)) ||
+ /* Two different explicit character sets */
+ (default_table_charset && cs &&
+ !my_charset_same(default_table_charset, cs))))
+ {
+ my_error(ER_CONFLICTING_DECLARATIONS, MYF(0),
+ "CHARACTER SET ", default_table_charset ?
+ default_table_charset->csname : "DEFAULT",
+ "CHARACTER SET ", cs ? cs->csname : "DEFAULT");
+ return true;
+ }
+ return false;
+}
diff --git a/sql/handler.h b/sql/handler.h
index 692d69cd7c5..a2bc0212974 100644
--- a/sql/handler.h
+++ b/sql/handler.h
@@ -1,8 +1,8 @@
#ifndef HANDLER_INCLUDED
#define HANDLER_INCLUDED
/*
- Copyright (c) 2000, 2011, Oracle and/or its affiliates.
- Copyright (c) 2009-2011 Monty Program Ab
+ Copyright (c) 2000, 2014, Oracle and/or its affiliates.
+ Copyright (c) 2009, 2014, Monty Program Ab.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
@@ -31,15 +31,20 @@
#include "thr_lock.h" /* thr_lock_type, THR_LOCK_DATA */
#include "sql_cache.h"
#include "structs.h" /* SHOW_COMP_OPTION */
+#include "sql_array.h" /* Dynamic_array<> */
+#include "mdl.h"
#include <my_compare.h>
#include <ft_global.h>
#include <keycache.h>
+#include <mysql/psi/mysql_table.h>
#if MAX_KEY > 128
#error MAX_KEY is too large. Values up to 128 are supported.
#endif
+class Alter_info;
+
// the following is for checking tables
#define HA_ADMIN_ALREADY_DONE 1
@@ -57,11 +62,27 @@
#define HA_ADMIN_NEEDS_ALTER -11
#define HA_ADMIN_NEEDS_CHECK -12
+/**
+ Return values for check_if_supported_inplace_alter().
+
+ @see check_if_supported_inplace_alter() for description of
+ the individual values.
+*/
+enum enum_alter_inplace_result {
+ HA_ALTER_ERROR,
+ HA_ALTER_INPLACE_NOT_SUPPORTED,
+ HA_ALTER_INPLACE_EXCLUSIVE_LOCK,
+ HA_ALTER_INPLACE_SHARED_LOCK_AFTER_PREPARE,
+ HA_ALTER_INPLACE_SHARED_LOCK,
+ HA_ALTER_INPLACE_NO_LOCK_AFTER_PREPARE,
+ HA_ALTER_INPLACE_NO_LOCK
+};
+
/* Bits in table_flags() to show what database can do */
-#define HA_NO_TRANSACTIONS (1 << 0) /* Doesn't support transactions */
-#define HA_PARTIAL_COLUMN_READ (1 << 1) /* read may not return all columns */
-#define HA_TABLE_SCAN_ON_INDEX (1 << 2) /* No separate data/index file */
+#define HA_NO_TRANSACTIONS (1ULL << 0) /* Doesn't support transactions */
+#define HA_PARTIAL_COLUMN_READ (1ULL << 1) /* read may not return all columns */
+#define HA_TABLE_SCAN_ON_INDEX (1ULL << 2) /* No separate data/index file */
/*
The following should be set if the following is not true when scanning
a table with rnd_next()
@@ -70,37 +91,37 @@
If this flag is not set, filesort will do a position() call for each matched
row to be able to find the row later.
*/
-#define HA_REC_NOT_IN_SEQ (1 << 3)
-#define HA_CAN_GEOMETRY (1 << 4)
+#define HA_REC_NOT_IN_SEQ (1ULL << 3)
+#define HA_CAN_GEOMETRY (1ULL << 4)
/*
Reading keys in random order is as fast as reading keys in sort order
(Used in records.cc to decide if we should use a record cache and by
filesort to decide if we should sort key + data or key + pointer-to-row
*/
-#define HA_FAST_KEY_READ (1 << 5)
+#define HA_FAST_KEY_READ (1ULL << 5)
/*
Set the following flag if we on delete should force all key to be read
and on update read all keys that changes
*/
-#define HA_REQUIRES_KEY_COLUMNS_FOR_DELETE (1 << 6)
-#define HA_NULL_IN_KEY (1 << 7) /* One can have keys with NULL */
-#define HA_DUPLICATE_POS (1 << 8) /* ha_position() gives dup row */
-#define HA_NO_BLOBS (1 << 9) /* Doesn't support blobs */
-#define HA_CAN_INDEX_BLOBS (1 << 10)
-#define HA_AUTO_PART_KEY (1 << 11) /* auto-increment in multi-part key */
-#define HA_REQUIRE_PRIMARY_KEY (1 << 12) /* .. and can't create a hidden one */
-#define HA_STATS_RECORDS_IS_EXACT (1 << 13) /* stats.records is exact */
+#define HA_REQUIRES_KEY_COLUMNS_FOR_DELETE (1ULL << 6)
+#define HA_NULL_IN_KEY (1ULL << 7) /* One can have keys with NULL */
+#define HA_DUPLICATE_POS (1ULL << 8) /* ha_position() gives dup row */
+#define HA_NO_BLOBS (1ULL << 9) /* Doesn't support blobs */
+#define HA_CAN_INDEX_BLOBS (1ULL << 10)
+#define HA_AUTO_PART_KEY (1ULL << 11) /* auto-increment in multi-part key */
+#define HA_REQUIRE_PRIMARY_KEY (1ULL << 12) /* .. and can't create a hidden one */
+#define HA_STATS_RECORDS_IS_EXACT (1ULL << 13) /* stats.records is exact */
/*
INSERT_DELAYED only works with handlers that uses MySQL internal table
level locks
*/
-#define HA_CAN_INSERT_DELAYED (1 << 14)
+#define HA_CAN_INSERT_DELAYED (1ULL << 14)
/*
If we get the primary key columns for free when we do an index read
- It also implies that we have to retrive the primary key when using
- position() and rnd_pos().
+ (usually, it also implies that HA_PRIMARY_KEY_REQUIRED_FOR_POSITION
+ flag is set).
*/
-#define HA_PRIMARY_KEY_IN_READ_INDEX (1 << 15)
+#define HA_PRIMARY_KEY_IN_READ_INDEX (1ULL << 15)
/*
If HA_PRIMARY_KEY_REQUIRED_FOR_POSITION is set, it means that to position()
uses a primary key given by the record argument.
@@ -108,36 +129,36 @@
If not set, the position is returned as the current rows position
regardless of what argument is given.
*/
-#define HA_PRIMARY_KEY_REQUIRED_FOR_POSITION (1 << 16)
-#define HA_CAN_RTREEKEYS (1 << 17)
-#define HA_NOT_DELETE_WITH_CACHE (1 << 18)
+#define HA_PRIMARY_KEY_REQUIRED_FOR_POSITION (1ULL << 16)
+#define HA_CAN_RTREEKEYS (1ULL << 17)
+#define HA_NOT_DELETE_WITH_CACHE (1ULL << 18)
/*
The following is we need to a primary key to delete (and update) a row.
If there is no primary key, all columns needs to be read on update and delete
*/
-#define HA_PRIMARY_KEY_REQUIRED_FOR_DELETE (1 << 19)
-#define HA_NO_PREFIX_CHAR_KEYS (1 << 20)
-#define HA_CAN_FULLTEXT (1 << 21)
-#define HA_CAN_SQL_HANDLER (1 << 22)
-#define HA_NO_AUTO_INCREMENT (1 << 23)
+#define HA_PRIMARY_KEY_REQUIRED_FOR_DELETE (1ULL << 19)
+#define HA_NO_PREFIX_CHAR_KEYS (1ULL << 20)
+#define HA_CAN_FULLTEXT (1ULL << 21)
+#define HA_CAN_SQL_HANDLER (1ULL << 22)
+#define HA_NO_AUTO_INCREMENT (1ULL << 23)
/* Has automatic checksums and uses the old checksum format */
-#define HA_HAS_OLD_CHECKSUM (1 << 24)
+#define HA_HAS_OLD_CHECKSUM (1ULL << 24)
/* Table data are stored in separate files (for lower_case_table_names) */
-#define HA_FILE_BASED (1 << 26)
-#define HA_NO_VARCHAR (1 << 27)
-#define HA_CAN_BIT_FIELD (1 << 28) /* supports bit fields */
-#define HA_NEED_READ_RANGE_BUFFER (1 << 29) /* for read_multi_range */
-#define HA_ANY_INDEX_MAY_BE_UNIQUE (1 << 30)
-#define HA_NO_COPY_ON_ALTER (LL(1) << 31)
-#define HA_HAS_RECORDS (LL(1) << 32) /* records() gives exact count*/
+#define HA_FILE_BASED (1ULL << 26)
+#define HA_NO_VARCHAR (1ULL << 27)
+#define HA_CAN_BIT_FIELD (1ULL << 28) /* supports bit fields */
+#define HA_NEED_READ_RANGE_BUFFER (1ULL << 29) /* for read_multi_range */
+#define HA_ANY_INDEX_MAY_BE_UNIQUE (1ULL << 30)
+#define HA_NO_COPY_ON_ALTER (1ULL << 31)
+#define HA_HAS_RECORDS (1ULL << 32) /* records() gives exact count*/
/* Has it's own method of binlog logging */
-#define HA_HAS_OWN_BINLOGGING (LL(1) << 33)
+#define HA_HAS_OWN_BINLOGGING (1ULL << 33)
/*
Engine is capable of row-format and statement-format logging,
respectively
*/
-#define HA_BINLOG_ROW_CAPABLE (LL(1) << 34)
-#define HA_BINLOG_STMT_CAPABLE (LL(1) << 35)
+#define HA_BINLOG_ROW_CAPABLE (1ULL << 34)
+#define HA_BINLOG_STMT_CAPABLE (1ULL << 35)
/*
When a multiple key conflict happens in a REPLACE command mysql
expects the conflicts to be reported in the ascending order of
@@ -160,20 +181,20 @@
This flag helps the underlying SE to inform the server that the keys are not
ordered.
*/
-#define HA_DUPLICATE_KEY_NOT_IN_ORDER (LL(1) << 36)
+#define HA_DUPLICATE_KEY_NOT_IN_ORDER (1ULL << 36)
/*
Engine supports REPAIR TABLE. Used by CHECK TABLE FOR UPGRADE if an
incompatible table is detected. If this flag is set, CHECK TABLE FOR UPGRADE
will report ER_TABLE_NEEDS_UPGRADE, otherwise ER_TABLE_NEED_REBUILD.
*/
-#define HA_CAN_REPAIR (LL(1) << 37)
+#define HA_CAN_REPAIR (1ULL << 37)
/* Has automatic checksums and uses the new checksum format */
-#define HA_HAS_NEW_CHECKSUM (LL(1) << 38)
-#define HA_CAN_VIRTUAL_COLUMNS (LL(1) << 39)
-#define HA_MRR_CANT_SORT (LL(1) << 40)
-#define HA_RECORD_MUST_BE_CLEAN_ON_WRITE (LL(1) << 41)
+#define HA_HAS_NEW_CHECKSUM (1ULL << 38)
+#define HA_CAN_VIRTUAL_COLUMNS (1ULL << 39)
+#define HA_MRR_CANT_SORT (1ULL << 40)
+#define HA_RECORD_MUST_BE_CLEAN_ON_WRITE (1ULL << 41)
/*
Table condition pushdown must be performed regardless of
@@ -186,7 +207,55 @@
then the "query=..." condition must be always pushed down into storage
engine.
*/
-#define HA_MUST_USE_TABLE_CONDITION_PUSHDOWN (LL(1) << 42)
+#define HA_MUST_USE_TABLE_CONDITION_PUSHDOWN (1ULL << 42)
+
+/**
+ The handler supports read before write removal optimization
+
+ Read before write removal may be used for storage engines which support
+ write without previous read of the row to be updated. Handler returning
+ this flag must implement start_read_removal() and end_read_removal().
+ The handler may return "fake" rows constructed from the key of the row
+ asked for. This is used to optimize UPDATE and DELETE by reducing the
+ numer of roundtrips between handler and storage engine.
+
+ Example:
+ UPDATE a=1 WHERE pk IN (<keys>)
+
+ mysql_update()
+ {
+ if (<conditions for starting read removal>)
+ start_read_removal()
+ -> handler returns true if read removal supported for this table/query
+
+ while(read_record("pk=<key>"))
+ -> handler returns fake row with column "pk" set to <key>
+
+ ha_update_row()
+ -> handler sends write "a=1" for row with "pk=<key>"
+
+ end_read_removal()
+ -> handler returns the number of rows actually written
+ }
+
+ @note This optimization in combination with batching may be used to
+ remove even more roundtrips.
+*/
+#define HA_READ_BEFORE_WRITE_REMOVAL (1LL << 43)
+
+/*
+ Engine supports extended fulltext API
+ */
+#define HA_CAN_FULLTEXT_EXT (1LL << 44)
+
+/*
+ Storage engine supports table export using the
+ FLUSH TABLE <table_list> FOR EXPORT statement
+ (meaning, after this statement one can copy table files out of the
+ datadir and later "import" (somehow) in another MariaDB instance)
+ */
+#define HA_CAN_EXPORT (1LL << 45)
+
/*
Set of all binlog flags. Currently only contain the capabilities
@@ -318,7 +387,8 @@
#define HA_LEX_CREATE_TMP_TABLE 1
#define HA_LEX_CREATE_IF_NOT_EXISTS 2
#define HA_LEX_CREATE_TABLE_LIKE 4
-#define HA_CREATE_TMP_ALTER 8
+#define HA_CREATE_TMP_ALTER 8
+#define HA_LEX_CREATE_REPLACE 16
#define HA_MAX_REC_LENGTH 65535
/* Table caching type */
@@ -327,8 +397,24 @@
#define HA_CACHE_TBL_ASKTRANSACT 2
#define HA_CACHE_TBL_TRANSACT 4
-/* Options of START TRANSACTION statement (and later of SET TRANSACTION stmt) */
-#define MYSQL_START_TRANS_OPT_WITH_CONS_SNAPSHOT 1
+/**
+ Options for the START TRANSACTION statement.
+
+ Note that READ ONLY and READ WRITE are logically mutually exclusive.
+ This is enforced by the parser and depended upon by trans_begin().
+
+ We need two flags instead of one in order to differentiate between
+ situation when no READ WRITE/ONLY clause were given and thus transaction
+ is implicitly READ WRITE and the case when READ WRITE clause was used
+ explicitly.
+*/
+
+// WITH CONSISTENT SNAPSHOT option
+static const uint MYSQL_START_TRANS_OPT_WITH_CONS_SNAPSHOT = 1;
+// READ ONLY option
+static const uint MYSQL_START_TRANS_OPT_READ_ONLY = 2;
+// READ WRITE option
+static const uint MYSQL_START_TRANS_OPT_READ_WRITE = 4;
/* Flags for method is_fatal_error */
#define HA_CHECK_DUP_KEY 1
@@ -337,26 +423,23 @@
enum legacy_db_type
{
- DB_TYPE_UNKNOWN=0,DB_TYPE_DIAB_ISAM=1,
- DB_TYPE_HASH,DB_TYPE_MISAM,DB_TYPE_PISAM,
- DB_TYPE_RMS_ISAM, DB_TYPE_HEAP, DB_TYPE_ISAM,
- DB_TYPE_MRG_ISAM, DB_TYPE_MYISAM, DB_TYPE_MRG_MYISAM,
- DB_TYPE_BERKELEY_DB, DB_TYPE_INNODB,
- DB_TYPE_GEMINI, DB_TYPE_NDBCLUSTER,
- DB_TYPE_EXAMPLE_DB, DB_TYPE_ARCHIVE_DB, DB_TYPE_CSV_DB,
- DB_TYPE_FEDERATED_DB,
- DB_TYPE_BLACKHOLE_DB,
- DB_TYPE_PARTITION_DB,
- DB_TYPE_BINLOG,
- DB_TYPE_SOLID,
- DB_TYPE_PBXT,
- DB_TYPE_TABLE_FUNCTION,
- DB_TYPE_MEMCACHE,
- DB_TYPE_FALCON,
- DB_TYPE_MARIA,
- /** Performance schema engine. */
- DB_TYPE_PERFORMANCE_SCHEMA,
- DB_TYPE_WSREP,
+ /* note these numerical values are fixed and can *not* be changed */
+ DB_TYPE_UNKNOWN=0,
+ DB_TYPE_HEAP=6,
+ DB_TYPE_MYISAM=9,
+ DB_TYPE_MRG_MYISAM=10,
+ DB_TYPE_INNODB=12,
+ DB_TYPE_NDBCLUSTER=14,
+ DB_TYPE_EXAMPLE_DB=15,
+ DB_TYPE_ARCHIVE_DB=16,
+ DB_TYPE_CSV_DB=17,
+ DB_TYPE_FEDERATED_DB=18,
+ DB_TYPE_BLACKHOLE_DB=19,
+ DB_TYPE_PARTITION_DB=20,
+ DB_TYPE_BINLOG=21,
+ DB_TYPE_PBXT=23,
+ DB_TYPE_PERFORMANCE_SCHEMA=28,
+ DB_TYPE_WSREP=41,
DB_TYPE_ARIA=42,
DB_TYPE_TOKUDB=43,
DB_TYPE_FIRST_DYNAMIC=44,
@@ -375,6 +458,13 @@ enum row_type { ROW_TYPE_NOT_USED=-1, ROW_TYPE_DEFAULT, ROW_TYPE_FIXED,
/* not part of the enum, so that it shouldn't be in switch(row_type) */
#define ROW_TYPE_MAX ((uint)ROW_TYPE_PAGE + 1)
+/* Specifies data storage format for individual columns */
+enum column_format_type {
+ COLUMN_FORMAT_TYPE_DEFAULT= 0, /* Not specified (use engine default) */
+ COLUMN_FORMAT_TYPE_FIXED= 1, /* FIXED format */
+ COLUMN_FORMAT_TYPE_DYNAMIC= 2 /* DYNAMIC format */
+};
+
enum enum_binlog_func {
BFN_RESET_LOGS= 1,
BFN_RESET_SLAVE= 2,
@@ -419,6 +509,45 @@ enum enum_binlog_command {
/* The following two are used by Maria engine: */
#define HA_CREATE_USED_TRANSACTIONAL (1L << 20)
#define HA_CREATE_USED_PAGE_CHECKSUM (1L << 21)
+/** This is set whenever STATS_PERSISTENT=0|1|default has been
+specified in CREATE/ALTER TABLE. See also HA_OPTION_STATS_PERSISTENT in
+include/my_base.h. It is possible to distinguish whether
+STATS_PERSISTENT=default has been specified or no STATS_PERSISTENT= is
+given at all. */
+#define HA_CREATE_USED_STATS_PERSISTENT (1L << 22)
+/**
+ This is set whenever STATS_AUTO_RECALC=0|1|default has been
+ specified in CREATE/ALTER TABLE. See enum_stats_auto_recalc.
+ It is possible to distinguish whether STATS_AUTO_RECALC=default
+ has been specified or no STATS_AUTO_RECALC= is given at all.
+*/
+#define HA_CREATE_USED_STATS_AUTO_RECALC (1L << 23)
+/**
+ This is set whenever STATS_SAMPLE_PAGES=N|default has been
+ specified in CREATE/ALTER TABLE. It is possible to distinguish whether
+ STATS_SAMPLE_PAGES=default has been specified or no STATS_SAMPLE_PAGES= is
+ given at all.
+*/
+#define HA_CREATE_USED_STATS_SAMPLE_PAGES (1L << 24)
+
+
+/*
+ This is master database for most of system tables. However there
+ can be other databases which can hold system tables. Respective
+ storage engines define their own system database names.
+*/
+extern const char *mysqld_system_database;
+
+/*
+ Structure to hold list of system_database.system_table.
+ This is used at both mysqld and storage engine layer.
+*/
+struct st_system_tablename
+{
+ const char *db;
+ const char *tablename;
+};
+
typedef ulonglong my_xid; // this line is the same as in log_event.h
#define MYSQL_XID_PREFIX "MySQLXid"
@@ -597,14 +726,18 @@ struct TABLE;
*/
enum enum_schema_tables
{
- SCH_CHARSETS= 0,
+ SCH_ALL_PLUGINS,
+ SCH_APPLICABLE_ROLES,
+ SCH_CHARSETS,
SCH_CLIENT_STATS,
SCH_COLLATIONS,
SCH_COLLATION_CHARACTER_SET_APPLICABILITY,
SCH_COLUMNS,
SCH_COLUMN_PRIVILEGES,
+ SCH_ENABLED_ROLES,
SCH_ENGINES,
SCH_EVENTS,
+ SCH_EXPLAIN,
SCH_FILES,
SCH_GLOBAL_STATUS,
SCH_GLOBAL_VARIABLES,
@@ -639,6 +772,7 @@ enum enum_schema_tables
};
struct TABLE_SHARE;
+struct HA_CREATE_INFO;
struct st_foreign_key_info;
typedef struct st_foreign_key_info FOREIGN_KEY_INFO;
typedef bool (stat_print_fn)(THD *thd, const char *type, uint type_len,
@@ -718,22 +852,26 @@ struct ha_index_option_struct;
enum ha_option_type { HA_OPTION_TYPE_ULL, /* unsigned long long */
HA_OPTION_TYPE_STRING, /* char * */
HA_OPTION_TYPE_ENUM, /* uint */
- HA_OPTION_TYPE_BOOL}; /* bool */
+ HA_OPTION_TYPE_BOOL, /* bool */
+ HA_OPTION_TYPE_SYSVAR};/* type of the sysval */
#define HA_xOPTION_NUMBER(name, struc, field, def, min, max, blk_siz) \
{ HA_OPTION_TYPE_ULL, name, sizeof(name)-1, \
- offsetof(struc, field), def, min, max, blk_siz, 0 }
+ offsetof(struc, field), def, min, max, blk_siz, 0, 0 }
#define HA_xOPTION_STRING(name, struc, field) \
{ HA_OPTION_TYPE_STRING, name, sizeof(name)-1, \
- offsetof(struc, field), 0, 0, 0, 0, 0 }
+ offsetof(struc, field), 0, 0, 0, 0, 0, 0}
#define HA_xOPTION_ENUM(name, struc, field, values, def) \
{ HA_OPTION_TYPE_ENUM, name, sizeof(name)-1, \
offsetof(struc, field), def, 0, \
- sizeof(values)-1, 0, values }
+ sizeof(values)-1, 0, values, 0 }
#define HA_xOPTION_BOOL(name, struc, field, def) \
{ HA_OPTION_TYPE_BOOL, name, sizeof(name)-1, \
- offsetof(struc, field), def, 0, 1, 0, 0 }
-#define HA_xOPTION_END { HA_OPTION_TYPE_ULL, 0, 0, 0, 0, 0, 0, 0, 0 }
+ offsetof(struc, field), def, 0, 1, 0, 0, 0 }
+#define HA_xOPTION_SYSVAR(name, struc, field, sysvar) \
+ { HA_OPTION_TYPE_SYSVAR, name, sizeof(name)-1, \
+ offsetof(struc, field), 0, 0, 0, 0, 0, MYSQL_SYSVAR(sysvar) }
+#define HA_xOPTION_END { HA_OPTION_TYPE_ULL, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
#define HA_TOPTION_NUMBER(name, field, def, min, max, blk_siz) \
HA_xOPTION_NUMBER(name, ha_table_option_struct, field, def, min, max, blk_siz)
@@ -743,6 +881,8 @@ enum ha_option_type { HA_OPTION_TYPE_ULL, /* unsigned long long */
HA_xOPTION_ENUM(name, ha_table_option_struct, field, values, def)
#define HA_TOPTION_BOOL(name, field, def) \
HA_xOPTION_BOOL(name, ha_table_option_struct, field, def)
+#define HA_TOPTION_SYSVAR(name, field, sysvar) \
+ HA_xOPTION_SYSVAR(name, ha_table_option_struct, field, sysvar)
#define HA_TOPTION_END HA_xOPTION_END
#define HA_FOPTION_NUMBER(name, field, def, min, max, blk_siz) \
@@ -753,6 +893,8 @@ enum ha_option_type { HA_OPTION_TYPE_ULL, /* unsigned long long */
HA_xOPTION_ENUM(name, ha_field_option_struct, field, values, def)
#define HA_FOPTION_BOOL(name, field, def) \
HA_xOPTION_BOOL(name, ha_field_option_struct, field, def)
+#define HA_FOPTION_SYSVAR(name, field, sysvar) \
+ HA_xOPTION_SYSVAR(name, ha_field_option_struct, field, sysvar)
#define HA_FOPTION_END HA_xOPTION_END
#define HA_IOPTION_NUMBER(name, field, def, min, max, blk_siz) \
@@ -763,6 +905,8 @@ enum ha_option_type { HA_OPTION_TYPE_ULL, /* unsigned long long */
HA_xOPTION_ENUM(name, ha_index_option_struct, field, values, def)
#define HA_IOPTION_BOOL(name, field, def) \
HA_xOPTION_BOOL(name, ha_index_option_struct, field, def)
+#define HA_IOPTION_SYSVAR(name, field, sysvar) \
+ HA_xOPTION_SYSVAR(name, ha_index_option_struct, field, sysvar)
#define HA_IOPTION_END HA_xOPTION_END
typedef struct st_ha_create_table_option {
@@ -773,6 +917,7 @@ typedef struct st_ha_create_table_option {
ulonglong def_value;
ulonglong min_value, max_value, block_size;
const char *values;
+ struct st_mysql_sys_var *var;
} ha_create_table_option;
enum handler_iterator_type
@@ -883,6 +1028,13 @@ struct handlerton
to the savepoint_set call
*/
int (*savepoint_rollback)(handlerton *hton, THD *thd, void *sv);
+ /**
+ Check if storage engine allows to release metadata locks which were
+ acquired after the savepoint if rollback to savepoint is done.
+ @return true - If it is safe to release MDL locks.
+ false - If it is not.
+ */
+ bool (*savepoint_rollback_can_release_mdl)(handlerton *hton, THD *thd);
int (*savepoint_release)(handlerton *hton, THD *thd, void *sv);
/*
'all' is true if it's a real commit, that makes persistent changes
@@ -985,6 +1137,46 @@ struct handlerton
int (*recover)(handlerton *hton, XID *xid_list, uint len);
int (*commit_by_xid)(handlerton *hton, XID *xid);
int (*rollback_by_xid)(handlerton *hton, XID *xid);
+ /*
+ The commit_checkpoint_request() handlerton method is used to checkpoint
+ the XA recovery process for storage engines that support two-phase
+ commit.
+
+ The method is optional - an engine that does not implemented is expected
+ to work the traditional way, where every commit() durably flushes the
+ transaction to disk in the engine before completion, so XA recovery will
+ no longer be needed for that transaction.
+
+ An engine that does implement commit_checkpoint_request() is also
+ expected to implement commit_ordered(), so that ordering of commits is
+ consistent between 2pc participants. Such engine is no longer required to
+ durably flush to disk transactions in commit(), provided that the
+ transaction has been successfully prepare()d and commit_ordered(); thus
+ potentionally saving one fsync() call. (Engine must still durably flush
+ to disk in commit() when no prepare()/commit_ordered() steps took place,
+ at least if durable commits are wanted; this happens eg. if binlog is
+ disabled).
+
+ The TC will periodically (eg. once per binlog rotation) call
+ commit_checkpoint_request(). When this happens, the engine must arrange
+ for all transaction that have completed commit_ordered() to be durably
+ flushed to disk (this does not include transactions that might be in the
+ middle of executing commit_ordered()). When such flush has completed, the
+ engine must call commit_checkpoint_notify_ha(), passing back the opaque
+ "cookie".
+
+ The flush and call of commit_checkpoint_notify_ha() need not happen
+ immediately - it can be scheduled and performed asynchroneously (ie. as
+ part of next prepare(), or sync every second, or whatever), but should
+ not be postponed indefinitely. It is however also permissible to do it
+ immediately, before returning from commit_checkpoint_request().
+
+ When commit_checkpoint_notify_ha() is called, the TC will know that the
+ transactions are durably committed, and thus no longer require XA
+ recovery. It uses that to reduce the work needed for any subsequent XA
+ recovery process.
+ */
+ void (*commit_checkpoint_request)(handlerton *hton, void *cookie);
/*
"Disable or enable checkpointing internal to the storage engine. This is
used for FLUSH TABLES WITH READ LOCK AND DISABLE CHECKPOINT to ensure that
@@ -1041,23 +1233,7 @@ struct handlerton
enum handler_create_iterator_result
(*create_iterator)(handlerton *hton, enum handler_iterator_type type,
struct handler_iterator *fill_this_in);
- int (*discover)(handlerton *hton, THD* thd, const char *db,
- const char *name,
- uchar **frmblob,
- size_t *frmlen);
- int (*find_files)(handlerton *hton, THD *thd,
- const char *db,
- const char *path,
- const char *wild, bool dir, List<LEX_STRING> *files);
- int (*table_exists_in_engine)(handlerton *hton, THD* thd, const char *db,
- const char *name);
- int (*wsrep_abort_transaction)(handlerton *hton, THD *bf_thd,
- THD *victim_thd, my_bool signal);
- int (*wsrep_set_checkpoint)(handlerton *hton, const XID* xid);
- int (*wsrep_get_checkpoint)(handlerton *hton, XID* xid);
- void (*wsrep_fake_trx_id)(handlerton *hton, THD *thd);
- uint32 license; /* Flag for Engine License */
/*
Optional clauses in the CREATE/ALTER TABLE
*/
@@ -1065,14 +1241,137 @@ struct handlerton
ha_create_table_option *field_options; // these are specified per field
ha_create_table_option *index_options; // these are specified per index
+ /**
+ The list of extensions of files created for a single table in the
+ database directory (datadir/db_name/).
+
+ Used by open_table_error(), by the default rename_table and delete_table
+ handler methods, and by the default discovery implementation.
+
+ For engines that have more than one file name extentions (separate
+ metadata, index, and/or data files), the order of elements is relevant.
+ First element of engine file name extentions array should be metadata
+ file extention. This is implied by the open_table_error()
+ and the default discovery implementation.
+
+ Second element - data file extention. This is implied
+ assumed by REPAIR TABLE ... USE_FRM implementation.
+ */
+ const char **tablefile_extensions; // by default - empty list
+
+ /*********************************************************************
+ Table discovery API.
+ It allows the server to "discover" tables that exist in the storage
+ engine, without user issuing an explicit CREATE TABLE statement.
+ **********************************************************************/
+
+ /*
+ This method is required for any engine that supports automatic table
+ discovery, there is no default implementation.
+
+ Given a TABLE_SHARE discover_table() fills it in with a correct table
+ structure using one of the TABLE_SHARE::init_from_* methods.
+
+ Returns HA_ERR_NO_SUCH_TABLE if the table did not exist in the engine,
+ zero if the table was discovered successfully, or any other
+ HA_ERR_* error code as appropriate if the table existed, but the
+ discovery failed.
+ */
+ int (*discover_table)(handlerton *hton, THD* thd, TABLE_SHARE *share);
+
+ /*
+ The discover_table_names method tells the server
+ about all tables in the specified database that the engine
+ knows about. Tables (or file names of tables) are added to
+ the provided discovered_list collector object using
+ add_table() or add_file() methods.
+ */
+ class discovered_list
+ {
+ public:
+ virtual bool add_table(const char *tname, size_t tlen) = 0;
+ virtual bool add_file(const char *fname) = 0;
+ protected: virtual ~discovered_list() {}
+ };
+
+ /*
+ By default (if not implemented by the engine, but the discovery_table() is
+ implemented) it will perform a file-based discovery:
+
+ - if tablefile_extensions[0] is not null, this will discovers all tables
+ with the tablefile_extensions[0] extension.
+
+ Returns 0 on success and 1 on error.
+ */
+ int (*discover_table_names)(handlerton *hton, LEX_STRING *db, MY_DIR *dir,
+ discovered_list *result);
+
+ /*
+ This is a method that allows to server to check if a table exists without
+ an overhead of the complete discovery.
+
+ By default (if not implemented by the engine, but the discovery_table() is
+ implemented) it will try to perform a file-based discovery:
+
+ - if tablefile_extensions[0] is not null this will look for a file name
+ with the tablefile_extensions[0] extension.
+
+ - if tablefile_extensions[0] is null, this will resort to discover_table().
+
+ Note that resorting to discover_table() is slow and the engine
+ should probably implement its own discover_table_existence() method,
+ if its tablefile_extensions[0] is null.
+
+ Returns 1 if the table exists and 0 if it does not.
+ */
+ int (*discover_table_existence)(handlerton *hton, const char *db,
+ const char *table_name);
+
+ /*
+ This is the assisted table discovery method. Unlike the fully
+ automatic discovery as above, here a user is expected to issue an
+ explicit CREATE TABLE with the appropriate table attributes to
+ "assist" the discovery of a table. But this "discovering" CREATE TABLE
+ statement will not specify the table structure - the engine discovers
+ it using this method. For example, FederatedX uses it in
+
+ CREATE TABLE t1 ENGINE=FEDERATED CONNECTION="mysql://foo/bar/t1";
+
+ Given a TABLE_SHARE discover_table_structure() fills it in with a correct
+ table structure using one of the TABLE_SHARE::init_from_* methods.
+
+ Assisted discovery works independently from the automatic discover.
+ An engine is allowed to support only assisted discovery and not
+ support automatic one. Or vice versa.
+ */
+ int (*discover_table_structure)(handlerton *hton, THD* thd,
+ TABLE_SHARE *share, HA_CREATE_INFO *info);
+
+#ifdef WITH_WSREP
+ int (*wsrep_abort_transaction)(handlerton *hton, THD *bf_thd,
+ THD *victim_thd, my_bool signal);
+ int (*wsrep_set_checkpoint)(handlerton *hton, const XID* xid);
+ int (*wsrep_get_checkpoint)(handlerton *hton, XID* xid);
+ void (*wsrep_fake_trx_id)(handlerton *hton, THD *thd);
+#endif /* WITH_WSREP */
};
-inline LEX_STRING *hton_name(const handlerton *hton)
+static inline LEX_STRING *hton_name(const handlerton *hton)
{
return &(hton2plugin[hton->slot]->name);
}
+static inline handlerton *plugin_hton(plugin_ref plugin)
+{
+ return plugin_data(plugin, handlerton *);
+}
+
+static inline sys_var *find_hton_sysvar(handlerton *hton, st_mysql_sys_var *var)
+{
+ return find_plugin_sysvar(hton2plugin[hton->slot], var);
+}
+
/* Possible flags of a handlerton (there can be 32 of them) */
#define HTON_NO_FLAGS 0
@@ -1080,11 +1379,27 @@ inline LEX_STRING *hton_name(const handlerton *hton)
#define HTON_ALTER_NOT_SUPPORTED (1 << 1) //Engine does not support alter
#define HTON_CAN_RECREATE (1 << 2) //Delete all is used for truncate
#define HTON_HIDDEN (1 << 3) //Engine does not appear in lists
+#define HTON_FLUSH_AFTER_RENAME (1 << 4)
#define HTON_NOT_USER_SELECTABLE (1 << 5)
#define HTON_TEMPORARY_NOT_SUPPORTED (1 << 6) //Having temporary tables not supported
#define HTON_SUPPORT_LOG_TABLES (1 << 7) //Engine supports log tables
-#define HTON_NO_PARTITION (1 << 8) //You can not partition these tables
-#define HTON_EXTENDED_KEYS (1 << 9) //supports extended keys
+#define HTON_NO_PARTITION (1 << 8) //Not partition of these tables
+
+/*
+ This flag should be set when deciding that the engine does not allow
+ row based binary logging (RBL) optimizations.
+
+ Currently, setting this flag, means that table's read/write_set will
+ be left untouched when logging changes to tables in this engine. In
+ practice this means that the server will not mess around with
+ table->write_set and/or table->read_set when using RBL and deciding
+ whether to log full or minimal rows.
+
+ It's valuable for instance for virtual tables, eg: Performance
+ Schema which have no meaning for replication.
+*/
+#define HTON_NO_BINLOG_ROW_OPT (1 << 9)
+#define HTON_SUPPORTS_EXTENDED_KEYS (1 <<10) //supports extended keys
// MySQL compatibility. Unused.
#define HTON_SUPPORTS_FOREIGN_KEYS (1 << 0) //Foreign key constraint supported.
@@ -1130,6 +1445,22 @@ struct THD_TRANS
void reset() { no_2pc= FALSE; modified_non_trans_table= FALSE; }
bool is_empty() const { return ha_list == NULL; }
THD_TRANS() {} /* Remove gcc warning */
+
+ unsigned int m_unsafe_rollback_flags;
+ /*
+ Define the type of statemens which cannot be rolled back safely.
+ Each type occupies one bit in m_unsafe_rollback_flags.
+ */
+ static unsigned int const MODIFIED_NON_TRANS_TABLE= 0x01;
+ static unsigned int const CREATED_TEMP_TABLE= 0x02;
+ static unsigned int const DROPPED_TEMP_TABLE= 0x04;
+
+ void mark_created_temp_table()
+ {
+ DBUG_PRINT("debug", ("mark_created_temp_table"));
+ m_unsafe_rollback_flags|= CREATED_TEMP_TABLE;
+ }
+
};
@@ -1253,13 +1584,17 @@ struct st_table_log_memory_entry;
class partition_info;
struct st_partition_iter;
-#define NOT_A_PARTITION_ID ((uint32)-1)
enum ha_choice { HA_CHOICE_UNDEF, HA_CHOICE_NO, HA_CHOICE_YES, HA_CHOICE_MAX };
-typedef struct st_ha_create_information
+enum enum_stats_auto_recalc { HA_STATS_AUTO_RECALC_DEFAULT= 0,
+ HA_STATS_AUTO_RECALC_ON,
+ HA_STATS_AUTO_RECALC_OFF };
+
+struct HA_CREATE_INFO
{
CHARSET_INFO *table_charset, *default_table_charset;
+ LEX_CUSTRING tabledef_version;
LEX_STRING connect_string;
const char *password, *tablespace;
LEX_STRING comment;
@@ -1267,10 +1602,20 @@ typedef struct st_ha_create_information
const char *alias;
ulonglong max_rows,min_rows;
ulonglong auto_increment_value;
- ulong table_options;
+ ulong table_options; ///< HA_OPTION_ values
ulong avg_row_length;
ulong used_fields;
ulong key_block_size;
+ /*
+ number of pages to sample during
+ stats estimation, if used, otherwise 0.
+ */
+ uint stats_sample_pages;
+ uint null_bits; /* NULL bits at start of record */
+ uint options; /* OR of HA_CREATE_ options */
+ uint org_options; /* original options from query */
+ uint merge_insert_method;
+ uint extra_size; /* length of extra data segment */
SQL_I_List<TABLE_LIST> merge_list;
handlerton *db_type;
/**
@@ -1280,24 +1625,371 @@ typedef struct st_ha_create_information
For ALTER TABLE defaults to ROW_TYPE_NOT_USED (means "keep the current").
Can be changed either explicitly by the parser.
- If nothing speficied inherits the value of the original table (if present).
+ If nothing specified inherits the value of the original table (if present).
*/
enum row_type row_type;
- uint null_bits; /* NULL bits at start of record */
- uint options; /* OR of HA_CREATE_ options */
- uint merge_insert_method;
- uint extra_size; /* length of extra data segment */
enum ha_choice transactional;
- bool frm_only; ///< 1 if no ha_create_table()
- bool varchar; ///< 1 if table has a VARCHAR
enum ha_storage_media storage_media; ///< DEFAULT, DISK or MEMORY
enum ha_choice page_checksum; ///< If we have page_checksums
engine_option_value *option_list; ///< list of table create options
+ enum_stats_auto_recalc stats_auto_recalc;
+ bool varchar; ///< 1 if table has a VARCHAR
+
/* the following three are only for ALTER TABLE, check_if_incompatible_data() */
ha_table_option_struct *option_struct; ///< structure with parsed table options
ha_field_option_struct **fields_option_struct; ///< array of field option structures
ha_index_option_struct **indexes_option_struct; ///< array of index option structures
-} HA_CREATE_INFO;
+
+ /* The following is used to remember the old state for CREATE OR REPLACE */
+ TABLE *table;
+ TABLE_LIST *pos_in_locked_tables;
+ MDL_ticket *mdl_ticket;
+ bool table_was_deleted;
+
+ bool tmp_table() { return options & HA_LEX_CREATE_TMP_TABLE; }
+ bool check_conflicting_charset_declarations(CHARSET_INFO *cs);
+ bool add_table_option_default_charset(CHARSET_INFO *cs)
+ {
+ // cs can be NULL, e.g.: CREATE TABLE t1 (..) CHARACTER SET DEFAULT;
+ if (check_conflicting_charset_declarations(cs))
+ return true;
+ default_table_charset= cs;
+ used_fields|= HA_CREATE_USED_DEFAULT_CHARSET;
+ return false;
+ }
+ bool add_alter_list_item_convert_to_charset(CHARSET_INFO *cs)
+ {
+ /*
+ cs cannot be NULL, as sql_yacc.yy translates
+ CONVERT TO CHARACTER SET DEFAULT
+ to
+ CONVERT TO CHARACTER SET <character-set-of-the-current-database>
+ TODO: Should't we postpone resolution of DEFAULT until the
+ character set of the table owner database is loaded from its db.opt?
+ */
+ DBUG_ASSERT(cs);
+ if (check_conflicting_charset_declarations(cs))
+ return true;
+ table_charset= default_table_charset= cs;
+ used_fields|= (HA_CREATE_USED_CHARSET | HA_CREATE_USED_DEFAULT_CHARSET);
+ return false;
+ }
+};
+
+
+/**
+ In-place alter handler context.
+
+ This is a superclass intended to be subclassed by individual handlers
+ in order to store handler unique context between in-place alter API calls.
+
+ The handler is responsible for creating the object. This can be done
+ as early as during check_if_supported_inplace_alter().
+
+ The SQL layer is responsible for destroying the object.
+ The class extends Sql_alloc so the memory will be mem root allocated.
+
+ @see Alter_inplace_info
+*/
+
+class inplace_alter_handler_ctx : public Sql_alloc
+{
+public:
+ inplace_alter_handler_ctx() {}
+
+ virtual ~inplace_alter_handler_ctx() {}
+};
+
+
+/**
+ Class describing changes to be done by ALTER TABLE.
+ Instance of this class is passed to storage engine in order
+ to determine if this ALTER TABLE can be done using in-place
+ algorithm. It is also used for executing the ALTER TABLE
+ using in-place algorithm.
+*/
+
+class Alter_inplace_info
+{
+public:
+ /**
+ Bits to show in detail what operations the storage engine is
+ to execute.
+
+ All these operations are supported as in-place operations by the
+ SQL layer. This means that operations that by their nature must
+ be performed by copying the table to a temporary table, will not
+ have their own flags here.
+
+ We generally try to specify handler flags only if there are real
+ changes. But in cases when it is cumbersome to determine if some
+ attribute has really changed we might choose to set flag
+ pessimistically, for example, relying on parser output only.
+ */
+ typedef ulong HA_ALTER_FLAGS;
+
+ // Add non-unique, non-primary index
+ static const HA_ALTER_FLAGS ADD_INDEX = 1L << 0;
+
+ // Drop non-unique, non-primary index
+ static const HA_ALTER_FLAGS DROP_INDEX = 1L << 1;
+
+ // Add unique, non-primary index
+ static const HA_ALTER_FLAGS ADD_UNIQUE_INDEX = 1L << 2;
+
+ // Drop unique, non-primary index
+ static const HA_ALTER_FLAGS DROP_UNIQUE_INDEX = 1L << 3;
+
+ // Add primary index
+ static const HA_ALTER_FLAGS ADD_PK_INDEX = 1L << 4;
+
+ // Drop primary index
+ static const HA_ALTER_FLAGS DROP_PK_INDEX = 1L << 5;
+
+ // Add column
+ static const HA_ALTER_FLAGS ADD_COLUMN = 1L << 6;
+
+ // Drop column
+ static const HA_ALTER_FLAGS DROP_COLUMN = 1L << 7;
+
+ // Rename column
+ static const HA_ALTER_FLAGS ALTER_COLUMN_NAME = 1L << 8;
+
+ // Change column datatype
+ static const HA_ALTER_FLAGS ALTER_COLUMN_TYPE = 1L << 9;
+
+ /**
+ Change column datatype in such way that new type has compatible
+ packed representation with old type, so it is theoretically
+ possible to perform change by only updating data dictionary
+ without changing table rows.
+ */
+ static const HA_ALTER_FLAGS ALTER_COLUMN_EQUAL_PACK_LENGTH = 1L << 10;
+
+ // Reorder column
+ static const HA_ALTER_FLAGS ALTER_COLUMN_ORDER = 1L << 11;
+
+ // Change column from NOT NULL to NULL
+ static const HA_ALTER_FLAGS ALTER_COLUMN_NULLABLE = 1L << 12;
+
+ // Change column from NULL to NOT NULL
+ static const HA_ALTER_FLAGS ALTER_COLUMN_NOT_NULLABLE = 1L << 13;
+
+ // Set or remove default column value
+ static const HA_ALTER_FLAGS ALTER_COLUMN_DEFAULT = 1L << 14;
+
+ // Add foreign key
+ static const HA_ALTER_FLAGS ADD_FOREIGN_KEY = 1L << 15;
+
+ // Drop foreign key
+ static const HA_ALTER_FLAGS DROP_FOREIGN_KEY = 1L << 16;
+
+ // table_options changed, see HA_CREATE_INFO::used_fields for details.
+ static const HA_ALTER_FLAGS CHANGE_CREATE_OPTION = 1L << 17;
+
+ // Table is renamed
+ static const HA_ALTER_FLAGS ALTER_RENAME = 1L << 18;
+
+ // column's engine options changed, something in field->option_struct
+ static const HA_ALTER_FLAGS ALTER_COLUMN_OPTION = 1L << 19;
+
+ // MySQL alias for the same thing:
+ static const HA_ALTER_FLAGS ALTER_COLUMN_STORAGE_TYPE = 1L << 19;
+
+ // Change the column format of column
+ static const HA_ALTER_FLAGS ALTER_COLUMN_COLUMN_FORMAT = 1L << 20;
+
+ // Add partition
+ static const HA_ALTER_FLAGS ADD_PARTITION = 1L << 21;
+
+ // Drop partition
+ static const HA_ALTER_FLAGS DROP_PARTITION = 1L << 22;
+
+ // Changing partition options
+ static const HA_ALTER_FLAGS ALTER_PARTITION = 1L << 23;
+
+ // Coalesce partition
+ static const HA_ALTER_FLAGS COALESCE_PARTITION = 1L << 24;
+
+ // Reorganize partition ... into
+ static const HA_ALTER_FLAGS REORGANIZE_PARTITION = 1L << 25;
+
+ // Reorganize partition
+ static const HA_ALTER_FLAGS ALTER_TABLE_REORG = 1L << 26;
+
+ // Remove partitioning
+ static const HA_ALTER_FLAGS ALTER_REMOVE_PARTITIONING = 1L << 27;
+
+ // Partition operation with ALL keyword
+ static const HA_ALTER_FLAGS ALTER_ALL_PARTITION = 1L << 28;
+
+ /**
+ Recreate the table for ALTER TABLE FORCE, ALTER TABLE ENGINE
+ and OPTIMIZE TABLE operations.
+ */
+ static const HA_ALTER_FLAGS RECREATE_TABLE = 1L << 29;
+
+ // Virtual columns changed
+ static const HA_ALTER_FLAGS ALTER_COLUMN_VCOL = 1L << 30;
+
+ // ALTER TABLE for a partitioned table
+ static const HA_ALTER_FLAGS ALTER_PARTITIONED = 1L << 31;
+
+ /**
+ Create options (like MAX_ROWS) for the new version of table.
+
+ @note The referenced instance of HA_CREATE_INFO object was already
+ used to create new .FRM file for table being altered. So it
+ has been processed by mysql_prepare_create_table() already.
+ For example, this means that it has HA_OPTION_PACK_RECORD
+ flag in HA_CREATE_INFO::table_options member correctly set.
+ */
+ HA_CREATE_INFO *create_info;
+
+ /**
+ Alter options, fields and keys for the new version of table.
+
+ @note The referenced instance of Alter_info object was already
+ used to create new .FRM file for table being altered. So it
+ has been processed by mysql_prepare_create_table() already.
+ In particular, this means that in Create_field objects for
+ fields which were present in some form in the old version
+ of table, Create_field::field member points to corresponding
+ Field instance for old version of table.
+ */
+ Alter_info *alter_info;
+
+ /**
+ Array of KEYs for new version of table - including KEYs to be added.
+
+ @note Currently this array is produced as result of
+ mysql_prepare_create_table() call.
+ This means that it follows different convention for
+ KEY_PART_INFO::fieldnr values than objects in TABLE::key_info
+ array.
+
+ @todo This is mainly due to the fact that we need to keep compatibility
+ with removed handler::add_index() call. We plan to switch to
+ TABLE::key_info numbering later.
+
+ KEYs are sorted - see sort_keys().
+ */
+ KEY *key_info_buffer;
+
+ /** Size of key_info_buffer array. */
+ uint key_count;
+
+ /** Size of index_drop_buffer array. */
+ uint index_drop_count;
+
+ /**
+ Array of pointers to KEYs to be dropped belonging to the TABLE instance
+ for the old version of the table.
+ */
+ KEY **index_drop_buffer;
+
+ /** Size of index_add_buffer array. */
+ uint index_add_count;
+
+ /**
+ Array of indexes into key_info_buffer for KEYs to be added,
+ sorted in increasing order.
+ */
+ uint *index_add_buffer;
+
+ /**
+ Context information to allow handlers to keep context between in-place
+ alter API calls.
+
+ @see inplace_alter_handler_ctx for information about object lifecycle.
+ */
+ inplace_alter_handler_ctx *handler_ctx;
+
+ /**
+ If the table uses several handlers, like ha_partition uses one handler
+ per partition, this contains a Null terminated array of ctx pointers
+ that should all be committed together.
+ Or NULL if only handler_ctx should be committed.
+ Set to NULL if the low level handler::commit_inplace_alter_table uses it,
+ to signal to the main handler that everything was committed as atomically.
+
+ @see inplace_alter_handler_ctx for information about object lifecycle.
+ */
+ inplace_alter_handler_ctx **group_commit_ctx;
+
+ /**
+ Flags describing in detail which operations the storage engine is to execute.
+ */
+ HA_ALTER_FLAGS handler_flags;
+
+ /**
+ Partition_info taking into account the partition changes to be performed.
+ Contains all partitions which are present in the old version of the table
+ with partitions to be dropped or changed marked as such + all partitions
+ to be added in the new version of table marked as such.
+ */
+ partition_info *modified_part_info;
+
+ /** true for ALTER IGNORE TABLE ... */
+ const bool ignore;
+
+ /** true for online operation (LOCK=NONE) */
+ bool online;
+
+ /**
+ Can be set by handler to describe why a given operation cannot be done
+ in-place (HA_ALTER_INPLACE_NOT_SUPPORTED) or why it cannot be done
+ online (HA_ALTER_INPLACE_NO_LOCK or
+ HA_ALTER_INPLACE_NO_LOCK_AFTER_PREPARE)
+ If set, it will be used with ER_ALTER_OPERATION_NOT_SUPPORTED_REASON if
+ results from handler::check_if_supported_inplace_alter() doesn't match
+ requirements set by user. If not set, the more generic
+ ER_ALTER_OPERATION_NOT_SUPPORTED will be used.
+
+ Please set to a properly localized string, for example using
+ my_get_err_msg(), so that the error message as a whole is localized.
+ */
+ const char *unsupported_reason;
+
+ Alter_inplace_info(HA_CREATE_INFO *create_info_arg,
+ Alter_info *alter_info_arg,
+ KEY *key_info_arg, uint key_count_arg,
+ partition_info *modified_part_info_arg,
+ bool ignore_arg)
+ : create_info(create_info_arg),
+ alter_info(alter_info_arg),
+ key_info_buffer(key_info_arg),
+ key_count(key_count_arg),
+ index_drop_count(0),
+ index_drop_buffer(NULL),
+ index_add_count(0),
+ index_add_buffer(NULL),
+ handler_ctx(NULL),
+ group_commit_ctx(NULL),
+ handler_flags(0),
+ modified_part_info(modified_part_info_arg),
+ ignore(ignore_arg),
+ online(false),
+ unsupported_reason(NULL)
+ {}
+
+ ~Alter_inplace_info()
+ {
+ delete handler_ctx;
+ }
+
+ /**
+ Used after check_if_supported_inplace_alter() to report
+ error if the result does not match the LOCK/ALGORITHM
+ requirements set by the user.
+
+ @param not_supported Part of statement that was not supported.
+ @param try_instead Suggestion as to what the user should
+ replace not_supported with.
+ */
+ void report_unsupported_error(const char *not_supported,
+ const char *try_instead);
+};
typedef struct st_key_create_information
@@ -1306,6 +1998,12 @@ typedef struct st_key_create_information
ulong block_size;
LEX_STRING parser_name;
LEX_STRING comment;
+ /**
+ A flag to determine if we will check for duplicate indexes.
+ This typically means that the key information was specified
+ directly by the user (set by the parser).
+ */
+ bool check_for_duplicate_indexes;
} KEY_CREATE_INFO;
@@ -1464,21 +2162,24 @@ typedef struct st_range_seq_if
typedef bool (*SKIP_INDEX_TUPLE_FUNC) (range_seq_t seq, range_id_t range_info);
-class COST_VECT
+class Cost_estimate
{
public:
double io_count; /* number of I/O */
double avg_io_cost; /* cost of an average I/O oper. */
double cpu_cost; /* cost of operations in CPU */
- double mem_cost; /* cost of used memory */
double import_cost; /* cost of remote operations */
+ double mem_cost; /* cost of used memory */
enum { IO_COEFF=1 };
enum { CPU_COEFF=1 };
enum { MEM_COEFF=1 };
enum { IMPORT_COEFF=1 };
- COST_VECT() {} // keep gcc happy
+ Cost_estimate()
+ {
+ reset();
+ }
double total_cost()
{
@@ -1486,7 +2187,17 @@ public:
MEM_COEFF*mem_cost + IMPORT_COEFF*import_cost;
}
- void zero()
+ /**
+ Whether or not all costs in the object are zero
+
+ @return true if all costs are zero, false otherwise
+ */
+ bool is_zero() const
+ {
+ return !(io_count || cpu_cost || import_cost || mem_cost);
+ }
+
+ void reset()
{
avg_io_cost= 1.0;
io_count= cpu_cost= mem_cost= import_cost= 0.0;
@@ -1500,13 +2211,14 @@ public:
/* Don't multiply mem_cost */
}
- void add(const COST_VECT* cost)
+ void add(const Cost_estimate* cost)
{
double io_count_sum= io_count + cost->io_count;
add_io(cost->io_count, cost->avg_io_cost);
io_count= io_count_sum;
cpu_cost += cost->cpu_cost;
}
+
void add_io(double add_io_cnt, double add_avg_cost)
{
/* In edge cases add_io_cnt may be zero */
@@ -1519,20 +2231,28 @@ public:
}
}
+ /// Add to CPU cost
+ void add_cpu(double add_cpu_cost) { cpu_cost+= add_cpu_cost; }
+
+ /// Add to import cost
+ void add_import(double add_import_cost) { import_cost+= add_import_cost; }
+
+ /// Add to memory cost
+ void add_mem(double add_mem_cost) { mem_cost+= add_mem_cost; }
+
/*
To be used when we go from old single value-based cost calculations to
- the new COST_VECT-based.
+ the new Cost_estimate-based.
*/
void convert_from_cost(double cost)
{
- zero();
- avg_io_cost= 1.0;
+ reset();
io_count= cost;
}
};
void get_sweep_read_cost(TABLE *table, ha_rows nrows, bool interrupted,
- COST_VECT *cost);
+ Cost_estimate *cost);
/*
Indicates that all scanned ranges will be singlepoint (aka equality) ranges.
@@ -1685,34 +2405,61 @@ uint calculate_key_len(TABLE *, uint, const uchar *, key_part_map);
#define make_prev_keypart_map(N) (((key_part_map)1 << (N)) - 1)
-/**
- Index creation context.
- Created by handler::add_index() and destroyed by handler::final_add_index().
- And finally freed at the end of the statement.
- (Sql_alloc does not free in delete).
-*/
-
-class handler_add_index : public Sql_alloc
+/** Base class to be used by handlers different shares */
+class Handler_share
{
public:
- /* Table where the indexes are added */
- TABLE* const table;
- /* Indexes being created */
- KEY* const key_info;
- /* Size of key_info[] */
- const uint num_of_keys;
- handler_add_index(TABLE *table_arg, KEY *key_info_arg, uint num_of_keys_arg)
- : table (table_arg), key_info (key_info_arg), num_of_keys (num_of_keys_arg)
- {}
- virtual ~handler_add_index() {}
+ Handler_share() {}
+ virtual ~Handler_share() {}
};
-class Query_cache;
-struct Query_cache_block_table;
+
/**
The handler class is the interface for dynamically loadable
storage engines. Do not add ifdefs and take care when adding or
changing virtual functions to avoid vtable confusion
+
+ Functions in this class accept and return table columns data. Two data
+ representation formats are used:
+ 1. TableRecordFormat - Used to pass [partial] table records to/from
+ storage engine
+
+ 2. KeyTupleFormat - used to pass index search tuples (aka "keys") to
+ storage engine. See opt_range.cc for description of this format.
+
+ TableRecordFormat
+ =================
+ [Warning: this description is work in progress and may be incomplete]
+ The table record is stored in a fixed-size buffer:
+
+ record: null_bytes, column1_data, column2_data, ...
+
+ The offsets of the parts of the buffer are also fixed: every column has
+ an offset to its column{i}_data, and if it is nullable it also has its own
+ bit in null_bytes.
+
+ The record buffer only includes data about columns that are marked in the
+ relevant column set (table->read_set and/or table->write_set, depending on
+ the situation).
+ <not-sure>It could be that it is required that null bits of non-present
+ columns are set to 1</not-sure>
+
+ VARIOUS EXCEPTIONS AND SPECIAL CASES
+
+ If the table has no nullable columns, then null_bytes is still
+ present, its length is one byte <not-sure> which must be set to 0xFF
+ at all times. </not-sure>
+
+ If the table has columns of type BIT, then certain bits from those columns
+ may be stored in null_bytes as well. Grep around for Field_bit for
+ details.
+
+ For blob columns (see Field_blob), the record buffer stores length of the
+ data, following by memory pointer to the blob data. The pointer is owned
+ by the storage engine and is valid until the next operation.
+
+ If a blob column has NULL value, then its length and blob data pointer
+ must be set to 0.
*/
class handler :public Sql_alloc
@@ -1765,7 +2512,6 @@ public:
uint ref_length;
FT_INFO *ft_handler;
enum {NONE=0, INDEX, RND} inited;
- bool locked;
bool implicit_emptied; /* Can be !=0 only if HEAP */
const COND *pushed_cond;
/**
@@ -1822,6 +2568,24 @@ public:
*/
PSI_table *m_psi;
+ virtual void unbind_psi();
+ virtual void rebind_psi();
+
+private:
+ /**
+ The lock type set by when calling::ha_external_lock(). This is
+ propagated down to the storage engine. The reason for also storing
+ it here, is that when doing MRR we need to create/clone a second handler
+ object. This cloned handler object needs to know about the lock_type used.
+ */
+ int m_lock_type;
+ /**
+ Pointer where to store/retrieve the Handler_share pointer.
+ For non partitioned handlers this is &TABLE_SHARE::ha_share.
+ */
+ Handler_share **ha_share;
+
+public:
handler(handlerton *ht_arg, TABLE_SHARE *share_arg)
:table_share(share_arg), table(0),
estimation_rows_to_insert(0), ht(ht_arg),
@@ -1829,18 +2593,21 @@ public:
in_range_check_pushed_down(FALSE),
ref_length(sizeof(my_off_t)),
ft_handler(0), inited(NONE),
- locked(FALSE), implicit_emptied(0),
+ implicit_emptied(0),
pushed_cond(0), next_insert_id(0), insert_id_for_cur_row(0),
pushed_idx_cond(NULL),
pushed_idx_cond_keyno(MAX_KEY),
auto_inc_intervals_count(0),
- m_psi(NULL)
+ m_psi(NULL), m_lock_type(F_UNLCK), ha_share(NULL)
{
+ DBUG_PRINT("info",
+ ("handler created F_UNLCK %d F_RDLCK %d F_WRLCK %d",
+ F_UNLCK, F_RDLCK, F_WRLCK));
reset_statistics();
}
virtual ~handler(void)
{
- DBUG_ASSERT(locked == FALSE);
+ DBUG_ASSERT(m_lock_type == F_UNLCK);
DBUG_ASSERT(inited == NONE);
}
virtual handler *clone(const char *name, MEM_ROOT *mem_root);
@@ -1850,7 +2617,7 @@ public:
cached_table_flags= table_flags();
}
/* ha_ methods: pubilc wrappers for private virtual API */
-
+
int ha_open(TABLE *table, const char *name, int mode, uint test_if_locked);
int ha_index_init(uint idx, bool sorted)
{
@@ -1933,11 +2700,11 @@ public:
/** to be actually called to get 'check()' functionality*/
int ha_check(THD *thd, HA_CHECK_OPT *check_opt);
int ha_repair(THD* thd, HA_CHECK_OPT* check_opt);
- void ha_start_bulk_insert(ha_rows rows)
+ void ha_start_bulk_insert(ha_rows rows, uint flags= 0)
{
DBUG_ENTER("handler::ha_start_bulk_insert");
estimation_rows_to_insert= rows;
- start_bulk_insert(rows);
+ start_bulk_insert(rows, flags);
DBUG_VOID_RETURN;
}
int ha_end_bulk_insert()
@@ -1958,15 +2725,14 @@ public:
int ha_disable_indexes(uint mode);
int ha_enable_indexes(uint mode);
int ha_discard_or_import_tablespace(my_bool discard);
- void ha_prepare_for_alter();
int ha_rename_table(const char *from, const char *to);
int ha_delete_table(const char *name);
void ha_drop_table(const char *name);
int ha_create(const char *name, TABLE *form, HA_CREATE_INFO *info);
- int ha_create_handler_files(const char *name, const char *old_name,
- int action_flag, HA_CREATE_INFO *info);
+ int ha_create_partitioning_metadata(const char *name, const char *old_name,
+ int action_flag);
int ha_change_partitions(HA_CREATE_INFO *create_info,
const char *path,
@@ -1979,10 +2745,32 @@ public:
void adjust_next_insert_id_after_explicit_value(ulonglong nr);
int update_auto_increment();
- void print_keydup_error(uint key_nr, const char *msg, myf errflag);
virtual void print_error(int error, myf errflag);
virtual bool get_error_message(int error, String *buf);
uint get_dup_key(int error);
+ /**
+ Retrieves the names of the table and the key for which there was a
+ duplicate entry in the case of HA_ERR_FOREIGN_DUPLICATE_KEY.
+
+ If any of the table or key name is not available this method will return
+ false and will not change any of child_table_name or child_key_name.
+
+ @param child_table_name[out] Table name
+ @param child_table_name_len[in] Table name buffer size
+ @param child_key_name[out] Key name
+ @param child_key_name_len[in] Key name buffer size
+
+ @retval true table and key names were available
+ and were written into the corresponding
+ out parameters.
+ @retval false table and key names were not available,
+ the out parameters were not touched.
+ */
+ virtual bool get_foreign_dup_key(char *child_table_name,
+ uint child_table_name_len,
+ char *child_key_name,
+ uint child_key_name_len)
+ { DBUG_ASSERT(false); return(false); }
void reset_statistics()
{
rows_read= rows_changed= rows_tmp_read= 0;
@@ -1996,6 +2784,18 @@ public:
}
virtual double scan_time()
{ return ulonglong2double(stats.data_file_length) / IO_SIZE + 2; }
+
+ /**
+ The cost of reading a set of ranges from the table using an index
+ to access it.
+
+ @param index The index number.
+ @param ranges The number of ranges to be read.
+ @param rows Total number of rows to be read.
+
+ This method can be used to calculate the total cost of scanning a table
+ using an index by calling it using read_time(index, 1, table_size).
+ */
virtual double read_time(uint index, uint ranges, ha_rows rows)
{ return rows2double(ranges+rows); }
@@ -2162,18 +2962,17 @@ protected:
}
public:
- /* Similar functions like the above, but does statistics counting */
- inline int ha_index_read_map(uchar * buf, const uchar * key,
- key_part_map keypart_map,
- enum ha_rkey_function find_flag);
- inline int ha_index_read_idx_map(uchar * buf, uint index, const uchar * key,
- key_part_map keypart_map,
- enum ha_rkey_function find_flag);
- inline int ha_index_next(uchar * buf);
- inline int ha_index_prev(uchar * buf);
- inline int ha_index_first(uchar * buf);
- inline int ha_index_last(uchar * buf);
- inline int ha_index_next_same(uchar *buf, const uchar *key, uint keylen);
+ int ha_index_read_map(uchar * buf, const uchar * key,
+ key_part_map keypart_map,
+ enum ha_rkey_function find_flag);
+ int ha_index_read_idx_map(uchar * buf, uint index, const uchar * key,
+ key_part_map keypart_map,
+ enum ha_rkey_function find_flag);
+ int ha_index_next(uchar * buf);
+ int ha_index_prev(uchar * buf);
+ int ha_index_first(uchar * buf);
+ int ha_index_last(uchar * buf);
+ int ha_index_next_same(uchar *buf, const uchar *key, uint keylen);
/*
TODO: should we make for those functions non-virtual ha_func_name wrappers,
too?
@@ -2181,10 +2980,11 @@ public:
virtual ha_rows multi_range_read_info_const(uint keyno, RANGE_SEQ_IF *seq,
void *seq_init_param,
uint n_ranges, uint *bufsz,
- uint *mrr_mode, COST_VECT *cost);
+ uint *mrr_mode,
+ Cost_estimate *cost);
virtual ha_rows multi_range_read_info(uint keyno, uint n_ranges, uint keys,
uint key_parts, uint *bufsz,
- uint *mrr_mode, COST_VECT *cost);
+ uint *mrr_mode, Cost_estimate *cost);
virtual int multi_range_read_init(RANGE_SEQ_IF *seq, void *seq_init_param,
uint n_ranges, uint mrr_mode,
HANDLER_BUFFER *buf);
@@ -2219,6 +3019,7 @@ public:
const key_range *end_key,
bool eq_range, bool sorted);
virtual int read_range_next();
+ void set_end_range(const key_range *end_key);
int compare_key(key_range *range);
int compare_key2(key_range *range);
virtual int ft_init() { return HA_ERR_WRONG_COMMAND; }
@@ -2236,6 +3037,7 @@ private:
*/
virtual int rnd_pos_by_record(uchar *record)
{
+ DBUG_ASSERT(table_flags() & HA_PRIMARY_KEY_REQUIRED_FOR_POSITION);
position(record);
return rnd_pos(record, ref);
}
@@ -2244,8 +3046,8 @@ public:
/* Same as above, but with statistics */
inline int ha_ft_read(uchar *buf);
- inline int ha_rnd_next(uchar *buf);
- inline int ha_rnd_pos(uchar *buf, uchar *pos);
+ int ha_rnd_next(uchar *buf);
+ int ha_rnd_pos(uchar *buf, uchar *pos);
inline int ha_rnd_pos_by_record(uchar *buf);
inline int ha_read_first_row(uchar *buf, uint primary_key);
@@ -2351,10 +3153,15 @@ public:
{ return FALSE; }
virtual char* get_foreign_key_create_info()
{ return(NULL);} /* gets foreign key create string from InnoDB */
- virtual char* get_tablespace_name(THD *thd, char *name, uint name_len)
- { return(NULL);} /* gets tablespace name from handler */
- /** used in ALTER TABLE; 1 if changing storage engine is allowed */
- virtual bool can_switch_engines() { return 1; }
+ /**
+ Used in ALTER TABLE to check if changing storage engine is allowed.
+
+ @note Called without holding thr_lock.c lock.
+
+ @retval true Changing storage engine is allowed.
+ @retval false Changing storage engine not allowed.
+ */
+ virtual bool can_switch_engines() { return true; }
virtual int can_continue_handler_scan() { return 0; }
/**
Get the list of foreign keys in this table.
@@ -2389,19 +3196,8 @@ public:
{ return; } /* prepare InnoDB for HANDLER */
virtual void free_foreign_key_create_info(char* str) {}
/** The following can be called without an open handler */
- virtual const char *table_type() const =0;
- /**
- If frm_error() is called then we will use this to find out what file
- extentions exist for the storage engine. This is also used by the default
- rename_table and delete_table method in handler.cc.
-
- For engines that have two file name extentions (separate meta/index file
- and data file), the order of elements is relevant. First element of engine
- file name extentions array should be meta/index file extention. Second
- element - data file extention. This order is assumed by
- prepare_for_repair() when REPAIR TABLE ... USE_FRM is issued.
- */
- virtual const char **bas_ext() const =0;
+ const char *table_type() const { return hton_name(ht)->str; }
+ const char **bas_ext() const { return ht->tablefile_extensions; }
virtual int get_default_no_partitions(HA_CREATE_INFO *create_info)
{ return 1;}
@@ -2416,52 +3212,16 @@ public:
virtual ulong index_flags(uint idx, uint part, bool all_parts) const =0;
-/**
- First phase of in-place add index.
- Handlers are supposed to create new indexes here but not make them
- visible.
-
- @param table_arg Table to add index to
- @param key_info Information about new indexes
- @param num_of_key Number of new indexes
- @param add[out] Context of handler specific information needed
- for final_add_index().
-
- @note This function can be called with less than exclusive metadata
- lock depending on which flags are listed in alter_table_flags.
-*/
- virtual int add_index(TABLE *table_arg, KEY *key_info, uint num_of_keys,
- handler_add_index **add)
- { return (HA_ERR_WRONG_COMMAND); }
-
-/**
- Second and last phase of in-place add index.
- Commit or rollback pending new indexes.
-
- @param add Context of handler specific information from add_index().
- @param commit If true, commit. If false, rollback index changes.
-
- @note This function is called with exclusive metadata lock.
-*/
- virtual int final_add_index(handler_add_index *add, bool commit)
- { return (HA_ERR_WRONG_COMMAND); }
-
- virtual int prepare_drop_index(TABLE *table_arg, uint *key_num,
- uint num_of_keys)
- { return (HA_ERR_WRONG_COMMAND); }
- virtual int final_drop_index(TABLE *table_arg)
- { return (HA_ERR_WRONG_COMMAND); }
-
uint max_record_length() const
- { return min(HA_MAX_REC_LENGTH, max_supported_record_length()); }
+ { return MY_MIN(HA_MAX_REC_LENGTH, max_supported_record_length()); }
uint max_keys() const
- { return min(MAX_KEY, max_supported_keys()); }
+ { return MY_MIN(MAX_KEY, max_supported_keys()); }
uint max_key_parts() const
- { return min(MAX_REF_PARTS, max_supported_key_parts()); }
+ { return MY_MIN(MAX_REF_PARTS, max_supported_key_parts()); }
uint max_key_length() const
- { return min(MAX_KEY_LENGTH, max_supported_key_length()); }
+ { return MY_MIN(MAX_KEY_LENGTH, max_supported_key_length()); }
uint max_key_part_length() const
- { return min(MAX_KEY_LENGTH, max_supported_key_part_length()); }
+ { return MY_MIN(MAX_KEY_LENGTH, max_supported_key_part_length()); }
virtual uint max_supported_record_length() const { return HA_MAX_REC_LENGTH; }
virtual uint max_supported_keys() const { return 0; }
@@ -2685,10 +3445,274 @@ public:
pushed_idx_cond_keyno= MAX_KEY;
in_range_check_pushed_down= false;
}
+ /**
+ Part of old, deprecated in-place ALTER API.
+ */
virtual bool check_if_incompatible_data(HA_CREATE_INFO *create_info,
uint table_changes)
{ return COMPATIBLE_DATA_NO; }
+ /* On-line/in-place ALTER TABLE interface. */
+
+ /*
+ Here is an outline of on-line/in-place ALTER TABLE execution through
+ this interface.
+
+ Phase 1 : Initialization
+ ========================
+ During this phase we determine which algorithm should be used
+ for execution of ALTER TABLE and what level concurrency it will
+ require.
+
+ *) This phase starts by opening the table and preparing description
+ of the new version of the table.
+ *) Then we check if it is impossible even in theory to carry out
+ this ALTER TABLE using the in-place algorithm. For example, because
+ we need to change storage engine or the user has explicitly requested
+ usage of the "copy" algorithm.
+ *) If in-place ALTER TABLE is theoretically possible, we continue
+ by compiling differences between old and new versions of the table
+ in the form of HA_ALTER_FLAGS bitmap. We also build a few
+ auxiliary structures describing requested changes and store
+ all these data in the Alter_inplace_info object.
+ *) Then the handler::check_if_supported_inplace_alter() method is called
+ in order to find if the storage engine can carry out changes requested
+ by this ALTER TABLE using the in-place algorithm. To determine this,
+ the engine can rely on data in HA_ALTER_FLAGS/Alter_inplace_info
+ passed to it as well as on its own checks. If the in-place algorithm
+ can be used for this ALTER TABLE, the level of required concurrency for
+ its execution is also returned.
+ If any errors occur during the handler call, ALTER TABLE is aborted
+ and no further handler functions are called.
+ *) Locking requirements of the in-place algorithm are compared to any
+ concurrency requirements specified by user. If there is a conflict
+ between them, we either switch to the copy algorithm or emit an error.
+
+ Phase 2 : Execution
+ ===================
+
+ In this phase the operations are executed.
+
+ *) As the first step, we acquire a lock corresponding to the concurrency
+ level which was returned by handler::check_if_supported_inplace_alter()
+ and requested by the user. This lock is held for most of the
+ duration of in-place ALTER (if HA_ALTER_INPLACE_SHARED_LOCK_AFTER_PREPARE
+ or HA_ALTER_INPLACE_NO_LOCK_AFTER_PREPARE were returned we acquire an
+ exclusive lock for duration of the next step only).
+ *) After that we call handler::ha_prepare_inplace_alter_table() to give the
+ storage engine a chance to update its internal structures with a higher
+ lock level than the one that will be used for the main step of algorithm.
+ After that we downgrade the lock if it is necessary.
+ *) After that, the main step of this phase and algorithm is executed.
+ We call the handler::ha_inplace_alter_table() method, which carries out the
+ changes requested by ALTER TABLE but does not makes them visible to other
+ connections yet.
+ *) We ensure that no other connection uses the table by upgrading our
+ lock on it to exclusive.
+ *) a) If the previous step succeeds, handler::ha_commit_inplace_alter_table() is
+ called to allow the storage engine to do any final updates to its structures,
+ to make all earlier changes durable and visible to other connections.
+ b) If we have failed to upgrade lock or any errors have occured during the
+ handler functions calls (including commit), we call
+ handler::ha_commit_inplace_alter_table()
+ to rollback all changes which were done during previous steps.
+
+ Phase 3 : Final
+ ===============
+
+ In this phase we:
+
+ *) Update SQL-layer data-dictionary by installing .FRM file for the new version
+ of the table.
+ *) Inform the storage engine about this change by calling the
+ handler::ha_notify_table_changed() method.
+ *) Destroy the Alter_inplace_info and handler_ctx objects.
+
+ */
+
+ /**
+ Check if a storage engine supports a particular alter table in-place
+
+ @param altered_table TABLE object for new version of table.
+ @param ha_alter_info Structure describing changes to be done
+ by ALTER TABLE and holding data used
+ during in-place alter.
+
+ @retval HA_ALTER_ERROR Unexpected error.
+ @retval HA_ALTER_INPLACE_NOT_SUPPORTED Not supported, must use copy.
+ @retval HA_ALTER_INPLACE_EXCLUSIVE_LOCK Supported, but requires X lock.
+ @retval HA_ALTER_INPLACE_SHARED_LOCK_AFTER_PREPARE
+ Supported, but requires SNW lock
+ during main phase. Prepare phase
+ requires X lock.
+ @retval HA_ALTER_INPLACE_SHARED_LOCK Supported, but requires SNW lock.
+ @retval HA_ALTER_INPLACE_NO_LOCK_AFTER_PREPARE
+ Supported, concurrent reads/writes
+ allowed. However, prepare phase
+ requires X lock.
+ @retval HA_ALTER_INPLACE_NO_LOCK Supported, concurrent
+ reads/writes allowed.
+
+ @note The default implementation uses the old in-place ALTER API
+ to determine if the storage engine supports in-place ALTER or not.
+
+ @note Called without holding thr_lock.c lock.
+ */
+ virtual enum_alter_inplace_result
+ check_if_supported_inplace_alter(TABLE *altered_table,
+ Alter_inplace_info *ha_alter_info);
+
+
+ /**
+ Public functions wrapping the actual handler call.
+ @see prepare_inplace_alter_table()
+ */
+ bool ha_prepare_inplace_alter_table(TABLE *altered_table,
+ Alter_inplace_info *ha_alter_info);
+
+
+ /**
+ Public function wrapping the actual handler call.
+ @see inplace_alter_table()
+ */
+ bool ha_inplace_alter_table(TABLE *altered_table,
+ Alter_inplace_info *ha_alter_info)
+ {
+ return inplace_alter_table(altered_table, ha_alter_info);
+ }
+
+
+ /**
+ Public function wrapping the actual handler call.
+ Allows us to enforce asserts regardless of handler implementation.
+ @see commit_inplace_alter_table()
+ */
+ bool ha_commit_inplace_alter_table(TABLE *altered_table,
+ Alter_inplace_info *ha_alter_info,
+ bool commit);
+
+
+ /**
+ Public function wrapping the actual handler call.
+ @see notify_table_changed()
+ */
+ void ha_notify_table_changed()
+ {
+ notify_table_changed();
+ }
+
+
+protected:
+ /**
+ Allows the storage engine to update internal structures with concurrent
+ writes blocked. If check_if_supported_inplace_alter() returns
+ HA_ALTER_INPLACE_NO_LOCK_AFTER_PREPARE or
+ HA_ALTER_INPLACE_SHARED_AFTER_PREPARE, this function is called with
+ exclusive lock otherwise the same level of locking as for
+ inplace_alter_table() will be used.
+
+ @note Storage engines are responsible for reporting any errors by
+ calling my_error()/print_error()
+
+ @note If this function reports error, commit_inplace_alter_table()
+ will be called with commit= false.
+
+ @note For partitioning, failing to prepare one partition, means that
+ commit_inplace_alter_table() will be called to roll back changes for
+ all partitions. This means that commit_inplace_alter_table() might be
+ called without prepare_inplace_alter_table() having been called first
+ for a given partition.
+
+ @param altered_table TABLE object for new version of table.
+ @param ha_alter_info Structure describing changes to be done
+ by ALTER TABLE and holding data used
+ during in-place alter.
+
+ @retval true Error
+ @retval false Success
+ */
+ virtual bool prepare_inplace_alter_table(TABLE *altered_table,
+ Alter_inplace_info *ha_alter_info)
+ { return false; }
+
+
+ /**
+ Alter the table structure in-place with operations specified using HA_ALTER_FLAGS
+ and Alter_inplace_info. The level of concurrency allowed during this
+ operation depends on the return value from check_if_supported_inplace_alter().
+
+ @note Storage engines are responsible for reporting any errors by
+ calling my_error()/print_error()
+
+ @note If this function reports error, commit_inplace_alter_table()
+ will be called with commit= false.
+
+ @param altered_table TABLE object for new version of table.
+ @param ha_alter_info Structure describing changes to be done
+ by ALTER TABLE and holding data used
+ during in-place alter.
+
+ @retval true Error
+ @retval false Success
+ */
+ virtual bool inplace_alter_table(TABLE *altered_table,
+ Alter_inplace_info *ha_alter_info)
+ { return false; }
+
+
+ /**
+ Commit or rollback the changes made during prepare_inplace_alter_table()
+ and inplace_alter_table() inside the storage engine.
+ Note that in case of rollback the allowed level of concurrency during
+ this operation will be the same as for inplace_alter_table() and thus
+ might be higher than during prepare_inplace_alter_table(). (For example,
+ concurrent writes were blocked during prepare, but might not be during
+ rollback).
+
+ @note Storage engines are responsible for reporting any errors by
+ calling my_error()/print_error()
+
+ @note If this function with commit= true reports error, it will be called
+ again with commit= false.
+
+ @note In case of partitioning, this function might be called for rollback
+ without prepare_inplace_alter_table() having been called first.
+ Also partitioned tables sets ha_alter_info->group_commit_ctx to a NULL
+ terminated array of the partitions handlers and if all of them are
+ committed as one, then group_commit_ctx should be set to NULL to indicate
+ to the partitioning handler that all partitions handlers are committed.
+ @see prepare_inplace_alter_table().
+
+ @param altered_table TABLE object for new version of table.
+ @param ha_alter_info Structure describing changes to be done
+ by ALTER TABLE and holding data used
+ during in-place alter.
+ @param commit True => Commit, False => Rollback.
+
+ @retval true Error
+ @retval false Success
+ */
+ virtual bool commit_inplace_alter_table(TABLE *altered_table,
+ Alter_inplace_info *ha_alter_info,
+ bool commit)
+{
+ /* Nothing to commit/rollback, mark all handlers committed! */
+ ha_alter_info->group_commit_ctx= NULL;
+ return false;
+}
+
+
+ /**
+ Notify the storage engine that the table structure (.FRM) has been updated.
+
+ @note No errors are allowed during notify_table_changed().
+ */
+ virtual void notify_table_changed();
+
+public:
+ /* End of On-line/in-place ALTER TABLE interface. */
+
+
/**
use_hidden_primary_key() is called in case of an update/delete when
(table_flags() and HA_PRIMARY_KEY_REQUIRED_FOR_DELETE) is defined
@@ -2728,36 +3752,9 @@ protected:
/**
Acquire the instrumented table information from a table share.
- @param share a table share
@return an instrumented table share, or NULL.
*/
- PSI_table_share *ha_table_share_psi(const TABLE_SHARE *share) const;
-
- inline void psi_open()
- {
- DBUG_ASSERT(m_psi == NULL);
- DBUG_ASSERT(table_share != NULL);
-#ifdef HAVE_PSI_INTERFACE
- if (PSI_server)
- {
- PSI_table_share *share_psi= ha_table_share_psi(table_share);
- if (share_psi)
- m_psi= PSI_server->open_table(share_psi, this);
- }
-#endif
- }
-
- inline void psi_close()
- {
-#ifdef HAVE_PSI_INTERFACE
- if (PSI_server && m_psi)
- {
- PSI_server->close_table(m_psi);
- m_psi= NULL; /* instrumentation handle, invalid after close_table() */
- }
-#endif
- DBUG_ASSERT(m_psi == NULL);
- }
+ PSI_table_share *ha_table_share_psi() const;
/**
Default rename_table() and delete_table() rename/delete files with a
@@ -2804,6 +3801,14 @@ private:
return HA_ERR_WRONG_COMMAND;
}
+ /**
+ Update a single row.
+
+ Note: If HA_ERR_FOUND_DUPP_KEY is returned, the handler must read
+ all columns of the row so MySQL can create an error message. If
+ the columns required for the error message are not read, the error
+ message will contain garbage.
+ */
virtual int update_row(const uchar *old_data __attribute__((unused)),
uchar *new_data __attribute__((unused)))
{
@@ -2865,11 +3870,14 @@ private:
DBUG_ASSERT(!(ha_table_flags() & HA_CAN_REPAIR));
return HA_ADMIN_NOT_IMPLEMENTED;
}
- virtual void start_bulk_insert(ha_rows rows) {}
+ virtual void start_bulk_insert(ha_rows rows, uint flags) {}
virtual int end_bulk_insert() { return 0; }
+protected:
virtual int index_read(uchar * buf, const uchar * key, uint key_len,
enum ha_rkey_function find_flag)
{ return HA_ERR_WRONG_COMMAND; }
+ friend class ha_partition;
+public:
/**
This method is similar to update_row, however the handler doesn't need
to execute the updates at this point in time. The handler can be certain
@@ -2939,8 +3947,8 @@ private:
virtual void drop_table(const char *name);
virtual int create(const char *name, TABLE *form, HA_CREATE_INFO *info)=0;
- virtual int create_handler_files(const char *name, const char *old_name,
- int action_flag, HA_CREATE_INFO *info)
+ virtual int create_partitioning_metadata(const char *name, const char *old_name,
+ int action_flag)
{ return FALSE; }
virtual int change_partitions(HA_CREATE_INFO *create_info,
@@ -2954,7 +3962,16 @@ private:
{ return HA_ERR_WRONG_COMMAND; }
virtual int rename_partitions(const char *path)
{ return HA_ERR_WRONG_COMMAND; }
- friend class ha_partition;
+ virtual bool set_ha_share_ref(Handler_share **arg_ha_share)
+ {
+ DBUG_ASSERT(!ha_share);
+ DBUG_ASSERT(arg_ha_share);
+ if (ha_share || !arg_ha_share)
+ return true;
+ ha_share= arg_ha_share;
+ return false;
+ }
+ int get_lock_type() const { return m_lock_type; }
public:
/* XXX to be removed, see ha_partition::partition_ht() */
virtual handlerton *partition_ht() const
@@ -2965,6 +3982,11 @@ public:
virtual void set_lock_type(enum thr_lock_type lock);
friend enum icp_result handler_index_cond_check(void* h_arg);
+protected:
+ Handler_share *get_ha_share_ptr();
+ void set_ha_share_ptr(Handler_share *arg_ha_share);
+ void lock_shared_ha_data();
+ void unlock_shared_ha_data();
};
#include "multi_range_read.h"
@@ -3006,7 +4028,7 @@ static inline const char *ha_resolve_storage_engine_name(const handlerton *db_ty
static inline bool ha_check_storage_engine_flag(const handlerton *db_type, uint32 flag)
{
- return db_type == NULL ? FALSE : test(db_type->flags & flag);
+ return db_type == NULL ? FALSE : MY_TEST(db_type->flags & flag);
}
static inline bool ha_storage_engine_is_enabled(const handlerton *db_type)
@@ -3015,6 +4037,8 @@ static inline bool ha_storage_engine_is_enabled(const handlerton *db_type)
(db_type->state == SHOW_OPTION_YES) : FALSE;
}
+#define view_pseudo_hton ((handlerton *)1)
+
/* basic stuff */
int ha_init_errors(void);
int ha_init(void);
@@ -3029,10 +4053,10 @@ void ha_kill_query(THD* thd, enum thd_kill_levels level);
bool ha_flush_logs(handlerton *db_type);
void ha_drop_database(char* path);
void ha_checkpoint_state(bool disable);
+void ha_commit_checkpoint_request(void *cookie, void (*pre_hook)(void *));
int ha_create_table(THD *thd, const char *path,
const char *db, const char *table_name,
- HA_CREATE_INFO *create_info,
- bool update_create_info);
+ HA_CREATE_INFO *create_info, LEX_CUSTRING *frm);
int ha_delete_table(THD *thd, handlerton *db_type, const char *path,
const char *db, const char *alias, bool generate_warning);
@@ -3040,14 +4064,34 @@ int ha_delete_table(THD *thd, handlerton *db_type, const char *path,
bool ha_show_status(THD *thd, handlerton *db_type, enum ha_stat_type stat);
/* discovery */
-int ha_create_table_from_engine(THD* thd, const char *db, const char *name);
-bool ha_check_if_table_exists(THD* thd, const char *db, const char *name,
- bool *exists);
-int ha_discover(THD* thd, const char* dbname, const char* name,
- uchar** frmblob, size_t* frmlen);
-int ha_find_files(THD *thd,const char *db,const char *path,
- const char *wild, bool dir, List<LEX_STRING>* files);
-int ha_table_exists_in_engine(THD* thd, const char* db, const char* name);
+#ifdef MYSQL_SERVER
+class Discovered_table_list: public handlerton::discovered_list
+{
+ THD *thd;
+ const char *wild, *wend;
+ bool with_temps; // whether to include temp tables in the result
+public:
+ Dynamic_array<LEX_STRING*> *tables;
+
+ Discovered_table_list(THD *thd_arg, Dynamic_array<LEX_STRING*> *tables_arg,
+ const LEX_STRING *wild_arg);
+ Discovered_table_list(THD *thd_arg, Dynamic_array<LEX_STRING*> *tables_arg)
+ : thd(thd_arg), wild(NULL), with_temps(true), tables(tables_arg) {}
+ ~Discovered_table_list() {}
+
+ bool add_table(const char *tname, size_t tlen);
+ bool add_file(const char *fname);
+
+ void sort();
+ void remove_duplicates(); // assumes that the list is sorted
+};
+
+int ha_discover_table(THD *thd, TABLE_SHARE *share);
+int ha_discover_table_names(THD *thd, LEX_STRING *db, MY_DIR *dirp,
+ Discovered_table_list *result, bool reusable);
+bool ha_table_exists(THD *thd, const char *db, const char *table_name,
+ handlerton **hton= 0);
+#endif
/* key cache */
extern "C" int ha_init_key_cache(const char *name, KEY_CACHE *key_cache, void *);
@@ -3073,6 +4117,7 @@ int ha_enable_transaction(THD *thd, bool on);
/* savepoints */
int ha_rollback_to_savepoint(THD *thd, SAVEPOINT *sv);
+bool ha_rollback_to_savepoint_can_release_mdl(THD *thd);
int ha_savepoint(THD *thd, SAVEPOINT *sv);
int ha_release_savepoint(THD *thd, SAVEPOINT *sv);
#ifdef WITH_WSREP
@@ -3116,9 +4161,13 @@ void wsrep_brute_force_aborts();
const char *get_canonical_filename(handler *file, const char *path,
char *tmp_path);
bool mysql_xa_recover(THD *thd);
+void commit_checkpoint_notify_ha(handlerton *hton, void *cookie);
inline const char *table_case_name(HA_CREATE_INFO *info, const char *name)
{
return ((lower_case_table_names == 2 && info->alias) ? info->alias : name);
}
+
+void print_keydup_error(TABLE *table, KEY *key, const char *msg, myf errflag);
+void print_keydup_error(TABLE *table, KEY *key, myf errflag);
#endif
diff --git a/sql/hash_filo.cc b/sql/hash_filo.cc
index 7c275ffc617..fc89bb83a9d 100644
--- a/sql/hash_filo.cc
+++ b/sql/hash_filo.cc
@@ -23,6 +23,7 @@
#pragma implementation // gcc: Class implementation
#endif
+#include <my_global.h>
#include "sql_priv.h"
#include "hash_filo.h"
diff --git a/sql/hash_filo.h b/sql/hash_filo.h
index dab54928a55..4c8c7575efc 100644
--- a/sql/hash_filo.h
+++ b/sql/hash_filo.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
+/* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.
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
@@ -32,17 +32,26 @@
class hash_filo_element
{
+private:
hash_filo_element *next_used,*prev_used;
public:
hash_filo_element() {}
+ hash_filo_element *next()
+ { return next_used; }
+ hash_filo_element *prev()
+ { return prev_used; }
+
friend class hash_filo;
};
class hash_filo
{
- const uint size, key_offset, key_length;
+private:
+ const uint key_offset, key_length;
const my_hash_get_key get_key;
+ /** Size of this hash table. */
+ uint m_size;
my_hash_free_key free_element;
bool init;
CHARSET_INFO *hash_charset;
@@ -55,9 +64,12 @@ public:
hash_filo(uint size_arg, uint key_offset_arg , uint key_length_arg,
my_hash_get_key get_key_arg, my_hash_free_key free_element_arg,
CHARSET_INFO *hash_charset_arg)
- :size(size_arg), key_offset(key_offset_arg), key_length(key_length_arg),
- get_key(get_key_arg), free_element(free_element_arg),init(0),
- hash_charset(hash_charset_arg)
+ :key_offset(key_offset_arg), key_length(key_length_arg),
+ get_key(get_key_arg), m_size(size_arg),
+ free_element(free_element_arg),init(0),
+ hash_charset(hash_charset_arg),
+ first_link(NULL),
+ last_link(NULL)
{
bzero((char*) &cache,sizeof(cache));
}
@@ -80,32 +92,61 @@ public:
}
if (!locked)
mysql_mutex_lock(&lock);
+ first_link= NULL;
+ last_link= NULL;
(void) my_hash_free(&cache);
- (void) my_hash_init(&cache,hash_charset,size,key_offset,
+ (void) my_hash_init(&cache,hash_charset,m_size,key_offset,
key_length, get_key, free_element,0);
if (!locked)
mysql_mutex_unlock(&lock);
- first_link=last_link=0;
+ }
+
+ hash_filo_element *first()
+ {
+ mysql_mutex_assert_owner(&lock);
+ return first_link;
+ }
+
+ hash_filo_element *last()
+ {
+ mysql_mutex_assert_owner(&lock);
+ return last_link;
}
hash_filo_element *search(uchar* key, size_t length)
{
+ mysql_mutex_assert_owner(&lock);
+
hash_filo_element *entry=(hash_filo_element*)
my_hash_search(&cache,(uchar*) key,length);
if (entry)
{ // Found; link it first
+ DBUG_ASSERT(first_link != NULL);
+ DBUG_ASSERT(last_link != NULL);
if (entry != first_link)
{ // Relink used-chain
if (entry == last_link)
- last_link=entry->prev_used;
+ {
+ last_link= last_link->prev_used;
+ /*
+ The list must have at least 2 elements,
+ otherwise entry would be equal to first_link.
+ */
+ DBUG_ASSERT(last_link != NULL);
+ last_link->next_used= NULL;
+ }
else
{
+ DBUG_ASSERT(entry->next_used != NULL);
+ DBUG_ASSERT(entry->prev_used != NULL);
entry->next_used->prev_used = entry->prev_used;
entry->prev_used->next_used = entry->next_used;
}
- if ((entry->next_used= first_link))
- first_link->prev_used=entry;
- first_link=entry;
+ entry->prev_used= NULL;
+ entry->next_used= first_link;
+
+ first_link->prev_used= entry;
+ first_link=entry;
}
}
return entry;
@@ -113,10 +154,20 @@ public:
bool add(hash_filo_element *entry)
{
- if (cache.records == size)
+ if (!m_size) return 1;
+ if (cache.records == m_size)
{
hash_filo_element *tmp=last_link;
- last_link=last_link->prev_used;
+ last_link= last_link->prev_used;
+ if (last_link != NULL)
+ {
+ last_link->next_used= NULL;
+ }
+ else
+ {
+ /* Pathological case, m_size == 1 */
+ first_link= NULL;
+ }
my_hash_delete(&cache,(uchar*) tmp);
}
if (my_hash_insert(&cache,(uchar*) entry))
@@ -125,13 +176,40 @@ public:
(*free_element)(entry); // This should never happen
return 1;
}
- if ((entry->next_used=first_link))
- first_link->prev_used=entry;
+ entry->prev_used= NULL;
+ entry->next_used= first_link;
+ if (first_link != NULL)
+ first_link->prev_used= entry;
else
- last_link=entry;
- first_link=entry;
+ last_link= entry;
+ first_link= entry;
+
return 0;
}
+
+ uint size()
+ { return m_size; }
+
+ void resize(uint new_size)
+ {
+ mysql_mutex_lock(&lock);
+ m_size= new_size;
+ clear(true);
+ mysql_mutex_unlock(&lock);
+ }
+};
+
+template <class T> class Hash_filo: public hash_filo
+{
+public:
+ Hash_filo(uint size_arg, uint key_offset_arg, uint key_length_arg,
+ my_hash_get_key get_key_arg, my_hash_free_key free_element_arg,
+ CHARSET_INFO *hash_charset_arg) :
+ hash_filo(size_arg, key_offset_arg, key_length_arg,
+ get_key_arg, free_element_arg, hash_charset_arg) {}
+ T* first() { return (T*)hash_filo::first(); }
+ T* last() { return (T*)hash_filo::last(); }
+ T* search(uchar* key, size_t len) { return (T*)hash_filo::search(key, len); }
};
#endif
diff --git a/sql/hostname.cc b/sql/hostname.cc
index f9a6f4b3fcf..f08ae247398 100644
--- a/sql/hostname.cc
+++ b/sql/hostname.cc
@@ -1,4 +1,4 @@
-/* Copyright (c) 2000, 2012, Oracle and/or its affiliates.
+/* Copyright (c) 2000, 2013, Oracle and/or its affiliates.
Copyright (c) 2011, 2014, SkySQL Ab.
This program is free software; you can redistribute it and/or modify
@@ -24,8 +24,9 @@
Hostnames are checked with reverse name lookup and checked that they
doesn't resemble an IP address.
*/
-
+#include <my_global.h>
#include "sql_priv.h"
+#include "unireg.h" // SPECIAL_NO_HOST_CACHE
#include "hostname.h"
#include "my_global.h"
#ifndef __WIN__
@@ -50,60 +51,107 @@ extern "C" { // Because of SCO 3.2V4.2
}
#endif
-/*
- HOST_ENTRY_KEY_SIZE -- size of IP address string in the hash cache.
-*/
-
-#define HOST_ENTRY_KEY_SIZE INET6_ADDRSTRLEN
-
-/**
- An entry in the hostname hash table cache.
-
- Host name cache does two things:
- - caches host names to save DNS look ups;
- - counts connect errors from IP.
-
- Host name can be NULL (that means DNS look up failed), but connect errors
- still are counted.
-*/
-
-class Host_entry :public hash_filo_element
+Host_errors::Host_errors()
+: m_connect(0),
+ m_host_blocked(0),
+ m_nameinfo_transient(0),
+ m_nameinfo_permanent(0),
+ m_format(0),
+ m_addrinfo_transient(0),
+ m_addrinfo_permanent(0),
+ m_FCrDNS(0),
+ m_host_acl(0),
+ m_no_auth_plugin(0),
+ m_auth_plugin(0),
+ m_handshake(0),
+ m_proxy_user(0),
+ m_proxy_user_acl(0),
+ m_authentication(0),
+ m_ssl(0),
+ m_max_user_connection(0),
+ m_max_user_connection_per_hour(0),
+ m_default_database(0),
+ m_init_connect(0),
+ m_local(0)
+{}
+
+Host_errors::~Host_errors()
+{}
+
+void Host_errors::reset()
{
-public:
- /**
- Client IP address. This is the key used with the hash table.
-
- The client IP address is always expressed in IPv6, even when the
- network IPv6 stack is not present.
-
- This IP address is never used to connect to a socket.
- */
- char ip_key[HOST_ENTRY_KEY_SIZE];
-
- /**
- Number of errors during handshake phase from the IP address.
- */
- uint connect_errors;
+ m_connect= 0;
+ m_host_blocked= 0;
+ m_nameinfo_transient= 0;
+ m_nameinfo_permanent= 0;
+ m_format= 0;
+ m_addrinfo_transient= 0;
+ m_addrinfo_permanent= 0;
+ m_FCrDNS= 0;
+ m_host_acl= 0;
+ m_no_auth_plugin= 0;
+ m_auth_plugin= 0;
+ m_handshake= 0;
+ m_proxy_user= 0;
+ m_proxy_user_acl= 0;
+ m_authentication= 0;
+ m_ssl= 0;
+ m_max_user_connection= 0;
+ m_max_user_connection_per_hour= 0;
+ m_default_database= 0;
+ m_init_connect= 0;
+ m_local= 0;
+}
- /**
- One of the host names for the IP address. May be NULL.
- */
- const char *hostname;
-};
+void Host_errors::aggregate(const Host_errors *errors)
+{
+ m_connect+= errors->m_connect;
+ m_host_blocked+= errors->m_host_blocked;
+ m_nameinfo_transient+= errors->m_nameinfo_transient;
+ m_nameinfo_permanent+= errors->m_nameinfo_permanent;
+ m_format+= errors->m_format;
+ m_addrinfo_transient+= errors->m_addrinfo_transient;
+ m_addrinfo_permanent+= errors->m_addrinfo_permanent;
+ m_FCrDNS+= errors->m_FCrDNS;
+ m_host_acl+= errors->m_host_acl;
+ m_no_auth_plugin+= errors->m_no_auth_plugin;
+ m_auth_plugin+= errors->m_auth_plugin;
+ m_handshake+= errors->m_handshake;
+ m_proxy_user+= errors->m_proxy_user;
+ m_proxy_user_acl+= errors->m_proxy_user_acl;
+ m_authentication+= errors->m_authentication;
+ m_ssl+= errors->m_ssl;
+ m_max_user_connection+= errors->m_max_user_connection;
+ m_max_user_connection_per_hour+= errors->m_max_user_connection_per_hour;
+ m_default_database+= errors->m_default_database;
+ m_init_connect+= errors->m_init_connect;
+ m_local+= errors->m_local;
+}
-static hash_filo *hostname_cache;
+static Hash_filo<Host_entry> *hostname_cache;
+ulong host_cache_size;
void hostname_cache_refresh()
{
hostname_cache->clear();
}
+uint hostname_cache_size()
+{
+ return hostname_cache->size();
+}
+
+void hostname_cache_resize(uint size)
+{
+ hostname_cache->resize(size);
+}
+
bool hostname_cache_init()
{
Host_entry tmp;
uint key_offset= (uint) ((char*) (&tmp.ip_key) - (char*) &tmp);
- if (!(hostname_cache= new hash_filo(HOST_CACHE_SIZE,
+ if (!(hostname_cache= new Hash_filo<Host_entry>(host_cache_size,
key_offset, HOST_ENTRY_KEY_SIZE,
NULL, (my_hash_free_key) free,
&my_charset_bin)))
@@ -120,6 +168,16 @@ void hostname_cache_free()
hostname_cache= NULL;
}
+void hostname_cache_lock()
+{
+ mysql_mutex_lock(&hostname_cache->lock);
+}
+
+void hostname_cache_unlock()
+{
+ mysql_mutex_unlock(&hostname_cache->lock);
+}
+
static void prepare_hostname_cache_key(const char *ip_string,
char *ip_key)
{
@@ -130,69 +188,119 @@ static void prepare_hostname_cache_key(const char *ip_string,
memcpy(ip_key, ip_string, ip_string_length);
}
+Host_entry *hostname_cache_first()
+{ return hostname_cache->first(); }
+
static inline Host_entry *hostname_cache_search(const char *ip_key)
{
- return (Host_entry *) hostname_cache->search((uchar *) ip_key, 0);
+ return hostname_cache->search((uchar *) ip_key, 0);
}
-static bool add_hostname_impl(const char *ip_key, const char *hostname)
+static void add_hostname_impl(const char *ip_key, const char *hostname,
+ bool validated, Host_errors *errors,
+ ulonglong now)
{
- if (hostname_cache_search(ip_key))
- return FALSE;
-
- size_t hostname_size= hostname ? strlen(hostname) + 1 : 0;
-
- Host_entry *entry= (Host_entry *) malloc(sizeof (Host_entry) + hostname_size);
-
- if (!entry)
- return TRUE;
-
- char *hostname_copy;
+ Host_entry *entry;
+ bool need_add= false;
- memcpy(&entry->ip_key, ip_key, HOST_ENTRY_KEY_SIZE);
+ entry= hostname_cache_search(ip_key);
- if (hostname_size)
+ if (likely(entry == NULL))
{
- hostname_copy= (char *) (entry + 1);
- memcpy(hostname_copy, hostname, hostname_size);
-
- DBUG_PRINT("info", ("Adding '%s' -> '%s' to the hostname cache...'",
- (const char *) ip_key,
- (const char *) hostname_copy));
+ entry= (Host_entry *) malloc(sizeof (Host_entry));
+ if (entry == NULL)
+ return;
+
+ need_add= true;
+ memcpy(&entry->ip_key, ip_key, HOST_ENTRY_KEY_SIZE);
+ entry->m_errors.reset();
+ entry->m_hostname_length= 0;
+ entry->m_host_validated= false;
+ entry->m_first_seen= now;
+ entry->m_last_seen= now;
+ entry->m_first_error_seen= 0;
+ entry->m_last_error_seen= 0;
}
else
{
- hostname_copy= NULL;
+ entry->m_last_seen= now;
+ }
- DBUG_PRINT("info", ("Adding '%s' -> NULL to the hostname cache...'",
- (const char *) ip_key));
+ if (validated)
+ {
+ if (hostname != NULL)
+ {
+ uint len= strlen(hostname);
+ if (len > sizeof(entry->m_hostname) - 1)
+ len= sizeof(entry->m_hostname) - 1;
+ memcpy(entry->m_hostname, hostname, len);
+ entry->m_hostname[len]= '\0';
+ entry->m_hostname_length= len;
+
+ DBUG_PRINT("info",
+ ("Adding/Updating '%s' -> '%s' (validated) to the hostname cache...'",
+ (const char *) ip_key,
+ (const char *) entry->m_hostname));
+ }
+ else
+ {
+ entry->m_hostname_length= 0;
+ DBUG_PRINT("info",
+ ("Adding/Updating '%s' -> NULL (validated) to the hostname cache...'",
+ (const char *) ip_key));
+ }
+ entry->m_host_validated= true;
+ /*
+ New errors that are considered 'blocking',
+ that will eventually cause the IP to be black listed and blocked.
+ */
+ errors->sum_connect_errors();
+ }
+ else
+ {
+ entry->m_hostname_length= 0;
+ entry->m_host_validated= false;
+ /* Do not count new blocking errors during DNS failures. */
+ errors->clear_connect_errors();
+ DBUG_PRINT("info",
+ ("Adding/Updating '%s' -> NULL (not validated) to the hostname cache...'",
+ (const char *) ip_key));
}
- entry->hostname= hostname_copy;
- entry->connect_errors= 0;
+ if (errors->has_error())
+ entry->set_error_timestamps(now);
+
+ entry->m_errors.aggregate(errors);
- return hostname_cache->add(entry);
+ if (need_add)
+ hostname_cache->add(entry);
+
+ return;
}
-static bool add_hostname(const char *ip_key, const char *hostname)
+static void add_hostname(const char *ip_key, const char *hostname,
+ bool validated, Host_errors *errors)
{
if (specialflag & SPECIAL_NO_HOST_CACHE)
- return FALSE;
+ return;
+
+ ulonglong now= my_hrtime().val;
mysql_mutex_lock(&hostname_cache->lock);
- bool err_status= add_hostname_impl(ip_key, hostname);
+ add_hostname_impl(ip_key, hostname, validated, errors, now);
mysql_mutex_unlock(&hostname_cache->lock);
- return err_status;
+ return;
}
-void inc_host_errors(const char *ip_string)
+void inc_host_errors(const char *ip_string, Host_errors *errors)
{
if (!ip_string)
return;
+ ulonglong now= my_hrtime().val;
char ip_key[HOST_ENTRY_KEY_SIZE];
prepare_hostname_cache_key(ip_string, ip_key);
@@ -201,13 +309,20 @@ void inc_host_errors(const char *ip_string)
Host_entry *entry= hostname_cache_search(ip_key);
if (entry)
- entry->connect_errors++;
+ {
+ if (entry->m_host_validated)
+ errors->sum_connect_errors();
+ else
+ errors->clear_connect_errors();
+
+ entry->m_errors.aggregate(errors);
+ entry->set_error_timestamps(now);
+ }
mysql_mutex_unlock(&hostname_cache->lock);
}
-
-void reset_host_errors(const char *ip_string)
+void reset_host_connect_errors(const char *ip_string)
{
if (!ip_string)
return;
@@ -220,12 +335,11 @@ void reset_host_errors(const char *ip_string)
Host_entry *entry= hostname_cache_search(ip_key);
if (entry)
- entry->connect_errors= 0;
+ entry->m_errors.clear_connect_errors();
mysql_mutex_unlock(&hostname_cache->lock);
}
-
static inline bool is_ip_loopback(const struct sockaddr *ip)
{
switch (ip->sa_family) {
@@ -277,6 +391,7 @@ static inline bool is_hostname_valid(const char *hostname)
- returns host name if IP-address is validated;
- set value to out-variable connect_errors -- this variable represents the
number of connection errors from the specified IP-address.
+ - update the host_cache statistics
NOTE: connect_errors are counted (are supported) only for the clients
where IP-address can be resolved and FCrDNS check is passed.
@@ -287,37 +402,43 @@ static inline bool is_hostname_valid(const char *hostname)
@param [out] connect_errors
@return Error status
- @retval FALSE Success
- @retval TRUE Error
+ @retval 0 Success
+ @retval RC_BLOCKED_HOST The host is blocked.
The function does not set/report MySQL server error in case of failure.
It's caller's responsibility to handle failures of this function
properly.
*/
-bool ip_to_hostname(struct sockaddr_storage *ip_storage,
- const char *ip_string,
- char **hostname, uint *connect_errors)
+int ip_to_hostname(struct sockaddr_storage *ip_storage,
+ const char *ip_string,
+ char **hostname,
+ uint *connect_errors)
{
const struct sockaddr *ip= (const sockaddr *) ip_storage;
int err_code;
- bool err_status;
+ bool err_status __attribute__((unused));
+ Host_errors errors;
DBUG_ENTER("ip_to_hostname");
DBUG_PRINT("info", ("IP address: '%s'; family: %d.",
(const char *) ip_string,
(int) ip->sa_family));
+ /* Default output values, for most cases. */
+ *hostname= NULL;
+ *connect_errors= 0;
+
/* Check if we have loopback address (127.0.0.1 or ::1). */
if (is_ip_loopback(ip))
{
DBUG_PRINT("info", ("Loopback address detected."));
- *connect_errors= 0; /* Do not count connect errors from localhost. */
+ /* Do not count connect errors from localhost. */
*hostname= (char *) my_localhost;
- DBUG_RETURN(FALSE);
+ DBUG_RETURN(0);
}
/* Prepare host name cache key. */
@@ -329,27 +450,45 @@ bool ip_to_hostname(struct sockaddr_storage *ip_storage,
if (!(specialflag & SPECIAL_NO_HOST_CACHE))
{
+ ulonglong now= my_hrtime().val;
+
mysql_mutex_lock(&hostname_cache->lock);
Host_entry *entry= hostname_cache_search(ip_key);
if (entry)
{
- *connect_errors= entry->connect_errors;
- *hostname= NULL;
+ entry->m_last_seen= now;
+ *connect_errors= entry->m_errors.m_connect;
+
+ if (entry->m_errors.m_connect >= max_connect_errors)
+ {
+ entry->m_errors.m_host_blocked++;
+ entry->set_error_timestamps(now);
+ mysql_mutex_unlock(&hostname_cache->lock);
+ DBUG_RETURN(RC_BLOCKED_HOST);
+ }
- if (entry->hostname)
- *hostname= my_strdup(entry->hostname, MYF(0));
+ /*
+ If there is an IP -> HOSTNAME association in the cache,
+ but for a hostname that was not validated,
+ do not return that hostname: perform the network validation again.
+ */
+ if (entry->m_host_validated)
+ {
+ if (entry->m_hostname_length)
+ *hostname= my_strdup(entry->m_hostname, MYF(0));
- DBUG_PRINT("info",("IP (%s) has been found in the cache. "
- "Hostname: '%s'; connect_errors: %d",
- (const char *) ip_key,
- (const char *) (*hostname? *hostname : "null"),
- (int) *connect_errors));
+ DBUG_PRINT("info",("IP (%s) has been found in the cache. "
+ "Hostname: '%s'",
+ (const char *) ip_key,
+ (const char *) (*hostname? *hostname : "null")
+ ));
- mysql_mutex_unlock(&hostname_cache->lock);
+ mysql_mutex_unlock(&hostname_cache->lock);
- DBUG_RETURN(FALSE);
+ DBUG_RETURN(0);
+ }
}
mysql_mutex_unlock(&hostname_cache->lock);
@@ -367,13 +506,60 @@ bool ip_to_hostname(struct sockaddr_storage *ip_storage,
err_code= vio_getnameinfo(ip, hostname_buffer, NI_MAXHOST, NULL, 0,
NI_NAMEREQD);
- /* BEGIN : DEBUG */
- DBUG_EXECUTE_IF("addr_fake_ipv4",
+ /*
+ ===========================================================================
+ DEBUG code only (begin)
+ Simulate various output from vio_getnameinfo().
+ ===========================================================================
+ */
+
+ DBUG_EXECUTE_IF("getnameinfo_error_noname",
+ {
+ strcpy(hostname_buffer, "<garbage>");
+ err_code= EAI_NONAME;
+ }
+ );
+
+ DBUG_EXECUTE_IF("getnameinfo_error_again",
+ {
+ strcpy(hostname_buffer, "<garbage>");
+ err_code= EAI_AGAIN;
+ }
+ );
+
+ DBUG_EXECUTE_IF("getnameinfo_fake_ipv4",
{
strcpy(hostname_buffer, "santa.claus.ipv4.example.com");
err_code= 0;
- };);
- /* END : DEBUG */
+ }
+ );
+
+ DBUG_EXECUTE_IF("getnameinfo_fake_ipv6",
+ {
+ strcpy(hostname_buffer, "santa.claus.ipv6.example.com");
+ err_code= 0;
+ }
+ );
+
+ DBUG_EXECUTE_IF("getnameinfo_format_ipv4",
+ {
+ strcpy(hostname_buffer, "12.12.12.12");
+ err_code= 0;
+ }
+ );
+
+ DBUG_EXECUTE_IF("getnameinfo_format_ipv6",
+ {
+ strcpy(hostname_buffer, "12:DEAD:BEEF:0");
+ err_code= 0;
+ }
+ );
+
+ /*
+ ===========================================================================
+ DEBUG code only (end)
+ ===========================================================================
+ */
if (err_code)
{
@@ -387,23 +573,29 @@ bool ip_to_hostname(struct sockaddr_storage *ip_storage,
(const char *) ip_key,
(const char *) gai_strerror(err_code));
+ bool validated;
if (vio_is_no_name_error(err_code))
{
/*
The no-name error means that there is no reverse address mapping
for the IP address. A host name can not be resolved.
-
+ */
+ errors.m_nameinfo_permanent= 1;
+ validated= true;
+ }
+ else
+ {
+ /*
If it is not the no-name error, we should not cache the hostname
(or rather its absence), because the failure might be transient.
+ Only the ip error statistics are cached.
*/
-
- add_hostname(ip_key, NULL);
-
- *hostname= NULL;
- *connect_errors= 0; /* New IP added to the cache. */
+ errors.m_nameinfo_transient= 1;
+ validated= false;
}
+ add_hostname(ip_key, NULL, validated, &errors);
- DBUG_RETURN(FALSE);
+ DBUG_RETURN(0);
}
DBUG_PRINT("info", ("IP '%s' resolved to '%s'.",
@@ -439,24 +631,21 @@ bool ip_to_hostname(struct sockaddr_storage *ip_storage,
(const char *) ip_key,
(const char *) hostname_buffer);
- err_status= add_hostname(ip_key, NULL);
-
- *hostname= NULL;
- *connect_errors= 0; /* New IP added to the cache. */
+ errors.m_format= 1;
+ add_hostname(ip_key, hostname_buffer, false, &errors);
- DBUG_RETURN(err_status);
+ DBUG_RETURN(false);
}
- /*
- To avoid crashing the server in DBUG_EXECUTE_IF,
- Define a variable which depicts state of addr_info_list.
- */
- bool free_addr_info_list= false;
-
/* Get IP-addresses for the resolved host name (FCrDNS technique). */
struct addrinfo hints;
struct addrinfo *addr_info_list;
+ /*
+ Makes fault injection with DBUG_EXECUTE_IF easier.
+ Invoking free_addr_info(NULL) crashes on some platforms.
+ */
+ bool free_addr_info_list= false;
memset(&hints, 0, sizeof (struct addrinfo));
hints.ai_flags= AI_PASSIVE;
@@ -470,8 +659,72 @@ bool ip_to_hostname(struct sockaddr_storage *ip_storage,
if (err_code == 0)
free_addr_info_list= true;
- /* BEGIN : DEBUG */
- DBUG_EXECUTE_IF("addr_fake_ipv4",
+ /*
+ ===========================================================================
+ DEBUG code only (begin)
+ Simulate various output from getaddrinfo().
+ ===========================================================================
+ */
+ DBUG_EXECUTE_IF("getaddrinfo_error_noname",
+ {
+ if (free_addr_info_list)
+ freeaddrinfo(addr_info_list);
+
+ addr_info_list= NULL;
+ err_code= EAI_NONAME;
+ free_addr_info_list= false;
+ }
+ );
+
+ DBUG_EXECUTE_IF("getaddrinfo_error_again",
+ {
+ if (free_addr_info_list)
+ freeaddrinfo(addr_info_list);
+
+ addr_info_list= NULL;
+ err_code= EAI_AGAIN;
+ free_addr_info_list= false;
+ }
+ );
+
+ DBUG_EXECUTE_IF("getaddrinfo_fake_bad_ipv4",
+ {
+ if (free_addr_info_list)
+ freeaddrinfo(addr_info_list);
+
+ struct sockaddr_in *debug_addr;
+ /*
+ Not thread safe, which is ok.
+ Only one connection at a time is tested with
+ fault injection.
+ */
+ static struct sockaddr_in debug_sock_addr[2];
+ static struct addrinfo debug_addr_info[2];
+ /* Simulating ipv4 192.0.2.126 */
+ debug_addr= & debug_sock_addr[0];
+ debug_addr->sin_family= AF_INET;
+ debug_addr->sin_addr.s_addr= inet_addr("192.0.2.126");
+
+ /* Simulating ipv4 192.0.2.127 */
+ debug_addr= & debug_sock_addr[1];
+ debug_addr->sin_family= AF_INET;
+ debug_addr->sin_addr.s_addr= inet_addr("192.0.2.127");
+
+ debug_addr_info[0].ai_addr= (struct sockaddr*) & debug_sock_addr[0];
+ debug_addr_info[0].ai_addrlen= sizeof (struct sockaddr_in);
+ debug_addr_info[0].ai_next= & debug_addr_info[1];
+
+ debug_addr_info[1].ai_addr= (struct sockaddr*) & debug_sock_addr[1];
+ debug_addr_info[1].ai_addrlen= sizeof (struct sockaddr_in);
+ debug_addr_info[1].ai_next= NULL;
+
+ addr_info_list= & debug_addr_info[0];
+ err_code= 0;
+ free_addr_info_list= false;
+ }
+ );
+
+ DBUG_EXECUTE_IF("getaddrinfo_fake_good_ipv4",
{
if (free_addr_info_list)
freeaddrinfo(addr_info_list);
@@ -500,30 +753,186 @@ bool ip_to_hostname(struct sockaddr_storage *ip_storage,
addr_info_list= & debug_addr_info[0];
err_code= 0;
free_addr_info_list= false;
- };);
+ }
+ );
- /* END : DEBUG */
+#ifdef HAVE_IPV6
+ DBUG_EXECUTE_IF("getaddrinfo_fake_bad_ipv6",
+ {
+ if (free_addr_info_list)
+ freeaddrinfo(addr_info_list);
- if (err_code == EAI_NONAME)
- {
- /*
- Don't cache responses when the DNS server is down, as otherwise
- transient DNS failure may leave any number of clients (those
- that attempted to connect during the outage) unable to connect
- indefinitely.
- */
+ struct sockaddr_in6 *debug_addr;
+ struct in6_addr *ip6;
+ /*
+ Not thread safe, which is ok.
+ Only one connection at a time is tested with
+ fault injection.
+ */
+ static struct sockaddr_in6 debug_sock_addr[2];
+ static struct addrinfo debug_addr_info[2];
+ /* Simulating ipv6 2001:DB8::6:7E */
+ debug_addr= & debug_sock_addr[0];
+ debug_addr->sin6_family= AF_INET6;
+ ip6= & debug_addr->sin6_addr;
+ /* inet_pton not available on Windows XP. */
+ ip6->s6_addr[ 0] = 0x20;
+ ip6->s6_addr[ 1] = 0x01;
+ ip6->s6_addr[ 2] = 0x0d;
+ ip6->s6_addr[ 3] = 0xb8;
+ ip6->s6_addr[ 4] = 0x00;
+ ip6->s6_addr[ 5] = 0x00;
+ ip6->s6_addr[ 6] = 0x00;
+ ip6->s6_addr[ 7] = 0x00;
+ ip6->s6_addr[ 8] = 0x00;
+ ip6->s6_addr[ 9] = 0x00;
+ ip6->s6_addr[10] = 0x00;
+ ip6->s6_addr[11] = 0x00;
+ ip6->s6_addr[12] = 0x00;
+ ip6->s6_addr[13] = 0x06;
+ ip6->s6_addr[14] = 0x00;
+ ip6->s6_addr[15] = 0x7e;
+
+ /* Simulating ipv6 2001:DB8::6:7F */
+ debug_addr= & debug_sock_addr[1];
+ debug_addr->sin6_family= AF_INET6;
+ ip6= & debug_addr->sin6_addr;
+ ip6->s6_addr[ 0] = 0x20;
+ ip6->s6_addr[ 1] = 0x01;
+ ip6->s6_addr[ 2] = 0x0d;
+ ip6->s6_addr[ 3] = 0xb8;
+ ip6->s6_addr[ 4] = 0x00;
+ ip6->s6_addr[ 5] = 0x00;
+ ip6->s6_addr[ 6] = 0x00;
+ ip6->s6_addr[ 7] = 0x00;
+ ip6->s6_addr[ 8] = 0x00;
+ ip6->s6_addr[ 9] = 0x00;
+ ip6->s6_addr[10] = 0x00;
+ ip6->s6_addr[11] = 0x00;
+ ip6->s6_addr[12] = 0x00;
+ ip6->s6_addr[13] = 0x06;
+ ip6->s6_addr[14] = 0x00;
+ ip6->s6_addr[15] = 0x7f;
+
+ debug_addr_info[0].ai_addr= (struct sockaddr*) & debug_sock_addr[0];
+ debug_addr_info[0].ai_addrlen= sizeof (struct sockaddr_in6);
+ debug_addr_info[0].ai_next= & debug_addr_info[1];
- err_status= add_hostname(ip_key, NULL);
+ debug_addr_info[1].ai_addr= (struct sockaddr*) & debug_sock_addr[1];
+ debug_addr_info[1].ai_addrlen= sizeof (struct sockaddr_in6);
+ debug_addr_info[1].ai_next= NULL;
- *hostname= NULL;
- *connect_errors= 0; /* New IP added to the cache. */
+ addr_info_list= & debug_addr_info[0];
+ err_code= 0;
+ free_addr_info_list= false;
+ }
+ );
- DBUG_RETURN(err_status);
- }
- else if (err_code)
+ DBUG_EXECUTE_IF("getaddrinfo_fake_good_ipv6",
+ {
+ if (free_addr_info_list)
+ freeaddrinfo(addr_info_list);
+
+ struct sockaddr_in6 *debug_addr;
+ struct in6_addr *ip6;
+ /*
+ Not thread safe, which is ok.
+ Only one connection at a time is tested with
+ fault injection.
+ */
+ static struct sockaddr_in6 debug_sock_addr[2];
+ static struct addrinfo debug_addr_info[2];
+ /* Simulating ipv6 2001:DB8::6:7 */
+ debug_addr= & debug_sock_addr[0];
+ debug_addr->sin6_family= AF_INET6;
+ ip6= & debug_addr->sin6_addr;
+ ip6->s6_addr[ 0] = 0x20;
+ ip6->s6_addr[ 1] = 0x01;
+ ip6->s6_addr[ 2] = 0x0d;
+ ip6->s6_addr[ 3] = 0xb8;
+ ip6->s6_addr[ 4] = 0x00;
+ ip6->s6_addr[ 5] = 0x00;
+ ip6->s6_addr[ 6] = 0x00;
+ ip6->s6_addr[ 7] = 0x00;
+ ip6->s6_addr[ 8] = 0x00;
+ ip6->s6_addr[ 9] = 0x00;
+ ip6->s6_addr[10] = 0x00;
+ ip6->s6_addr[11] = 0x00;
+ ip6->s6_addr[12] = 0x00;
+ ip6->s6_addr[13] = 0x06;
+ ip6->s6_addr[14] = 0x00;
+ ip6->s6_addr[15] = 0x07;
+
+ /* Simulating ipv6 2001:DB8::6:6 */
+ debug_addr= & debug_sock_addr[1];
+ debug_addr->sin6_family= AF_INET6;
+ ip6= & debug_addr->sin6_addr;
+ ip6->s6_addr[ 0] = 0x20;
+ ip6->s6_addr[ 1] = 0x01;
+ ip6->s6_addr[ 2] = 0x0d;
+ ip6->s6_addr[ 3] = 0xb8;
+ ip6->s6_addr[ 4] = 0x00;
+ ip6->s6_addr[ 5] = 0x00;
+ ip6->s6_addr[ 6] = 0x00;
+ ip6->s6_addr[ 7] = 0x00;
+ ip6->s6_addr[ 8] = 0x00;
+ ip6->s6_addr[ 9] = 0x00;
+ ip6->s6_addr[10] = 0x00;
+ ip6->s6_addr[11] = 0x00;
+ ip6->s6_addr[12] = 0x00;
+ ip6->s6_addr[13] = 0x06;
+ ip6->s6_addr[14] = 0x00;
+ ip6->s6_addr[15] = 0x06;
+
+ debug_addr_info[0].ai_addr= (struct sockaddr*) & debug_sock_addr[0];
+ debug_addr_info[0].ai_addrlen= sizeof (struct sockaddr_in6);
+ debug_addr_info[0].ai_next= & debug_addr_info[1];
+
+ debug_addr_info[1].ai_addr= (struct sockaddr*) & debug_sock_addr[1];
+ debug_addr_info[1].ai_addrlen= sizeof (struct sockaddr_in6);
+ debug_addr_info[1].ai_next= NULL;
+
+ addr_info_list= & debug_addr_info[0];
+ err_code= 0;
+ free_addr_info_list= false;
+ }
+ );
+#endif /* HAVE_IPV6 */
+
+ /*
+ ===========================================================================
+ DEBUG code only (end)
+ ===========================================================================
+ */
+
+ if (err_code != 0)
{
- DBUG_PRINT("error", ("getaddrinfo() failed with error code %d.", err_code));
- DBUG_RETURN(TRUE);
+ sql_print_warning("Host name '%s' could not be resolved: %s",
+ (const char *) hostname_buffer,
+ (const char *) gai_strerror(err_code));
+
+ bool validated;
+
+ if (err_code == EAI_NONAME)
+ {
+ errors.m_addrinfo_permanent= 1;
+ validated= true;
+ }
+ else
+ {
+ /*
+ Don't cache responses when the DNS server is down, as otherwise
+ transient DNS failure may leave any number of clients (those
+ that attempted to connect during the outage) unable to connect
+ indefinitely.
+ Only cache error statistics.
+ */
+ errors.m_addrinfo_transient= 1;
+ validated= false;
+ }
+ add_hostname(ip_key, NULL, validated, &errors);
+
+ DBUG_RETURN(false);
}
/* Check that getaddrinfo() returned the used IP (FCrDNS technique). */
@@ -545,7 +954,7 @@ bool ip_to_hostname(struct sockaddr_storage *ip_storage,
DBUG_PRINT("info", (" - '%s'", (const char *) ip_buffer));
- if (strcmp(ip_key, ip_buffer) == 0)
+ if (strcasecmp(ip_key, ip_buffer) == 0)
{
/* Copy host name string to be stored in the cache. */
@@ -557,7 +966,7 @@ bool ip_to_hostname(struct sockaddr_storage *ip_storage,
if (free_addr_info_list)
freeaddrinfo(addr_info_list);
- DBUG_RETURN(TRUE);
+ DBUG_RETURN(true);
}
break;
@@ -568,9 +977,11 @@ bool ip_to_hostname(struct sockaddr_storage *ip_storage,
if (!*hostname)
{
- sql_print_information("Hostname '%s' does not resolve to '%s'.",
- (const char *) hostname_buffer,
- (const char *) ip_key);
+ errors.m_FCrDNS= 1;
+
+ sql_print_warning("Hostname '%s' does not resolve to '%s'.",
+ (const char *) hostname_buffer,
+ (const char *) ip_key);
sql_print_information("Hostname '%s' has the following IP addresses:",
(const char *) hostname_buffer);
@@ -584,30 +995,16 @@ bool ip_to_hostname(struct sockaddr_storage *ip_storage,
ip_buffer, sizeof (ip_buffer));
DBUG_ASSERT(!err_status);
- sql_print_information(" - %s\n", (const char *) ip_buffer);
+ sql_print_information(" - %s", (const char *) ip_buffer);
}
}
- /* Free the result of getaddrinfo(). */
+ /* Add an entry for the IP to the cache. */
+ add_hostname(ip_key, *hostname, true, &errors);
+ /* Free the result of getaddrinfo(). */
if (free_addr_info_list)
freeaddrinfo(addr_info_list);
- /* Add an entry for the IP to the cache. */
-
- if (*hostname)
- {
- err_status= add_hostname(ip_key, *hostname);
- *connect_errors= 0;
- }
- else
- {
- DBUG_PRINT("error",("Couldn't verify hostname with getaddrinfo()."));
-
- err_status= add_hostname(ip_key, NULL);
- *hostname= NULL;
- *connect_errors= 0;
- }
-
- DBUG_RETURN(err_status);
+ DBUG_RETURN(false);
}
diff --git a/sql/hostname.h b/sql/hostname.h
index 6e9535c2947..81a1d0de88d 100644
--- a/sql/hostname.h
+++ b/sql/hostname.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
+/* Copyright (c) 2006, 2011, Oracle and/or its affiliates. All rights reserved.
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
@@ -17,14 +17,168 @@
#define HOSTNAME_INCLUDED
#include "my_global.h" /* uint */
+#include "my_net.h"
+#include "hash_filo.h"
-bool ip_to_hostname(struct sockaddr_storage *ip_storage,
- const char *ip_string,
- char **hostname, uint *connect_errors);
-void inc_host_errors(const char *ip_string);
-void reset_host_errors(const char *ip_string);
+struct Host_errors
+{
+public:
+ Host_errors();
+ ~Host_errors();
+
+ void reset();
+ void aggregate(const Host_errors *errors);
+
+ /** Number of connect errors. */
+ ulong m_connect;
+
+ /** Number of host blocked errors. */
+ ulong m_host_blocked;
+ /** Number of transient errors from getnameinfo(). */
+ ulong m_nameinfo_transient;
+ /** Number of permanent errors from getnameinfo(). */
+ ulong m_nameinfo_permanent;
+ /** Number of errors from is_hostname_valid(). */
+ ulong m_format;
+ /** Number of transient errors from getaddrinfo(). */
+ ulong m_addrinfo_transient;
+ /** Number of permanent errors from getaddrinfo(). */
+ ulong m_addrinfo_permanent;
+ /** Number of errors from Forward-Confirmed reverse DNS checks. */
+ ulong m_FCrDNS;
+ /** Number of errors from host grants. */
+ ulong m_host_acl;
+ /** Number of errors from missing auth plugin. */
+ ulong m_no_auth_plugin;
+ /** Number of errors from auth plugin. */
+ ulong m_auth_plugin;
+ /** Number of errors from authentication plugins. */
+ ulong m_handshake;
+ /** Number of errors from proxy user. */
+ ulong m_proxy_user;
+ /** Number of errors from proxy user acl. */
+ ulong m_proxy_user_acl;
+ /** Number of errors from authentication. */
+ ulong m_authentication;
+ /** Number of errors from ssl. */
+ ulong m_ssl;
+ /** Number of errors from max user connection. */
+ ulong m_max_user_connection;
+ /** Number of errors from max user connection per hour. */
+ ulong m_max_user_connection_per_hour;
+ /** Number of errors from the default database. */
+ ulong m_default_database;
+ /** Number of errors from init_connect. */
+ ulong m_init_connect;
+ /** Number of errors from the server itself. */
+ ulong m_local;
+
+ bool has_error() const
+ {
+ return ((m_host_blocked != 0)
+ || (m_nameinfo_transient != 0)
+ || (m_nameinfo_permanent != 0)
+ || (m_format != 0)
+ || (m_addrinfo_transient != 0)
+ || (m_addrinfo_permanent != 0)
+ || (m_FCrDNS != 0)
+ || (m_host_acl != 0)
+ || (m_no_auth_plugin != 0)
+ || (m_auth_plugin != 0)
+ || (m_handshake != 0)
+ || (m_proxy_user != 0)
+ || (m_proxy_user_acl != 0)
+ || (m_authentication != 0)
+ || (m_ssl != 0)
+ || (m_max_user_connection != 0)
+ || (m_max_user_connection_per_hour != 0)
+ || (m_default_database != 0)
+ || (m_init_connect != 0)
+ || (m_local != 0));
+ }
+
+ void sum_connect_errors()
+ {
+ /* Current (historical) behavior: */
+ m_connect= m_handshake;
+ }
+
+ void clear_connect_errors()
+ {
+ m_connect= 0;
+ }
+};
+
+/** Size of IP address string in the hash cache. */
+#define HOST_ENTRY_KEY_SIZE INET6_ADDRSTRLEN
+
+/**
+ An entry in the hostname hash table cache.
+
+ Host name cache does two things:
+ - caches host names to save DNS look ups;
+ - counts errors from IP.
+
+ Host name can be empty (that means DNS look up failed),
+ but errors still are counted.
+*/
+class Host_entry : public hash_filo_element
+{
+public:
+ Host_entry *next()
+ { return (Host_entry*) hash_filo_element::next(); }
+
+ /**
+ Client IP address. This is the key used with the hash table.
+
+ The client IP address is always expressed in IPv6, even when the
+ network IPv6 stack is not present.
+
+ This IP address is never used to connect to a socket.
+ */
+ char ip_key[HOST_ENTRY_KEY_SIZE];
+
+ /**
+ One of the host names for the IP address. May be a zero length string.
+ */
+ char m_hostname[HOSTNAME_LENGTH + 1];
+ /** Length in bytes of @c m_hostname. */
+ uint m_hostname_length;
+ /** The hostname is validated and used for authorization. */
+ bool m_host_validated;
+ ulonglong m_first_seen;
+ ulonglong m_last_seen;
+ ulonglong m_first_error_seen;
+ ulonglong m_last_error_seen;
+ /** Error statistics. */
+ Host_errors m_errors;
+
+ void set_error_timestamps(ulonglong now)
+ {
+ if (m_first_error_seen == 0)
+ m_first_error_seen= now;
+ m_last_error_seen= now;
+ }
+};
+
+/** The size of the host_cache. */
+extern ulong host_cache_size;
+
+#define RC_OK 0
+#define RC_BLOCKED_HOST 1
+int ip_to_hostname(struct sockaddr_storage *ip_storage,
+ const char *ip_string,
+ char **hostname, uint *connect_errors);
+
+void inc_host_errors(const char *ip_string, Host_errors *errors);
+void reset_host_connect_errors(const char *ip_string);
bool hostname_cache_init();
void hostname_cache_free();
void hostname_cache_refresh(void);
+uint hostname_cache_size();
+void hostname_cache_resize(uint size);
+void hostname_cache_lock();
+void hostname_cache_unlock();
+Host_entry *hostname_cache_first();
#endif /* HOSTNAME_INCLUDED */
diff --git a/sql/init.cc b/sql/init.cc
index 86915b7aa01..8001e60b65e 100644
--- a/sql/init.cc
+++ b/sql/init.cc
@@ -21,6 +21,7 @@
Init and dummy functions for interface with unireg
*/
+#include <my_global.h>
#include "sql_priv.h"
#include "init.h"
#include "my_sys.h"
@@ -41,9 +42,6 @@ void unireg_init(ulong options)
current_pid=(ulong) getpid(); /* Save for later ref */
my_init_time(); /* Init time-functions (read zone) */
-#ifndef EMBEDDED_LIBRARY
- my_abort_hook=unireg_abort; /* Abort with close of databases */
-#endif
(void) strmov(reg_ext,".frm");
reg_ext_length= 4;
diff --git a/sql/innodb_priv.h b/sql/innodb_priv.h
index 33ba7b0f5b3..b9e471b3b13 100644
--- a/sql/innodb_priv.h
+++ b/sql/innodb_priv.h
@@ -26,11 +26,11 @@ int get_quote_char_for_identifier(THD *thd, const char *name, uint length);
bool schema_table_store_record(THD *thd, TABLE *table);
void localtime_to_TIME(MYSQL_TIME *to, struct tm *from);
bool check_global_access(THD *thd, ulong want_access, bool no_errors=false);
-uint strconvert(CHARSET_INFO *from_cs, const char *from,
+uint strconvert(CHARSET_INFO *from_cs, const char *from, uint from_length,
CHARSET_INFO *to_cs, char *to, uint to_length,
uint *errors);
void sql_print_error(const char *format, ...);
-
+#define thd_binlog_pos(X, Y, Z) mysql_bin_log_commit_pos(X, Z, Y)
#endif /* INNODB_PRIV_INCLUDED */
diff --git a/sql/item.cc b/sql/item.cc
index 3d3f472afc5..bed8824f68f 100644
--- a/sql/item.cc
+++ b/sql/item.cc
@@ -19,9 +19,8 @@
#ifdef USE_PRAGMA_IMPLEMENTATION
#pragma implementation // gcc: Class implementation
#endif
-#include "my_global.h" /* NO_EMBEDDED_ACCESS_CHECKS */
+#include <my_global.h> /* NO_EMBEDDED_ACCESS_CHECKS */
#include "sql_priv.h"
-#include "unireg.h" // REQUIRED: for other includes
#include <mysql.h>
#include <m_ctype.h>
#include "my_dir.h"
@@ -41,7 +40,6 @@
// REPORT_EXCEPT_NOT_FOUND,
// find_item_in_list,
// RESOLVED_AGAINST_ALIAS, ...
-#include "log_event.h" // append_query_string
#include "sql_expression_cache.h"
const String my_null_string("NULL", 4, default_charset_info);
@@ -105,7 +103,7 @@ void
Hybrid_type_traits_decimal::fix_length_and_dec(Item *item, Item *arg) const
{
item->decimals= arg->decimals;
- item->max_length= min(arg->max_length + DECIMAL_LONGLONG_DIGITS,
+ item->max_length= MY_MIN(arg->max_length + DECIMAL_LONGLONG_DIGITS,
DECIMAL_MAX_STR_LENGTH);
}
@@ -198,7 +196,7 @@ Hybrid_type_traits_integer::fix_length_and_dec(Item *item, Item *arg) const
void item_init(void)
{
- item_user_lock_init();
+ item_func_sleep_init();
uuid_short_init();
}
@@ -234,6 +232,36 @@ bool Item::val_bool()
}
+/**
+ Get date/time/datetime.
+ Optionally extend TIME result to DATETIME.
+*/
+bool Item::get_date_with_conversion(MYSQL_TIME *ltime, ulonglong fuzzydate)
+{
+ /*
+ Some TIME type items return error when trying to do get_date()
+ without TIME_TIME_ONLY set (e.g. Item_field for Field_time).
+ In the SQL standard time->datetime conversion mode we add TIME_TIME_ONLY.
+ In the legacy time->datetime conversion mode we do not add TIME_TIME_ONLY
+ and leave it to get_date() to check date.
+ */
+ ulonglong time_flag= (field_type() == MYSQL_TYPE_TIME &&
+ !(current_thd->variables.old_behavior & OLD_MODE_ZERO_DATE_TIME_CAST)) ?
+ TIME_TIME_ONLY : 0;
+ if (get_date(ltime, fuzzydate | time_flag))
+ return true;
+ if (ltime->time_type == MYSQL_TIMESTAMP_TIME &&
+ !(fuzzydate & TIME_TIME_ONLY))
+ {
+ MYSQL_TIME tmp;
+ if (time_to_datetime_with_warn(current_thd, ltime, &tmp, fuzzydate))
+ return null_value= true;
+ *ltime= tmp;
+ }
+ return false;
+}
+
+
/*
For the items which don't have its own fast val_str_ascii()
implementation we provide a generic slower version,
@@ -312,11 +340,29 @@ String *Item::val_string_from_decimal(String *str)
}
+/*
+ All val_xxx_from_date() must call this method, to expose consistent behaviour
+ regarding SQL_MODE when converting DATE/DATETIME to other data types.
+*/
+bool Item::get_temporal_with_sql_mode(MYSQL_TIME *ltime)
+{
+ return get_date(ltime, field_type() == MYSQL_TYPE_TIME
+ ? TIME_TIME_ONLY
+ : sql_mode_for_dates(current_thd));
+}
+
+
+bool Item::is_null_from_temporal()
+{
+ MYSQL_TIME ltime;
+ return get_temporal_with_sql_mode(&ltime);
+}
+
+
String *Item::val_string_from_date(String *str)
{
MYSQL_TIME ltime;
- if (get_date(&ltime,
- field_type() == MYSQL_TYPE_TIME ? TIME_TIME_ONLY : 0) ||
+ if (get_temporal_with_sql_mode(&ltime) ||
str->alloc(MAX_DATE_STRING_REP_LENGTH))
{
null_value= 1;
@@ -360,7 +406,7 @@ my_decimal *Item::val_decimal_from_string(my_decimal *decimal_value)
decimal_value) & E_DEC_BAD_NUM)
{
ErrConvString err(res);
- push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
ER_TRUNCATED_WRONG_VALUE,
ER(ER_TRUNCATED_WRONG_VALUE), "DECIMAL",
err.ptr());
@@ -373,7 +419,7 @@ my_decimal *Item::val_decimal_from_date(my_decimal *decimal_value)
{
DBUG_ASSERT(fixed == 1);
MYSQL_TIME ltime;
- if (get_date(&ltime, 0))
+ if (get_temporal_with_sql_mode(&ltime))
{
my_decimal_set_zero(decimal_value);
null_value= 1; // set NULL, stop processing
@@ -400,7 +446,7 @@ longlong Item::val_int_from_date()
{
DBUG_ASSERT(fixed == 1);
MYSQL_TIME ltime;
- if (get_date(&ltime, 0))
+ if (get_temporal_with_sql_mode(&ltime))
return 0;
longlong v= TIME_to_ulonglong(&ltime);
return ltime.neg ? -v : v;
@@ -411,7 +457,7 @@ double Item::val_real_from_date()
{
DBUG_ASSERT(fixed == 1);
MYSQL_TIME ltime;
- if (get_date(&ltime, 0))
+ if (get_temporal_with_sql_mode(&ltime))
return 0;
return TIME_to_double(&ltime);
}
@@ -453,9 +499,7 @@ int Item::save_time_in_field(Field *field)
int Item::save_date_in_field(Field *field)
{
MYSQL_TIME ltime;
- if (get_date(&ltime, (current_thd->variables.sql_mode &
- (MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE |
- MODE_INVALID_DATES))))
+ if (get_date(&ltime, sql_mode_for_dates(current_thd)))
return set_field_to_null_with_conversions(field, 0);
field->set_notnull();
return field->store_time_dec(&ltime, decimals);
@@ -570,26 +614,10 @@ uint Item::decimal_precision() const
uint prec=
my_decimal_length_to_precision(max_char_length(), decimals,
unsigned_flag);
- return min(prec, DECIMAL_MAX_PRECISION);
- }
- return min(max_char_length(), DECIMAL_MAX_PRECISION);
-}
-
-
-#if MARIADB_VERSION_ID < 1000000
-static uint ms_to_precision(uint ms)
-{
- uint cut, precision;
- for (cut= 10, precision= 6 ; precision > 0 ; cut*= 10, precision--)
- {
- if (ms % cut)
- return precision;
+ return MY_MIN(prec, DECIMAL_MAX_PRECISION);
}
- return 0;
+ return MY_MIN(max_char_length(), DECIMAL_MAX_PRECISION);
}
-#else
-#error Change the code to use MYSQL_TIME_STATUS::precision instead.
-#endif
uint Item::temporal_precision(enum_field_types type)
@@ -599,18 +627,17 @@ uint Item::temporal_precision(enum_field_types type)
{
MYSQL_TIME ltime;
String buf, *tmp;
- int was_cut;
+ MYSQL_TIME_STATUS status;
DBUG_ASSERT(fixed);
if ((tmp= val_str(&buf)) &&
- (type == MYSQL_TYPE_TIME ?
+ !(type == MYSQL_TYPE_TIME ?
str_to_time(tmp->charset(), tmp->ptr(), tmp->length(),
- &ltime, TIME_TIME_ONLY, &was_cut) :
+ &ltime, TIME_TIME_ONLY, &status) :
str_to_datetime(tmp->charset(), tmp->ptr(), tmp->length(),
- &ltime, TIME_FUZZY_DATES, &was_cut)) >
- MYSQL_TIMESTAMP_ERROR)
- return min(ms_to_precision(ltime.second_part), TIME_SECOND_PART_DIGITS);
+ &ltime, TIME_FUZZY_DATES, &status)))
+ return MY_MIN(status.precision, TIME_SECOND_PART_DIGITS);
}
- return min(decimals, TIME_SECOND_PART_DIGITS);
+ return MY_MIN(decimals, TIME_SECOND_PART_DIGITS);
}
@@ -658,7 +685,7 @@ void Item::cleanup()
{
DBUG_ENTER("Item::cleanup");
DBUG_PRINT("enter", ("this: %p", this));
- fixed=0;
+ fixed= 0;
marker= 0;
join_tab_idx= MAX_TABLES;
if (orig_name)
@@ -728,9 +755,12 @@ Item_result Item::cmp_type() const
case MYSQL_TYPE_GEOMETRY:
return STRING_RESULT;
case MYSQL_TYPE_TIMESTAMP:
+ case MYSQL_TYPE_TIMESTAMP2:
case MYSQL_TYPE_DATE:
case MYSQL_TYPE_TIME:
+ case MYSQL_TYPE_TIME2:
case MYSQL_TYPE_DATETIME:
+ case MYSQL_TYPE_DATETIME2:
case MYSQL_TYPE_NEWDATE:
return TIME_RESULT;
};
@@ -866,7 +896,7 @@ void Item_ident::cleanup()
We can trust that depended_from set correctly only if this item
was fixed
*/
- can_be_depended= test(depended_from);
+ can_be_depended= MY_TEST(depended_from);
}
DBUG_VOID_RETURN;
}
@@ -884,10 +914,15 @@ bool Item_ident::remove_dependence_processor(uchar * arg)
bool Item_ident::collect_outer_ref_processor(uchar *param)
{
Collect_deps_prm *prm= (Collect_deps_prm *)param;
- if (depended_from &&
+ if (depended_from &&
depended_from->nest_level_base == prm->nest_level_base &&
depended_from->nest_level < prm->nest_level)
- prm->parameters->add_unique(this, &cmp_items);
+ {
+ if (prm->collect)
+ prm->parameters->add_unique(this, &cmp_items);
+ else
+ prm->count++;
+ }
return FALSE;
}
@@ -1037,10 +1072,15 @@ void Item::set_name(const char *str, uint length, CHARSET_INFO *cs)
name_length= 0;
return;
}
- if (cs->ctype)
- {
- const char *str_start= str;
+ const char *str_start= str;
+ if (!cs->ctype || cs->mbminlen > 1)
+ {
+ str+= cs->cset->scan(cs, str, str + length, MY_SEQ_SPACES);
+ length-= str - str_start;
+ }
+ else
+ {
/*
This will probably need a better implementation in the future:
a function in CHARSET_INFO structure.
@@ -1050,21 +1090,21 @@ void Item::set_name(const char *str, uint length, CHARSET_INFO *cs)
length--;
str++;
}
- if (str != str_start && !is_autogenerated_name)
- {
- char buff[SAFE_NAME_LEN];
- strmake(buff, str_start,
- min(sizeof(buff)-1, length + (int) (str-str_start)));
-
- if (length == 0)
- push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
- ER_NAME_BECOMES_EMPTY, ER(ER_NAME_BECOMES_EMPTY),
- buff);
- else
- push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
- ER_REMOVED_SPACES, ER(ER_REMOVED_SPACES),
- buff);
- }
+ }
+ if (str != str_start && !is_autogenerated_name)
+ {
+ char buff[SAFE_NAME_LEN];
+ strmake(buff, str_start,
+ MY_MIN(sizeof(buff)-1, length + (int) (str-str_start)));
+
+ if (length == 0)
+ push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
+ ER_NAME_BECOMES_EMPTY, ER(ER_NAME_BECOMES_EMPTY),
+ buff);
+ else
+ push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
+ ER_REMOVED_SPACES, ER(ER_REMOVED_SPACES),
+ buff);
}
if (!my_charset_same(cs, system_charset_info))
{
@@ -1075,7 +1115,7 @@ void Item::set_name(const char *str, uint length, CHARSET_INFO *cs)
name_length= res_length;
}
else
- name= sql_strmake(str, (name_length= min(length,MAX_ALIAS_NAME)));
+ name= sql_strmake(str, (name_length= MY_MIN(length,MAX_ALIAS_NAME)));
}
@@ -1130,6 +1170,8 @@ bool Item::eq(const Item *item, bool binary_cmp) const
Item *Item::safe_charset_converter(CHARSET_INFO *tocs)
{
+ if (!needs_charset_converter(tocs))
+ return this;
Item_func_conv_charset *conv= new Item_func_conv_charset(this, tocs, 1);
return conv->safe ? conv : NULL;
}
@@ -1192,62 +1234,55 @@ Item *Item_num::safe_charset_converter(CHARSET_INFO *tocs)
if (!(tocs->state & MY_CS_NONASCII))
return this;
- Item_string *conv;
- uint conv_errors;
- char buf[64], buf2[64];
- String tmp(buf, sizeof(buf), &my_charset_bin);
- String cstr(buf2, sizeof(buf2), &my_charset_bin);
- String *ostr= val_str(&tmp);
- char *ptr;
- cstr.copy(ostr->ptr(), ostr->length(), ostr->charset(), tocs, &conv_errors);
- if (conv_errors || !(conv= new Item_string(cstr.ptr(), cstr.length(),
- cstr.charset(),
- collation.derivation)))
- {
- /*
- Safe conversion is not possible (or EOM).
- We could not convert a string into the requested character set
- without data loss. The target charset does not cover all the
- characters from the string. Operation cannot be done correctly.
- */
- return NULL;
- }
- if (!(ptr= current_thd->strmake(cstr.ptr(), cstr.length())))
- return NULL;
- conv->str_value.set(ptr, cstr.length(), cstr.charset());
- /* Ensure that no one is going to change the result string */
- conv->str_value.mark_as_const();
- conv->fix_char_length(max_char_length());
+ Item *conv;
+ if ((conv= const_charset_converter(tocs, true)))
+ conv->fix_char_length(max_char_length());
return conv;
}
-Item *Item_static_float_func::safe_charset_converter(CHARSET_INFO *tocs)
+/**
+ Create character set converter for constant items
+ using Item_null, Item_string or Item_static_string_func.
+
+ @param tocs Character set to to convert the string to.
+ @param lossless Whether data loss is acceptable.
+ @param func_name Function name, or NULL.
+
+ @return this, if conversion is not needed,
+ NULL, if safe conversion is not possible, or
+ a new item representing the converted constant.
+*/
+Item *Item::const_charset_converter(CHARSET_INFO *tocs,
+ bool lossless,
+ const char *func_name)
{
- Item_string *conv;
- char buf[64];
- String *s, tmp(buf, sizeof(buf), &my_charset_bin);
- s= val_str(&tmp);
- if ((conv= new Item_static_string_func(func_name, s->ptr(), s->length(),
- s->charset())))
+ DBUG_ASSERT(const_item());
+ DBUG_ASSERT(fixed);
+ StringBuffer<64>tmp;
+ String *s= val_str(&tmp);
+ if (!s)
+ return new Item_null((char *) func_name, tocs);
+
+ if (!needs_charset_converter(s->length(), tocs))
{
- conv->str_value.copy();
- conv->str_value.mark_as_const();
+ if (collation.collation == &my_charset_bin && tocs != &my_charset_bin &&
+ !this->check_well_formed_result(s, true))
+ return NULL;
+ return this;
}
- return conv;
-}
-
-Item *Item_string::safe_charset_converter(CHARSET_INFO *tocs)
-{
- Item_string *conv;
uint conv_errors;
- char *ptr;
- String tmp, cstr, *ostr= val_str(&tmp);
- cstr.copy(ostr->ptr(), ostr->length(), ostr->charset(), tocs, &conv_errors);
- if (conv_errors || !(conv= new Item_string(cstr.ptr(), cstr.length(),
- cstr.charset(),
- collation.derivation)))
+ Item_string *conv= func_name ?
+ new Item_static_string_func(func_name,
+ s, tocs, &conv_errors,
+ collation.derivation,
+ collation.repertoire) :
+ new Item_string(s, tocs, &conv_errors,
+ collation.derivation,
+ collation.repertoire);
+
+ if (!conv || (conv_errors && lossless))
{
/*
Safe conversion is not possible (or EOM).
@@ -1257,70 +1292,28 @@ Item *Item_string::safe_charset_converter(CHARSET_INFO *tocs)
*/
return NULL;
}
- if (!(ptr= current_thd->strmake(cstr.ptr(), cstr.length())))
+ if (s->charset() == &my_charset_bin && tocs != &my_charset_bin &&
+ !conv->check_well_formed_result(true))
return NULL;
- conv->str_value.set(ptr, cstr.length(), cstr.charset());
- /* Ensure that no one is going to change the result string */
- conv->str_value.mark_as_const();
return conv;
}
Item *Item_param::safe_charset_converter(CHARSET_INFO *tocs)
{
- if (const_item())
- {
- uint cnv_errors;
- String *ostr= val_str(&cnvstr);
- cnvitem->str_value.copy(ostr->ptr(), ostr->length(),
- ostr->charset(), tocs, &cnv_errors);
- if (cnv_errors)
- return NULL;
- cnvitem->str_value.mark_as_const();
- cnvitem->max_length= cnvitem->str_value.numchars() * tocs->mbmaxlen;
- return cnvitem;
- }
- return Item::safe_charset_converter(tocs);
-}
-
-
-Item *Item_static_string_func::safe_charset_converter(CHARSET_INFO *tocs)
-{
- Item_string *conv;
- uint conv_errors;
- String tmp, cstr, *ostr= val_str(&tmp);
- cstr.copy(ostr->ptr(), ostr->length(), ostr->charset(), tocs, &conv_errors);
- if (conv_errors ||
- !(conv= new Item_static_string_func(func_name,
- cstr.ptr(), cstr.length(),
- cstr.charset(),
- collation.derivation)))
- {
- /*
- Safe conversion is not possible (or EOM).
- We could not convert a string into the requested character set
- without data loss. The target charset does not cover all the
- characters from the string. Operation cannot be done correctly.
- */
- return NULL;
- }
- conv->str_value.copy();
- /* Ensure that no one is going to change the result string */
- conv->str_value.mark_as_const();
- return conv;
-}
+ /*
+ Return "this" if in prepare. result_type may change at execition time,
+ to it's possible that the converter will not be needed at all:
+ PREPARE stmt FROM 'SELECT * FROM t1 WHERE field = ?';
+ SET @@arg= 1;
+ EXECUTE stms USING @arg;
-bool Item_string::eq(const Item *item, bool binary_cmp) const
-{
- if (type() == item->type() && item->basic_const_item())
- {
- if (binary_cmp)
- return !stringcmp(&str_value, &item->str_value);
- return (collation.collation == item->collation.collation &&
- !sortcmp(&str_value, &item->str_value, collation.collation));
- }
- return 0;
+ In the above example result_type is STRING_RESULT at prepare time,
+ and INT_RESULT at execution time.
+ */
+ return !const_item() || state == NULL_VALUE ?
+ this : const_charset_converter(tocs, true);
}
@@ -1379,7 +1372,7 @@ bool Item::get_date(MYSQL_TIME *ltime,ulonglong fuzzydate)
String tmp(buff,sizeof(buff), &my_charset_bin),*res;
if (!(res=val_str(&tmp)) ||
str_to_datetime_with_warn(res->charset(), res->ptr(), res->length(),
- ltime, fuzzydate) <= MYSQL_TIMESTAMP_ERROR)
+ ltime, fuzzydate))
goto err;
break;
}
@@ -2191,33 +2184,10 @@ bool agg_item_set_converter(DTCollation &coll, const char *fname,
for (i= 0, arg= args; i < nargs; i++, arg+= item_sep)
{
- Item* conv;
- uint32 dummy_offset;
- if (!String::needs_conversion(1, (*arg)->collation.collation,
- coll.collation,
- &dummy_offset))
- continue;
-
- /*
- No needs to add converter if an "arg" is NUMERIC or DATETIME
- value (which is pure ASCII) and at the same time target DTCollation
- is ASCII-compatible. For example, no needs to rewrite:
- SELECT * FROM t1 WHERE datetime_field = '2010-01-01';
- to
- SELECT * FROM t1 WHERE CONVERT(datetime_field USING cs) = '2010-01-01';
-
- TODO: avoid conversion of any values with
- repertoire ASCII and 7bit-ASCII-compatible,
- not only numeric/datetime origin.
- */
- if ((*arg)->collation.derivation == DERIVATION_NUMERIC &&
- (*arg)->collation.repertoire == MY_REPERTOIRE_ASCII &&
- !((*arg)->collation.collation->state & MY_CS_NONASCII) &&
- !(coll.collation->state & MY_CS_NONASCII))
+ Item* conv= (*arg)->safe_charset_converter(coll.collation);
+ if (conv == *arg)
continue;
-
- if (!(conv= (*arg)->safe_charset_converter(coll.collation)) &&
- ((*arg)->collation.repertoire == MY_REPERTOIRE_ASCII))
+ if (!conv && ((*arg)->collation.repertoire == MY_REPERTOIRE_ASCII))
conv= new Item_func_conv_charset(*arg, coll.collation, 1);
if (!conv)
@@ -2475,7 +2445,7 @@ void Item_field::set_field(Field *field_par)
field_name= field_par->field_name;
db_name= field_par->table->s->db.str;
alias_name_used= field_par->table->alias_name_used;
- unsigned_flag=test(field_par->flags & UNSIGNED_FLAG);
+ unsigned_flag= MY_TEST(field_par->flags & UNSIGNED_FLAG);
collation.set(field_par->charset(), field_par->derivation(),
field_par->repertoire());
fix_char_length(field_par->char_length());
@@ -3003,7 +2973,7 @@ String *Item_float::val_str(String *str)
{
// following assert is redundant, because fixed=1 assigned in constructor
DBUG_ASSERT(fixed == 1);
- str->set_real(value,decimals,&my_charset_bin);
+ str->set_real(value, decimals, &my_charset_numeric);
return str;
}
@@ -3098,7 +3068,7 @@ double_from_string_with_check(CHARSET_INFO *cs, const char *cptr,
We can use err.ptr() here as ErrConvString is guranteed to put an
end \0 here.
*/
- push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
ER_TRUNCATED_WRONG_VALUE,
ER(ER_TRUNCATED_WRONG_VALUE), "DOUBLE",
err.ptr());
@@ -3135,7 +3105,7 @@ longlong_from_string_with_check(CHARSET_INFO *cs, const char *cptr,
(end != end_of_num && !check_if_only_end_space(cs, end_of_num, end))))
{
ErrConvString err(cptr, end - cptr, cs);
- push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
ER_TRUNCATED_WRONG_VALUE,
ER(ER_TRUNCATED_WRONG_VALUE), "INTEGER",
err.ptr());
@@ -3162,10 +3132,6 @@ my_decimal *Item_string::val_decimal(my_decimal *decimal_value)
}
-bool Item_null::eq(const Item *item, bool binary_cmp) const
-{ return item->type() == type(); }
-
-
double Item_null::val_real()
{
// following assert is redundant, because fixed=1 assigned in constructor
@@ -3235,8 +3201,6 @@ Item_param::Item_param(uint pos_in_query_arg) :
value is set.
*/
maybe_null= 1;
- cnvitem= new Item_string("", 0, &my_charset_bin, DERIVATION_COERCIBLE);
- cnvstr.set(cnvbuf, sizeof(cnvbuf), &my_charset_bin);
}
@@ -3341,14 +3305,10 @@ void Item_param::set_time(MYSQL_TIME *tm, timestamp_type time_type,
value.time= *tm;
value.time.time_type= time_type;
- if (value.time.year > 9999 || value.time.month > 12 ||
- value.time.day > 31 ||
- (time_type != MYSQL_TIMESTAMP_TIME && value.time.hour > 23) ||
- value.time.minute > 59 || value.time.second > 59 ||
- value.time.second_part > TIME_MAX_SECOND_PART)
+ if (check_datetime_range(&value.time))
{
ErrConvTime str(&value.time);
- make_truncated_value_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ make_truncated_value_warning(current_thd, Sql_condition::WARN_LEVEL_WARN,
&str, time_type, 0);
set_zero_time(&value.time, MYSQL_TIMESTAMP_ERROR);
}
@@ -3607,7 +3567,7 @@ double Item_param::val_real()
This works for example when user says SELECT ?+0.0 and supplies
time value for the placeholder.
*/
- return ulonglong2double(TIME_to_ulonglong(&value.time));
+ return TIME_to_double(&value.time);
case NULL_VALUE:
return 0.0;
default:
@@ -3665,9 +3625,7 @@ my_decimal *Item_param::val_decimal(my_decimal *dec)
return dec;
case TIME_VALUE:
{
- longlong i= (longlong) TIME_to_ulonglong(&value.time);
- int2my_decimal(E_DEC_FATAL_ERROR, i, 0, dec);
- return dec;
+ return TIME_to_my_decimal(&value.time, dec);
}
case NULL_VALUE:
return 0;
@@ -3761,8 +3719,9 @@ const String *Item_param::query_val_str(THD *thd, String* str) const
case LONG_DATA_VALUE:
{
str->length(0);
- append_query_string(thd, value.cs_info.character_set_client, &str_value,
- str);
+ append_query_string(value.cs_info.character_set_client, str,
+ str_value.ptr(), str_value.length(),
+ thd->variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES);
break;
}
case NULL_VALUE:
@@ -3801,18 +3760,14 @@ bool Item_param::convert_str_value(THD *thd)
str_value.set_charset(value.cs_info.final_character_set_of_str_value);
/* Here str_value is guaranteed to be in final_character_set_of_str_value */
- max_length= str_value.numchars() * str_value.charset()->mbmaxlen;
-
- /* For the strings converted to numeric form within some functions */
- decimals= NOT_FIXED_DEC;
/*
str_value_ptr is returned from val_str(). It must be not alloced
to prevent it's modification by val_str() invoker.
*/
str_value_ptr.set(str_value.ptr(), str_value.length(),
str_value.charset());
- /* Synchronize item charset with value charset */
- collation.set(str_value.charset(), DERIVATION_COERCIBLE);
+ /* Synchronize item charset and length with value charset */
+ fix_charset_and_length_from_str_value(DERIVATION_COERCIBLE);
}
return rc;
}
@@ -3842,7 +3797,8 @@ Item_param::clone_item()
case STRING_VALUE:
case LONG_DATA_VALUE:
return new Item_string(name, str_value.c_ptr_quick(), str_value.length(),
- str_value.charset());
+ str_value.charset(),
+ collation.derivation, collation.repertoire);
case TIME_VALUE:
break;
case NO_VALUE:
@@ -3854,30 +3810,21 @@ Item_param::clone_item()
bool
-Item_param::eq(const Item *arg, bool binary_cmp) const
+Item_param::eq(const Item *item, bool binary_cmp) const
{
- Item *item;
- if (!basic_const_item() || !arg->basic_const_item() || arg->type() != type())
+ if (!basic_const_item())
return FALSE;
- /*
- We need to cast off const to call val_int(). This should be OK for
- a basic constant.
- */
- item= (Item*) arg;
switch (state) {
case NULL_VALUE:
- return TRUE;
+ return null_eq(item);
case INT_VALUE:
- return value.integer == item->val_int() &&
- unsigned_flag == item->unsigned_flag;
+ return int_eq(value.integer, item);
case REAL_VALUE:
- return value.real == item->val_real();
+ return real_eq(value.real, item);
case STRING_VALUE:
case LONG_DATA_VALUE:
- if (binary_cmp)
- return !stringcmp(&str_value, &item->str_value);
- return !sortcmp(&str_value, &item->str_value, collation.collation);
+ return str_eq(&str_value, item, binary_cmp);
default:
break;
}
@@ -4137,8 +4084,8 @@ double Item_copy_string::val_real()
longlong Item_copy_string::val_int()
{
int err;
- return null_value ? LL(0) : my_strntoll(str_value.charset(),str_value.ptr(),
- str_value.length(),10, (char**) 0,
+ return null_value ? 0 : my_strntoll(str_value.charset(),str_value.ptr(),
+ str_value.length(), 10, (char**) 0,
&err);
}
@@ -4308,7 +4255,7 @@ double Item_copy_decimal::val_real()
longlong Item_copy_decimal::val_int()
{
if (null_value)
- return LL(0);
+ return 0;
else
{
longlong result;
@@ -4434,7 +4381,7 @@ static bool mark_as_dependent(THD *thd, SELECT_LEX *last, SELECT_LEX *current,
resolved_item->db_name : "");
const char *table_name= (resolved_item->table_name ?
resolved_item->table_name : "");
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
ER_WARN_FIELD_RESOLVED, ER(ER_WARN_FIELD_RESOLVED),
db_name, (db_name[0] ? "." : ""),
table_name, (table_name [0] ? "." : ""),
@@ -4682,7 +4629,7 @@ resolve_ref_in_select_and_group(THD *thd, Item_ident *ref, SELECT_LEX *select)
!((*group_by_ref)->eq(*select_ref, 0)))
{
ambiguous_fields= TRUE;
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_NON_UNIQ_ERROR,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_NON_UNIQ_ERROR,
ER(ER_NON_UNIQ_ERROR), ref->full_name(),
current_thd->where);
@@ -5390,13 +5337,6 @@ bool Item_field::vcol_in_partition_func_processor(uchar *int_arg)
}
-Item *Item_field::safe_charset_converter(CHARSET_INFO *tocs)
-{
- no_const_subst= 1;
- return Item::safe_charset_converter(tocs);
-}
-
-
void Item_field::cleanup()
{
DBUG_ENTER("Item_field::cleanup");
@@ -5714,10 +5654,7 @@ String *Item::check_well_formed_result(String *str, bool send_error)
{
/* Check whether we got a well-formed string */
CHARSET_INFO *cs= str->charset();
- int well_formed_error;
- uint wlen= cs->cset->well_formed_len(cs,
- str->ptr(), str->ptr() + str->length(),
- str->length(), &well_formed_error);
+ uint wlen= str->well_formed_length();
if (wlen < str->length())
{
THD *thd= current_thd;
@@ -5731,8 +5668,7 @@ String *Item::check_well_formed_result(String *str, bool send_error)
cs->csname, hexbuf);
return 0;
}
- if ((thd->variables.sql_mode &
- (MODE_STRICT_TRANS_TABLES | MODE_STRICT_ALL_TABLES)))
+ if (thd->is_strict_mode())
{
null_value= 1;
str= 0;
@@ -5741,7 +5677,7 @@ String *Item::check_well_formed_result(String *str, bool send_error)
{
str->length(wlen);
}
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_INVALID_CHARACTER_STRING,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_INVALID_CHARACTER_STRING,
ER(ER_INVALID_CHARACTER_STRING), cs->csname, hexbuf);
}
return str;
@@ -5877,29 +5813,23 @@ Field *Item::tmp_table_field_from_field_type(TABLE *table, bool fixed_length)
field= new Field_double((uchar*) 0, max_length, null_ptr, 0, Field::NONE,
name, decimals, 0, unsigned_flag);
break;
- case MYSQL_TYPE_NULL:
- field= new Field_null((uchar*) 0, max_length, Field::NONE,
- name, &my_charset_bin);
- break;
case MYSQL_TYPE_INT24:
field= new Field_medium((uchar*) 0, max_length, null_ptr, 0, Field::NONE,
name, 0, unsigned_flag);
break;
case MYSQL_TYPE_NEWDATE:
case MYSQL_TYPE_DATE:
- field= new Field_newdate(0, null_ptr, 0, Field::NONE, name, &my_charset_bin);
+ field= new Field_newdate(0, null_ptr, 0, Field::NONE, name);
break;
case MYSQL_TYPE_TIME:
- field= new_Field_time(0, null_ptr, 0, Field::NONE, name,
- decimals, &my_charset_bin);
+ field= new_Field_time(0, null_ptr, 0, Field::NONE, name, decimals);
break;
case MYSQL_TYPE_TIMESTAMP:
field= new_Field_timestamp(0, null_ptr, 0,
- Field::NONE, name, 0, decimals, &my_charset_bin);
+ Field::NONE, name, 0, decimals);
break;
case MYSQL_TYPE_DATETIME:
- field= new_Field_datetime(0, null_ptr, 0, Field::NONE, name,
- decimals, &my_charset_bin);
+ field= new_Field_datetime(0, null_ptr, 0, Field::NONE, name, decimals);
break;
case MYSQL_TYPE_YEAR:
field= new Field_year((uchar*) 0, max_length, null_ptr, 0, Field::NONE,
@@ -5913,6 +5843,7 @@ Field *Item::tmp_table_field_from_field_type(TABLE *table, bool fixed_length)
/* This case should never be chosen */
DBUG_ASSERT(0);
/* If something goes awfully wrong, it's better to get a string than die */
+ case MYSQL_TYPE_NULL:
case MYSQL_TYPE_STRING:
if (fixed_length && !too_big_for_varchar())
{
@@ -6013,13 +5944,51 @@ static int save_field_in_field(Field *from, bool *null_value,
}
+static int memcpy_field_value(Field *to, Field *from)
+{
+ if (to->ptr != from->ptr)
+ memcpy(to->ptr,from->ptr, to->pack_length());
+ return 0;
+}
+
+fast_field_copier Item_field::setup_fast_field_copier(Field *to)
+{
+ DBUG_ENTER("Item_field::setup_fast_field_copier");
+ DBUG_RETURN(memcpy_field_possible(to, field) ?
+ &memcpy_field_value :
+ &field_conv_incompatible);
+}
+
+
/**
Set a field's value from a item.
*/
-void Item_field::save_org_in_field(Field *to)
+void Item_field::save_org_in_field(Field *to,
+ fast_field_copier fast_field_copier_func)
{
- save_field_in_field(field, &null_value, to, TRUE);
+ DBUG_ENTER("Item_field::save_org_in_field");
+ DBUG_PRINT("enter", ("setup: 0x%lx data: 0x%lx",
+ (ulong) to, (ulong) fast_field_copier_func));
+ if (fast_field_copier_func)
+ {
+ if (field->is_null())
+ {
+ null_value= TRUE;
+ set_field_to_null_with_conversions(to, TRUE);
+ DBUG_VOID_RETURN;
+ }
+ to->set_notnull();
+ if (to == field)
+ {
+ null_value= 0;
+ DBUG_VOID_RETURN;
+ }
+ (*fast_field_copier_func)(to, field);
+ }
+ else
+ save_field_in_field(field, &null_value, to, TRUE);
+ DBUG_VOID_RETURN;
}
@@ -6173,24 +6142,6 @@ int Item_decimal::save_in_field(Field *field, bool no_conversions)
}
-bool Item_int::eq(const Item *arg, bool binary_cmp) const
-{
- /* No need to check for null value as basic constant can't be NULL */
- if (arg->basic_const_item() && arg->type() == type())
- {
- /*
- We need to cast off const to call val_int(). This should be OK for
- a basic constant.
- */
- Item *item= (Item*) arg;
- return (item->val_int() == value &&
- ((longlong) value >= 0 ||
- (item->unsigned_flag == unsigned_flag)));
- }
- return FALSE;
-}
-
-
Item *Item_int_with_ref::clone_item()
{
DBUG_ASSERT(ref->const_item());
@@ -6308,27 +6259,6 @@ void Item_float::print(String *str, enum_query_type query_type)
}
-/*
- hex item
- In string context this is a binary string.
- In number context this is a longlong value.
-*/
-
-bool Item_float::eq(const Item *arg, bool binary_cmp) const
-{
- if (arg->basic_const_item() && arg->type() == type())
- {
- /*
- We need to cast off const to call val_int(). This should be OK for
- a basic constant.
- */
- Item *item= (Item*) arg;
- return item->val_real() == value;
- }
- return FALSE;
-}
-
-
inline uint char_val(char X)
{
return (uint) (X >= '0' && X <= '9' ? X-'0' :
@@ -6366,7 +6296,7 @@ longlong Item_hex_hybrid::val_int()
// following assert is redundant, because fixed=1 assigned in constructor
DBUG_ASSERT(fixed == 1);
char *end=(char*) str_value.ptr()+str_value.length(),
- *ptr=end-min(str_value.length(),sizeof(longlong));
+ *ptr=end-MY_MIN(str_value.length(),sizeof(longlong));
ulonglong value=0;
for (; ptr != end ; ptr++)
@@ -6384,8 +6314,6 @@ int Item_hex_hybrid::save_in_field(Field *field, bool no_conversions)
ulonglong nr;
uint32 length= str_value.length();
- if (!length)
- return 1;
if (length > 8)
{
@@ -6402,7 +6330,7 @@ int Item_hex_hybrid::save_in_field(Field *field, bool no_conversions)
warn:
if (!field->store((longlong) nr, TRUE))
- field->set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE,
+ field->set_warning(Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE,
1);
return 1;
}
@@ -6410,7 +6338,7 @@ warn:
void Item_hex_hybrid::print(String *str, enum_query_type query_type)
{
- uint32 len= min(str_value.length(), sizeof(longlong));
+ uint32 len= MY_MIN(str_value.length(), sizeof(longlong));
const char *ptr= str_value.ptr() + str_value.length() - len;
str->append("0x");
str->append_hex(ptr, len);
@@ -6425,32 +6353,6 @@ void Item_hex_string::print(String *str, enum_query_type query_type)
}
-bool Item_hex_constant::eq(const Item *arg, bool binary_cmp) const
-{
- if (arg->basic_const_item() && arg->type() == type() &&
- arg->cast_to_int_type() == cast_to_int_type())
- {
- if (binary_cmp)
- return !stringcmp(&str_value, &arg->str_value);
- return !sortcmp(&str_value, &arg->str_value, collation.collation);
- }
- return FALSE;
-}
-
-
-Item *Item_hex_constant::safe_charset_converter(CHARSET_INFO *tocs)
-{
- Item_string *conv;
- String tmp, *str= val_str(&tmp);
-
- if (!(conv= new Item_string(str->ptr(), str->length(), tocs)))
- return NULL;
- conv->str_value.copy();
- conv->str_value.mark_as_const();
- return conv;
-}
-
-
/*
bin item.
In string context this is a binary string.
@@ -6495,6 +6397,78 @@ Item_bin_string::Item_bin_string(const char *str, uint str_length)
}
+bool Item_temporal_literal::eq(const Item *item, bool binary_cmp) const
+{
+ return
+ item->basic_const_item() && type() == item->type() &&
+ field_type() == ((Item_temporal_literal *) item)->field_type() &&
+ !my_time_compare(&cached_time,
+ &((Item_temporal_literal *) item)->cached_time);
+}
+
+
+void Item_date_literal::print(String *str, enum_query_type query_type)
+{
+ str->append("DATE'");
+ char buf[MAX_DATE_STRING_REP_LENGTH];
+ my_date_to_str(&cached_time, buf);
+ str->append(buf);
+ str->append('\'');
+}
+
+
+bool Item_date_literal::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date)
+{
+ DBUG_ASSERT(fixed);
+ fuzzy_date |= sql_mode_for_dates(current_thd);
+ *ltime= cached_time;
+ return (null_value= check_date_with_warn(ltime, fuzzy_date,
+ MYSQL_TIMESTAMP_ERROR));
+}
+
+
+void Item_datetime_literal::print(String *str, enum_query_type query_type)
+{
+ str->append("TIMESTAMP'");
+ char buf[MAX_DATE_STRING_REP_LENGTH];
+ my_datetime_to_str(&cached_time, buf, decimals);
+ str->append(buf);
+ str->append('\'');
+}
+
+
+bool Item_datetime_literal::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date)
+{
+ DBUG_ASSERT(fixed);
+ fuzzy_date |= sql_mode_for_dates(current_thd);
+ *ltime= cached_time;
+ return (null_value= check_date_with_warn(ltime, fuzzy_date,
+ MYSQL_TIMESTAMP_ERROR));
+}
+
+
+void Item_time_literal::print(String *str, enum_query_type query_type)
+{
+ str->append("TIME'");
+ char buf[MAX_DATE_STRING_REP_LENGTH];
+ my_time_to_str(&cached_time, buf, decimals);
+ str->append(buf);
+ str->append('\'');
+}
+
+
+bool Item_time_literal::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date)
+{
+ DBUG_ASSERT(fixed);
+ *ltime= cached_time;
+ if (fuzzy_date & TIME_TIME_ONLY)
+ return (null_value= false);
+ return (null_value= check_date_with_warn(ltime, fuzzy_date,
+ MYSQL_TIMESTAMP_ERROR));
+}
+
+
+
/**
Pack data in buffer for sending.
*/
@@ -6596,7 +6570,7 @@ bool Item::send(Protocol *protocol, String *buffer)
case MYSQL_TYPE_TIMESTAMP:
{
MYSQL_TIME tm;
- get_date(&tm, sql_mode_for_dates());
+ get_date(&tm, sql_mode_for_dates(current_thd));
if (!null_value)
{
if (f_type == MYSQL_TYPE_DATE)
@@ -6682,6 +6656,13 @@ Item* Item::cache_const_expr_transformer(uchar *arg)
return this;
}
+/**
+ Find Item by reference in the expression
+*/
+bool Item::find_item_processor(uchar *arg)
+{
+ return (this == ((Item *) arg));
+}
bool Item_field::send(Protocol *protocol, String *buffer)
{
@@ -7468,9 +7449,9 @@ int Item_ref::save_in_field(Field *to, bool no_conversions)
}
-void Item_ref::save_org_in_field(Field *field)
+void Item_ref::save_org_in_field(Field *field, fast_field_copier optimizer_data)
{
- (*ref)->save_org_in_field(field);
+ (*ref)->save_org_in_field(field, optimizer_data);
}
@@ -8344,7 +8325,7 @@ int Item_default_value::save_in_field(Field *field_arg, bool no_conversions)
{
TABLE_LIST *view= field_arg->table->pos_in_table_list->top_table();
push_warning_printf(field_arg->table->in_use,
- MYSQL_ERROR::WARN_LEVEL_WARN,
+ Sql_condition::WARN_LEVEL_WARN,
ER_NO_DEFAULT_FOR_VIEW_FIELD,
ER(ER_NO_DEFAULT_FOR_VIEW_FIELD),
view->view_db.str,
@@ -8353,7 +8334,7 @@ int Item_default_value::save_in_field(Field *field_arg, bool no_conversions)
else
{
push_warning_printf(field_arg->table->in_use,
- MYSQL_ERROR::WARN_LEVEL_WARN,
+ Sql_condition::WARN_LEVEL_WARN,
ER_NO_DEFAULT_FOR_FIELD,
ER(ER_NO_DEFAULT_FOR_FIELD),
field_arg->field_name);
@@ -8766,6 +8747,28 @@ int stored_field_cmp_to_item(THD *thd, Field *field, Item *item)
{
Item_result res_type=item_cmp_type(field->result_type(),
item->result_type());
+ /*
+ We have to check field->cmp_type() instead of res_type,
+ as result_type() - and thus res_type - can never be TIME_RESULT (yet).
+ */
+ if (field->cmp_type() == TIME_RESULT)
+ {
+ MYSQL_TIME field_time, item_time, item_time2, *item_time_cmp= &item_time;
+ if (field->type() == MYSQL_TYPE_TIME)
+ {
+ field->get_time(&field_time);
+ item->get_time(&item_time);
+ }
+ else
+ {
+ field->get_date(&field_time, TIME_INVALID_DATES);
+ item->get_date(&item_time, TIME_INVALID_DATES);
+ if (item_time.time_type == MYSQL_TIMESTAMP_TIME)
+ if (time_to_datetime(thd, &item_time, item_time_cmp= &item_time2))
+ return 1;
+ }
+ return my_time_compare(&field_time, item_time_cmp);
+ }
if (res_type == STRING_RESULT)
{
char item_buff[MAX_FIELD_WIDTH];
@@ -8816,25 +8819,6 @@ int stored_field_cmp_to_item(THD *thd, Field *field, Item *item)
return my_decimal_cmp(field_val, item_val);
}
/*
- We have to check field->cmp_type() instead of res_type,
- as result_type() - and thus res_type - can never be TIME_RESULT (yet).
- */
- if (field->cmp_type() == TIME_RESULT)
- {
- MYSQL_TIME field_time, item_time;
- if (field->type() == MYSQL_TYPE_TIME)
- {
- field->get_time(&field_time);
- item->get_time(&item_time);
- }
- else
- {
- field->get_date(&field_time, TIME_INVALID_DATES);
- item->get_date(&item_time, TIME_INVALID_DATES);
- }
- return my_time_compare(&field_time, &item_time);
- }
- /*
The patch for Bug#13463415 started using this function for comparing
BIGINTs. That uncovered a bug in Visual Studio 32bit optimized mode.
Prefixing the auto variables with volatile fixes the problem....
@@ -9549,7 +9533,7 @@ bool Item_type_holder::join_types(THD *thd, Item *item)
/* fix variable decimals which always is NOT_FIXED_DEC */
if (Field::result_merge_type(fld_type) == INT_RESULT)
item_decimals= 0;
- decimals= max(decimals, item_decimals);
+ decimals= MY_MAX(decimals, item_decimals);
}
if (fld_type == FIELD_TYPE_GEOMETRY)
@@ -9558,10 +9542,10 @@ bool Item_type_holder::join_types(THD *thd, Item *item)
if (Field::result_merge_type(fld_type) == DECIMAL_RESULT)
{
- decimals= min(max(decimals, item->decimals), DECIMAL_MAX_SCALE);
+ decimals= MY_MIN(MY_MAX(decimals, item->decimals), DECIMAL_MAX_SCALE);
int item_int_part= item->decimal_int_part();
- int item_prec = max(prev_decimal_int_part, item_int_part) + decimals;
- int precision= min(item_prec, DECIMAL_MAX_PRECISION);
+ int item_prec = MY_MAX(prev_decimal_int_part, item_int_part) + decimals;
+ int precision= MY_MIN(item_prec, DECIMAL_MAX_PRECISION);
unsigned_flag&= item->unsigned_flag;
max_length= my_decimal_precision_to_length_no_truncation(precision,
decimals,
@@ -9592,7 +9576,7 @@ bool Item_type_holder::join_types(THD *thd, Item *item)
*/
if (collation.collation != &my_charset_bin)
{
- max_length= max(old_max_chars * collation.collation->mbmaxlen,
+ max_length= MY_MAX(old_max_chars * collation.collation->mbmaxlen,
display_length(item) /
item->collation.collation->mbmaxlen *
collation.collation->mbmaxlen);
@@ -9614,7 +9598,7 @@ bool Item_type_holder::join_types(THD *thd, Item *item)
{
int delta1= max_length_orig - decimals_orig;
int delta2= item->max_length - item->decimals;
- max_length= max(delta1, delta2) + decimals;
+ max_length= MY_MAX(delta1, delta2) + decimals;
if (fld_type == MYSQL_TYPE_FLOAT && max_length > FLT_DIG + 2)
{
max_length= MAX_FLOAT_STR_LENGTH;
@@ -9632,7 +9616,7 @@ bool Item_type_holder::join_types(THD *thd, Item *item)
break;
}
default:
- max_length= max(max_length, display_length(item));
+ max_length= MY_MAX(max_length, display_length(item));
};
maybe_null|= item->maybe_null;
get_full_info(item);
@@ -9928,14 +9912,3 @@ const char *dbug_print_item(Item *item)
#endif /*DBUG_OFF*/
-/*****************************************************************************
-** Instantiate templates
-*****************************************************************************/
-
-#ifdef HAVE_EXPLICIT_TEMPLATE_INSTANTIATION
-template class List<Item>;
-template class List_iterator<Item>;
-template class List_iterator_fast<Item>;
-template class List_iterator_fast<Item_field>;
-template class List<List_item>;
-#endif
diff --git a/sql/item.h b/sql/item.h
index d09f6572487..4347bdb6c07 100644
--- a/sql/item.h
+++ b/sql/item.h
@@ -25,7 +25,6 @@
#include "sql_priv.h" /* STRING_BUFFER_USUAL_SIZE */
#include "unireg.h"
#include "sql_const.h" /* RAND_TABLE_BIT, MAX_FIELD_NAME */
-#include "unireg.h" // REQUIRED: for other includes
#include "thr_malloc.h" /* sql_calloc */
#include "field.h" /* Derivation */
@@ -164,6 +163,12 @@ public:
default: return "UNKNOWN";
}
}
+ int sortcmp(const String *s, const String *t) const
+ {
+ return collation->coll->strnncollsp(collation,
+ (uchar *) s->ptr(), s->length(),
+ (uchar *) t->ptr(), t->length(), 0);
+ }
};
/*************************************************************************/
@@ -531,7 +536,7 @@ public:
struct st_dyncall_create_def
{
- Item *num, *value;
+ Item *key, *value;
CHARSET_INFO *cs;
uint len, frac;
DYNAMIC_COLUMN_TYPE type;
@@ -556,11 +561,21 @@ typedef bool (Item::*Item_analyzer) (uchar **argp);
typedef Item* (Item::*Item_transformer) (uchar *arg);
typedef void (*Cond_traverser) (const Item *item, void *arg);
+struct st_cond_statistic;
+
+struct find_selective_predicates_list_processor_data
+{
+ TABLE *table;
+ List<st_cond_statistic> list;
+};
+
class Item_equal;
class COND_EQUAL;
class st_select_lex_unit;
+class Item_func_not;
+
class Item {
Item(const Item &); /* Prevent use of these */
void operator=(Item &);
@@ -591,7 +606,8 @@ public:
SUBSELECT_ITEM, ROW_ITEM, CACHE_ITEM, TYPE_HOLDER,
PARAM_ITEM, TRIGGER_FIELD_ITEM, DECIMAL_ITEM,
XPATH_NODESET, XPATH_NODESET_CMP,
- VIEW_FIXER_ITEM, EXPR_CACHE_ITEM};
+ VIEW_FIXER_ITEM, EXPR_CACHE_ITEM,
+ DATE_ITEM};
enum cond_result { COND_UNDEF,COND_OK,COND_TRUE,COND_FALSE };
@@ -603,11 +619,20 @@ public:
/* Reuse size, only used by SP local variable assignment, otherwize 0 */
uint rsize;
+protected:
/*
str_values's main purpose is to be used to cache the value in
save_in_field
*/
String str_value;
+
+public:
+ /*
+ Cache val_str() into the own buffer, e.g. to evaluate constant
+ expressions with subqueries in the ORDER/GROUP clauses.
+ */
+ String *val_str() { return val_str(&str_value); }
+
char * name; /* Name from select */
/* Original item name (if it was renamed)*/
char * orig_name;
@@ -698,8 +723,12 @@ public:
/* Function returns 1 on overflow and -1 on fatal errors */
int save_in_field_no_warnings(Field *field, bool no_conversions);
virtual int save_in_field(Field *field, bool no_conversions);
- virtual void save_org_in_field(Field *field)
+ virtual void save_org_in_field(Field *field,
+ fast_field_copier data
+ __attribute__ ((__unused__)))
{ (void) save_in_field(field, 1); }
+ virtual fast_field_copier setup_fast_field_copier(Field *field)
+ { return NULL; }
virtual int save_safe_in_field(Field *field)
{ return save_in_field(field, 1); }
virtual bool send(Protocol *protocol, String *str);
@@ -935,7 +964,7 @@ public:
save_val() is method of val_* family which stores value in the given
field.
*/
- virtual void save_val(Field *to) { save_org_in_field(to); }
+ virtual void save_val(Field *to) { save_org_in_field(to, NULL); }
/*
save_result() is method of val*result() family which stores value in
the given field.
@@ -956,6 +985,11 @@ public:
double val_real_from_decimal();
double val_real_from_date();
+ // Get TIME, DATE or DATETIME using proper sql_mode flags for the field type
+ bool get_temporal_with_sql_mode(MYSQL_TIME *ltime);
+ // Check NULL value for a TIME, DATE or DATETIME expression
+ bool is_null_from_temporal();
+
int save_time_in_field(Field *field);
int save_date_in_field(Field *field);
int save_str_value_in_field(Field *field, String *result);
@@ -1029,7 +1063,7 @@ public:
return decimals < NOT_FIXED_DEC ? decimals :
is_temporal_type_with_time(field_type()) ?
TIME_SECOND_PART_DIGITS :
- min(max_length, DECIMAL_MAX_SCALE);
+ MY_MIN(max_length, DECIMAL_MAX_SCALE);
}
/*
Returns how many digits a divisor adds into a division result.
@@ -1097,7 +1131,9 @@ public:
Item **ref, bool skip_registered);
virtual bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate);
bool get_time(MYSQL_TIME *ltime)
- { return get_date(ltime, TIME_TIME_ONLY); }
+ { return get_date(ltime, TIME_TIME_ONLY | TIME_INVALID_DATES); }
+ // Get date with automatic TIME->DATETIME conversion
+ bool get_date_with_conversion(MYSQL_TIME *ltime, ulonglong fuzzydate);
bool get_seconds(ulonglong *sec, ulong *sec_part);
virtual bool get_date_result(MYSQL_TIME *ltime, ulonglong fuzzydate)
{ return get_date(ltime,fuzzydate); }
@@ -1153,8 +1189,8 @@ public:
*/
virtual CHARSET_INFO *charset_for_protocol(void) const
{
- return result_type() == STRING_RESULT ? collation.collation :
- &my_charset_bin;
+ return cmp_type() == STRING_RESULT ? collation.collation :
+ &my_charset_bin;
};
virtual bool walk(Item_processor processor, bool walk_subquery, uchar *arg)
@@ -1162,6 +1198,11 @@ public:
return (this->*processor)(arg);
}
+ virtual bool walk_top_and(Item_processor processor, uchar *arg)
+ {
+ return (this->*processor)(arg);
+ }
+
virtual Item* transform(Item_transformer transformer, uchar *arg);
/*
@@ -1204,11 +1245,11 @@ public:
virtual bool intro_version(uchar *int_arg) { return 0; }
virtual bool remove_dependence_processor(uchar * arg) { return 0; }
- virtual bool remove_fixed(uchar * arg) { fixed= 0; return 0; }
virtual bool cleanup_processor(uchar *arg);
virtual bool collect_item_field_processor(uchar * arg) { return 0; }
virtual bool add_field_to_set_processor(uchar * arg) { return 0; }
virtual bool find_item_in_field_list_processor(uchar *arg) { return 0; }
+ virtual bool find_item_processor(uchar *arg);
virtual bool change_context_processor(uchar *context) { return 0; }
virtual bool reset_query_id_processor(uchar *query_id_arg) { return 0; }
virtual bool is_expensive_processor(uchar *arg) { return 0; }
@@ -1224,9 +1265,12 @@ public:
virtual bool is_subquery_processor (uchar *opt_arg) { return 0; }
virtual bool count_sargable_conds(uchar *arg) { return 0; }
virtual bool limit_index_condition_pushdown_processor(uchar *opt_arg)
- {
+ {
return FALSE;
}
+ virtual bool exists2in_processor(uchar *opt_arg) { return 0; }
+ virtual bool find_selective_predicates_list_processor(uchar *opt_arg)
+ { return 0; }
/* To call bool function for all arguments */
struct bool_func_call_args
@@ -1243,6 +1287,7 @@ public:
return FALSE;
}
+
/*
The next function differs from the previous one that a bitmap to be updated
is passed as uchar *arg.
@@ -1372,7 +1417,9 @@ public:
List<Item> *parameters;
/* unit from which we count nest_level */
st_select_lex_unit *nest_level_base;
+ uint count;
int nest_level;
+ bool collect;
};
/**
Collect outer references
@@ -1422,13 +1469,55 @@ public:
virtual void bring_value() {}
Field *tmp_table_field_from_field_type(TABLE *table, bool fixed_length);
- virtual Item_field *filed_for_view_update() { return 0; }
+ virtual Item_field *field_for_view_update() { return 0; }
virtual Item *neg_transformer(THD *thd) { return NULL; }
virtual Item *update_value_transformer(uchar *select_arg) { return this; }
virtual Item *expr_cache_insert_transformer(uchar *thd_arg) { return this; }
virtual bool expr_cache_is_needed(THD *) { return FALSE; }
virtual Item *safe_charset_converter(CHARSET_INFO *tocs);
+ bool needs_charset_converter(uint32 length, CHARSET_INFO *tocs)
+ {
+ /*
+ This will return "true" if conversion happens:
+ - between two non-binary different character sets
+ - from "binary" to "unsafe" character set
+ (those that can have non-well-formed string)
+ - from "binary" to UCS2-alike character set with mbminlen>1,
+ when prefix left-padding is needed for an incomplete character:
+ binary 0xFF -> ucs2 0x00FF)
+ */
+ if (!String::needs_conversion_on_storage(length,
+ collation.collation, tocs))
+ return false;
+ /*
+ No needs to add converter if an "arg" is NUMERIC or DATETIME
+ value (which is pure ASCII) and at the same time target DTCollation
+ is ASCII-compatible. For example, no needs to rewrite:
+ SELECT * FROM t1 WHERE datetime_field = '2010-01-01';
+ to
+ SELECT * FROM t1 WHERE CONVERT(datetime_field USING cs) = '2010-01-01';
+
+ TODO: avoid conversion of any values with
+ repertoire ASCII and 7bit-ASCII-compatible,
+ not only numeric/datetime origin.
+ */
+ if (collation.derivation == DERIVATION_NUMERIC &&
+ collation.repertoire == MY_REPERTOIRE_ASCII &&
+ !(collation.collation->state & MY_CS_NONASCII) &&
+ !(tocs->state & MY_CS_NONASCII))
+ return false;
+ return true;
+ }
+ bool needs_charset_converter(CHARSET_INFO *tocs)
+ {
+ // Pass 1 as length to force conversion if tocs->mbminlen>1.
+ return needs_charset_converter(1, tocs);
+ }
+ Item *const_charset_converter(CHARSET_INFO *tocs, bool lossless,
+ const char *func_name);
+ Item *const_charset_converter(CHARSET_INFO *tocs, bool lossless)
+ { return const_charset_converter(tocs, lossless, NULL); }
void delete_self()
{
cleanup();
@@ -1477,7 +1566,7 @@ public:
{
if (is_expensive_cache < 0)
is_expensive_cache= walk(&Item::is_expensive_processor, 0, (uchar*)0);
- return test(is_expensive_cache);
+ return MY_TEST(is_expensive_cache);
}
virtual Field::geometry_type get_geometry_type() const
{ return Field::GEOM_GEOMETRY; };
@@ -1541,6 +1630,15 @@ public:
virtual void get_cache_parameters(List<Item> &parameters) { };
virtual void mark_as_condition_AND_part(TABLE_LIST *embedding) {};
+
+ /* how much position should be reserved for Exists2In transformation */
+ virtual uint exists2in_reserved_items() { return 0; };
+
+ /**
+ Inform the item that it is located under a NOT, which is a top-level item.
+ */
+ virtual void under_not(Item_func_not * upper
+ __attribute__((unused))) {};
};
@@ -1577,12 +1675,102 @@ public:
};
class sp_head;
+class Item_string;
-class Item_basic_constant :public Item
+
+/**
+ A common class for Item_basic_constant and Item_param
+*/
+class Item_basic_value :public Item
+{
+ bool is_basic_value(const Item *item, Type type_arg) const
+ {
+ return item->basic_const_item() && item->type() == type_arg;
+ }
+ bool is_basic_value(Type type_arg) const
+ {
+ return basic_const_item() && type() == type_arg;
+ }
+ bool str_eq(const String *value,
+ const String *other, CHARSET_INFO *cs, bool binary_cmp) const
+ {
+ return binary_cmp ?
+ value->bin_eq(other) :
+ collation.collation == cs && value->eq(other, collation.collation);
+ }
+
+protected:
+ // Value metadata, e.g. to make string processing easier
+ class Metadata: private MY_STRING_METADATA
+ {
+ public:
+ Metadata(const String *str)
+ {
+ my_string_metadata_get(this, str->charset(), str->ptr(), str->length());
+ }
+ Metadata(const String *str, uint repertoire)
+ {
+ MY_STRING_METADATA::repertoire= repertoire;
+ MY_STRING_METADATA::char_length= str->numchars();
+ }
+ uint repertoire() const { return MY_STRING_METADATA::repertoire; }
+ size_t char_length() const { return MY_STRING_METADATA::char_length; }
+ };
+ void fix_charset_and_length_from_str_value(Derivation dv, Metadata metadata)
+ {
+ /*
+ We have to have a different max_length than 'length' here to
+ ensure that we get the right length if we do use the item
+ to create a new table. In this case max_length must be the maximum
+ number of chars for a string of this type because we in Create_field::
+ divide the max_length with mbmaxlen).
+ */
+ collation.set(str_value.charset(), dv, metadata.repertoire());
+ fix_char_length(metadata.char_length());
+ decimals= NOT_FIXED_DEC;
+ }
+ void fix_charset_and_length_from_str_value(Derivation dv)
+ {
+ fix_charset_and_length_from_str_value(dv, Metadata(&str_value));
+ }
+ Item_basic_value(): Item() {}
+ /*
+ In the xxx_eq() methods below we need to cast off "const" to
+ call val_xxx(). This is OK for Item_basic_constant and Item_param.
+ */
+ bool null_eq(const Item *item) const
+ {
+ DBUG_ASSERT(is_basic_value(NULL_ITEM));
+ return item->type() == NULL_ITEM;
+ }
+ bool str_eq(const String *value, const Item *item, bool binary_cmp) const
+ {
+ DBUG_ASSERT(is_basic_value(STRING_ITEM));
+ return is_basic_value(item, STRING_ITEM) &&
+ str_eq(value, ((Item_basic_value*)item)->val_str(NULL),
+ item->collation.collation, binary_cmp);
+ }
+ bool real_eq(double value, const Item *item) const
+ {
+ DBUG_ASSERT(is_basic_value(REAL_ITEM));
+ return is_basic_value(item, REAL_ITEM) &&
+ value == ((Item_basic_value*)item)->val_real();
+ }
+ bool int_eq(longlong value, const Item *item) const
+ {
+ DBUG_ASSERT(is_basic_value(INT_ITEM));
+ return is_basic_value(item, INT_ITEM) &&
+ value == ((Item_basic_value*)item)->val_int() &&
+ (value >= 0 || item->unsigned_flag == unsigned_flag);
+ }
+};
+
+
+class Item_basic_constant :public Item_basic_value
{
table_map used_table_map;
public:
- Item_basic_constant(): Item(), used_table_map(0) {};
+ Item_basic_constant(): Item_basic_value(), used_table_map(0) {};
void set_used_tables(table_map map) { used_table_map= map; }
table_map used_tables() const { return used_table_map; }
/* to prevent drop fixed flag (no need parent cleanup call) */
@@ -2076,7 +2264,8 @@ public:
void fix_after_pullout(st_select_lex *new_parent, Item **ref);
void make_field(Send_field *tmp_field);
int save_in_field(Field *field,bool no_conversions);
- void save_org_in_field(Field *field);
+ void save_org_in_field(Field *field, fast_field_copier optimizer_data);
+ fast_field_copier setup_fast_field_copier(Field *field);
table_map used_tables() const;
table_map all_used_tables() const;
enum Item_result result_type () const
@@ -2144,8 +2333,7 @@ public:
bool set_no_const_sub(uchar *arg);
Item *replace_equal_field(uchar *arg);
inline uint32 max_disp_length() { return field->max_display_length(); }
- Item_field *filed_for_view_update() { return this; }
- Item *safe_charset_converter(CHARSET_INFO *tocs);
+ Item_field *field_for_view_update() { return this; }
int fix_outer_field(THD *thd, Field **field, Item **reference);
virtual Item *update_value_transformer(uchar *select_arg);
virtual void print(String *str, enum_query_type query_type);
@@ -2169,16 +2357,16 @@ public:
class Item_null :public Item_basic_constant
{
public:
- Item_null(char *name_par=0)
+ Item_null(char *name_par=0, CHARSET_INFO *cs= &my_charset_bin)
{
maybe_null= null_value= TRUE;
max_length= 0;
name= name_par ? name_par : (char*) "NULL";
fixed= 1;
- collation.set(&my_charset_bin, DERIVATION_IGNORABLE);
+ collation.set(cs, DERIVATION_IGNORABLE);
}
enum Type type() const { return NULL_ITEM; }
- bool eq(const Item *item, bool binary_cmp) const;
+ bool eq(const Item *item, bool binary_cmp) const { return null_eq(item); }
double val_real();
longlong val_int();
String *val_str(String *str);
@@ -2221,13 +2409,9 @@ public:
/* Item represents one placeholder ('?') of prepared statement */
-class Item_param :public Item,
+class Item_param :public Item_basic_value,
private Settable_routine_parameter
{
- char cnvbuf[MAX_FIELD_WIDTH];
- String cnvstr;
- Item *cnvitem;
-
public:
enum enum_item_param_state
{
@@ -2295,7 +2479,6 @@ public:
Item_param(uint pos_in_query_arg);
enum Item_result result_type () const { return item_result_type; }
- enum Item_result cast_to_int_type() const { return item_result_type; }
enum Type type() const { return item_type; }
enum_field_types field_type() const { return param_type; }
@@ -2411,8 +2594,9 @@ public:
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)); }
- bool eq(const Item *, bool binary_cmp) const;
+ { return (uint) (max_length - MY_TEST(value < 0)); }
+ bool eq(const Item *item, bool binary_cmp) const
+ { return int_eq(value, item); }
bool check_partition_func_processor(uchar *bool_arg) { return FALSE;}
bool check_vcol_func_processor(uchar *arg) { return FALSE;}
};
@@ -2533,7 +2717,8 @@ public:
{ return new Item_float(name, value, decimals, max_length); }
Item_num *neg() { value= -value; return this; }
virtual void print(String *str, enum_query_type query_type);
- bool eq(const Item *, bool binary_cmp) const;
+ bool eq(const Item *item, bool binary_cmp) const
+ { return real_eq(value, item); }
};
@@ -2551,70 +2736,98 @@ public:
str->append(func_name);
}
- Item *safe_charset_converter(CHARSET_INFO *tocs);
+ Item *safe_charset_converter(CHARSET_INFO *tocs)
+ {
+ return const_charset_converter(tocs, true, func_name);
+ }
};
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)
+ bool m_cs_specified;
+protected:
+ /**
+ 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)
{
- str_value.set_or_copy_aligned(str, length, cs);
- collation.set(cs, dv, repertoire);
- /*
- We have to have a different max_length than 'length' here to
- ensure that we get the right length if we do use the item
- to create a new table. In this case max_length must be the maximum
- number of chars for a string of this type because we in Create_field::
- divide the max_length with mbmaxlen).
- */
- max_length= str_value.numchars()*cs->mbmaxlen;
- set_name(str, length, cs);
- decimals=NOT_FIXED_DEC;
+ m_cs_specified= cs_specified;
+ }
+ void fix_from_value(Derivation dv, const Metadata metadata)
+ {
+ fix_charset_and_length_from_str_value(dv, metadata);
// it is constant => can be used without fix_fields (and frequently used)
fixed= 1;
}
+ void fix_and_set_name_from_value(Derivation dv, const Metadata metadata)
+ {
+ fix_from_value(dv, metadata);
+ set_name(str_value.ptr(), str_value.length(), str_value.charset());
+ }
+protected:
/* 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;
- set_name(NULL, 0, cs);
+ set_name(NULL, 0, system_charset_info);
decimals= NOT_FIXED_DEC;
fixed= 1;
}
- Item_string(const char *name_par, const char *str, uint length,
- CHARSET_INFO *cs, Derivation dv= DERIVATION_COERCIBLE,
- uint repertoire= MY_REPERTOIRE_UNICODE30)
+public:
+ // Constructors with the item name set from its value
+ Item_string(const char *str, uint length, CHARSET_INFO *cs,
+ Derivation dv, uint repertoire)
: m_cs_specified(FALSE)
{
str_value.set_or_copy_aligned(str, length, cs);
- collation.set(cs, dv, repertoire);
- max_length= str_value.numchars()*cs->mbmaxlen;
- set_name(name_par, 0, cs);
- decimals=NOT_FIXED_DEC;
- // it is constant => can be used without fix_fields (and frequently used)
- fixed= 1;
+ fix_and_set_name_from_value(dv, Metadata(&str_value, repertoire));
}
- /*
- This is used in stored procedures to avoid memory leaks and
- does a deep copy of its argument.
- */
- void set_str_with_copy(const char *str_arg, uint length_arg)
+ Item_string(const char *str, uint length,
+ CHARSET_INFO *cs, Derivation dv= DERIVATION_COERCIBLE)
+ : m_cs_specified(FALSE)
{
- str_value.copy(str_arg, length_arg, collation.collation);
- max_length= str_value.numchars() * collation.collation->mbmaxlen;
+ str_value.set_or_copy_aligned(str, length, cs);
+ fix_and_set_name_from_value(dv, Metadata(&str_value));
+ }
+ Item_string(const String *str, CHARSET_INFO *tocs, uint *conv_errors,
+ Derivation dv, uint repertoire)
+ :m_cs_specified(false)
+ {
+ if (str_value.copy(str, tocs, conv_errors))
+ str_value.set("", 0, tocs); // EOM ?
+ str_value.mark_as_const();
+ fix_and_set_name_from_value(dv, Metadata(&str_value, repertoire));
+ }
+ // Constructors with an externally provided item name
+ Item_string(const char *name_par, const char *str, uint length,
+ CHARSET_INFO *cs, Derivation dv= DERIVATION_COERCIBLE)
+ :m_cs_specified(false)
+ {
+ str_value.set_or_copy_aligned(str, length, cs);
+ fix_from_value(dv, Metadata(&str_value));
+ set_name(name_par, 0, system_charset_info);
+ }
+ Item_string(const char *name_par, const char *str, uint length,
+ CHARSET_INFO *cs, Derivation dv, uint repertoire)
+ :m_cs_specified(false)
+ {
+ str_value.set_or_copy_aligned(str, length, cs);
+ fix_from_value(dv, Metadata(&str_value, repertoire));
+ set_name(name_par, 0, system_charset_info);
}
- void set_repertoire_from_value()
+ void print_value(String *to) const
{
- collation.repertoire= my_string_repertoire(str_value.charset(),
- str_value.ptr(),
- str_value.length());
+ str_value.print(to);
}
enum Type type() const { return STRING_ITEM; }
double val_real();
@@ -2629,13 +2842,19 @@ public:
enum Item_result result_type () const { return STRING_RESULT; }
enum_field_types field_type() const { return MYSQL_TYPE_VARCHAR; }
bool basic_const_item() const { return 1; }
- bool eq(const Item *item, bool binary_cmp) const;
+ bool eq(const Item *item, bool binary_cmp) const
+ {
+ return str_eq(&str_value, item, binary_cmp);
+ }
Item *clone_item()
{
return new Item_string(name, str_value.ptr(),
- str_value.length(), collation.collation);
+ str_value.length(), collation.collation);
+ }
+ Item *safe_charset_converter(CHARSET_INFO *tocs)
+ {
+ return const_charset_converter(tocs, true);
}
- Item *safe_charset_converter(CHARSET_INFO *tocs);
inline void append(char *str, uint length)
{
str_value.append(str, length);
@@ -2669,23 +2888,80 @@ public:
return m_cs_specified;
}
- /**
- Set the value of m_cs_specified attribute.
+ String *check_well_formed_result(bool send_error)
+ { return Item::check_well_formed_result(&str_value, send_error); }
- 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.
+ enum_field_types odbc_temporal_literal_type(const LEX_STRING *type_str) const
+ {
+ /*
+ If string is a reasonably short pure ASCII string literal,
+ try to parse known ODBC style date, time or timestamp literals,
+ e.g:
+ SELECT {d'2001-01-01'};
+ SELECT {t'10:20:30'};
+ SELECT {ts'2001-01-01 10:20:30'};
+ */
+ if (collation.repertoire == MY_REPERTOIRE_ASCII &&
+ str_value.length() < MAX_DATE_STRING_REP_LENGTH * 4)
+ {
+ if (type_str->length == 1)
+ {
+ if (type_str->str[0] == 'd') /* {d'2001-01-01'} */
+ return MYSQL_TYPE_DATE;
+ else if (type_str->str[0] == 't') /* {t'10:20:30'} */
+ return MYSQL_TYPE_TIME;
+ }
+ else if (type_str->length == 2) /* {ts'2001-01-01 10:20:30'} */
+ {
+ if (type_str->str[0] == 't' && type_str->str[1] == 's')
+ return MYSQL_TYPE_DATETIME;
+ }
+ }
+ return MYSQL_TYPE_STRING; // Not a temporal literal
+ }
+};
- This operation is to be called from the parser during parsing an input
- query.
- */
- inline void set_cs_specified(bool cs_specified)
+
+class Item_string_with_introducer :public Item_string
+{
+public:
+ Item_string_with_introducer(const char *str, uint length, CHARSET_INFO *cs)
+ :Item_string(str, length, cs)
{
- m_cs_specified= cs_specified;
+ set_cs_specified(true);
+ }
+ Item_string_with_introducer(const char *name,
+ const char *str, uint length, CHARSET_INFO *tocs)
+ :Item_string(name, str, length, tocs)
+ {
+ set_cs_specified(true);
}
+};
-private:
- bool m_cs_specified;
+
+class Item_string_sys :public Item_string
+{
+public:
+ Item_string_sys(const char *str, uint length)
+ :Item_string(str, length, system_charset_info)
+ { }
+ Item_string_sys(const char *str)
+ :Item_string(str, strlen(str), system_charset_info)
+ { }
+};
+
+
+class Item_string_ascii :public Item_string
+{
+public:
+ Item_string_ascii(const char *str, uint length)
+ :Item_string(str, length, &my_charset_latin1,
+ DERIVATION_COERCIBLE, MY_REPERTOIRE_ASCII)
+ { }
+ Item_string_ascii(const char *str)
+ :Item_string(str, strlen(str), &my_charset_latin1,
+ DERIVATION_COERCIBLE, MY_REPERTOIRE_ASCII)
+ { }
};
@@ -2705,7 +2981,17 @@ public:
Derivation dv= DERIVATION_COERCIBLE)
:Item_string(NullS, str, length, cs, dv), func_name(name_par)
{}
- Item *safe_charset_converter(CHARSET_INFO *tocs);
+ Item_static_string_func(const char *name_par,
+ const String *str,
+ CHARSET_INFO *tocs, uint *conv_errors,
+ Derivation dv, uint repertoire)
+ :Item_string(str, tocs, conv_errors, dv, repertoire),
+ func_name(name_par)
+ {}
+ Item *safe_charset_converter(CHARSET_INFO *tocs)
+ {
+ return const_charset_converter(tocs, true, func_name);
+ }
virtual inline void print(String *str, enum_query_type query_type)
{
@@ -2808,11 +3094,19 @@ public:
enum Type type() const { return VARBIN_ITEM; }
enum Item_result result_type () const { return STRING_RESULT; }
enum_field_types field_type() const { return MYSQL_TYPE_VARCHAR; }
- virtual Item *safe_charset_converter(CHARSET_INFO *tocs);
+ virtual Item *safe_charset_converter(CHARSET_INFO *tocs)
+ {
+ return const_charset_converter(tocs, true);
+ }
bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
bool check_vcol_func_processor(uchar *arg) { return FALSE;}
bool basic_const_item() const { return 1; }
- bool eq(const Item *item, bool binary_cmp) const;
+ bool eq(const Item *item, bool binary_cmp) const
+ {
+ return item->basic_const_item() && item->type() == type() &&
+ item->cast_to_int_type() == cast_to_int_type() &&
+ str_value.bin_eq(&((Item_hex_constant*)item)->str_value);
+ }
String *val_str(String*) { DBUG_ASSERT(fixed == 1); return &str_value; }
};
@@ -2900,6 +3194,121 @@ public:
Item_bin_string(const char *str,uint str_length);
};
+
+class Item_temporal_literal :public Item_basic_constant
+{
+protected:
+ MYSQL_TIME cached_time;
+public:
+ /**
+ Constructor for Item_date_literal.
+ @param ltime DATE value.
+ */
+ Item_temporal_literal(MYSQL_TIME *ltime) :Item_basic_constant()
+ {
+ collation.set(&my_charset_numeric, DERIVATION_NUMERIC, MY_REPERTOIRE_ASCII);
+ decimals= 0;
+ cached_time= *ltime;
+ }
+ Item_temporal_literal(MYSQL_TIME *ltime, uint dec_arg) :Item_basic_constant()
+ {
+ collation.set(&my_charset_numeric, DERIVATION_NUMERIC, MY_REPERTOIRE_ASCII);
+ decimals= dec_arg;
+ cached_time= *ltime;
+ }
+ bool basic_const_item() const { return true; }
+ bool const_item() const { return true; }
+ enum Type type() const { return DATE_ITEM; }
+ bool eq(const Item *item, bool binary_cmp) const;
+ enum Item_result result_type () const { return STRING_RESULT; }
+ Item_result cmp_type() const { return TIME_RESULT; }
+
+ bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
+ bool check_vcol_func_processor(uchar *arg) { return FALSE;}
+
+ bool is_null()
+ { return is_null_from_temporal(); }
+ bool get_date_with_sql_mode(MYSQL_TIME *to);
+ String *val_str(String *str)
+ { return val_string_from_date(str); }
+ longlong val_int()
+ { return val_int_from_date(); }
+ double val_real()
+ { return val_real_from_date(); }
+ my_decimal *val_decimal(my_decimal *decimal_value)
+ { return val_decimal_from_date(decimal_value); }
+ Field *tmp_table_field(TABLE *table)
+ { return tmp_table_field_from_field_type(table, 0); }
+ int save_in_field(Field *field, bool no_conversions)
+ { return save_date_in_field(field); }
+};
+
+
+/**
+ DATE'2010-01-01'
+*/
+class Item_date_literal: public Item_temporal_literal
+{
+public:
+ Item_date_literal(MYSQL_TIME *ltime)
+ :Item_temporal_literal(ltime)
+ {
+ max_length= MAX_DATE_WIDTH;
+ fixed= 1;
+ /*
+ If date has zero month or day, it can return NULL in case of
+ NO_ZERO_DATE or NO_ZERO_IN_DATE.
+ We can't just check the current sql_mode here in constructor,
+ because sql_mode can change in case of prepared statements
+ between PREPARE and EXECUTE.
+ */
+ maybe_null= !ltime->month || !ltime->day;
+ }
+ enum_field_types field_type() const { return MYSQL_TYPE_DATE; }
+ void print(String *str, enum_query_type query_type);
+ bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date);
+};
+
+
+/**
+ TIME'10:10:10'
+*/
+class Item_time_literal: public Item_temporal_literal
+{
+public:
+ Item_time_literal(MYSQL_TIME *ltime, uint dec_arg)
+ :Item_temporal_literal(ltime, dec_arg)
+ {
+ max_length= MIN_TIME_WIDTH + (decimals ? decimals + 1 : 0);
+ fixed= 1;
+ }
+ enum_field_types field_type() const { return MYSQL_TYPE_TIME; }
+ void print(String *str, enum_query_type query_type);
+ bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date);
+};
+
+
+/**
+ TIMESTAMP'2001-01-01 10:20:30'
+*/
+class Item_datetime_literal: public Item_temporal_literal
+{
+public:
+ Item_datetime_literal(MYSQL_TIME *ltime, uint dec_arg)
+ :Item_temporal_literal(ltime, dec_arg)
+ {
+ max_length= MAX_DATETIME_WIDTH + (decimals ? decimals + 1 : 0);
+ fixed= 1;
+ // See the comment on maybe_null in Item_date_literal
+ maybe_null= !ltime->month || !ltime->day;
+ }
+ enum_field_types field_type() const { return MYSQL_TYPE_DATETIME; }
+ void print(String *str, enum_query_type query_type);
+ bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date);
+};
+
+
+
class Item_result_field :public Item /* Item with result field */
{
public:
@@ -3008,7 +3417,9 @@ public:
bool fix_fields(THD *, Item **);
void fix_after_pullout(st_select_lex *new_parent, Item **ref);
int save_in_field(Field *field, bool no_conversions);
- void save_org_in_field(Field *field);
+ void save_org_in_field(Field *field, fast_field_copier optimizer_data);
+ fast_field_copier setup_fast_field_copier(Field *field)
+ { return (*ref)->setup_fast_field_copier(field); }
enum Item_result result_type () const { return (*ref)->result_type(); }
enum_field_types field_type() const { return (*ref)->field_type(); }
Field *get_tmp_table_field()
@@ -3057,8 +3468,8 @@ public:
}
virtual void print(String *str, enum_query_type query_type);
void cleanup();
- Item_field *filed_for_view_update()
- { return (*ref)->filed_for_view_update(); }
+ Item_field *field_for_view_update()
+ { return (*ref)->field_for_view_update(); }
virtual Ref_Type ref_type() { return REF; }
// Row emulation: forwarding of ROW-related calls to ref
@@ -3133,6 +3544,13 @@ public:
alias_name_used_arg)
{}
+ bool fix_fields(THD *thd, Item **it)
+ {
+ if ((!(*ref)->fixed && (*ref)->fix_fields(thd, ref)) ||
+ (*ref)->check_cols(1))
+ return TRUE;
+ return Item_ref::fix_fields(thd, it);
+ }
void save_val(Field *to);
double val_real();
longlong val_int();
@@ -3230,7 +3648,8 @@ public:
bool is_null();
bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate);
bool send(Protocol *protocol, String *buffer);
- void save_org_in_field(Field *field)
+ void save_org_in_field(Field *field,
+ fast_field_copier data __attribute__ ((__unused__)))
{
save_val(field);
}
@@ -3271,8 +3690,8 @@ public:
}
bool enumerate_field_refs_processor(uchar *arg)
{ return orig_item->enumerate_field_refs_processor(arg); }
- Item_field *filed_for_view_update()
- { return orig_item->filed_for_view_update(); }
+ Item_field *field_for_view_update()
+ { return orig_item->field_for_view_update(); }
/* Row emulation: forwarding of ROW-related calls to orig_item */
uint cols()
@@ -3351,6 +3770,8 @@ public:
bool eq(const Item *item, bool binary_cmp) const;
Item *get_tmp_table_item(THD *thd)
{
+ if (const_item())
+ return copy_or_same(thd);
Item *item= Item_ref::get_tmp_table_item(thd);
item->name= name;
return item;
@@ -3437,7 +3858,8 @@ public:
return Item_direct_ref::get_date(ltime, fuzzydate);
}
bool send(Protocol *protocol, String *buffer);
- void save_org_in_field(Field *field)
+ void save_org_in_field(Field *field,
+ fast_field_copier data __attribute__ ((__unused__)))
{
if (check_null_ref())
field->set_null();
@@ -3493,7 +3915,7 @@ public:
{
ref= &outer_ref;
set_properties();
- fixed= 0;
+ fixed= 0; /* reset flag set in set_properties() */
}
Item_outer_ref(Name_resolution_context *context_arg, Item **item,
const char *table_name_arg, const char *field_name_arg,
@@ -3504,7 +3926,7 @@ public:
{}
void save_in_result_field(bool no_conversions)
{
- outer_ref->save_org_in_field(result_field);
+ outer_ref->save_org_in_field(result_field, NULL);
}
bool fix_fields(THD *, Item **);
void fix_after_pullout(st_select_lex *new_parent, Item **ref);
@@ -3731,7 +4153,7 @@ public:
}
virtual longlong val_int()
{
- return null_value ? LL(0) : cached_value;
+ return null_value ? 0 : cached_value;
}
virtual void copy();
};
@@ -4135,7 +4557,7 @@ public:
virtual void store(Item *item);
virtual bool cache_value()= 0;
bool basic_const_item() const
- { return test(example && example->basic_const_item());}
+ { return MY_TEST(example && example->basic_const_item()); }
virtual void clear() { null_value= TRUE; value_cached= FALSE; }
bool is_null() { return null_value; }
virtual bool is_expensive()
diff --git a/sql/item_buff.cc b/sql/item_buff.cc
index 86e0fd32774..d1134525f7b 100644
--- a/sql/item_buff.cc
+++ b/sql/item_buff.cc
@@ -22,6 +22,7 @@
Buffers to save and compare item values
*/
+#include <my_global.h>
#include "sql_priv.h"
/*
It is necessary to include set_var.h instead of item.h because there
@@ -71,7 +72,7 @@ Cached_item::~Cached_item() {}
Cached_item_str::Cached_item_str(THD *thd, Item *arg)
:item(arg),
- value_max_length(min(arg->max_length, thd->variables.max_sort_length)),
+ value_max_length(MY_MIN(arg->max_length, thd->variables.max_sort_length)),
value(value_max_length)
{}
@@ -81,7 +82,7 @@ bool Cached_item_str::cmp(void)
bool tmp;
if ((res=item->val_str(&tmp_value)))
- res->length(min(res->length(), value_max_length));
+ res->length(MY_MIN(res->length(), value_max_length));
if (null_value != item->null_value)
{
if ((null_value= item->null_value))
@@ -173,12 +174,3 @@ bool Cached_item_decimal::cmp()
return FALSE;
}
-
-/*****************************************************************************
-** Instansiate templates
-*****************************************************************************/
-
-#ifdef HAVE_EXPLICIT_TEMPLATE_INSTANTIATION
-template class List<Cached_item>;
-template class List_iterator<Cached_item>;
-#endif
diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc
index 81688f3321c..b2c580db507 100644
--- a/sql/item_cmpfunc.cc
+++ b/sql/item_cmpfunc.cc
@@ -26,6 +26,7 @@
#pragma implementation // gcc: Class implementation
#endif
+#include <my_global.h>
#include "sql_priv.h"
#include <m_ctype.h>
#include "sql_select.h"
@@ -503,7 +504,7 @@ static bool convert_const_to_int(THD *thd, Item_field *field_item,
if (0 == field_cmp)
{
Item *tmp= new Item_int_with_ref(field->val_int(), *item,
- test(field->flags & UNSIGNED_FLAG));
+ MY_TEST(field->flags & UNSIGNED_FLAG));
if (tmp)
thd->change_item_tree(item, tmp);
result= 1; // Item was replaced
@@ -620,17 +621,6 @@ int Arg_comparator::set_compare_func(Item_result_field *item, Item_result type)
}
case STRING_RESULT:
{
- /*
- We must set cmp_charset here as we may be called from for an automatic
- generated item, like in natural join
- */
- if (cmp_collation.set((*a)->collation, (*b)->collation) ||
- cmp_collation.derivation == DERIVATION_NONE)
- {
- my_coll_agg_error((*a)->collation, (*b)->collation,
- owner->func_name());
- return 1;
- }
if (cmp_collation.collation == &my_charset_bin)
{
/*
@@ -679,7 +669,7 @@ int Arg_comparator::set_compare_func(Item_result_field *item, Item_result type)
{
if ((*a)->decimals < NOT_FIXED_DEC && (*b)->decimals < NOT_FIXED_DEC)
{
- precision= 5 / log_10[max((*a)->decimals, (*b)->decimals) + 1];
+ precision= 5 / log_10[MY_MAX((*a)->decimals, (*b)->decimals) + 1];
if (func == &Arg_comparator::compare_real)
func= &Arg_comparator::compare_real_fixed;
else if (func == &Arg_comparator::compare_e_real)
@@ -721,32 +711,32 @@ bool get_mysql_time_from_str(THD *thd, String *str, timestamp_type warn_type,
const char *warn_name, MYSQL_TIME *l_time)
{
bool value;
- int error;
- enum_mysql_timestamp_type timestamp_type;
+ MYSQL_TIME_STATUS status;
int flags= TIME_FUZZY_DATES | MODE_INVALID_DATES;
ErrConvString err(str);
- if (warn_type == MYSQL_TIMESTAMP_TIME)
- flags|= TIME_TIME_ONLY;
-
- timestamp_type=
- str_to_datetime(str->charset(), str->ptr(), str->length(),
- l_time, flags, &error);
+ DBUG_ASSERT(warn_type != MYSQL_TIMESTAMP_TIME);
- if (timestamp_type > MYSQL_TIMESTAMP_ERROR)
+ if (!str_to_datetime(str->charset(), str->ptr(), str->length(),
+ l_time, flags, &status))
+ {
+ DBUG_ASSERT(l_time->time_type == MYSQL_TIMESTAMP_DATETIME ||
+ l_time->time_type == MYSQL_TIMESTAMP_DATE);
/*
Do not return yet, we may still want to throw a "trailing garbage"
warning.
*/
value= FALSE;
+ }
else
{
+ DBUG_ASSERT(l_time->time_type != MYSQL_TIMESTAMP_TIME);
+ DBUG_ASSERT(status.warnings != 0); // Must be set by set_to_datetime()
value= TRUE;
- error= 1; /* force warning */
}
- if (error > 0)
- make_truncated_value_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ if (status.warnings > 0)
+ make_truncated_value_warning(thd, Sql_condition::WARN_LEVEL_WARN,
&err, warn_type, warn_name);
return value;
@@ -754,6 +744,37 @@ bool get_mysql_time_from_str(THD *thd, String *str, timestamp_type warn_type,
/**
+ Aggregate comparator argument charsets for comparison.
+ One of the arguments ("a" or "b") can be replaced,
+ typically by Item_string or Item_func_conv_charset.
+
+ @return Aggregation result
+ @retval false - if no conversion is needed,
+ or if one of the arguments was converted
+ @retval true - on error, if arguments are not comparable.
+
+ TODO: get rid of this method eventually and refactor the calling code.
+ Argument conversion should happen on the Item_func level.
+ Arg_comparator should get comparable arguments.
+*/
+bool Arg_comparator::agg_arg_charsets_for_comparison()
+{
+ if (cmp_collation.set((*a)->collation, (*b)->collation, MY_COLL_CMP_CONV) ||
+ cmp_collation.derivation == DERIVATION_NONE)
+ {
+ my_coll_agg_error((*a)->collation, (*b)->collation, owner->func_name());
+ return true;
+ }
+ if (agg_item_set_converter(cmp_collation, owner->func_name(),
+ a, 1, MY_COLL_CMP_CONV, 1) ||
+ agg_item_set_converter(cmp_collation, owner->func_name(),
+ b, 1, MY_COLL_CMP_CONV, 1))
+ return true;
+ return false;
+}
+
+
+/**
Prepare the comparator (set the comparison function) for comparing
items *a1 and *a2 in the context of 'type'.
@@ -780,10 +801,11 @@ int Arg_comparator::set_cmp_func(Item_result_field *owner_arg,
(*a)->result_type() == STRING_RESULT &&
(*b)->result_type() == STRING_RESULT)
{
- DTCollation coll;
- coll.set((*a)->collation.collation);
- if (agg_item_set_converter(coll, owner->func_name(),
- b, 1, MY_COLL_CMP_CONV, 1))
+ /*
+ We must set cmp_collation here as we may be called from for an automatic
+ generated item, like in natural join
+ */
+ if (agg_arg_charsets_for_comparison())
return 1;
}
if (type == INT_RESULT &&
@@ -900,9 +922,11 @@ get_datetime_value(THD *thd, Item ***item_arg, Item **cache_arg,
{
MYSQL_TIME ltime;
uint fuzzydate= TIME_FUZZY_DATES | TIME_INVALID_DATES;
- if (f_type == MYSQL_TYPE_TIME)
- fuzzydate|= TIME_TIME_ONLY;
- if (item->get_date(&ltime, fuzzydate))
+ if ((item->field_type() == MYSQL_TYPE_TIME &&
+ is_temporal_type_with_date(warn_item->field_type())) ?
+ item->get_date_with_conversion(&ltime, fuzzydate) :
+ item->get_date(&ltime, fuzzydate |
+ (f_type == MYSQL_TYPE_TIME ? TIME_TIME_ONLY : 0)))
value= 0; /* invalid date */
else
value= pack_time(&ltime);
@@ -1023,7 +1047,7 @@ int Arg_comparator::compare_binary_string()
owner->null_value= 0;
uint res1_length= res1->length();
uint res2_length= res2->length();
- int cmp= memcmp(res1->ptr(), res2->ptr(), min(res1_length,res2_length));
+ int cmp= memcmp(res1->ptr(), res2->ptr(), MY_MIN(res1_length,res2_length));
return cmp ? cmp : (int) (res1_length - res2_length);
}
}
@@ -1044,8 +1068,8 @@ int Arg_comparator::compare_e_string()
res1= (*a)->val_str(&value1);
res2= (*b)->val_str(&value2);
if (!res1 || !res2)
- return test(res1 == res2);
- return test(sortcmp(res1, res2, cmp_collation.collation) == 0);
+ return MY_TEST(res1 == res2);
+ return MY_TEST(sortcmp(res1, res2, cmp_collation.collation) == 0);
}
@@ -1055,8 +1079,8 @@ int Arg_comparator::compare_e_binary_string()
res1= (*a)->val_str(&value1);
res2= (*b)->val_str(&value2);
if (!res1 || !res2)
- return test(res1 == res2);
- return test(stringcmp(res1, res2) == 0);
+ return MY_TEST(res1 == res2);
+ return MY_TEST(stringcmp(res1, res2) == 0);
}
@@ -1111,8 +1135,8 @@ int Arg_comparator::compare_e_real()
double val1= (*a)->val_real();
double val2= (*b)->val_real();
if ((*a)->null_value || (*b)->null_value)
- return test((*a)->null_value && (*b)->null_value);
- return test(val1 == val2);
+ return MY_TEST((*a)->null_value && (*b)->null_value);
+ return MY_TEST(val1 == val2);
}
int Arg_comparator::compare_e_decimal()
@@ -1121,8 +1145,8 @@ int Arg_comparator::compare_e_decimal()
my_decimal *val1= (*a)->val_decimal(&decimal1);
my_decimal *val2= (*b)->val_decimal(&decimal2);
if ((*a)->null_value || (*b)->null_value)
- return test((*a)->null_value && (*b)->null_value);
- return test(my_decimal_cmp(val1, val2) == 0);
+ return MY_TEST((*a)->null_value && (*b)->null_value);
+ return MY_TEST(my_decimal_cmp(val1, val2) == 0);
}
@@ -1160,8 +1184,8 @@ int Arg_comparator::compare_e_real_fixed()
double val1= (*a)->val_real();
double val2= (*b)->val_real();
if ((*a)->null_value || (*b)->null_value)
- return test((*a)->null_value && (*b)->null_value);
- return test(val1 == val2 || fabs(val1 - val2) < precision);
+ return MY_TEST((*a)->null_value && (*b)->null_value);
+ return MY_TEST(val1 == val2 || fabs(val1 - val2) < precision);
}
@@ -1272,8 +1296,8 @@ int Arg_comparator::compare_e_int()
longlong val1= (*a)->val_int();
longlong val2= (*b)->val_int();
if ((*a)->null_value || (*b)->null_value)
- return test((*a)->null_value && (*b)->null_value);
- return test(val1 == val2);
+ return MY_TEST((*a)->null_value && (*b)->null_value);
+ return MY_TEST(val1 == val2);
}
/**
@@ -1284,8 +1308,8 @@ int Arg_comparator::compare_e_int_diff_signedness()
longlong val1= (*a)->val_int();
longlong val2= (*b)->val_int();
if ((*a)->null_value || (*b)->null_value)
- return test((*a)->null_value && (*b)->null_value);
- return (val1 >= 0) && test(val1 == val2);
+ return MY_TEST((*a)->null_value && (*b)->null_value);
+ return (val1 >= 0) && MY_TEST(val1 == val2);
}
int Arg_comparator::compare_row()
@@ -1439,7 +1463,7 @@ bool Item_in_optimizer::eval_not_null_tables(uchar *opt_arg)
}
-bool Item_in_optimizer::fix_left(THD *thd, Item **ref)
+bool Item_in_optimizer::fix_left(THD *thd)
{
DBUG_ENTER("Item_in_optimizer::fix_left");
/*
@@ -1520,6 +1544,13 @@ bool Item_in_optimizer::fix_left(THD *thd, Item **ref)
cache->store(args[0]);
cache->cache_value();
}
+ if (args[1]->fixed)
+ {
+ /* to avoid overriding is called to update left expression */
+ used_tables_cache|= args[1]->used_tables();
+ with_sum_func= with_sum_func || args[1]->with_sum_func;
+ const_item_cache= const_item_cache && args[1]->const_item();
+ }
DBUG_RETURN(0);
}
@@ -1537,15 +1568,16 @@ bool Item_in_optimizer::fix_fields(THD *thd, Item **ref)
if (args[1]->type() == Item::SUBSELECT_ITEM)
sub= (Item_subselect *)args[1];
- if (fix_left(thd, ref))
+ if (fix_left(thd))
return TRUE;
if (args[0]->maybe_null)
maybe_null=1;
if (!args[1]->fixed && args[1]->fix_fields(thd, args+1))
return TRUE;
- if ((sub && ((col= args[0]->cols()) != sub->engine->cols())) ||
- (!sub && (args[1]->cols() != (col= 1))))
+ if (!invisible_mode() &&
+ ((sub && ((col= args[0]->cols()) != sub->engine->cols())) ||
+ (!sub && (args[1]->cols() != (col= 1)))))
{
my_error(ER_OPERAND_COLUMNS, MYF(0), col);
return TRUE;
@@ -1561,6 +1593,30 @@ bool Item_in_optimizer::fix_fields(THD *thd, Item **ref)
return FALSE;
}
+/**
+ Check if Item_in_optimizer should work as a pass-through item for its
+ arguments.
+
+ @note
+ Item_in_optimizer should work as pass-through for
+ - subqueries that were processed by ALL/ANY->MIN/MAX rewrite
+ - subqueries taht were originally EXISTS subqueries (and were coverted by
+ the EXISTS->IN rewrite)
+
+ When Item_in_optimizer is not not working as a pass-through, it
+ - caches its "left argument", args[0].
+ - makes adjustments to subquery item's return value for proper NULL
+ value handling
+*/
+
+bool Item_in_optimizer::invisible_mode()
+{
+ /* MAX/MIN transformed or EXISTS->IN prepared => do nothing */
+ return (args[1]->type() != Item::SUBSELECT_ITEM ||
+ ((Item_subselect *)args[1])->substype() ==
+ Item_subselect::EXISTS_SUBS);
+}
+
/**
Add an expression cache for this subquery if it is needed
@@ -1584,8 +1640,9 @@ Item *Item_in_optimizer::expr_cache_insert_transformer(uchar *thd_arg)
{
THD *thd= (THD*) thd_arg;
DBUG_ENTER("Item_in_optimizer::expr_cache_insert_transformer");
- if (args[1]->type() != Item::SUBSELECT_ITEM)
- DBUG_RETURN(this); // MAX/MIN transformed => do nothing
+
+ if (invisible_mode())
+ DBUG_RETURN(this);
if (expr_cache)
DBUG_RETURN(expr_cache);
@@ -1608,13 +1665,16 @@ Item *Item_in_optimizer::expr_cache_insert_transformer(uchar *thd_arg)
void Item_in_optimizer::get_cache_parameters(List<Item> &parameters)
{
/* Add left expression to the list of the parameters of the subquery */
- if (args[0]->cols() == 1)
- parameters.add_unique(args[0], &cmp_items);
- else
+ if (!invisible_mode())
{
- for (uint i= 0; i < args[0]->cols(); i++)
+ if (args[0]->cols() == 1)
+ parameters.add_unique(args[0], &cmp_items);
+ else
{
- parameters.add_unique(args[0]->element_index(i), &cmp_items);
+ for (uint i= 0; i < args[0]->cols(); i++)
+ {
+ parameters.add_unique(args[0]->element_index(i), &cmp_items);
+ }
}
}
args[1]->get_cache_parameters(parameters);
@@ -1697,17 +1757,19 @@ longlong Item_in_optimizer::val_int()
DBUG_ASSERT(fixed == 1);
cache->store(args[0]);
cache->cache_value();
+ DBUG_ENTER(" Item_in_optimizer::val_int");
- if (args[1]->type() != Item::SUBSELECT_ITEM)
+ if (invisible_mode())
{
- /* MAX/MIN transformed => pass through */
longlong res= args[1]->val_int();
null_value= args[1]->null_value;
- return (res);
+ DBUG_PRINT("info", ("pass trough"));
+ DBUG_RETURN(res);
}
if (cache->null_value)
{
+ DBUG_PRINT("info", ("Left NULL..."));
/*
We're evaluating
"<outer_value_list> [NOT] IN (SELECT <inner_value_list>...)"
@@ -1779,11 +1841,11 @@ longlong Item_in_optimizer::val_int()
for (uint i= 0; i < ncols; i++)
item_subs->set_cond_guard_var(i, TRUE);
}
- return 0;
+ DBUG_RETURN(0);
}
tmp= args[1]->val_bool_result();
null_value= args[1]->null_value;
- return tmp;
+ DBUG_RETURN(tmp);
}
@@ -1834,7 +1896,8 @@ bool Item_in_optimizer::is_null()
@retval NULL if an error occurred
*/
-Item *Item_in_optimizer::transform(Item_transformer transformer, uchar *argument)
+Item *Item_in_optimizer::transform(Item_transformer transformer,
+ uchar *argument)
{
Item *new_item;
@@ -1854,7 +1917,7 @@ Item *Item_in_optimizer::transform(Item_transformer transformer, uchar *argument
if ((*args) != new_item)
current_thd->change_item_tree(args, new_item);
- if (args[1]->type() != Item::SUBSELECT_ITEM)
+ if (invisible_mode())
{
/* MAX/MIN transformed => pass through */
new_item= args[1]->transform(transformer, argument);
@@ -1964,14 +2027,14 @@ longlong Item_func_lt::val_int()
longlong Item_func_strcmp::val_int()
{
DBUG_ASSERT(fixed == 1);
- String *a=args[0]->val_str(&cmp.value1);
- String *b=args[1]->val_str(&cmp.value2);
+ String *a= args[0]->val_str(&value1);
+ String *b= args[1]->val_str(&value2);
if (!a || !b)
{
null_value=1;
return 0;
}
- int value= sortcmp(a,b,cmp.cmp_collation.collation);
+ int value= cmp_collation.sortcmp(a, b);
null_value=0;
return !value ? 0 : (value < 0 ? (longlong) -1 : (longlong) 1);
}
@@ -2434,7 +2497,7 @@ Item_func_ifnull::fix_length_and_dec()
agg_result_type(&cached_result_type, args, 2);
cached_field_type= agg_field_type(args, 2);
maybe_null=args[1]->maybe_null;
- decimals= max(args[0]->decimals, args[1]->decimals);
+ decimals= MY_MAX(args[0]->decimals, args[1]->decimals);
unsigned_flag= args[0]->unsigned_flag && args[1]->unsigned_flag;
if (cached_result_type == DECIMAL_RESULT || cached_result_type == INT_RESULT)
@@ -2445,10 +2508,10 @@ Item_func_ifnull::fix_length_and_dec()
int len1= args[1]->max_char_length() - args[1]->decimals
- (args[1]->unsigned_flag ? 0 : 1);
- char_length= max(len0, len1) + decimals + (unsigned_flag ? 0 : 1);
+ char_length= MY_MAX(len0, len1) + decimals + (unsigned_flag ? 0 : 1);
}
else
- char_length= max(args[0]->max_char_length(), args[1]->max_char_length());
+ char_length= MY_MAX(args[0]->max_char_length(), args[1]->max_char_length());
switch (cached_result_type) {
case STRING_RESULT:
@@ -2474,9 +2537,9 @@ uint Item_func_ifnull::decimal_precision() const
{
int arg0_int_part= args[0]->decimal_int_part();
int arg1_int_part= args[1]->decimal_int_part();
- int max_int_part= max(arg0_int_part, arg1_int_part);
+ int max_int_part= MY_MAX(arg0_int_part, arg1_int_part);
int precision= max_int_part + decimals;
- return min(precision, DECIMAL_MAX_PRECISION);
+ return MY_MIN(precision, DECIMAL_MAX_PRECISION);
}
@@ -2556,9 +2619,9 @@ Item_func_ifnull::str_op(String *str)
bool Item_func_ifnull::date_op(MYSQL_TIME *ltime, uint fuzzydate)
{
DBUG_ASSERT(fixed == 1);
- if (!args[0]->get_date(ltime, fuzzydate & ~TIME_FUZZY_DATES))
+ if (!args[0]->get_date_with_conversion(ltime, fuzzydate & ~TIME_FUZZY_DATES))
return (null_value= false);
- if (!args[1]->get_date(ltime, fuzzydate & ~TIME_FUZZY_DATES))
+ if (!args[1]->get_date_with_conversion(ltime, fuzzydate & ~TIME_FUZZY_DATES))
return (null_value= false);
bzero((char*) ltime,sizeof(*ltime));
return null_value= !(fuzzydate & TIME_FUZZY_DATES);
@@ -2661,7 +2724,7 @@ Item_func_if::fix_length_and_dec()
agg_result_type(&cached_result_type, args + 1, 2);
cached_field_type= agg_field_type(args + 1, 2);
maybe_null= args[1]->maybe_null || args[2]->maybe_null;
- decimals= max(args[1]->decimals, args[2]->decimals);
+ decimals= MY_MAX(args[1]->decimals, args[2]->decimals);
unsigned_flag=args[1]->unsigned_flag && args[2]->unsigned_flag;
if (cached_result_type == STRING_RESULT)
@@ -2684,10 +2747,10 @@ Item_func_if::fix_length_and_dec()
int len2= args[2]->max_length - args[2]->decimals
- (args[2]->unsigned_flag ? 0 : 1);
- char_length= max(len1, len2) + decimals + (unsigned_flag ? 0 : 1);
+ char_length= MY_MAX(len1, len2) + decimals + (unsigned_flag ? 0 : 1);
}
else
- char_length= max(args[1]->max_char_length(), args[2]->max_char_length());
+ char_length= MY_MAX(args[1]->max_char_length(), args[2]->max_char_length());
fix_char_length(char_length);
}
@@ -2696,8 +2759,8 @@ uint Item_func_if::decimal_precision() const
{
int arg1_prec= args[1]->decimal_int_part();
int arg2_prec= args[2]->decimal_int_part();
- int precision=max(arg1_prec,arg2_prec) + decimals;
- return min(precision, DECIMAL_MAX_PRECISION);
+ int precision=MY_MAX(arg1_prec,arg2_prec) + decimals;
+ return MY_MIN(precision, DECIMAL_MAX_PRECISION);
}
@@ -2751,7 +2814,7 @@ bool Item_func_if::date_op(MYSQL_TIME *ltime, uint fuzzydate)
{
DBUG_ASSERT(fixed == 1);
Item *arg= args[0]->val_bool() ? args[1] : args[2];
- return (null_value= arg->get_date(ltime, fuzzydate));
+ return (null_value= arg->get_date_with_conversion(ltime, fuzzydate));
}
@@ -2996,7 +3059,7 @@ bool Item_func_case::date_op(MYSQL_TIME *ltime, uint fuzzydate)
Item *item= find_item(&dummy_str);
if (!item)
return (null_value= true);
- return (null_value= item->get_date(ltime, fuzzydate));
+ return (null_value= item->get_date_with_conversion(ltime, fuzzydate));
}
@@ -3020,7 +3083,7 @@ bool Item_func_case::fix_fields(THD *thd, Item **ref)
void Item_func_case::agg_str_lengths(Item* arg)
{
- fix_char_length(max(max_char_length(), arg->max_char_length()));
+ fix_char_length(MY_MAX(max_char_length(), arg->max_char_length()));
set_if_bigger(decimals, arg->decimals);
unsigned_flag= unsigned_flag && arg->unsigned_flag;
}
@@ -3212,7 +3275,7 @@ uint Item_func_case::decimal_precision() const
if (else_expr_num != -1)
set_if_bigger(max_int_part, args[else_expr_num]->decimal_int_part());
- return min(max_int_part + decimals, DECIMAL_MAX_PRECISION);
+ return MY_MIN(max_int_part + decimals, DECIMAL_MAX_PRECISION);
}
@@ -3314,7 +3377,8 @@ bool Item_func_coalesce::date_op(MYSQL_TIME *ltime,uint fuzzydate)
null_value= 0;
for (uint i= 0; i < arg_count; i++)
{
- bool res= args[i]->get_date(ltime, fuzzydate & ~TIME_FUZZY_DATES);
+ bool res= args[i]->get_date_with_conversion(ltime,
+ fuzzydate & ~TIME_FUZZY_DATES);
if (!args[i]->null_value)
return res;
}
@@ -4493,6 +4557,16 @@ bool Item_cond::walk(Item_processor processor, bool walk_subquery, uchar *arg)
return Item_func::walk(processor, walk_subquery, arg);
}
+bool Item_cond_and::walk_top_and(Item_processor processor, uchar *arg)
+{
+ List_iterator_fast<Item> li(list);
+ Item *item;
+ while ((item= li++))
+ if (item->walk_top_and(processor, arg))
+ return 1;
+ return Item_cond::walk_top_and(processor, arg);
+}
+
/**
Transform an Item_cond object with a transformer callback function.
@@ -4885,21 +4959,20 @@ longlong Item_func_like::val_int()
Item_func::optimize_type Item_func_like::select_optimize() const
{
- if (args[1]->const_item() && !args[1]->is_expensive())
- {
- String* res2= args[1]->val_str((String *)&cmp.value2);
- const char *ptr2;
+ if (!args[1]->const_item() || args[1]->is_expensive())
+ return OPTIMIZE_NONE;
- if (!res2 || !(ptr2= res2->ptr()))
- return OPTIMIZE_NONE;
+ String* res2= args[1]->val_str((String *)&cmp.value2);
+ if (!res2)
+ return OPTIMIZE_NONE;
- if (*ptr2 != wild_many)
- {
- if (args[0]->result_type() != STRING_RESULT || *ptr2 != wild_one)
- return OPTIMIZE_OP;
- }
- }
- return OPTIMIZE_NONE;
+ if (!res2->length()) // Can optimize empty wildcard: column LIKE ''
+ return OPTIMIZE_OP;
+
+ DBUG_ASSERT(res2->ptr());
+ char first= res2->ptr()[0];
+ return (first == wild_many || first == wild_one) ?
+ OPTIMIZE_NONE : OPTIMIZE_OP;
}
@@ -5009,6 +5082,7 @@ bool Item_func_like::fix_fields(THD *thd, Item **ref)
turboBM_compute_bad_character_shifts();
DBUG_PRINT("info",("done"));
}
+ use_sampling= (len > 2 && (*first == wild_many || *first == wild_one));
}
}
return FALSE;
@@ -5021,156 +5095,277 @@ void Item_func_like::cleanup()
Item_bool_func2::cleanup();
}
+
+bool Item_func_like::find_selective_predicates_list_processor(uchar *arg)
+{
+ find_selective_predicates_list_processor_data *data=
+ (find_selective_predicates_list_processor_data *) arg;
+ if (use_sampling && used_tables() == data->table->map)
+ {
+ COND_STATISTIC *stat= (COND_STATISTIC *)sql_alloc(sizeof(COND_STATISTIC));
+ if (!stat)
+ return TRUE;
+ stat->cond= this;
+ Item *arg0= args[0]->real_item();
+ if (args[1]->const_item() && arg0->type() == FIELD_ITEM)
+ stat->field_arg= ((Item_field *)arg0)->field;
+ else
+ stat->field_arg= NULL;
+ data->list.push_back(stat);
+ }
+ return FALSE;
+}
+
+
+int Regexp_processor_pcre::default_regex_flags()
+{
+ return default_regex_flags_pcre(current_thd);
+}
+
+
+/**
+ Convert string to lib_charset, if needed.
+*/
+String *Regexp_processor_pcre::convert_if_needed(String *str, String *converter)
+{
+ if (m_conversion_is_needed)
+ {
+ uint dummy_errors;
+ if (converter->copy(str->ptr(), str->length(), str->charset(),
+ m_library_charset, &dummy_errors))
+ return NULL;
+ str= converter;
+ }
+ return str;
+}
+
+
/**
@brief Compile regular expression.
+ @param[in] pattern the pattern to compile from.
@param[in] send_error send error message if any.
@details Make necessary character set conversion then
compile regular expression passed in the args[1].
- @retval 0 success.
- @retval 1 error occurred.
- @retval -1 given null regular expression.
+ @retval false success.
+ @retval true error occurred.
*/
-int Item_func_regex::regcomp(bool send_error)
+bool Regexp_processor_pcre::compile(String *pattern, bool send_error)
{
- char buff[MAX_FIELD_WIDTH];
- String tmp(buff,sizeof(buff),&my_charset_bin);
- String *res= args[1]->val_str(&tmp);
- int error;
+ const char *pcreErrorStr;
+ int pcreErrorOffset;
- if (args[1]->null_value)
- return -1;
-
- if (regex_compiled)
+ if (is_compiled())
{
- if (!stringcmp(res, &prev_regexp))
- return 0;
- prev_regexp.copy(*res);
- my_regfree(&preg);
- regex_compiled= 0;
+ if (!stringcmp(pattern, &m_prev_pattern))
+ return false;
+ m_prev_pattern.copy(*pattern);
+ pcre_free(m_pcre);
+ m_pcre= NULL;
}
- if (cmp_collation.collation != regex_lib_charset)
- {
- /* Convert UCS2 strings to UTF8 */
- uint dummy_errors;
- if (conv.copy(res->ptr(), res->length(), res->charset(),
- regex_lib_charset, &dummy_errors))
- return 1;
- res= &conv;
- }
+ if (!(pattern= convert_if_needed(pattern, &pattern_converter)))
+ return true;
- if ((error= my_regcomp(&preg, res->c_ptr_safe(),
- regex_lib_flags, regex_lib_charset)))
+ m_pcre= pcre_compile(pattern->c_ptr_safe(), m_library_flags,
+ &pcreErrorStr, &pcreErrorOffset, NULL);
+
+ if (m_pcre == NULL)
{
if (send_error)
{
- (void) my_regerror(error, &preg, buff, sizeof(buff));
+ char buff[MAX_FIELD_WIDTH];
+ my_snprintf(buff, sizeof(buff), "%s at offset %d", pcreErrorStr, pcreErrorOffset);
my_error(ER_REGEXP_ERROR, MYF(0), buff);
}
- return 1;
+ return true;
}
- regex_compiled= 1;
- return 0;
+ return false;
}
-bool
-Item_func_regex::fix_fields(THD *thd, Item **ref)
+bool Regexp_processor_pcre::compile(Item *item, bool send_error)
{
- DBUG_ASSERT(fixed == 0);
- if ((!args[0]->fixed &&
- args[0]->fix_fields(thd, args)) || args[0]->check_cols(1) ||
- (!args[1]->fixed &&
- args[1]->fix_fields(thd, args + 1)) || args[1]->check_cols(1))
- return TRUE; /* purecov: inspected */
- with_sum_func=args[0]->with_sum_func || args[1]->with_sum_func;
- with_field= args[0]->with_field || args[1]->with_field;
- with_subselect= args[0]->has_subquery() || args[1]->has_subquery();
- max_length= 1;
- decimals= 0;
+ char buff[MAX_FIELD_WIDTH];
+ String tmp(buff, sizeof(buff), &my_charset_bin);
+ String *pattern= item->val_str(&tmp);
+ if (item->null_value || compile(pattern, send_error))
+ return true;
+ return false;
+}
- if (agg_arg_charsets_for_comparison(cmp_collation, args, 2))
- return TRUE;
- regex_lib_flags= (cmp_collation.collation->state &
- (MY_CS_BINSORT | MY_CS_CSSORT)) ?
- REG_EXTENDED | REG_NOSUB :
- REG_EXTENDED | REG_NOSUB | REG_ICASE;
+/**
+ Send a warning explaining an error code returned by pcre_exec().
+*/
+void Regexp_processor_pcre::pcre_exec_warn(int rc) const
+{
+ char buf[64];
+ const char *errmsg= NULL;
/*
- If the case of UCS2 and other non-ASCII character sets,
- we will convert patterns and strings to UTF8.
+ Make a descriptive message only for those pcre_exec() error codes
+ that can actually happen in MariaDB.
*/
- regex_lib_charset= (cmp_collation.collation->mbminlen > 1) ?
- &my_charset_utf8_general_ci :
- cmp_collation.collation;
+ switch (rc)
+ {
+ case PCRE_ERROR_NOMEMORY:
+ errmsg= "pcre_exec: Out of memory";
+ break;
+ case PCRE_ERROR_BADUTF8:
+ errmsg= "pcre_exec: Invalid utf8 byte sequence in the subject string";
+ break;
+ case PCRE_ERROR_RECURSELOOP:
+ errmsg= "pcre_exec: Recursion loop detected";
+ break;
+ default:
+ /*
+ As other error codes should normally not happen,
+ we just report the error code without textual description
+ of the code.
+ */
+ my_snprintf(buf, sizeof(buf), "pcre_exec: Internal error (%d)", rc);
+ errmsg= buf;
+ }
+ push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
+ ER_REGEXP_ERROR, ER(ER_REGEXP_ERROR), errmsg);
+}
- used_tables_cache=args[0]->used_tables() | args[1]->used_tables();
- not_null_tables_cache= (args[0]->not_null_tables() |
- args[1]->not_null_tables());
- const_item_cache=args[0]->const_item() && args[1]->const_item();
- if (!regex_compiled && args[1]->const_item())
+
+/**
+ Call pcre_exec() and send a warning if pcre_exec() returned with an error.
+*/
+int Regexp_processor_pcre::pcre_exec_with_warn(const pcre *code,
+ const pcre_extra *extra,
+ const char *subject,
+ int length, int startoffset,
+ int options, int *ovector,
+ int ovecsize)
+{
+ int rc= pcre_exec(code, extra, subject, length,
+ startoffset, options, ovector, ovecsize);
+ DBUG_EXECUTE_IF("pcre_exec_error_123", rc= -123;);
+ if (rc < PCRE_ERROR_NOMATCH)
+ pcre_exec_warn(rc);
+ return rc;
+}
+
+
+bool Regexp_processor_pcre::exec(const char *str, int length, int offset)
+{
+ m_pcre_exec_rc= pcre_exec_with_warn(m_pcre, NULL, str, length, offset, 0,
+ m_SubStrVec, m_subpatterns_needed * 3);
+ return false;
+}
+
+
+bool Regexp_processor_pcre::exec(String *str, int offset,
+ uint n_result_offsets_to_convert)
+{
+ if (!(str= convert_if_needed(str, &subject_converter)))
+ return true;
+ m_pcre_exec_rc= pcre_exec_with_warn(m_pcre, NULL,
+ str->c_ptr_safe(), str->length(),
+ offset, 0,
+ m_SubStrVec, m_subpatterns_needed * 3);
+ if (m_pcre_exec_rc > 0)
{
- int comp_res= regcomp(TRUE);
- if (comp_res == -1)
- { // Will always return NULL
- maybe_null=1;
- fixed= 1;
- return FALSE;
+ uint i;
+ for (i= 0; i < n_result_offsets_to_convert; i++)
+ {
+ /*
+ Convert byte offset into character offset.
+ */
+ m_SubStrVec[i]= (int) str->charset()->cset->numchars(str->charset(),
+ str->ptr(),
+ str->ptr() +
+ m_SubStrVec[i]);
}
- else if (comp_res)
- return TRUE;
- regex_is_const= 1;
- maybe_null= args[0]->maybe_null;
}
- else
- maybe_null=1;
- fixed= 1;
- return FALSE;
+ return false;
}
-longlong Item_func_regex::val_int()
+bool Regexp_processor_pcre::exec(Item *item, int offset,
+ uint n_result_offsets_to_convert)
{
- DBUG_ASSERT(fixed == 1);
char buff[MAX_FIELD_WIDTH];
String tmp(buff,sizeof(buff),&my_charset_bin);
- String *res= args[0]->val_str(&tmp);
+ String *res= item->val_str(&tmp);
+ if (item->null_value)
+ return true;
+ return exec(res, offset, n_result_offsets_to_convert);
+}
- if ((null_value= (args[0]->null_value ||
- (!regex_is_const && regcomp(FALSE)))))
- return 0;
- if (cmp_collation.collation != regex_lib_charset)
+void Regexp_processor_pcre::fix_owner(Item_func *owner,
+ Item *subject_arg,
+ Item *pattern_arg)
+{
+ if (!is_compiled() && pattern_arg->const_item())
{
- /* Convert UCS2 strings to UTF8 */
- uint dummy_errors;
- if (conv.copy(res->ptr(), res->length(), res->charset(),
- regex_lib_charset, &dummy_errors))
+ if (compile(pattern_arg, true))
{
- null_value= 1;
- return 0;
+ owner->maybe_null= 1; // Will always return NULL
+ return;
}
- res= &conv;
+ set_const(true);
+ owner->maybe_null= subject_arg->maybe_null;
}
- return my_regexec(&preg,res->c_ptr_safe(),0,(my_regmatch_t*) 0,0) ? 0 : 1;
+ else
+ owner->maybe_null= 1;
}
-void Item_func_regex::cleanup()
+void
+Item_func_regex::fix_length_and_dec()
{
- DBUG_ENTER("Item_func_regex::cleanup");
- Item_bool_func::cleanup();
- if (regex_compiled)
- {
- my_regfree(&preg);
- regex_compiled=0;
- prev_regexp.length(0);
- }
- DBUG_VOID_RETURN;
+ Item_bool_func::fix_length_and_dec();
+
+ if (agg_arg_charsets_for_comparison(cmp_collation, args, 2))
+ return;
+
+ re.init(cmp_collation.collation, 0, 0);
+ re.fix_owner(this, args[0], args[1]);
+}
+
+
+longlong Item_func_regex::val_int()
+{
+ DBUG_ASSERT(fixed == 1);
+ if ((null_value= re.recompile(args[1])))
+ return 0;
+
+ if ((null_value= re.exec(args[0], 0, 0)))
+ return 0;
+
+ return re.match();
+}
+
+
+void
+Item_func_regexp_instr::fix_length_and_dec()
+{
+ if (agg_arg_charsets_for_comparison(cmp_collation, args, 2))
+ return;
+
+ re.init(cmp_collation.collation, 0, 1);
+ re.fix_owner(this, args[0], args[1]);
+}
+
+
+longlong Item_func_regexp_instr::val_int()
+{
+ DBUG_ASSERT(fixed == 1);
+ if ((null_value= re.recompile(args[1])))
+ return 0;
+
+ if ((null_value= re.exec(args[0], 0, 1)))
+ return 0;
+
+ return re.match() ? re.subpattern_start(0) + 1 : 0;
}
@@ -5206,7 +5401,7 @@ void Item_func_like::turboBM_compute_suffixes(int *suff)
else
{
if (i < g)
- g = i; // g = min(i, g)
+ g = i; // g = MY_MIN(i, g)
f = i;
while (g >= 0 && pattern[g] == pattern[g + plm1 - f])
g--;
@@ -5225,7 +5420,7 @@ void Item_func_like::turboBM_compute_suffixes(int *suff)
else
{
if (i < g)
- g = i; // g = min(i, g)
+ g = i; // g = MY_MIN(i, g)
f = i;
while (g >= 0 &&
likeconv(cs, pattern[g]) == likeconv(cs, pattern[g + plm1 - f]))
@@ -5346,14 +5541,14 @@ bool Item_func_like::turboBM_matches(const char* text, int text_len) const
register const int v = plm1 - i;
turboShift = u - v;
bcShift = bmBc[(uint) (uchar) text[i + j]] - plm1 + i;
- shift = max(turboShift, bcShift);
- shift = max(shift, bmGs[i]);
+ shift = MY_MAX(turboShift, bcShift);
+ shift = MY_MAX(shift, bmGs[i]);
if (shift == bmGs[i])
- u = min(pattern_len - shift, v);
+ u = MY_MIN(pattern_len - shift, v);
else
{
if (turboShift < bcShift)
- shift = max(shift, u + 1);
+ shift = MY_MAX(shift, u + 1);
u = 0;
}
j+= shift;
@@ -5377,14 +5572,14 @@ bool Item_func_like::turboBM_matches(const char* text, int text_len) const
register const int v = plm1 - i;
turboShift = u - v;
bcShift = bmBc[(uint) likeconv(cs, text[i + j])] - plm1 + i;
- shift = max(turboShift, bcShift);
- shift = max(shift, bmGs[i]);
+ shift = MY_MAX(turboShift, bcShift);
+ shift = MY_MAX(shift, bmGs[i]);
if (shift == bmGs[i])
- u = min(pattern_len - shift, v);
+ u = MY_MIN(pattern_len - shift, v);
else
{
if (turboShift < bcShift)
- shift = max(shift, u + 1);
+ shift = MY_MAX(shift, u + 1);
u = 0;
}
j+= shift;
@@ -5461,6 +5656,7 @@ Item *Item_func_not::neg_transformer(THD *thd) /* NOT(x) -> x */
bool Item_func_not::fix_fields(THD *thd, Item **ref)
{
+ args[0]->under_not(this);
if (args[0]->type() == FIELD_ITEM)
{
/* replace "NOT <field>" with "<filed> == 0" */
@@ -5638,7 +5834,7 @@ Item *Item_bool_rowready_func2::negated_item()
Item_equal::Item_equal(Item *f1, Item *f2, bool with_const_item)
: Item_bool_func(), eval_item(0), cond_false(0), cond_true(0),
- context_field(NULL)
+ context_field(NULL), link_equal_fields(FALSE)
{
const_item_cache= 0;
with_const= with_const_item;
@@ -5664,7 +5860,7 @@ Item_equal::Item_equal(Item *f1, Item *f2, bool with_const_item)
Item_equal::Item_equal(Item_equal *item_equal)
: Item_bool_func(), eval_item(0), cond_false(0), cond_true(0),
- context_field(NULL)
+ context_field(NULL), link_equal_fields(FALSE)
{
const_item_cache= 0;
List_iterator_fast<Item> li(item_equal->equal_items);
@@ -6024,6 +6220,9 @@ bool Item_equal::fix_fields(THD *thd, Item **ref)
DBUG_ASSERT(fixed == 0);
Item_equal_fields_iterator it(*this);
Item *item;
+ Field *first_equal_field= NULL;
+ Field *last_equal_field= NULL;
+ Field *prev_equal_field= NULL;
not_null_tables_cache= used_tables_cache= 0;
const_item_cache= 0;
while ((item= it++))
@@ -6037,7 +6236,18 @@ bool Item_equal::fix_fields(THD *thd, Item **ref)
maybe_null= 1;
if (!item->get_item_equal())
item->set_item_equal(this);
+ if (link_equal_fields && item->real_item()->type() == FIELD_ITEM)
+ {
+ last_equal_field= ((Item_field *) (item->real_item()))->field;
+ if (!prev_equal_field)
+ first_equal_field= last_equal_field;
+ else
+ prev_equal_field->next_equal_field= last_equal_field;
+ prev_equal_field= last_equal_field;
+ }
}
+ if (prev_equal_field && last_equal_field != first_equal_field)
+ last_equal_field->next_equal_field= first_equal_field;
fix_length_and_dec();
fixed= 1;
return FALSE;
@@ -6312,23 +6522,87 @@ Item* Item_equal::get_first(JOIN_TAB *context, Item *field_item)
}
-longlong Item_func_dyncol_exists::val_int()
+longlong Item_func_dyncol_check::val_int()
{
char buff[STRING_BUFFER_USUAL_SIZE];
String tmp(buff, sizeof(buff), &my_charset_bin);
DYNAMIC_COLUMN col;
String *str;
- ulonglong num;
enum enum_dyncol_func_result rc;
- num= args[1]->val_int();
+ str= args[0]->val_str(&tmp);
+ if (args[0]->null_value)
+ goto null;
+ col.length= str->length();
+ /* We do not change the string, so could do this trick */
+ col.str= (char *)str->ptr();
+ rc= mariadb_dyncol_check(&col);
+ if (rc < 0 && rc != ER_DYNCOL_FORMAT)
+ {
+ dynamic_column_error_message(rc);
+ goto null;
+ }
+ null_value= FALSE;
+ return rc == ER_DYNCOL_OK;
+
+null:
+ null_value= TRUE;
+ return 0;
+}
+
+longlong Item_func_dyncol_exists::val_int()
+{
+ char buff[STRING_BUFFER_USUAL_SIZE], nmstrbuf[11];
+ String tmp(buff, sizeof(buff), &my_charset_bin),
+ nmbuf(nmstrbuf, sizeof(nmstrbuf), system_charset_info);
+ DYNAMIC_COLUMN col;
+ String *str;
+ LEX_STRING buf, *name= NULL;
+ ulonglong num= 0;
+ enum enum_dyncol_func_result rc;
+
+ if (args[1]->result_type() == INT_RESULT)
+ num= args[1]->val_int();
+ else
+ {
+ String *nm= args[1]->val_str(&nmbuf);
+ if (!nm || args[1]->null_value)
+ {
+ null_value= 1;
+ return 1;
+ }
+ if (my_charset_same(nm->charset(), &my_charset_utf8_general_ci))
+ {
+ buf.str= (char *) nm->ptr();
+ buf.length= nm->length();
+ }
+ else
+ {
+ uint strlen;
+ uint dummy_errors;
+ buf.str= (char *)sql_alloc((strlen= nm->length() *
+ my_charset_utf8_general_ci.mbmaxlen + 1));
+ if (buf.str)
+ {
+ buf.length=
+ copy_and_convert(buf.str, strlen, &my_charset_utf8_general_ci,
+ nm->ptr(), nm->length(), nm->charset(),
+ &dummy_errors);
+ }
+ else
+ buf.length= 0;
+ }
+ name= &buf;
+ }
str= args[0]->val_str(&tmp);
if (args[0]->null_value || args[1]->null_value || num > UINT_MAX16)
goto null;
col.length= str->length();
/* We do not change the string, so could do this trick */
col.str= (char *)str->ptr();
- rc= dynamic_column_exists(&col, (uint) num);
+ rc= ((name == NULL) ?
+ mariadb_dyncol_exists_num(&col, (uint) num) :
+ mariadb_dyncol_exists_named(&col, name));
if (rc < 0)
{
dynamic_column_error_message(rc);
diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h
index 0faba016ba8..d109e412f0c 100644
--- a/sql/item_cmpfunc.h
+++ b/sql/item_cmpfunc.h
@@ -25,7 +25,8 @@
#include "thr_malloc.h" /* sql_calloc */
#include "item_func.h" /* Item_int_func, Item_bool_func */
-#include "my_regex.h"
+#define PCRE_STATIC 1 /* Important on Windows */
+#include "pcre.h" /* pcre header file */
extern Item_result item_cmp_type(Item_result a,Item_result b);
class Item_bool_func2;
@@ -47,6 +48,14 @@ class Arg_comparator: public Sql_alloc
THD *thd;
Item *a_cache, *b_cache; // Cached values of a and b items
// when one of arguments is NULL.
+ int set_compare_func(Item_result_field *owner, Item_result type);
+ inline int set_compare_func(Item_result_field *owner_arg)
+ {
+ return set_compare_func(owner_arg, item_cmp_type((*a)->result_type(),
+ (*b)->result_type()));
+ }
+ bool agg_arg_charsets_for_comparison();
+
public:
DTCollation cmp_collation;
/* Allow owner function to use string buffers. */
@@ -57,12 +66,6 @@ public:
Arg_comparator(Item **a1, Item **a2): a(a1), b(a2), set_null(TRUE),
comparators(0), thd(0), a_cache(0), b_cache(0) {};
- int set_compare_func(Item_result_field *owner, Item_result type);
- inline int set_compare_func(Item_result_field *owner_arg)
- {
- return set_compare_func(owner_arg, item_cmp_type((*a)->result_type(),
- (*b)->result_type()));
- }
int set_cmp_func(Item_result_field *owner_arg,
Item **a1, Item **a2,
Item_result type);
@@ -121,6 +124,8 @@ public:
Item_bool_func() :Item_int_func() {}
Item_bool_func(Item *a) :Item_int_func(a) {}
Item_bool_func(Item *a,Item *b) :Item_int_func(a,b) {}
+ Item_bool_func(Item *a, Item *b, Item *c) :Item_int_func(a, b, c) {}
+ Item_bool_func(List<Item> &list) :Item_int_func(list) { }
Item_bool_func(THD *thd, Item_bool_func *item) :Item_int_func(thd, item) {}
bool is_bool_func() { return 1; }
void fix_length_and_dec() { decimals=0; max_length=1; }
@@ -246,12 +251,12 @@ protected:
*/
int result_for_null_param;
public:
- Item_in_optimizer(Item *a, Item_in_subselect *b):
- Item_bool_func(a, reinterpret_cast<Item *>(b)), cache(0), expr_cache(0),
+ Item_in_optimizer(Item *a, Item *b):
+ Item_bool_func(a, b), cache(0), expr_cache(0),
save_cache(0), result_for_null_param(UNKNOWN)
{ with_subselect= true; }
bool fix_fields(THD *, Item **);
- bool fix_left(THD *thd, Item **ref);
+ bool fix_left(THD *thd);
table_map not_null_tables() const { return 0; }
bool is_null();
longlong val_int();
@@ -269,6 +274,8 @@ public:
bool is_top_level_item();
bool eval_not_null_tables(uchar *opt_arg);
void fix_after_pullout(st_select_lex *new_parent, Item **ref);
+ bool invisible_mode();
+ void reset_cache() { cache= NULL; }
};
class Comp_creator
@@ -361,7 +368,7 @@ public:
virtual bool l_op() const { return 1; }
};
-class Item_bool_func2 :public Item_int_func
+class Item_bool_func2 :public Item_bool_func
{ /* Bool with 2 string args */
protected:
Arg_comparator cmp;
@@ -369,7 +376,7 @@ protected:
public:
Item_bool_func2(Item *a,Item *b)
- :Item_int_func(a,b), cmp(tmp_arg, tmp_arg+1),
+ :Item_bool_func(a,b), cmp(tmp_arg, tmp_arg+1),
abort_on_null(FALSE) { sargable= TRUE; }
void fix_length_and_dec();
int set_cmp_func()
@@ -385,15 +392,13 @@ public:
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; }
+ bool is_null() { return MY_TEST(args[0]->is_null() || args[1]->is_null()); }
CHARSET_INFO *compare_collation() { return cmp.cmp_collation.collation; }
- uint decimal_precision() const { return 1; }
void top_level_item() { abort_on_null= TRUE; }
Arg_comparator *get_comparator() { return &cmp; }
void cleanup()
{
- Item_int_func::cleanup();
+ Item_bool_func::cleanup();
cmp.cleanup();
}
@@ -437,8 +442,11 @@ public:
class Item_func_not :public Item_bool_func
{
+ bool abort_on_null;
public:
- Item_func_not(Item *a) :Item_bool_func(a) {}
+ Item_func_not(Item *a) :Item_bool_func(a), abort_on_null(FALSE) {}
+ virtual void top_level_item() { abort_on_null= 1; }
+ bool is_top_level_item() { return abort_on_null; }
longlong val_int();
enum Functype functype() const { return NOT_FUNC; }
const char *func_name() const { return "not"; }
@@ -496,16 +504,13 @@ class Item_func_not_all :public Item_func_not
Item_sum_hybrid *test_sum_item;
Item_maxmin_subselect *test_sub_item;
- bool abort_on_null;
public:
bool show;
Item_func_not_all(Item *a)
- :Item_func_not(a), test_sum_item(0), test_sub_item(0), abort_on_null(0),
+ :Item_func_not(a), test_sum_item(0), test_sub_item(0),
show(0)
{}
- virtual void top_level_item() { abort_on_null= 1; }
- bool is_top_level_item() { return abort_on_null; }
table_map not_null_tables() const { return 0; }
longlong val_int();
enum Functype functype() const { return NOT_ALL_FUNC; }
@@ -551,6 +556,7 @@ public:
- Otherwise, UINT_MAX
*/
uint in_equality_no;
+ virtual uint exists2in_reserved_items() { return 1; };
};
class Item_func_equal :public Item_bool_rowready_func2
@@ -642,16 +648,16 @@ public:
*/
-class Item_func_opt_neg :public Item_int_func
+class Item_func_opt_neg :public Item_bool_func
{
public:
bool negated; /* <=> the item represents NOT <func> */
bool pred_level; /* <=> [NOT] <func> is used on a predicate level */
public:
Item_func_opt_neg(Item *a, Item *b, Item *c)
- :Item_int_func(a, b, c), negated(0), pred_level(0) {}
+ :Item_bool_func(a, b, c), negated(0), pred_level(0) {}
Item_func_opt_neg(List<Item> &list)
- :Item_int_func(list), negated(0), pred_level(0) {}
+ :Item_bool_func(list), negated(0), pred_level(0) {}
public:
inline void negate() { negated= !negated; }
inline void top_level_item() { pred_level= 1; }
@@ -682,30 +688,25 @@ public:
bool fix_fields(THD *, Item **);
void fix_length_and_dec();
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; }
bool eval_not_null_tables(uchar *opt_arg);
void fix_after_pullout(st_select_lex *new_parent, Item **ref);
bool count_sargable_conds(uchar *arg);
};
-class Item_func_strcmp :public Item_bool_func2
+class Item_func_strcmp :public Item_int_func
{
+ String value1, value2;
+ DTCollation cmp_collation;
public:
- Item_func_strcmp(Item *a,Item *b) :Item_bool_func2(a,b) {}
+ Item_func_strcmp(Item *a,Item *b) :Item_int_func(a,b) {}
longlong val_int();
- optimize_type select_optimize() const { return OPTIMIZE_NONE; }
+ uint decimal_precision() const { return 1; }
const char *func_name() const { return "strcmp"; }
-
- virtual inline void print(String *str, enum_query_type query_type)
- {
- Item_func::print(str, query_type);
- }
void fix_length_and_dec()
{
- Item_bool_func2::fix_length_and_dec();
+ agg_arg_charsets_for_comparison(cmp_collation, args, 2);
fix_char_length(2); // returns "1" or "0" or "-1"
}
};
@@ -733,6 +734,11 @@ public:
void fix_length_and_dec();
const char *func_name() const { return "interval"; }
uint decimal_precision() const { return 2; }
+ void print(String *str, enum_query_type query_type)
+ {
+ str->append(func_name());
+ print_args(str, 0, query_type);
+ }
};
@@ -799,6 +805,7 @@ public:
Item_func_nullif(Item *a,Item *b)
:Item_bool_func2(a,b), cached_result_type(INT_RESULT)
{}
+ bool is_bool_func() { return false; }
double val_real();
longlong val_int();
String *val_str(String *str);
@@ -870,7 +877,7 @@ public:
/* Compare values number pos1 and pos2 for equality */
bool compare_elems(uint pos1, uint pos2)
{
- return test(compare(collation, base + pos1*size, base + pos2*size));
+ return MY_TEST(compare(collation, base + pos1 * size, base + pos2 * size));
}
virtual Item_result result_type()= 0;
};
@@ -879,6 +886,18 @@ class in_string :public in_vector
{
char buff[STRING_BUFFER_USUAL_SIZE];
String tmp;
+ class Item_string_for_in_vector: public Item_string
+ {
+ public:
+ Item_string_for_in_vector(CHARSET_INFO *cs):
+ Item_string(cs)
+ { }
+ void set_value(const String *str)
+ {
+ str_value= *str;
+ collation.set(str->charset());
+ }
+ };
public:
in_string(uint elements,qsort2_cmp cmp_func, CHARSET_INFO *cs);
~in_string();
@@ -886,13 +905,13 @@ public:
uchar *get_value(Item *item);
Item* create_item()
{
- return new Item_string(collation);
+ return new Item_string_for_in_vector(collation);
}
void value_to_item(uint pos, Item *item)
{
String *str=((String*) base)+pos;
- Item_string *to= (Item_string*)item;
- to->str_value= *str;
+ Item_string_for_in_vector *to= (Item_string_for_in_vector*) item;
+ to->set_value(str);
}
Item_result result_type() { return STRING_RESULT; }
};
@@ -1302,7 +1321,6 @@ public:
longlong val_int();
bool fix_fields(THD *, Item **);
void fix_length_and_dec();
- uint decimal_precision() const { return 1; }
void cleanup()
{
uint i;
@@ -1323,7 +1341,6 @@ public:
enum Functype functype() const { return IN_FUNC; }
const char *func_name() const { return " IN "; }
bool nulls_in_row();
- bool is_bool_func() { return 1; }
CHARSET_INFO *compare_collation() { return cmp_collation.collation; }
bool eval_not_null_tables(uchar *opt_arg);
void fix_after_pullout(st_select_lex *new_parent, Item **ref);
@@ -1462,8 +1479,9 @@ class Item_func_like :public Item_bool_func2
enum { alphabet_size = 256 };
Item *escape_item;
-
+
bool escape_used_in_parsing;
+ bool use_sampling;
public:
int escape;
@@ -1471,34 +1489,155 @@ public:
Item_func_like(Item *a,Item *b, Item *escape_arg, bool escape_used)
:Item_bool_func2(a,b), canDoTurboBM(FALSE), pattern(0), pattern_len(0),
bmGs(0), bmBc(0), escape_item(escape_arg),
- escape_used_in_parsing(escape_used) {}
+ escape_used_in_parsing(escape_used), use_sampling(0) {}
longlong val_int();
enum Functype functype() const { return LIKE_FUNC; }
optimize_type select_optimize() const;
- cond_result eq_cmp_result() const { return COND_TRUE; }
+ cond_result eq_cmp_result() const
+ {
+ /**
+ We cannot always rewrite conditions as follows:
+ from: WHERE expr1=const AND expr1 LIKE expr2
+ to: WHERE expr1=const AND const LIKE expr2
+ or
+ from: WHERE expr1=const AND expr2 LIKE expr1
+ to: WHERE expr1=const AND expr2 LIKE const
+
+ because LIKE works differently comparing to the regular "=" operator:
+
+ 1. LIKE performs a stricter one-character-to-one-character comparison
+ and does not recognize contractions and expansions.
+ Replacing "expr1" to "const in LIKE would make the condition
+ stricter in case of a complex collation.
+
+ 2. LIKE does not ignore trailing spaces and thus works differently
+ from the "=" operator in case of "PAD SPACE" collations
+ (which are the majority in MariaDB). So, for "PAD SPACE" collations:
+
+ - expr1=const - ignores trailing spaces
+ - const LIKE expr2 - does not ignore trailing spaces
+ - expr2 LIKE const - does not ignore trailing spaces
+
+ Allow only "binary" for now.
+ It neither ignores trailing spaces nor has contractions/expansions.
+
+ TODO:
+ We could still replace "expr1" to "const" in "expr1 LIKE expr2"
+ in case of a "PAD SPACE" collation, but only if "expr2" has '%'
+ at the end.
+ */
+ return ((Item_func_like *)this)->compare_collation() == &my_charset_bin ?
+ COND_TRUE : COND_OK;
+ }
const char *func_name() const { return "like"; }
bool fix_fields(THD *thd, Item **ref);
void cleanup();
+
+ bool find_selective_predicates_list_processor(uchar *arg);
+};
+
+
+class Regexp_processor_pcre
+{
+ pcre *m_pcre;
+ bool m_conversion_is_needed;
+ bool m_is_const;
+ int m_library_flags;
+ CHARSET_INFO *m_data_charset;
+ CHARSET_INFO *m_library_charset;
+ String m_prev_pattern;
+ int m_pcre_exec_rc;
+ int m_SubStrVec[30];
+ uint m_subpatterns_needed;
+ void pcre_exec_warn(int rc) const;
+ int pcre_exec_with_warn(const pcre *code, const pcre_extra *extra,
+ const char *subject, int length, int startoffset,
+ int options, int *ovector, int ovecsize);
+public:
+ String *convert_if_needed(String *src, String *converter);
+ String subject_converter;
+ String pattern_converter;
+ String replace_converter;
+ Regexp_processor_pcre() :
+ m_pcre(NULL), m_conversion_is_needed(true), m_is_const(0),
+ m_library_flags(0),
+ m_data_charset(&my_charset_utf8_general_ci),
+ m_library_charset(&my_charset_utf8_general_ci),
+ m_subpatterns_needed(0)
+ {}
+ int default_regex_flags();
+ void init(CHARSET_INFO *data_charset, int extra_flags, uint nsubpatterns)
+ {
+ m_library_flags= default_regex_flags() | extra_flags |
+ (data_charset != &my_charset_bin ?
+ (PCRE_UTF8 | PCRE_UCP) : 0) |
+ ((data_charset->state &
+ (MY_CS_BINSORT | MY_CS_CSSORT)) ? 0 : PCRE_CASELESS);
+
+ // Convert text data to utf-8.
+ m_library_charset= data_charset == &my_charset_bin ?
+ &my_charset_bin : &my_charset_utf8_general_ci;
+
+ m_conversion_is_needed= (data_charset != &my_charset_bin) &&
+ !my_charset_same(data_charset, m_library_charset);
+ m_subpatterns_needed= nsubpatterns;
+ }
+ void fix_owner(Item_func *owner, Item *subject_arg, Item *pattern_arg);
+ bool compile(String *pattern, bool send_error);
+ bool compile(Item *item, bool send_error);
+ bool recompile(Item *item)
+ {
+ return !m_is_const && compile(item, false);
+ }
+ bool exec(const char *str, int length, int offset);
+ bool exec(String *str, int offset, uint n_result_offsets_to_convert);
+ bool exec(Item *item, int offset, uint n_result_offsets_to_convert);
+ bool match() const { return m_pcre_exec_rc < 0 ? 0 : 1; }
+ int nsubpatterns() const { return m_pcre_exec_rc <= 0 ? 0 : m_pcre_exec_rc; }
+ int subpattern_start(int n) const
+ {
+ return m_pcre_exec_rc <= 0 ? 0 : m_SubStrVec[n * 2];
+ }
+ int subpattern_end(int n) const
+ {
+ return m_pcre_exec_rc <= 0 ? 0 : m_SubStrVec[n * 2 + 1];
+ }
+ int subpattern_length(int n) const
+ {
+ return subpattern_end(n) - subpattern_start(n);
+ }
+ void cleanup()
+ {
+ if (m_pcre)
+ {
+ pcre_free(m_pcre);
+ m_pcre= NULL;
+ }
+ m_prev_pattern.length(0);
+ }
+ bool is_compiled() const { return m_pcre != NULL; }
+ bool is_const() const { return m_is_const; }
+ void set_const(bool arg) { m_is_const= arg; }
+ CHARSET_INFO * library_charset() const { return m_library_charset; }
};
class Item_func_regex :public Item_bool_func
{
- my_regex_t preg;
- bool regex_compiled;
- bool regex_is_const;
- String prev_regexp;
+ Regexp_processor_pcre re;
DTCollation cmp_collation;
- CHARSET_INFO *regex_lib_charset;
- int regex_lib_flags;
- String conv;
- int regcomp(bool send_error);
public:
- Item_func_regex(Item *a,Item *b) :Item_bool_func(a,b),
- regex_compiled(0),regex_is_const(0) {}
- void cleanup();
+ Item_func_regex(Item *a,Item *b) :Item_bool_func(a,b)
+ {}
+ void cleanup()
+ {
+ DBUG_ENTER("Item_func_regex::cleanup");
+ Item_bool_func::cleanup();
+ re.cleanup();
+ DBUG_VOID_RETURN;
+ }
longlong val_int();
- bool fix_fields(THD *thd, Item **ref);
+ void fix_length_and_dec();
const char *func_name() const { return "regexp"; }
virtual inline void print(String *str, enum_query_type query_type)
@@ -1510,6 +1649,26 @@ public:
};
+class Item_func_regexp_instr :public Item_int_func
+{
+ Regexp_processor_pcre re;
+ DTCollation cmp_collation;
+public:
+ Item_func_regexp_instr(Item *a, Item *b) :Item_int_func(a, b)
+ {}
+ void cleanup()
+ {
+ DBUG_ENTER("Item_func_regexp_instr::cleanup");
+ Item_int_func::cleanup();
+ re.cleanup();
+ DBUG_VOID_RETURN;
+ }
+ longlong val_int();
+ void fix_length_and_dec();
+ const char *func_name() const { return "regexp_instr"; }
+};
+
+
typedef class Item COND;
class Item_cond :public Item_bool_func
@@ -1712,6 +1871,8 @@ class Item_equal: public Item_bool_func
*/
Item_field *context_field;
+ bool link_equal_fields;
+
public:
COND_EQUAL *upper_levels; /* multiple equalities of upper and levels */
@@ -1730,7 +1891,7 @@ public:
bool contains(Field *field);
Item* get_first(struct st_join_table *context, Item *field);
/** Get number of field items / references to field items in this object */
- uint n_field_items() { return equal_items.elements-test(with_const); }
+ uint n_field_items() { return equal_items.elements - MY_TEST(with_const); }
void merge(Item_equal *item);
bool merge_with_check(Item_equal *equal_item, bool save_merged);
void merge_into_list(List<Item_equal> *list, bool save_merged,
@@ -1750,6 +1911,8 @@ public:
CHARSET_INFO *compare_collation();
void set_context_field(Item_field *ctx_field) { context_field= ctx_field; }
+ void set_link_equal_fields(bool flag) { link_equal_fields= flag; }
+ friend class Item_equal_fields_iterator;
bool count_sargable_conds(uchar *arg);
friend class Item_equal_iterator<List_iterator_fast,Item>;
friend class Item_equal_iterator<List_iterator,Item>;
@@ -1886,6 +2049,8 @@ public:
}
Item *neg_transformer(THD *thd);
void mark_as_condition_AND_part(TABLE_LIST *embedding);
+ virtual uint exists2in_reserved_items() { return list.elements; };
+ bool walk_top_and(Item_processor processor, uchar *arg);
};
inline bool is_cond_and(Item *item)
@@ -1918,6 +2083,14 @@ public:
Item *neg_transformer(THD *thd);
};
+class Item_func_dyncol_check :public Item_bool_func
+{
+public:
+ Item_func_dyncol_check(Item *str) :Item_bool_func(str) {}
+ longlong val_int();
+ const char *func_name() const { return "column_check"; }
+};
+
class Item_func_dyncol_exists :public Item_bool_func
{
public:
diff --git a/sql/item_create.cc b/sql/item_create.cc
index 45de3850fcd..852891f7743 100644
--- a/sql/item_create.cc
+++ b/sql/item_create.cc
@@ -22,6 +22,7 @@
Functions to create an item. Used by sql_yac.yy
*/
+#include <my_global.h>
#include "sql_priv.h"
/*
It is necessary to include set_var.h instead of item.h because there
@@ -32,6 +33,8 @@
#include "set_var.h"
#include "sp_head.h"
#include "sp.h"
+#include "item_inetfunc.h"
+#include "sql_time.h"
/*
=============================================================================
@@ -55,7 +58,7 @@ static void wrong_precision_error(uint errcode, Item *a,
char buff[1024];
String buf(buff, sizeof(buff), system_charset_info);
- my_error(errcode, MYF(0), (uint) min(number, UINT_MAX32),
+ my_error(errcode, MYF(0), (uint) MY_MIN(number, UINT_MAX32),
item_name(a, &buf), maximum);
}
@@ -447,6 +450,19 @@ protected:
};
+class Create_func_binlog_gtid_pos : public Create_func_arg2
+{
+public:
+ virtual Item *create_2_arg(THD *thd, Item *arg1, Item *arg2);
+
+ static Create_func_binlog_gtid_pos s_singleton;
+
+protected:
+ Create_func_binlog_gtid_pos() {}
+ virtual ~Create_func_binlog_gtid_pos() {}
+};
+
+
class Create_func_bit_count : public Create_func_arg1
{
public:
@@ -526,6 +542,54 @@ protected:
virtual ~Create_func_coercibility() {}
};
+class Create_func_dyncol_check : public Create_func_arg1
+{
+public:
+ virtual Item *create_1_arg(THD *thd, Item *arg1);
+
+ static Create_func_dyncol_check s_singleton;
+
+protected:
+ Create_func_dyncol_check() {}
+ virtual ~Create_func_dyncol_check() {}
+};
+
+class Create_func_dyncol_exists : public Create_func_arg2
+{
+public:
+ virtual Item *create_2_arg(THD *thd, Item *arg1, Item *arg2);
+
+ static Create_func_dyncol_exists s_singleton;
+
+protected:
+ Create_func_dyncol_exists() {}
+ virtual ~Create_func_dyncol_exists() {}
+};
+
+class Create_func_dyncol_list : public Create_func_arg1
+{
+public:
+ virtual Item *create_1_arg(THD *thd, Item *arg1);
+
+ static Create_func_dyncol_list s_singleton;
+
+protected:
+ Create_func_dyncol_list() {}
+ virtual ~Create_func_dyncol_list() {}
+};
+
+class Create_func_dyncol_json : public Create_func_arg1
+{
+public:
+ virtual Item *create_1_arg(THD *thd, Item *arg1);
+
+ static Create_func_dyncol_json s_singleton;
+
+protected:
+ Create_func_dyncol_json() {}
+ virtual ~Create_func_dyncol_json() {}
+};
+
class Create_func_compress : public Create_func_arg1
{
@@ -553,6 +617,19 @@ protected:
};
+class Create_func_decode_histogram : public Create_func_arg2
+{
+public:
+ Item *create_2_arg(THD *thd, Item *arg1, Item *arg2);
+
+ static Create_func_decode_histogram s_singleton;
+
+protected:
+ Create_func_decode_histogram() {}
+ virtual ~Create_func_decode_histogram() {}
+};
+
+
class Create_func_concat_ws : public Create_native_func
{
public:
@@ -1076,6 +1153,19 @@ protected:
};
+class Create_func_from_base64 : public Create_func_arg1
+{
+public:
+ virtual Item *create_1_arg(THD *thd, Item *arg1);
+
+ static Create_func_from_base64 s_singleton;
+
+protected:
+ Create_func_from_base64() {}
+ virtual ~Create_func_from_base64() {}
+};
+
+
class Create_func_from_days : public Create_func_arg1
{
public:
@@ -1175,6 +1265,21 @@ protected:
};
+#if defined(HAVE_SPATIAL) && !defined(DBUG_OFF)
+class Create_func_gis_debug : public Create_func_arg1
+{
+ public:
+ virtual Item *create_1_arg(THD *thd, Item *arg1);
+
+ static Create_func_gis_debug s_singleton;
+
+ protected:
+ Create_func_gis_debug() {}
+ virtual ~Create_func_gis_debug() {}
+};
+#endif
+
+
#ifdef HAVE_SPATIAL
class Create_func_glength : public Create_func_arg1
{
@@ -1255,6 +1360,84 @@ protected:
};
+class Create_func_inet6_aton : public Create_func_arg1
+{
+public:
+ virtual Item *create_1_arg(THD *thd, Item *arg1);
+
+ static Create_func_inet6_aton s_singleton;
+
+protected:
+ Create_func_inet6_aton() {}
+ virtual ~Create_func_inet6_aton() {}
+};
+
+
+class Create_func_inet6_ntoa : public Create_func_arg1
+{
+public:
+ virtual Item *create_1_arg(THD *thd, Item *arg1);
+
+ static Create_func_inet6_ntoa s_singleton;
+
+protected:
+ Create_func_inet6_ntoa() {}
+ virtual ~Create_func_inet6_ntoa() {}
+};
+
+
+class Create_func_is_ipv4 : public Create_func_arg1
+{
+public:
+ virtual Item *create_1_arg(THD *thd, Item *arg1);
+
+ static Create_func_is_ipv4 s_singleton;
+
+protected:
+ Create_func_is_ipv4() {}
+ virtual ~Create_func_is_ipv4() {}
+};
+
+
+class Create_func_is_ipv6 : public Create_func_arg1
+{
+public:
+ virtual Item *create_1_arg(THD *thd, Item *arg1);
+
+ static Create_func_is_ipv6 s_singleton;
+
+protected:
+ Create_func_is_ipv6() {}
+ virtual ~Create_func_is_ipv6() {}
+};
+
+
+class Create_func_is_ipv4_compat : public Create_func_arg1
+{
+public:
+ virtual Item *create_1_arg(THD *thd, Item *arg1);
+
+ static Create_func_is_ipv4_compat s_singleton;
+
+protected:
+ Create_func_is_ipv4_compat() {}
+ virtual ~Create_func_is_ipv4_compat() {}
+};
+
+
+class Create_func_is_ipv4_mapped : public Create_func_arg1
+{
+public:
+ virtual Item *create_1_arg(THD *thd, Item *arg1);
+
+ static Create_func_is_ipv4_mapped s_singleton;
+
+protected:
+ Create_func_is_ipv4_mapped() {}
+ virtual ~Create_func_is_ipv4_mapped() {}
+};
+
+
class Create_func_instr : public Create_func_arg2
{
public:
@@ -1709,6 +1892,19 @@ protected:
};
+class Create_func_master_gtid_wait : public Create_native_func
+{
+public:
+ virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list);
+
+ static Create_func_master_gtid_wait s_singleton;
+
+protected:
+ Create_func_master_gtid_wait() {}
+ virtual ~Create_func_master_gtid_wait() {}
+};
+
+
class Create_func_md5 : public Create_func_arg1
{
public:
@@ -1940,6 +2136,45 @@ protected:
};
+class Create_func_regexp_instr : public Create_func_arg2
+{
+public:
+ virtual Item *create_2_arg(THD *thd, Item *arg1, Item *arg2);
+
+ static Create_func_regexp_instr s_singleton;
+
+protected:
+ Create_func_regexp_instr() {}
+ virtual ~Create_func_regexp_instr() {}
+};
+
+
+class Create_func_regexp_replace : public Create_func_arg3
+{
+public:
+ virtual Item *create_3_arg(THD *thd, Item *arg1, Item *arg2, Item *arg3);
+
+ static Create_func_regexp_replace s_singleton;
+
+protected:
+ Create_func_regexp_replace() {}
+ virtual ~Create_func_regexp_replace() {}
+};
+
+
+class Create_func_regexp_substr : public Create_func_arg2
+{
+public:
+ virtual Item *create_2_arg(THD *thd, Item *arg1, Item *arg2);
+
+ static Create_func_regexp_substr s_singleton;
+
+protected:
+ Create_func_regexp_substr() {}
+ virtual ~Create_func_regexp_substr() {}
+};
+
+
class Create_func_radians : public Create_func_arg1
{
public:
@@ -2005,19 +2240,6 @@ protected:
};
-class Create_func_row_count : public Create_func_arg0
-{
-public:
- virtual Item *create_builder(THD *thd);
-
- static Create_func_row_count s_singleton;
-
-protected:
- Create_func_row_count() {}
- virtual ~Create_func_row_count() {}
-};
-
-
class Create_func_rpad : public Create_func_arg3
{
public:
@@ -2295,6 +2517,19 @@ protected:
};
+class Create_func_to_base64 : public Create_func_arg1
+{
+public:
+ virtual Item *create_1_arg(THD *thd, Item *arg1);
+
+ static Create_func_to_base64 s_singleton;
+
+protected:
+ Create_func_to_base64() {}
+ virtual ~Create_func_to_base64() {}
+};
+
+
class Create_func_to_days : public Create_func_arg1
{
public:
@@ -3052,6 +3287,23 @@ Create_func_bin::create_1_arg(THD *thd, Item *arg1)
}
+Create_func_binlog_gtid_pos Create_func_binlog_gtid_pos::s_singleton;
+
+Item*
+Create_func_binlog_gtid_pos::create_2_arg(THD *thd, Item *arg1, Item *arg2)
+{
+#ifdef HAVE_REPLICATION
+ if (!mysql_bin_log.is_open())
+#endif
+ {
+ my_error(ER_NO_BINARY_LOGGING, MYF(0));
+ return NULL;
+ }
+ thd->lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_SYSTEM_FUNCTION);
+ return new (thd->mem_root) Item_func_binlog_gtid_pos(arg1, arg2);
+}
+
+
Create_func_bit_count Create_func_bit_count::s_singleton;
Item*
@@ -3108,6 +3360,38 @@ Create_func_coercibility::create_1_arg(THD *thd, Item *arg1)
}
+Create_func_dyncol_check Create_func_dyncol_check::s_singleton;
+
+Item*
+Create_func_dyncol_check::create_1_arg(THD *thd, Item *arg1)
+{
+ return new (thd->mem_root) Item_func_dyncol_check(arg1);
+}
+
+Create_func_dyncol_exists Create_func_dyncol_exists::s_singleton;
+
+Item*
+Create_func_dyncol_exists::create_2_arg(THD *thd, Item *arg1, Item *arg2)
+{
+ return new (thd->mem_root) Item_func_dyncol_exists(arg1, arg2);
+}
+
+Create_func_dyncol_list Create_func_dyncol_list::s_singleton;
+
+Item*
+Create_func_dyncol_list::create_1_arg(THD *thd, Item *arg1)
+{
+ return new (thd->mem_root) Item_func_dyncol_list(arg1);
+}
+
+Create_func_dyncol_json Create_func_dyncol_json::s_singleton;
+
+Item*
+Create_func_dyncol_json::create_1_arg(THD *thd, Item *arg1)
+{
+ return new (thd->mem_root) Item_func_dyncol_json(arg1);
+}
+
Create_func_concat Create_func_concat::s_singleton;
Item*
@@ -3128,6 +3412,13 @@ Create_func_concat::create_native(THD *thd, LEX_STRING name,
return new (thd->mem_root) Item_func_concat(*item_list);
}
+Create_func_decode_histogram Create_func_decode_histogram::s_singleton;
+
+Item *
+Create_func_decode_histogram::create_2_arg(THD *thd, Item *arg1, Item *arg2)
+{
+ return new (thd->mem_root) Item_func_decode_histogram(arg1, arg2);
+}
Create_func_concat_ws Create_func_concat_ws::s_singleton;
@@ -3701,6 +3992,16 @@ Create_func_format::create_native(THD *thd, LEX_STRING name,
}
+Create_func_from_base64 Create_func_from_base64::s_singleton;
+
+
+Item *
+Create_func_from_base64::create_1_arg(THD *thd, Item *arg1)
+{
+ return new (thd->mem_root) Item_func_from_base64(arg1);
+}
+
+
Create_func_found_rows Create_func_found_rows::s_singleton;
Item*
@@ -3874,6 +4175,17 @@ Create_func_get_lock::create_2_arg(THD *thd, Item *arg1, Item *arg2)
}
+#if defined(HAVE_SPATIAL) && !defined(DBUG_OFF)
+Create_func_gis_debug Create_func_gis_debug::s_singleton;
+
+Item*
+Create_func_gis_debug::create_1_arg(THD *thd, Item *arg1)
+{
+ return new (thd->mem_root) Item_func_gis_debug(arg1);
+}
+#endif
+
+
#ifdef HAVE_SPATIAL
Create_func_glength Create_func_glength::s_singleton;
@@ -3933,6 +4245,24 @@ Create_func_inet_ntoa::create_1_arg(THD *thd, Item *arg1)
}
+Create_func_inet6_aton Create_func_inet6_aton::s_singleton;
+
+Item*
+Create_func_inet6_aton::create_1_arg(THD *thd, Item *arg1)
+{
+ return new (thd->mem_root) Item_func_inet6_aton(arg1);
+}
+
+
+Create_func_inet6_ntoa Create_func_inet6_ntoa::s_singleton;
+
+Item*
+Create_func_inet6_ntoa::create_1_arg(THD *thd, Item *arg1)
+{
+ return new (thd->mem_root) Item_func_inet6_ntoa(arg1);
+}
+
+
Create_func_inet_aton Create_func_inet_aton::s_singleton;
Item*
@@ -3942,6 +4272,42 @@ Create_func_inet_aton::create_1_arg(THD *thd, Item *arg1)
}
+Create_func_is_ipv4 Create_func_is_ipv4::s_singleton;
+
+Item*
+Create_func_is_ipv4::create_1_arg(THD *thd, Item *arg1)
+{
+ return new (thd->mem_root) Item_func_is_ipv4(arg1);
+}
+
+
+Create_func_is_ipv6 Create_func_is_ipv6::s_singleton;
+
+Item*
+Create_func_is_ipv6::create_1_arg(THD *thd, Item *arg1)
+{
+ return new (thd->mem_root) Item_func_is_ipv6(arg1);
+}
+
+
+Create_func_is_ipv4_compat Create_func_is_ipv4_compat::s_singleton;
+
+Item*
+Create_func_is_ipv4_compat::create_1_arg(THD *thd, Item *arg1)
+{
+ return new (thd->mem_root) Item_func_is_ipv4_compat(arg1);
+}
+
+
+Create_func_is_ipv4_mapped Create_func_is_ipv4_mapped::s_singleton;
+
+Item*
+Create_func_is_ipv4_mapped::create_1_arg(THD *thd, Item *arg1)
+{
+ return new (thd->mem_root) Item_func_is_ipv4_mapped(arg1);
+}
+
+
Create_func_instr Create_func_instr::s_singleton;
Item*
@@ -4392,27 +4758,75 @@ Create_func_master_pos_wait::create_native(THD *thd, LEX_STRING name,
if (item_list != NULL)
arg_count= item_list->elements;
+ if (arg_count < 2 || arg_count > 4)
+ {
+ my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str);
+ return func;
+ }
+
+ thd->lex->safe_to_cache_query= 0;
+
+ Item *param_1= item_list->pop();
+ Item *param_2= item_list->pop();
switch (arg_count) {
case 2:
{
- Item *param_1= item_list->pop();
- Item *param_2= item_list->pop();
func= new (thd->mem_root) Item_master_pos_wait(param_1, param_2);
- thd->lex->safe_to_cache_query= 0;
break;
}
case 3:
{
- Item *param_1= item_list->pop();
- Item *param_2= item_list->pop();
Item *param_3= item_list->pop();
func= new (thd->mem_root) Item_master_pos_wait(param_1, param_2, param_3);
- thd->lex->safe_to_cache_query= 0;
break;
}
- default:
+ case 4:
+ {
+ Item *param_3= item_list->pop();
+ Item *param_4= item_list->pop();
+ func= new (thd->mem_root) Item_master_pos_wait(param_1, param_2, param_3,
+ param_4);
+ break;
+ }
+ }
+
+ return func;
+}
+
+
+Create_func_master_gtid_wait Create_func_master_gtid_wait::s_singleton;
+
+Item*
+Create_func_master_gtid_wait::create_native(THD *thd, LEX_STRING name,
+ List<Item> *item_list)
+{
+ Item *func= NULL;
+ int arg_count= 0;
+
+ thd->lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_SYSTEM_FUNCTION);
+
+ if (item_list != NULL)
+ arg_count= item_list->elements;
+
+ if (arg_count < 1 || arg_count > 2)
{
my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str);
+ return func;
+ }
+
+ thd->lex->safe_to_cache_query= 0;
+
+ Item *param_1= item_list->pop();
+ switch (arg_count) {
+ case 1:
+ {
+ func= new (thd->mem_root) Item_master_gtid_wait(param_1);
+ break;
+ }
+ case 2:
+ {
+ Item *param_2= item_list->pop();
+ func= new (thd->mem_root) Item_master_gtid_wait(param_1, param_2);
break;
}
}
@@ -4589,6 +5003,33 @@ Create_func_quote::create_1_arg(THD *thd, Item *arg1)
}
+Create_func_regexp_instr Create_func_regexp_instr::s_singleton;
+
+Item*
+Create_func_regexp_instr::create_2_arg(THD *thd, Item *arg1, Item *arg2)
+{
+ return new (thd->mem_root) Item_func_regexp_instr(arg1, arg2);
+}
+
+
+Create_func_regexp_replace Create_func_regexp_replace::s_singleton;
+
+Item*
+Create_func_regexp_replace::create_3_arg(THD *thd, Item *arg1, Item *arg2, Item *arg3)
+{
+ return new (thd->mem_root) Item_func_regexp_replace(arg1, arg2, arg3);
+}
+
+
+Create_func_regexp_substr Create_func_regexp_substr::s_singleton;
+
+Item*
+Create_func_regexp_substr::create_2_arg(THD *thd, Item *arg1, Item *arg2)
+{
+ return new (thd->mem_root) Item_func_regexp_substr(arg1, arg2);
+}
+
+
Create_func_radians Create_func_radians::s_singleton;
Item*
@@ -4707,18 +5148,6 @@ Create_func_round::create_native(THD *thd, LEX_STRING name,
}
-Create_func_row_count Create_func_row_count::s_singleton;
-
-Item*
-Create_func_row_count::create_builder(THD *thd)
-{
- DBUG_ENTER("Create_func_row_count::create");
- thd->lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_SYSTEM_FUNCTION);
- thd->lex->safe_to_cache_query= 0;
- DBUG_RETURN(new (thd->mem_root) Item_func_row_count());
-}
-
-
Create_func_rpad Create_func_rpad::s_singleton;
Item*
@@ -4807,26 +5236,7 @@ Create_func_space Create_func_space::s_singleton;
Item*
Create_func_space::create_1_arg(THD *thd, Item *arg1)
{
- /**
- TODO: Fix Bug#23637
- The parsed item tree should not depend on
- <code>thd->variables.collation_connection</code>.
- */
- CHARSET_INFO *cs= thd->variables.collation_connection;
- Item *sp;
-
- if (cs->mbminlen > 1)
- {
- uint dummy_errors;
- 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, DERIVATION_COERCIBLE, MY_REPERTOIRE_ASCII);
- }
-
- return new (thd->mem_root) Item_func_repeat(sp, arg1);
+ return new (thd->mem_root) Item_func_space(arg1);
}
@@ -4934,6 +5344,15 @@ Create_func_timediff::create_2_arg(THD *thd, Item *arg1, Item *arg2)
}
+Create_func_to_base64 Create_func_to_base64::s_singleton;
+
+Item*
+Create_func_to_base64::create_1_arg(THD *thd, Item *arg1)
+{
+ return new (thd->mem_root) Item_func_to_base64(arg1);
+}
+
+
Create_func_to_days Create_func_to_days::s_singleton;
Item*
@@ -5235,6 +5654,7 @@ static Native_func_registry func_array[] =
{ { C_STRING_WITH_LEN("ATAN2") }, BUILDER(Create_func_atan)},
{ { C_STRING_WITH_LEN("BENCHMARK") }, BUILDER(Create_func_benchmark)},
{ { C_STRING_WITH_LEN("BIN") }, BUILDER(Create_func_bin)},
+ { { C_STRING_WITH_LEN("BINLOG_GTID_POS") }, BUILDER(Create_func_binlog_gtid_pos)},
{ { C_STRING_WITH_LEN("BIT_COUNT") }, BUILDER(Create_func_bit_count)},
{ { C_STRING_WITH_LEN("BIT_LENGTH") }, BUILDER(Create_func_bit_length)},
{ { C_STRING_WITH_LEN("BUFFER") }, GEOM_BUILDER(Create_func_buffer)},
@@ -5244,6 +5664,10 @@ static Native_func_registry func_array[] =
{ { C_STRING_WITH_LEN("CHARACTER_LENGTH") }, BUILDER(Create_func_char_length)},
{ { C_STRING_WITH_LEN("CHAR_LENGTH") }, BUILDER(Create_func_char_length)},
{ { C_STRING_WITH_LEN("COERCIBILITY") }, BUILDER(Create_func_coercibility)},
+ { { C_STRING_WITH_LEN("COLUMN_CHECK") }, BUILDER(Create_func_dyncol_check)},
+ { { C_STRING_WITH_LEN("COLUMN_EXISTS") }, BUILDER(Create_func_dyncol_exists)},
+ { { C_STRING_WITH_LEN("COLUMN_LIST") }, BUILDER(Create_func_dyncol_list)},
+ { { C_STRING_WITH_LEN("COLUMN_JSON") }, BUILDER(Create_func_dyncol_json)},
{ { C_STRING_WITH_LEN("COMPRESS") }, BUILDER(Create_func_compress)},
{ { C_STRING_WITH_LEN("CONCAT") }, BUILDER(Create_func_concat)},
{ { C_STRING_WITH_LEN("CONCAT_WS") }, BUILDER(Create_func_concat_ws)},
@@ -5262,6 +5686,7 @@ static Native_func_registry func_array[] =
{ { C_STRING_WITH_LEN("DAYOFYEAR") }, BUILDER(Create_func_dayofyear)},
{ { C_STRING_WITH_LEN("DECODE") }, BUILDER(Create_func_decode)},
{ { C_STRING_WITH_LEN("DEGREES") }, BUILDER(Create_func_degrees)},
+ { { C_STRING_WITH_LEN("DECODE_HISTOGRAM") }, BUILDER(Create_func_decode_histogram)},
{ { C_STRING_WITH_LEN("DES_DECRYPT") }, BUILDER(Create_func_des_decrypt)},
{ { C_STRING_WITH_LEN("DES_ENCRYPT") }, BUILDER(Create_func_des_encrypt)},
{ { C_STRING_WITH_LEN("DIMENSION") }, GEOM_BUILDER(Create_func_dimension)},
@@ -5281,6 +5706,7 @@ static Native_func_registry func_array[] =
{ { C_STRING_WITH_LEN("FLOOR") }, BUILDER(Create_func_floor)},
{ { C_STRING_WITH_LEN("FORMAT") }, BUILDER(Create_func_format)},
{ { C_STRING_WITH_LEN("FOUND_ROWS") }, BUILDER(Create_func_found_rows)},
+ { { C_STRING_WITH_LEN("FROM_BASE64") }, BUILDER(Create_func_from_base64)},
{ { C_STRING_WITH_LEN("FROM_DAYS") }, BUILDER(Create_func_from_days)},
{ { C_STRING_WITH_LEN("FROM_UNIXTIME") }, BUILDER(Create_func_from_unixtime)},
{ { C_STRING_WITH_LEN("GEOMCOLLFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
@@ -5300,6 +5726,12 @@ static Native_func_registry func_array[] =
{ { C_STRING_WITH_LEN("IFNULL") }, BUILDER(Create_func_ifnull)},
{ { C_STRING_WITH_LEN("INET_ATON") }, BUILDER(Create_func_inet_aton)},
{ { C_STRING_WITH_LEN("INET_NTOA") }, BUILDER(Create_func_inet_ntoa)},
+ { { C_STRING_WITH_LEN("INET6_ATON") }, BUILDER(Create_func_inet6_aton)},
+ { { C_STRING_WITH_LEN("INET6_NTOA") }, BUILDER(Create_func_inet6_ntoa)},
+ { { C_STRING_WITH_LEN("IS_IPV4") }, BUILDER(Create_func_is_ipv4)},
+ { { C_STRING_WITH_LEN("IS_IPV6") }, BUILDER(Create_func_is_ipv6)},
+ { { C_STRING_WITH_LEN("IS_IPV4_COMPAT") }, BUILDER(Create_func_is_ipv4_compat)},
+ { { C_STRING_WITH_LEN("IS_IPV4_MAPPED") }, BUILDER(Create_func_is_ipv4_mapped)},
{ { C_STRING_WITH_LEN("INSTR") }, BUILDER(Create_func_instr)},
{ { C_STRING_WITH_LEN("INTERIORRINGN") }, GEOM_BUILDER(Create_func_interiorringn)},
{ { C_STRING_WITH_LEN("INTERSECTS") }, GEOM_BUILDER(Create_func_mbr_intersects)},
@@ -5334,6 +5766,7 @@ static Native_func_registry func_array[] =
{ { C_STRING_WITH_LEN("MAKEDATE") }, BUILDER(Create_func_makedate)},
{ { C_STRING_WITH_LEN("MAKETIME") }, BUILDER(Create_func_maketime)},
{ { C_STRING_WITH_LEN("MAKE_SET") }, BUILDER(Create_func_make_set)},
+ { { C_STRING_WITH_LEN("MASTER_GTID_WAIT") }, BUILDER(Create_func_master_gtid_wait)},
{ { C_STRING_WITH_LEN("MASTER_POS_WAIT") }, BUILDER(Create_func_master_pos_wait)},
{ { C_STRING_WITH_LEN("MBRCONTAINS") }, GEOM_BUILDER(Create_func_mbr_contains)},
{ { C_STRING_WITH_LEN("MBRDISJOINT") }, GEOM_BUILDER(Create_func_mbr_disjoint)},
@@ -5378,12 +5811,14 @@ static Native_func_registry func_array[] =
{ { C_STRING_WITH_LEN("POW") }, BUILDER(Create_func_pow)},
{ { C_STRING_WITH_LEN("POWER") }, BUILDER(Create_func_pow)},
{ { C_STRING_WITH_LEN("QUOTE") }, BUILDER(Create_func_quote)},
+ { { C_STRING_WITH_LEN("REGEXP_INSTR") }, BUILDER(Create_func_regexp_instr)},
+ { { C_STRING_WITH_LEN("REGEXP_REPLACE") }, BUILDER(Create_func_regexp_replace)},
+ { { C_STRING_WITH_LEN("REGEXP_SUBSTR") }, BUILDER(Create_func_regexp_substr)},
{ { C_STRING_WITH_LEN("RADIANS") }, BUILDER(Create_func_radians)},
{ { C_STRING_WITH_LEN("RAND") }, BUILDER(Create_func_rand)},
{ { C_STRING_WITH_LEN("RELEASE_LOCK") }, BUILDER(Create_func_release_lock)},
{ { C_STRING_WITH_LEN("REVERSE") }, BUILDER(Create_func_reverse)},
{ { C_STRING_WITH_LEN("ROUND") }, BUILDER(Create_func_round)},
- { { C_STRING_WITH_LEN("ROW_COUNT") }, BUILDER(Create_func_row_count)},
{ { C_STRING_WITH_LEN("RPAD") }, BUILDER(Create_func_rpad)},
{ { C_STRING_WITH_LEN("RTRIM") }, BUILDER(Create_func_rtrim)},
{ { C_STRING_WITH_LEN("SEC_TO_TIME") }, BUILDER(Create_func_sec_to_time)},
@@ -5427,6 +5862,9 @@ static Native_func_registry func_array[] =
{ { C_STRING_WITH_LEN("ST_GEOMETRYTYPE") }, GEOM_BUILDER(Create_func_geometry_type)},
{ { C_STRING_WITH_LEN("ST_GEOMFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)},
{ { C_STRING_WITH_LEN("ST_GEOMFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)},
+#ifndef DBUG_OFF
+ { { C_STRING_WITH_LEN("ST_GIS_DEBUG") }, GEOM_BUILDER(Create_func_gis_debug)},
+#endif
{ { C_STRING_WITH_LEN("ST_EQUALS") }, GEOM_BUILDER(Create_func_equals)},
{ { C_STRING_WITH_LEN("ST_INTERIORRINGN") }, GEOM_BUILDER(Create_func_interiorringn)},
{ { C_STRING_WITH_LEN("ST_INTERSECTS") }, GEOM_BUILDER(Create_func_intersects)},
@@ -5465,6 +5903,7 @@ static Native_func_registry func_array[] =
{ { C_STRING_WITH_LEN("TIME_FORMAT") }, BUILDER(Create_func_time_format)},
{ { C_STRING_WITH_LEN("TIME_TO_SEC") }, BUILDER(Create_func_time_to_sec)},
{ { C_STRING_WITH_LEN("TOUCHES") }, GEOM_BUILDER(Create_func_touches)},
+ { { C_STRING_WITH_LEN("TO_BASE64") }, BUILDER(Create_func_to_base64)},
{ { C_STRING_WITH_LEN("TO_DAYS") }, BUILDER(Create_func_to_days)},
{ { C_STRING_WITH_LEN("TO_SECONDS") }, BUILDER(Create_func_to_seconds)},
{ { C_STRING_WITH_LEN("UCASE") }, BUILDER(Create_func_ucase)},
@@ -5685,6 +6124,84 @@ create_func_cast(THD *thd, Item *a, Cast_target cast_type,
}
+static bool
+have_important_literal_warnings(const MYSQL_TIME_STATUS *status)
+{
+ return (status->warnings & ~MYSQL_TIME_NOTE_TRUNCATED) != 0;
+}
+
+
+/**
+ Builder for datetime literals:
+ TIME'00:00:00', DATE'2001-01-01', TIMESTAMP'2001-01-01 00:00:00'.
+ @param thd The current thread
+ @param str Character literal
+ @param length Length of str
+ @param type Type of literal (TIME, DATE or DATETIME)
+ @param send_error Whether to generate an error on failure
+*/
+
+Item *create_temporal_literal(THD *thd,
+ const char *str, uint length,
+ CHARSET_INFO *cs,
+ enum_field_types type,
+ bool send_error)
+{
+ MYSQL_TIME_STATUS status;
+ MYSQL_TIME ltime;
+ Item *item= NULL;
+ ulonglong flags= sql_mode_for_dates(thd);
+
+ switch(type)
+ {
+ case MYSQL_TYPE_DATE:
+ case MYSQL_TYPE_NEWDATE:
+ if (!str_to_datetime(cs, str, length, &ltime, flags, &status) &&
+ ltime.time_type == MYSQL_TIMESTAMP_DATE && !status.warnings)
+ item= new (thd->mem_root) Item_date_literal(&ltime);
+ break;
+ case MYSQL_TYPE_DATETIME:
+ if (!str_to_datetime(cs, str, length, &ltime, flags, &status) &&
+ ltime.time_type == MYSQL_TIMESTAMP_DATETIME &&
+ !have_important_literal_warnings(&status))
+ item= new (thd->mem_root) Item_datetime_literal(&ltime,
+ status.precision);
+ break;
+ case MYSQL_TYPE_TIME:
+ if (!str_to_time(cs, str, length, &ltime, 0, &status) &&
+ ltime.time_type == MYSQL_TIMESTAMP_TIME &&
+ !have_important_literal_warnings(&status))
+ item= new (thd->mem_root) Item_time_literal(&ltime,
+ status.precision);
+ break;
+ default:
+ DBUG_ASSERT(0);
+ }
+
+ if (item)
+ {
+ if (status.warnings) // e.g. a note on nanosecond truncation
+ {
+ ErrConvString err(str, length, cs);
+ make_truncated_value_warning(current_thd,
+ Sql_condition::time_warn_level(status.warnings),
+ &err, ltime.time_type, 0);
+ }
+ return item;
+ }
+
+ if (send_error)
+ {
+ const char *typestr=
+ (type == MYSQL_TYPE_DATE) ? "DATE" :
+ (type == MYSQL_TYPE_TIME) ? "TIME" : "DATETIME";
+ ErrConvString err(str, length, thd->variables.character_set_client);
+ my_error(ER_WRONG_VALUE, MYF(0), typestr, err.ptr());
+ }
+ return NULL;
+}
+
+
static List<Item> *create_func_dyncol_prepare(THD *thd,
DYNCALL_CREATE_DEF **dfs,
List<DYNCALL_CREATE_DEF> &list)
@@ -5703,7 +6220,7 @@ static List<Item> *create_func_dyncol_prepare(THD *thd,
for (uint i= 0; (def= li++) ;)
{
dfs[0][i++]= *def;
- args->push_back(def->num);
+ args->push_back(def->key);
args->push_back(def->value);
}
return args;
@@ -5719,7 +6236,6 @@ Item *create_func_dyncol_create(THD *thd, List<DYNCALL_CREATE_DEF> &list)
return new (thd->mem_root) Item_func_dyncol_create(*args, dfs);
}
-
Item *create_func_dyncol_add(THD *thd, Item *str,
List<DYNCALL_CREATE_DEF> &list)
{
@@ -5739,7 +6255,7 @@ Item *create_func_dyncol_add(THD *thd, Item *str,
Item *create_func_dyncol_delete(THD *thd, Item *str, List<Item> &nums)
{
DYNCALL_CREATE_DEF *dfs;
- Item *num;
+ Item *key;
List_iterator_fast<Item> it(nums);
List<Item> *args= new (thd->mem_root) List<Item>;
@@ -5749,12 +6265,12 @@ Item *create_func_dyncol_delete(THD *thd, Item *str, List<Item> &nums)
if (!args || !dfs)
return NULL;
- for (uint i= 0; (num= it++); i++)
+ for (uint i= 0; (key= it++); i++)
{
- dfs[i].num= num;
+ dfs[i].key= key;
dfs[i].value= new Item_null();
dfs[i].type= DYN_COL_INT;
- args->push_back(dfs[i].num);
+ args->push_back(dfs[i].key);
args->push_back(dfs[i].value);
}
diff --git a/sql/item_create.h b/sql/item_create.h
index ac6b0f8454f..05fe48f656a 100644
--- a/sql/item_create.h
+++ b/sql/item_create.h
@@ -168,6 +168,20 @@ create_func_cast(THD *thd, Item *a, Cast_target cast_type,
const char *len, const char *dec,
CHARSET_INFO *cs);
+Item *create_temporal_literal(THD *thd,
+ const char *str, uint length,
+ CHARSET_INFO *cs,
+ enum_field_types type,
+ bool send_error);
+inline
+Item *create_temporal_literal(THD *thd, const String *str,
+ enum_field_types type,
+ bool send_error)
+{
+ return create_temporal_literal(thd,
+ str->ptr(), str->length(), str->charset(),
+ type, send_error);
+}
int item_create_init();
void item_create_cleanup();
@@ -180,5 +194,6 @@ Item *create_func_dyncol_get(THD *thd, Item *num, Item *str,
Cast_target cast_type,
const char *c_len, const char *c_dec,
CHARSET_INFO *cs);
+Item *create_func_dyncol_json(THD *thd, Item *str);
#endif
diff --git a/sql/item_func.cc b/sql/item_func.cc
index a0f22d283e3..b79be7e9ce4 100644
--- a/sql/item_func.cc
+++ b/sql/item_func.cc
@@ -25,7 +25,7 @@
#pragma implementation // gcc: Class implementation
#endif
-#include "my_global.h" /* NO_EMBEDDED_ACCESS_CHECKS */
+#include "sql_plugin.h"
#include "sql_priv.h"
/*
It is necessary to include set_var.h instead of item.h because there
@@ -53,8 +53,6 @@
#include "sp.h"
#include "set_var.h"
#include "debug_sync.h"
-#include <mysql/plugin.h>
-#include <mysql/service_thd_wait.h>
#ifdef NO_EMBEDDED_ACCESS_CHECKS
#define sp_restore_security_context(A,B) while (0) {}
@@ -652,7 +650,7 @@ void Item_func::count_decimal_length()
set_if_bigger(max_int_part, args[i]->decimal_int_part());
set_if_smaller(unsigned_flag, args[i]->unsigned_flag);
}
- int precision= min(max_int_part + decimals, DECIMAL_MAX_PRECISION);
+ int precision= MY_MIN(max_int_part + decimals, DECIMAL_MAX_PRECISION);
fix_char_length(my_decimal_precision_to_length_no_truncation(precision,
decimals,
unsigned_flag));
@@ -736,7 +734,7 @@ void Item_func::signal_divide_by_null()
{
THD *thd= current_thd;
if (thd->variables.sql_mode & MODE_ERROR_FOR_DIVISION_BY_ZERO)
- push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_DIVISION_BY_ZERO,
+ push_warning(thd, Sql_condition::WARN_LEVEL_WARN, ER_DIVISION_BY_ZERO,
ER(ER_DIVISION_BY_ZERO));
null_value= 1;
}
@@ -1121,7 +1119,7 @@ bool Item_func_hybrid_result_type::get_date(MYSQL_TIME *ltime,
String tmp(buff,sizeof(buff), &my_charset_bin),*res;
if (!(res= str_op_with_null_check(&tmp)) ||
str_to_datetime_with_warn(res->charset(), res->ptr(), res->length(),
- ltime, fuzzydate) <= MYSQL_TIMESTAMP_ERROR)
+ ltime, fuzzydate))
goto err;
break;
break;
@@ -1177,13 +1175,11 @@ longlong Item_func_signed::val_int_from_str(int *error)
value= cs->cset->strtoll10(cs, start, &end, error);
if (*error > 0 || end != start+ length)
{
- char err_buff[128];
- String err_tmp(err_buff,(uint32) sizeof(err_buff), system_charset_info);
- err_tmp.copy(start, length, system_charset_info);
- push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ ErrConvString err(res);
+ push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
ER_TRUNCATED_WRONG_VALUE,
ER(ER_TRUNCATED_WRONG_VALUE), "INTEGER",
- err_tmp.c_ptr());
+ err.ptr());
}
return value;
}
@@ -1217,7 +1213,7 @@ longlong Item_func_signed::val_int()
return value;
err:
- push_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_NOTE, ER_UNKNOWN_ERROR,
+ push_warning(current_thd, Sql_condition::WARN_LEVEL_NOTE, ER_UNKNOWN_ERROR,
"Cast to signed converted positive out-of-range integer to "
"it's negative complement");
return value;
@@ -1273,7 +1269,7 @@ longlong Item_func_unsigned::val_int()
return value;
err:
- push_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_NOTE, ER_UNKNOWN_ERROR,
+ push_warning(current_thd, Sql_condition::WARN_LEVEL_NOTE, ER_UNKNOWN_ERROR,
"Cast to unsigned converted negative integer to it's "
"positive complement");
return value;
@@ -1341,7 +1337,7 @@ my_decimal *Item_decimal_typecast::val_decimal(my_decimal *dec)
return dec;
err:
- push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
ER_WARN_DATA_OUT_OF_RANGE,
ER(ER_WARN_DATA_OUT_OF_RANGE),
name, 1L);
@@ -1383,7 +1379,7 @@ double Item_double_typecast::val_real()
if ((error= truncate_double(&tmp, max_length, decimals, 0, DBL_MAX)))
{
push_warning_printf(current_thd,
- MYSQL_ERROR::WARN_LEVEL_WARN,
+ Sql_condition::WARN_LEVEL_WARN,
ER_WARN_DATA_OUT_OF_RANGE,
ER(ER_WARN_DATA_OUT_OF_RANGE),
name, 1);
@@ -1521,10 +1517,10 @@ my_decimal *Item_func_plus::decimal_op(my_decimal *decimal_value)
*/
void Item_func_additive_op::result_precision()
{
- decimals= max(args[0]->decimal_scale(), args[1]->decimal_scale());
+ decimals= MY_MAX(args[0]->decimal_scale(), args[1]->decimal_scale());
int arg1_int= args[0]->decimal_precision() - args[0]->decimal_scale();
int arg2_int= args[1]->decimal_precision() - args[1]->decimal_scale();
- int precision= max(arg1_int, arg2_int) + 1 + decimals;
+ int precision= MY_MAX(arg1_int, arg2_int) + 1 + decimals;
DBUG_ASSERT(arg1_int >= 0);
DBUG_ASSERT(arg2_int >= 0);
@@ -1765,10 +1761,10 @@ void Item_func_mul::result_precision()
unsigned_flag= args[0]->unsigned_flag | args[1]->unsigned_flag;
else
unsigned_flag= args[0]->unsigned_flag & args[1]->unsigned_flag;
- decimals= min(args[0]->decimal_scale() + args[1]->decimal_scale(),
+ decimals= MY_MIN(args[0]->decimal_scale() + args[1]->decimal_scale(),
DECIMAL_MAX_SCALE);
uint est_prec = args[0]->decimal_precision() + args[1]->decimal_precision();
- uint precision= min(est_prec, DECIMAL_MAX_PRECISION);
+ uint precision= MY_MIN(est_prec, DECIMAL_MAX_PRECISION);
max_length= my_decimal_precision_to_length_no_truncation(precision, decimals,
unsigned_flag);
}
@@ -1832,7 +1828,7 @@ void Item_func_div::result_precision()
prec_increment // 4
which gives 10 decimals digits.
*/
- uint precision=min(args[0]->decimal_precision() +
+ uint precision=MY_MIN(args[0]->decimal_precision() +
args[1]->divisor_precision_increment() + prec_increment,
DECIMAL_MAX_PRECISION);
@@ -1841,7 +1837,7 @@ void Item_func_div::result_precision()
unsigned_flag= args[0]->unsigned_flag | args[1]->unsigned_flag;
else
unsigned_flag= args[0]->unsigned_flag & args[1]->unsigned_flag;
- decimals= min(args[0]->decimal_scale() + prec_increment, DECIMAL_MAX_SCALE);
+ decimals= MY_MIN(args[0]->decimal_scale() + prec_increment, DECIMAL_MAX_SCALE);
max_length= my_decimal_precision_to_length_no_truncation(precision, decimals,
unsigned_flag);
}
@@ -1855,7 +1851,7 @@ void Item_func_div::fix_length_and_dec()
switch (cached_result_type) {
case REAL_RESULT:
{
- decimals=max(args[0]->decimals,args[1]->decimals)+prec_increment;
+ decimals=MY_MAX(args[0]->decimals,args[1]->decimals)+prec_increment;
set_if_smaller(decimals, NOT_FIXED_DEC);
uint tmp=float_length(decimals);
if (decimals == NOT_FIXED_DEC)
@@ -2047,8 +2043,8 @@ my_decimal *Item_func_mod::decimal_op(my_decimal *decimal_value)
void Item_func_mod::result_precision()
{
- decimals= max(args[0]->decimal_scale(), args[1]->decimal_scale());
- max_length= max(args[0]->max_length, args[1]->max_length);
+ decimals= MY_MAX(args[0]->decimal_scale(), args[1]->decimal_scale());
+ max_length= MY_MAX(args[0]->max_length, args[1]->max_length);
}
@@ -2376,7 +2372,7 @@ longlong Item_func_shift_left::val_int()
return 0;
}
null_value=0;
- return (shift < sizeof(longlong)*8 ? (longlong) res : LL(0));
+ return (shift < sizeof(longlong)*8 ? (longlong) res : 0);
}
longlong Item_func_shift_right::val_int()
@@ -2391,7 +2387,7 @@ longlong Item_func_shift_right::val_int()
return 0;
}
null_value=0;
- return (shift < sizeof(longlong)*8 ? (longlong) res : LL(0));
+ return (shift < sizeof(longlong)*8 ? (longlong) res : 0);
}
@@ -2595,7 +2591,7 @@ void Item_func_round::fix_length_and_dec()
if (args[0]->decimals == NOT_FIXED_DEC)
{
- decimals= min(decimals_to_set, NOT_FIXED_DEC);
+ decimals= MY_MIN(decimals_to_set, NOT_FIXED_DEC);
max_length= float_length(decimals);
cached_result_type= REAL_RESULT;
return;
@@ -2605,13 +2601,14 @@ void Item_func_round::fix_length_and_dec()
case REAL_RESULT:
case STRING_RESULT:
cached_result_type= REAL_RESULT;
- decimals= min(decimals_to_set, NOT_FIXED_DEC);
+ decimals= MY_MIN(decimals_to_set, NOT_FIXED_DEC);
max_length= float_length(decimals);
break;
case INT_RESULT:
if ((!decimals_to_set && truncate) || (args[0]->decimal_precision() < DECIMAL_LONGLONG_DIGITS))
{
- int length_can_increase= test(!truncate && (val1 < 0) && !val1_unsigned);
+ int length_can_increase= MY_TEST(!truncate && (val1 < 0) &&
+ !val1_unsigned);
max_length= args[0]->max_length + length_can_increase;
/* Here we can keep INT_RESULT */
cached_result_type= INT_RESULT;
@@ -2622,13 +2619,13 @@ void Item_func_round::fix_length_and_dec()
case DECIMAL_RESULT:
{
cached_result_type= DECIMAL_RESULT;
- decimals_to_set= min(DECIMAL_MAX_SCALE, decimals_to_set);
+ decimals_to_set= MY_MIN(DECIMAL_MAX_SCALE, decimals_to_set);
int decimals_delta= args[0]->decimals - decimals_to_set;
int precision= args[0]->decimal_precision();
int length_increase= ((decimals_delta <= 0) || truncate) ? 0:1;
precision-= decimals_delta - length_increase;
- decimals= min(decimals_to_set, DECIMAL_MAX_SCALE);
+ decimals= MY_MIN(decimals_to_set, DECIMAL_MAX_SCALE);
max_length= my_decimal_precision_to_length_no_truncation(precision,
decimals,
unsigned_flag);
@@ -2661,6 +2658,9 @@ double my_double_round(double value, longlong dec, bool dec_unsigned,
volatile double value_div_tmp= value / tmp;
volatile double value_mul_tmp= value * tmp;
+ if (!dec_negative && my_isinf(tmp)) // "dec" is too large positive number
+ return value;
+
if (dec_negative && my_isinf(tmp))
tmp2= 0.0;
else if (!dec_negative && my_isinf(value_mul_tmp))
@@ -2739,7 +2739,7 @@ my_decimal *Item_func_round::decimal_op(my_decimal *decimal_value)
my_decimal val, *value= args[0]->val_decimal(&val);
longlong dec= args[1]->val_int();
if (dec >= 0 || args[1]->unsigned_flag)
- dec= min((ulonglong) dec, decimals);
+ dec= MY_MIN((ulonglong) dec, decimals);
else if (dec < INT_MIN)
dec= INT_MIN;
@@ -2962,7 +2962,7 @@ bool Item_func_min_max::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date)
ltime->hour+= (ltime->month * 32 + ltime->day) * 24;
ltime->year= ltime->month= ltime->day= 0;
if (adjust_time_range_with_warn(ltime,
- min(decimals, TIME_SECOND_PART_DIGITS)))
+ std::min<uint>(decimals, TIME_SECOND_PART_DIGITS)))
return (null_value= true);
}
@@ -3349,7 +3349,7 @@ void Item_func_find_in_set::fix_length_and_dec()
find->length(), 0);
enum_bit=0;
if (enum_value)
- enum_bit=LL(1) << (enum_value-1);
+ enum_bit=1LL << (enum_value-1);
}
}
}
@@ -3430,7 +3430,7 @@ longlong Item_func_find_in_set::val_int()
wc == (my_wc_t) separator)
return (longlong) ++position;
else
- return LL(0);
+ return 0;
}
}
return 0;
@@ -3626,7 +3626,7 @@ udf_handler::fix_fields(THD *thd, Item_result_field *func,
free_udf(u_d);
DBUG_RETURN(TRUE);
}
- func->max_length=min(initid.max_length,MAX_BLOB_WIDTH);
+ func->max_length=MY_MIN(initid.max_length,MAX_BLOB_WIDTH);
func->maybe_null=initid.maybe_null;
const_item_cache=initid.const_item;
/*
@@ -3635,7 +3635,7 @@ udf_handler::fix_fields(THD *thd, Item_result_field *func,
*/
if (!const_item_cache && !used_tables_cache)
used_tables_cache= RAND_TABLE_BIT;
- func->decimals=min(initid.decimals,NOT_FIXED_DEC);
+ func->decimals=MY_MIN(initid.decimals,NOT_FIXED_DEC);
}
initialized=1;
if (error)
@@ -3934,144 +3934,94 @@ udf_handler::~udf_handler()
bool udf_handler::get_arguments() { return 0; }
#endif /* HAVE_DLOPEN */
-/*
-** User level locks
-*/
-
-mysql_mutex_t LOCK_user_locks;
-static HASH hash_user_locks;
-class User_level_lock
+longlong Item_master_pos_wait::val_int()
{
- uchar *key;
- size_t key_length;
-
-public:
- int count;
- bool locked;
- mysql_cond_t cond;
- my_thread_id thread_id;
- void set_thread(THD *thd) { thread_id= thd->thread_id; }
+ DBUG_ASSERT(fixed == 1);
+ THD* thd = current_thd;
+ String *log_name = args[0]->val_str(&value);
+ int event_count= 0;
- User_level_lock(const uchar *key_arg,uint length, ulong id)
- :key_length(length),count(1),locked(1), thread_id(id)
+ null_value=0;
+ if (thd->slave_thread || !log_name || !log_name->length())
{
- key= (uchar*) my_memdup(key_arg,length,MYF(0));
- mysql_cond_init(key_user_level_lock_cond, &cond, NULL);
- if (key)
- {
- if (my_hash_insert(&hash_user_locks,(uchar*) this))
- {
- my_free(key);
- key=0;
- }
- }
+ null_value = 1;
+ return 0;
}
- ~User_level_lock()
- {
- if (key)
+#ifdef HAVE_REPLICATION
+ longlong pos = (ulong)args[1]->val_int();
+ longlong timeout = (arg_count>=3) ? args[2]->val_int() : 0 ;
+ String connection_name_buff;
+ LEX_STRING connection_name;
+ Master_info *mi;
+ if (arg_count >= 4)
+ {
+ String *con;
+ if (!(con= args[3]->val_str(&connection_name_buff)))
+ goto err;
+
+ connection_name.str= (char*) con->ptr();
+ connection_name.length= con->length();
+ if (check_master_connection_name(&connection_name))
{
- my_hash_delete(&hash_user_locks,(uchar*) this);
- my_free(key);
+ my_error(ER_WRONG_ARGUMENTS, MYF(ME_JUST_WARNING),
+ "MASTER_CONNECTION_NAME");
+ goto err;
}
- mysql_cond_destroy(&cond);
}
- inline bool initialized() { return key != 0; }
- friend void item_user_lock_release(User_level_lock *ull);
- friend uchar *ull_get_key(const User_level_lock *ull, size_t *length,
- my_bool not_used);
-};
-
-uchar *ull_get_key(const User_level_lock *ull, size_t *length,
- my_bool not_used __attribute__((unused)))
-{
- *length= ull->key_length;
- return ull->key;
-}
-
-#ifdef HAVE_PSI_INTERFACE
-static PSI_mutex_key key_LOCK_user_locks;
-
-static PSI_mutex_info all_user_mutexes[]=
-{
- { &key_LOCK_user_locks, "LOCK_user_locks", PSI_FLAG_GLOBAL}
-};
-
-static void init_user_lock_psi_keys(void)
-{
- const char* category= "sql";
- int count;
-
- if (PSI_server == NULL)
- return;
-
- count= array_elements(all_user_mutexes);
- PSI_server->register_mutex(category, all_user_mutexes, count);
-}
-#endif
+ else
+ connection_name= thd->variables.default_master_connection;
-static bool item_user_lock_inited= 0;
+ mysql_mutex_lock(&LOCK_active_mi);
+ mi= master_info_index->get_master_info(&connection_name,
+ Sql_condition::WARN_LEVEL_WARN);
+ mysql_mutex_unlock(&LOCK_active_mi);
+ if (!mi)
+ goto err;
-void item_user_lock_init(void)
-{
-#ifdef HAVE_PSI_INTERFACE
- init_user_lock_psi_keys();
+ if ((event_count = mi->rli.wait_for_pos(thd, log_name, pos, timeout)) == -2)
+ {
+ null_value = 1;
+ event_count=0;
+ }
#endif
+ return event_count;
- mysql_mutex_init(key_LOCK_user_locks, &LOCK_user_locks, MY_MUTEX_INIT_SLOW);
- my_hash_init(&hash_user_locks,system_charset_info,
- 16,0,0,(my_hash_get_key) ull_get_key,NULL,0);
- item_user_lock_inited= 1;
-}
-
-void item_user_lock_free(void)
-{
- if (item_user_lock_inited)
+#ifdef HAVE_REPLICATION
+err:
{
- item_user_lock_inited= 0;
- my_hash_free(&hash_user_locks);
- mysql_mutex_destroy(&LOCK_user_locks);
+ null_value = 1;
+ return 0;
}
+#endif
}
-void item_user_lock_release(User_level_lock *ull)
-{
- ull->locked=0;
- ull->thread_id= 0;
- if (--ull->count)
- mysql_cond_signal(&ull->cond);
- else
- delete ull;
-}
-
-/**
- Wait until we are at or past the given position in the master binlog
- on the slave.
-*/
-longlong Item_master_pos_wait::val_int()
+longlong Item_master_gtid_wait::val_int()
{
DBUG_ASSERT(fixed == 1);
- THD* thd = current_thd;
- String *log_name = args[0]->val_str(&value);
- int event_count= 0;
+ longlong result= 0;
- null_value=0;
- if (thd->slave_thread || !log_name || !log_name->length())
+ if (args[0]->null_value)
{
- null_value = 1;
+ null_value= 1;
return 0;
}
+
+ null_value=0;
#ifdef HAVE_REPLICATION
- longlong pos = (ulong)args[1]->val_int();
- longlong timeout = (arg_count==3) ? args[2]->val_int() : 0 ;
- if ((event_count = active_mi->rli.wait_for_pos(thd, log_name, pos, timeout)) == -2)
- {
- null_value = 1;
- event_count=0;
- }
+ THD* thd= current_thd;
+ longlong timeout_us;
+ String *gtid_pos = args[0]->val_str(&value);
+
+ if (arg_count==2 && !args[1]->null_value)
+ timeout_us= (longlong)(1e6*args[1]->val_real());
+ else
+ timeout_us= (longlong)-1;
+
+ result= rpl_global_gtid_waiting.wait_for_pos(thd, gtid_pos, timeout_us);
#endif
- return event_count;
+ return result;
}
@@ -4117,7 +4067,7 @@ class Interruptible_wait
/** Time to wait before polling the connection status. */
-const ulonglong Interruptible_wait::m_interrupt_interval= 5 * ULL(1000000000);
+const ulonglong Interruptible_wait::m_interrupt_interval= 5 * 1000000000ULL;
/**
@@ -4162,7 +4112,140 @@ int Interruptible_wait::wait(mysql_cond_t *cond, mysql_mutex_t *mutex)
/**
- Get a user level lock. If the thread has an old lock this is first released.
+ For locks with EXPLICIT duration, MDL returns a new ticket
+ every time a lock is granted. This allows to implement recursive
+ locks without extra allocation or additional data structures, such
+ as below. However, if there are too many tickets in the same
+ MDL_context, MDL_context::find_ticket() is getting too slow,
+ since it's using a linear search.
+ This is why a separate structure is allocated for a user
+ level lock, and before requesting a new lock from MDL,
+ GET_LOCK() checks thd->ull_hash if such lock is already granted,
+ and if so, simply increments a reference counter.
+*/
+
+class User_level_lock
+{
+public:
+ MDL_ticket *lock;
+ int refs;
+};
+
+
+/** Extract a hash key from User_level_lock. */
+
+uchar *ull_get_key(const uchar *ptr, size_t *length,
+ my_bool not_used __attribute__((unused)))
+{
+ User_level_lock *ull = (User_level_lock*) ptr;
+ MDL_key *key = ull->lock->get_key();
+ *length= key->length();
+ return (uchar*) key->ptr();
+}
+
+
+/**
+ Release all user level locks for this THD.
+*/
+
+void mysql_ull_cleanup(THD *thd)
+{
+ User_level_lock *ull;
+ DBUG_ENTER("mysql_ull_cleanup");
+
+ for (uint i= 0; i < thd->ull_hash.records; i++)
+ {
+ ull = (User_level_lock*) my_hash_element(&thd->ull_hash, i);
+ thd->mdl_context.release_lock(ull->lock);
+ my_free(ull);
+ }
+
+ my_hash_free(&thd->ull_hash);
+
+ DBUG_VOID_RETURN;
+}
+
+
+/**
+ Set explicit duration for metadata locks corresponding to
+ user level locks to protect them from being released at the end
+ of transaction.
+*/
+
+void mysql_ull_set_explicit_lock_duration(THD *thd)
+{
+ User_level_lock *ull;
+ DBUG_ENTER("mysql_ull_set_explicit_lock_duration");
+
+ for (uint i= 0; i < thd->ull_hash.records; i++)
+ {
+ ull= (User_level_lock*) my_hash_element(&thd->ull_hash, i);
+ thd->mdl_context.set_lock_duration(ull->lock, MDL_EXPLICIT);
+ }
+ DBUG_VOID_RETURN;
+}
+
+
+/**
+ When MDL detects a lock wait timeout, it pushes
+ an error into the statement diagnostics area.
+ For GET_LOCK(), lock wait timeout is not an error,
+ but a special return value (0).
+ Similarly, killing get_lock wait is not an error either,
+ but a return value NULL.
+ Capture and suppress lock wait timeouts and kills.
+*/
+
+class Lock_wait_timeout_handler: public Internal_error_handler
+{
+public:
+ Lock_wait_timeout_handler() :m_lock_wait_timeout(false) {}
+
+ bool m_lock_wait_timeout;
+
+ bool handle_condition(THD * /* thd */, uint sql_errno,
+ const char * /* sqlstate */,
+ Sql_condition::enum_warning_level /* level */,
+ const char *message,
+ Sql_condition ** /* cond_hdl */);
+};
+
+bool
+Lock_wait_timeout_handler::
+handle_condition(THD *thd, uint sql_errno,
+ const char * /* sqlstate */,
+ Sql_condition::enum_warning_level /* level */,
+ const char *message,
+ Sql_condition ** /* cond_hdl */)
+{
+ if (sql_errno == ER_LOCK_WAIT_TIMEOUT)
+ {
+ m_lock_wait_timeout= true;
+ return true; /* condition handled */
+ }
+ if (thd->is_killed())
+ return true;
+
+ return false;
+}
+
+
+static int ull_name_ok(String *name)
+{
+ if (!name || !name->length())
+ return 0;
+
+ if (name->length() > NAME_LEN)
+ {
+ my_error(ER_TOO_LONG_IDENT, MYF(0), name->c_ptr_safe());
+ return 0;
+ }
+ return 1;
+}
+
+
+/**
+ Get a user level lock.
@retval
1 : Got lock
@@ -4175,14 +4258,13 @@ int Interruptible_wait::wait(mysql_cond_t *cond, mysql_mutex_t *mutex)
longlong Item_func_get_lock::val_int()
{
DBUG_ASSERT(fixed == 1);
- String *res=args[0]->val_str(&value);
+ String *res= args[0]->val_str(&value);
ulonglong timeout= args[1]->val_int();
- THD *thd=current_thd;
+ THD *thd= current_thd;
User_level_lock *ull;
- int error;
- Interruptible_wait timed_cond(thd);
DBUG_ENTER("Item_func_get_lock::val_int");
+ null_value= 1;
/*
In slave thread no need to get locks, everything is serialized. Anyway
there is no way to make GET_LOCK() work on slave like it did on master
@@ -4204,109 +4286,71 @@ longlong Item_func_get_lock::val_int()
strmov(buf, "NULL");
else
llstr(((longlong) timeout), buf);
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_WRONG_VALUE_FOR_TYPE, ER(ER_WRONG_VALUE_FOR_TYPE),
"timeout", buf, "get_lock");
null_value= 1;
DBUG_RETURN(0);
}
- mysql_mutex_lock(&LOCK_user_locks);
-
- if (!res || !res->length())
+ if (!ull_name_ok(res))
+ DBUG_RETURN(0);
+ DBUG_PRINT("enter", ("lock: %.*s", res->length(), res->ptr()));
+ /* HASH entries are of type User_level_lock. */
+ if (! my_hash_inited(&thd->ull_hash) &&
+ my_hash_init(&thd->ull_hash, &my_charset_bin,
+ 16 /* small hash */, 0, 0, ull_get_key, NULL, 0))
{
- mysql_mutex_unlock(&LOCK_user_locks);
- null_value=1;
DBUG_RETURN(0);
}
- DBUG_PRINT("info", ("lock %.*s, thd=%ld", res->length(), res->ptr(),
- (long) thd->real_id));
- null_value=0;
- if (thd->ull)
- {
- item_user_lock_release(thd->ull);
- thd->ull=0;
- }
+ MDL_request ull_request;
+ ull_request.init(MDL_key::USER_LOCK, res->c_ptr_safe(), "",
+ MDL_SHARED_NO_WRITE, MDL_EXPLICIT);
+ MDL_key *ull_key = &ull_request.key;
+
- if (!(ull= ((User_level_lock *) my_hash_search(&hash_user_locks,
- (uchar*) res->ptr(),
- (size_t) res->length()))))
+ if ((ull= (User_level_lock*)
+ my_hash_search(&thd->ull_hash, ull_key->ptr(), ull_key->length())))
{
- ull= new User_level_lock((uchar*) res->ptr(), (size_t) res->length(),
- thd->thread_id);
- if (!ull || !ull->initialized())
- {
- delete ull;
- mysql_mutex_unlock(&LOCK_user_locks);
- null_value=1; // Probably out of memory
- DBUG_RETURN(0);
- }
- ull->set_thread(thd);
- thd->ull=ull;
- mysql_mutex_unlock(&LOCK_user_locks);
- DBUG_PRINT("info", ("made new lock"));
- DBUG_RETURN(1); // Got new lock
+ /* Recursive lock */
+ ull->refs++;
+ null_value = 0;
+ DBUG_PRINT("info", ("recursive lock, ref-count: %d", (int) ull->refs));
+ DBUG_RETURN(1);
}
- ull->count++;
- DBUG_PRINT("info", ("ull->count=%d", ull->count));
-
- /*
- Structure is now initialized. Try to get the lock.
- Set up control struct to allow others to abort locks.
- */
- thd_proc_info(thd, "User lock");
- thd->mysys_var->current_mutex= &LOCK_user_locks;
- thd->mysys_var->current_cond= &ull->cond;
- timed_cond.set_timeout(timeout * ULL(1000000000));
-
- error= 0;
- thd_wait_begin(thd, THD_WAIT_USER_LOCK);
- while (ull->locked && !thd->killed)
+ Lock_wait_timeout_handler lock_wait_timeout_handler;
+ thd->push_internal_handler(&lock_wait_timeout_handler);
+ bool error= thd->mdl_context.acquire_lock(&ull_request, timeout);
+ (void) thd->pop_internal_handler();
+ if (error)
{
- DBUG_PRINT("info", ("waiting on lock"));
- error= timed_cond.wait(&ull->cond, &LOCK_user_locks);
- if (error == ETIMEDOUT || error == ETIME)
- {
- DBUG_PRINT("info", ("lock wait timeout"));
- break;
- }
- error= 0;
+ if (lock_wait_timeout_handler.m_lock_wait_timeout)
+ null_value= 0;
+ DBUG_RETURN(0);
}
- thd_wait_end(thd);
- if (ull->locked)
+ ull= (User_level_lock*) my_malloc(sizeof(User_level_lock),
+ MYF(MY_WME|MY_THREAD_SPECIFIC));
+ if (ull == NULL)
{
- if (!--ull->count)
- {
- DBUG_ASSERT(0);
- delete ull; // Should never happen
- }
- if (!error) // Killed (thd->killed != 0)
- {
- error=1;
- null_value=1; // Return NULL
- }
+ thd->mdl_context.release_lock(ull_request.ticket);
+ DBUG_RETURN(0);
}
- else // We got the lock
+
+ ull->lock= ull_request.ticket;
+ ull->refs= 1;
+
+ if (my_hash_insert(&thd->ull_hash, (uchar*) ull))
{
- ull->locked=1;
- ull->set_thread(thd);
- ull->thread_id= thd->thread_id;
- thd->ull=ull;
- error=0;
- DBUG_PRINT("info", ("got the lock"));
+ thd->mdl_context.release_lock(ull->lock);
+ my_free(ull);
+ DBUG_RETURN(0);
}
- mysql_mutex_unlock(&LOCK_user_locks);
-
- mysql_mutex_lock(&thd->mysys_var->mutex);
- thd_proc_info(thd, 0);
- thd->mysys_var->current_mutex= 0;
- thd->mysys_var->current_cond= 0;
- mysql_mutex_unlock(&thd->mysys_var->mutex);
+ null_value= 0;
- DBUG_RETURN(!error ? 1 : 0);
+ DBUG_RETURN(1);
}
@@ -4321,43 +4365,87 @@ longlong Item_func_get_lock::val_int()
longlong Item_func_release_lock::val_int()
{
DBUG_ASSERT(fixed == 1);
- String *res=args[0]->val_str(&value);
- User_level_lock *ull;
- longlong result;
- THD *thd=current_thd;
+ String *res= args[0]->val_str(&value);
+ THD *thd= current_thd;
DBUG_ENTER("Item_func_release_lock::val_int");
- if (!res || !res->length())
- {
- null_value=1;
+ null_value= 1;
+
+ if (!ull_name_ok(res))
DBUG_RETURN(0);
- }
- DBUG_PRINT("info", ("lock %.*s", res->length(), res->ptr()));
- null_value=0;
- result=0;
- mysql_mutex_lock(&LOCK_user_locks);
- if (!(ull= ((User_level_lock*) my_hash_search(&hash_user_locks,
- (const uchar*) res->ptr(),
- (size_t) res->length()))))
+ DBUG_PRINT("enter", ("lock: %.*s", res->length(), res->ptr()));
+
+ MDL_key ull_key;
+ ull_key.mdl_key_init(MDL_key::USER_LOCK, res->c_ptr_safe(), "");
+
+ User_level_lock *ull;
+
+ if (!(ull=
+ (User_level_lock*) my_hash_search(&thd->ull_hash,
+ ull_key.ptr(), ull_key.length())))
{
- null_value=1;
+ null_value= thd->mdl_context.get_lock_owner(&ull_key) == 0;
+ DBUG_RETURN(0);
}
- else
+ DBUG_PRINT("info", ("ref count: %d", (int) ull->refs));
+ null_value= 0;
+ if (--ull->refs == 0)
{
- DBUG_PRINT("info", ("ull->locked=%d ull->thread=%lu thd=%lu",
- (int) ull->locked,
- (long)ull->thread_id,
- (long)thd->thread_id));
- if (ull->locked && current_thd->thread_id == ull->thread_id)
- {
- DBUG_PRINT("info", ("release lock"));
- result=1; // Release is ok
- item_user_lock_release(ull);
- thd->ull=0;
- }
+ my_hash_delete(&thd->ull_hash, (uchar*) ull);
+ thd->mdl_context.release_lock(ull->lock);
+ my_free(ull);
}
- mysql_mutex_unlock(&LOCK_user_locks);
- DBUG_RETURN(result);
+ DBUG_RETURN(1);
+}
+
+
+/**
+ Check a user level lock.
+
+ Sets null_value=TRUE on error.
+
+ @retval
+ 1 Available
+ @retval
+ 0 Already taken, or error
+*/
+
+longlong Item_func_is_free_lock::val_int()
+{
+ DBUG_ASSERT(fixed == 1);
+ String *res= args[0]->val_str(&value);
+ THD *thd= current_thd;
+ null_value= 1;
+
+ if (!ull_name_ok(res))
+ return 0;
+
+ MDL_key ull_key;
+ ull_key.mdl_key_init(MDL_key::USER_LOCK, res->c_ptr_safe(), "");
+
+ null_value= 0;
+ return thd->mdl_context.get_lock_owner(&ull_key) == 0;
+}
+
+
+longlong Item_func_is_used_lock::val_int()
+{
+ DBUG_ASSERT(fixed == 1);
+ String *res= args[0]->val_str(&value);
+ THD *thd= current_thd;
+ null_value= 1;
+
+ if (!ull_name_ok(res))
+ return 0;
+
+ MDL_key ull_key;
+ ull_key.mdl_key_init(MDL_key::USER_LOCK, res->c_ptr_safe(), "");
+ ulong thread_id = thd->mdl_context.get_lock_owner(&ull_key);
+ if (thread_id == 0)
+ return 0;
+
+ null_value= 0;
+ return thread_id;
}
@@ -4412,7 +4500,7 @@ longlong Item_func_benchmark::val_int()
{
char buff[22];
llstr(((longlong) loop_count), buff);
- push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
ER_WRONG_VALUE_FOR_TYPE, ER(ER_WRONG_VALUE_FOR_TYPE),
"count", buff, "benchmark");
}
@@ -4458,6 +4546,54 @@ void Item_func_benchmark::print(String *str, enum_query_type query_type)
}
+mysql_mutex_t LOCK_item_func_sleep;
+
+#ifdef HAVE_PSI_INTERFACE
+static PSI_mutex_key key_LOCK_item_func_sleep;
+
+static PSI_mutex_info item_func_sleep_mutexes[]=
+{
+ { &key_LOCK_item_func_sleep, "LOCK_user_locks", PSI_FLAG_GLOBAL}
+};
+
+
+static void init_item_func_sleep_psi_keys(void)
+{
+ const char* category= "sql";
+ int count;
+
+ if (PSI_server == NULL)
+ return;
+
+ count= array_elements(item_func_sleep_mutexes);
+ PSI_server->register_mutex(category, item_func_sleep_mutexes, count);
+}
+#endif
+
+static bool item_func_sleep_inited= 0;
+
+
+void item_func_sleep_init(void)
+{
+#ifdef HAVE_PSI_INTERFACE
+ init_item_func_sleep_psi_keys();
+#endif
+
+ mysql_mutex_init(key_LOCK_item_func_sleep, &LOCK_item_func_sleep, MY_MUTEX_INIT_SLOW);
+ item_func_sleep_inited= 1;
+}
+
+
+void item_func_sleep_free(void)
+{
+ if (item_func_sleep_inited)
+ {
+ item_func_sleep_inited= 0;
+ mysql_mutex_destroy(&LOCK_item_func_sleep);
+ }
+}
+
+
/** This function is just used to create tests with time gaps. */
longlong Item_func_sleep::val_int()
@@ -4486,24 +4622,23 @@ longlong Item_func_sleep::val_int()
timed_cond.set_timeout((ulonglong) (timeout * 1000000000.0));
mysql_cond_init(key_item_func_sleep_cond, &cond, NULL);
- mysql_mutex_lock(&LOCK_user_locks);
+ mysql_mutex_lock(&LOCK_item_func_sleep);
- thd_proc_info(thd, "User sleep");
- thd->mysys_var->current_mutex= &LOCK_user_locks;
+ THD_STAGE_INFO(thd, stage_user_sleep);
+ thd->mysys_var->current_mutex= &LOCK_item_func_sleep;
thd->mysys_var->current_cond= &cond;
error= 0;
thd_wait_begin(thd, THD_WAIT_SLEEP);
while (!thd->killed)
{
- error= timed_cond.wait(&cond, &LOCK_user_locks);
+ error= timed_cond.wait(&cond, &LOCK_item_func_sleep);
if (error == ETIMEDOUT || error == ETIME)
break;
error= 0;
}
thd_wait_end(thd);
- thd_proc_info(thd, 0);
- mysql_mutex_unlock(&LOCK_user_locks);
+ mysql_mutex_unlock(&LOCK_item_func_sleep);
mysql_mutex_lock(&thd->mysys_var->mutex);
thd->mysys_var->current_mutex= 0;
thd->mysys_var->current_cond= 0;
@@ -4516,13 +4651,13 @@ longlong Item_func_sleep::val_int()
(thd, STRING_WITH_LEN("dispatch_command_end SIGNAL query_done"));
};);
- return test(!error); // Return 1 killed
+ return MY_TEST(!error); // Return 1 killed
}
#define extra_size sizeof(double)
-static user_var_entry *get_variable(HASH *hash, LEX_STRING &name,
+user_var_entry *get_variable(HASH *hash, LEX_STRING &name,
bool create_if_not_exists)
{
user_var_entry *entry;
@@ -4534,7 +4669,9 @@ static user_var_entry *get_variable(HASH *hash, LEX_STRING &name,
uint size=ALIGN_SIZE(sizeof(user_var_entry))+name.length+1+extra_size;
if (!my_hash_inited(hash))
return 0;
- if (!(entry = (user_var_entry*) my_malloc(size,MYF(MY_WME | ME_FATALERROR))))
+ if (!(entry = (user_var_entry*) my_malloc(size,
+ MYF(MY_WME | ME_FATALERROR |
+ MY_THREAD_SPECIFIC))))
return 0;
entry->name.str=(char*) entry+ ALIGN_SIZE(sizeof(user_var_entry))+
extra_size;
@@ -4762,7 +4899,8 @@ update_hash(user_var_entry *entry, bool set_null, void *ptr, uint length,
entry->value=0;
entry->value= (char*) my_realloc(entry->value, length,
MYF(MY_ALLOW_ZERO_PTR | MY_WME |
- ME_FATALERROR));
+ ME_FATALERROR |
+ MY_THREAD_SPECIFIC));
if (!entry->value)
return 1;
}
@@ -4841,7 +4979,7 @@ double user_var_entry::val_real(bool *null_value)
longlong user_var_entry::val_int(bool *null_value) const
{
if ((*null_value= (value == 0)))
- return LL(0);
+ return 0;
switch (type) {
case REAL_RESULT:
@@ -4865,7 +5003,7 @@ longlong user_var_entry::val_int(bool *null_value) const
DBUG_ASSERT(0); // Impossible
break;
}
- return LL(0); // Impossible
+ return 0; // Impossible
}
@@ -5358,7 +5496,7 @@ longlong Item_func_get_user_var::val_int()
{
DBUG_ASSERT(fixed == 1);
if (!var_entry)
- return LL(0); // No such variable
+ return 0; // No such variable
return (var_entry->val_int(&null_value));
}
@@ -5840,28 +5978,18 @@ enum_field_types Item_func_get_system_var::field_type() const
}
-/*
- Uses var, var_type, component, cache_present, used_query_id, thd,
- cached_llval, null_value, cached_null_value
-*/
-#define get_sys_var_safe(type) \
-do { \
- type value; \
- mysql_mutex_lock(&LOCK_global_system_variables); \
- value= *(type*) var->value_ptr(thd, var_type, &component); \
- mysql_mutex_unlock(&LOCK_global_system_variables); \
- cache_present |= GET_SYS_VAR_CACHE_LONG; \
- used_query_id= thd->query_id; \
- cached_llval= null_value ? 0 : (longlong) value; \
- cached_null_value= null_value; \
- return cached_llval; \
-} while (0)
-
-
longlong Item_func_get_system_var::val_int()
{
THD *thd= current_thd;
+ DBUG_EXECUTE_IF("simulate_non_gtid_aware_master",
+ {
+ if (0 == strcmp("gtid_domain_id", var->name.str))
+ {
+ my_error(ER_VAR_CANT_BE_READ, MYF(0), var->name.str);
+ return 0;
+ }
+ });
if (cache_present && thd->query_id == used_query_id)
{
if (cache_present & GET_SYS_VAR_CACHE_LONG)
@@ -5891,51 +6019,11 @@ longlong Item_func_get_system_var::val_int()
}
}
- switch (var->show_type())
- {
- case SHOW_SINT: get_sys_var_safe (int);
- case SHOW_SLONG: get_sys_var_safe (long);
- case SHOW_SLONGLONG:get_sys_var_safe (longlong);
- case SHOW_UINT: get_sys_var_safe (uint);
- case SHOW_ULONG: get_sys_var_safe (ulong);
- case SHOW_ULONGLONG:get_sys_var_safe (ulonglong);
- case SHOW_HA_ROWS: get_sys_var_safe (ha_rows);
- case SHOW_BOOL: get_sys_var_safe (bool);
- case SHOW_MY_BOOL: get_sys_var_safe (my_bool);
- case SHOW_DOUBLE:
- {
- double dval= val_real();
-
- used_query_id= thd->query_id;
- cached_llval= (longlong) dval;
- cache_present|= GET_SYS_VAR_CACHE_LONG;
- return cached_llval;
- }
- case SHOW_CHAR:
- case SHOW_CHAR_PTR:
- case SHOW_LEX_STRING:
- {
- String *str_val= val_str(NULL);
-
- if (str_val && str_val->length())
- cached_llval= longlong_from_string_with_check (system_charset_info,
- str_val->c_ptr(),
- str_val->c_ptr() +
- str_val->length());
- else
- {
- null_value= TRUE;
- cached_llval= 0;
- }
-
- cache_present|= GET_SYS_VAR_CACHE_LONG;
- return cached_llval;
- }
-
- default:
- my_error(ER_VAR_CANT_BE_READ, MYF(0), var->name.str);
- return 0; // keep the compiler happy
- }
+ cached_llval= var->val_int(&null_value, thd, var_type, &component);
+ cache_present |= GET_SYS_VAR_CACHE_LONG;
+ used_query_id= thd->query_id;
+ cached_null_value= null_value;
+ return cached_llval;
}
@@ -5968,61 +6056,10 @@ String* Item_func_get_system_var::val_str(String* str)
}
}
- str= &cached_strval;
- switch (var->show_type())
- {
- case SHOW_CHAR:
- case SHOW_CHAR_PTR:
- case SHOW_LEX_STRING:
- {
- mysql_mutex_lock(&LOCK_global_system_variables);
- char *cptr= var->show_type() == SHOW_CHAR ?
- (char*) var->value_ptr(thd, var_type, &component) :
- *(char**) var->value_ptr(thd, var_type, &component);
- if (cptr)
- {
- size_t len= var->show_type() == SHOW_LEX_STRING ?
- ((LEX_STRING*)(var->value_ptr(thd, var_type, &component)))->length :
- strlen(cptr);
- if (str->copy(cptr, len, collation.collation))
- {
- null_value= TRUE;
- str= NULL;
- }
- }
- else
- {
- null_value= TRUE;
- str= NULL;
- }
- mysql_mutex_unlock(&LOCK_global_system_variables);
- break;
- }
-
- case SHOW_SINT:
- case SHOW_SLONG:
- case SHOW_SLONGLONG:
- case SHOW_UINT:
- case SHOW_ULONG:
- case SHOW_ULONGLONG:
- case SHOW_HA_ROWS:
- case SHOW_BOOL:
- case SHOW_MY_BOOL:
- str->set (val_int(), collation.collation);
- break;
- case SHOW_DOUBLE:
- str->set_real (val_real(), decimals, collation.collation);
- break;
-
- default:
- my_error(ER_VAR_CANT_BE_READ, MYF(0), var->name.str);
- str= NULL;
- break;
- }
-
+ str= var->val_str(&cached_strval, thd, var_type, &component);
cache_present|= GET_SYS_VAR_CACHE_STRING;
used_query_id= thd->query_id;
- cached_null_value= null_value;
+ cached_null_value= null_value= !str;
return str;
}
@@ -6060,58 +6097,11 @@ double Item_func_get_system_var::val_real()
}
}
- switch (var->show_type())
- {
- case SHOW_DOUBLE:
- mysql_mutex_lock(&LOCK_global_system_variables);
- cached_dval= *(double*) var->value_ptr(thd, var_type, &component);
- mysql_mutex_unlock(&LOCK_global_system_variables);
- used_query_id= thd->query_id;
- cached_null_value= null_value;
- if (null_value)
- cached_dval= 0;
- cache_present|= GET_SYS_VAR_CACHE_DOUBLE;
- return cached_dval;
- case SHOW_CHAR:
- case SHOW_LEX_STRING:
- case SHOW_CHAR_PTR:
- {
- mysql_mutex_lock(&LOCK_global_system_variables);
- char *cptr= var->show_type() == SHOW_CHAR ?
- (char*) var->value_ptr(thd, var_type, &component) :
- *(char**) var->value_ptr(thd, var_type, &component);
- if (cptr)
- cached_dval= double_from_string_with_check (system_charset_info,
- cptr, cptr + strlen (cptr));
- else
- {
- null_value= TRUE;
- cached_dval= 0;
- }
- mysql_mutex_unlock(&LOCK_global_system_variables);
- used_query_id= thd->query_id;
- cached_null_value= null_value;
- cache_present|= GET_SYS_VAR_CACHE_DOUBLE;
- return cached_dval;
- }
- case SHOW_SINT:
- case SHOW_SLONG:
- case SHOW_SLONGLONG:
- case SHOW_UINT:
- case SHOW_ULONG:
- case SHOW_ULONGLONG:
- case SHOW_HA_ROWS:
- case SHOW_BOOL:
- case SHOW_MY_BOOL:
- cached_dval= (double) val_int();
- cache_present|= GET_SYS_VAR_CACHE_DOUBLE;
- used_query_id= thd->query_id;
- cached_null_value= null_value;
- return cached_dval;
- default:
- my_error(ER_VAR_CANT_BE_READ, MYF(0), var->name.str);
- return 0;
- }
+ cached_dval= var->val_real(&null_value, thd, var_type, &component);
+ cache_present |= GET_SYS_VAR_CACHE_DOUBLE;
+ used_query_id= thd->query_id;
+ cached_null_value= null_value;
+ return cached_dval;
}
@@ -6138,61 +6128,6 @@ void Item_func_get_system_var::cleanup()
}
-longlong Item_func_inet_aton::val_int()
-{
- DBUG_ASSERT(fixed == 1);
- uint byte_result = 0;
- ulonglong result = 0; // We are ready for 64 bit addresses
- const char *p,* end;
- char c = '.'; // we mark c to indicate invalid IP in case length is 0
- char buff[36];
- int dot_count= 0;
-
- String *s, tmp(buff, sizeof(buff), &my_charset_latin1);
- if (!(s = args[0]->val_str_ascii(&tmp))) // If null value
- goto err;
- null_value=0;
-
- end= (p = s->ptr()) + s->length();
- while (p < end)
- {
- c = *p++;
- int digit = (int) (c - '0');
- if (digit >= 0 && digit <= 9)
- {
- if ((byte_result = byte_result * 10 + digit) > 255)
- goto err; // Wrong address
- }
- else if (c == '.')
- {
- dot_count++;
- result= (result << 8) + (ulonglong) byte_result;
- byte_result = 0;
- }
- else
- goto err; // Invalid character
- }
- if (c != '.') // IP number can't end on '.'
- {
- /*
- Handle short-forms addresses according to standard. Examples:
- 127 -> 0.0.0.127
- 127.1 -> 127.0.0.1
- 127.2.1 -> 127.2.0.1
- */
- switch (dot_count) {
- case 1: result<<= 8; /* Fall through */
- case 2: result<<= 8; /* Fall through */
- }
- return (result << 8) + (ulonglong) byte_result;
- }
-
-err:
- null_value=1;
- return 0;
-}
-
-
void Item_func_match::init_search(bool no_order)
{
DBUG_ENTER("Item_func_match::init_search");
@@ -6254,7 +6189,7 @@ void Item_func_match::init_search(bool no_order)
flags|=FT_SORTED;
if (key != NO_SUCH_KEY)
- thd_proc_info(table->in_use, "FULLTEXT initialization");
+ THD_STAGE_INFO(table->in_use, stage_fulltext_initialization);
ft_handler= table->file->ft_init_ext(flags, key, ft_tmp);
@@ -6288,6 +6223,7 @@ bool Item_func_match::fix_fields(THD *thd, Item **ref)
return TRUE;
}
+ bool allows_multi_table_search= true;
const_item_cache=0;
table= 0;
for (uint i=1 ; i < arg_count ; i++)
@@ -6318,7 +6254,10 @@ bool Item_func_match::fix_fields(THD *thd, Item **ref)
*/
if (item->type() == Item::FIELD_ITEM)
table= ((Item_field *)item)->field->table;
+
+ allows_multi_table_search &= allows_search_on_non_indexed_columns(table);
}
+
/*
Check that all columns come from the same table.
We've already checked that columns in MATCH are fields so
@@ -6327,14 +6266,14 @@ bool Item_func_match::fix_fields(THD *thd, Item **ref)
if ((used_tables_cache & ~PARAM_TABLE_BIT) != item->used_tables())
key=NO_SUCH_KEY;
- if (key == NO_SUCH_KEY && !(flags & FT_BOOL))
+ if (key == NO_SUCH_KEY && !allows_multi_table_search)
{
my_error(ER_WRONG_ARGUMENTS,MYF(0),"MATCH");
return TRUE;
}
if (!(table->file->ha_table_flags() & HA_CAN_FULLTEXT))
{
- my_error(ER_TABLE_CANT_HANDLE_FT, MYF(0));
+ my_error(ER_TABLE_CANT_HANDLE_FT, MYF(0), table->file->table_type());
return 1;
}
table->fulltext_searched=1;
@@ -6382,7 +6321,7 @@ bool Item_func_match::fix_index()
for (keynr=0 ; keynr < fts ; keynr++)
{
KEY *ft_key=&table->key_info[ft_to_key[keynr]];
- uint key_parts=ft_key->key_parts;
+ uint key_parts=ft_key->user_defined_key_parts;
for (uint part=0 ; part < key_parts ; part++)
{
@@ -6414,7 +6353,7 @@ bool Item_func_match::fix_index()
{
// partial keys doesn't work
if (max_cnt < arg_count-1 ||
- max_cnt < table->key_info[ft_to_key[keynr]].key_parts)
+ max_cnt < table->key_info[ft_to_key[keynr]].user_defined_key_parts)
continue;
key=ft_to_key[keynr];
@@ -6423,7 +6362,7 @@ bool Item_func_match::fix_index()
}
err:
- if (flags & FT_BOOL)
+ if (allows_search_on_non_indexed_columns(table))
{
key=NO_SUCH_KEY;
return 0;
@@ -6561,68 +6500,6 @@ Item *get_system_var(THD *thd, enum_var_type var_type, LEX_STRING name,
}
-/**
- Check a user level lock.
-
- Sets null_value=TRUE on error.
-
- @retval
- 1 Available
- @retval
- 0 Already taken, or error
-*/
-
-longlong Item_func_is_free_lock::val_int()
-{
- DBUG_ASSERT(fixed == 1);
- String *res=args[0]->val_str(&value);
- User_level_lock *ull;
- longlong ret_val= 0LL;
-
- null_value=0;
- if (!res || !res->length())
- {
- null_value=1;
- return ret_val;
- }
-
- mysql_mutex_lock(&LOCK_user_locks);
- ull= (User_level_lock *) my_hash_search(&hash_user_locks, (uchar*) res->ptr(),
- (size_t) res->length());
- if (!ull || !ull->locked)
- ret_val= 1;
- mysql_mutex_unlock(&LOCK_user_locks);
- DEBUG_SYNC(current_thd, "after_getting_user_level_lock_info");
-
- return ret_val;
-}
-
-longlong Item_func_is_used_lock::val_int()
-{
- DBUG_ASSERT(fixed == 1);
- String *res=args[0]->val_str(&value);
- User_level_lock *ull;
- my_thread_id thread_id= 0UL;
-
- null_value=1;
- if (!res || !res->length())
- return 0;
-
- mysql_mutex_lock(&LOCK_user_locks);
- ull= (User_level_lock *) my_hash_search(&hash_user_locks, (uchar*) res->ptr(),
- (size_t) res->length());
- if ((ull != NULL) && ull->locked)
- {
- null_value= 0;
- thread_id= ull->thread_id;
- }
- mysql_mutex_unlock(&LOCK_user_locks);
- DEBUG_SYNC(current_thd, "after_getting_user_level_lock_info");
-
- return thread_id;
-}
-
-
longlong Item_func_row_count::val_int()
{
DBUG_ASSERT(fixed == 1);
@@ -6803,7 +6680,7 @@ void Item_func_sp::fix_length_and_dec()
max_length= sp_result_field->field_length;
collation.set(sp_result_field->charset());
maybe_null= 1;
- unsigned_flag= test(sp_result_field->flags & UNSIGNED_FLAG);
+ unsigned_flag= MY_TEST(sp_result_field->flags & UNSIGNED_FLAG);
DBUG_VOID_RETURN;
}
@@ -6856,22 +6733,18 @@ Item_func_sp::execute_impl(THD *thd)
{
bool err_status= TRUE;
Sub_statement_state statement_state;
-#ifndef NO_EMBEDDED_ACCESS_CHECKS
Security_context *save_security_ctx= thd->security_ctx;
-#endif
enum enum_sp_data_access access=
(m_sp->m_chistics->daccess == SP_DEFAULT_ACCESS) ?
SP_DEFAULT_ACCESS_MAPPING : m_sp->m_chistics->daccess;
DBUG_ENTER("Item_func_sp::execute_impl");
-#ifndef NO_EMBEDDED_ACCESS_CHECKS
if (context->security_ctx)
{
/* Set view definer security context */
thd->security_ctx= context->security_ctx;
}
-#endif
if (sp_check_access(thd))
goto error;
@@ -6899,9 +6772,7 @@ Item_func_sp::execute_impl(THD *thd)
thd->restore_sub_statement_state(&statement_state);
error:
-#ifndef NO_EMBEDDED_ACCESS_CHECKS
thd->security_ctx= save_security_ctx;
-#endif
DBUG_RETURN(err_status);
}
@@ -6972,11 +6843,9 @@ Item_func_sp::sp_check_access(THD *thd)
{
DBUG_ENTER("Item_func_sp::sp_check_access");
DBUG_ASSERT(m_sp);
-#ifndef NO_EMBEDDED_ACCESS_CHECKS
if (check_routine_access(thd, EXECUTE_ACL,
m_sp->m_db.str, m_sp->m_name.str, 0, FALSE))
DBUG_RETURN(TRUE);
-#endif
DBUG_RETURN(FALSE);
}
@@ -6988,7 +6857,29 @@ Item_func_sp::fix_fields(THD *thd, Item **ref)
bool res;
DBUG_ENTER("Item_func_sp::fix_fields");
DBUG_ASSERT(fixed == 0);
-
+
+ /*
+ Checking privileges to execute the function while creating view and
+ executing the function of select.
+ */
+ if (!(thd->lex->context_analysis_only & CONTEXT_ANALYSIS_ONLY_VIEW) ||
+ (thd->lex->sql_command == SQLCOM_CREATE_VIEW))
+ {
+ Security_context *save_security_ctx= thd->security_ctx;
+ if (context->security_ctx)
+ thd->security_ctx= context->security_ctx;
+
+ res= check_routine_access(thd, EXECUTE_ACL, m_name->m_db.str,
+ m_name->m_name.str, 0, FALSE);
+ thd->security_ctx= save_security_ctx;
+
+ if (res)
+ {
+ context->process_error(thd);
+ DBUG_RETURN(res);
+ }
+ }
+
/*
We must call init_result_field before Item_func::fix_fields()
to make m_sp and result_field members available to fix_length_and_dec(),
@@ -7073,7 +6964,7 @@ ulonglong uuid_value;
void uuid_short_init()
{
- uuid_value= ((((ulonglong) server_id) << 56) +
+ uuid_value= ((((ulonglong) global_system_variables.server_id) << 56) +
(((ulonglong) server_start_time) << 24));
}
diff --git a/sql/item_func.h b/sql/item_func.h
index 33fa49f9168..0b3454fa4b0 100644
--- a/sql/item_func.h
+++ b/sql/item_func.h
@@ -39,9 +39,17 @@ protected:
0 means get this number from first argument
*/
uint allowed_arg_cols;
+ String *val_str_from_val_str_ascii(String *str, String *str2);
public:
uint arg_count;
- table_map used_tables_cache, not_null_tables_cache;
+ /*
+ In some cases used_tables_cache is not what used_tables() return
+ so the method should be used where one need used tables bit map
+ (even internally in Item_func_* code).
+ */
+ table_map used_tables_cache;
+ table_map not_null_tables_cache;
+
bool const_item_cache;
enum Functype { UNKNOWN_FUNC,EQ_FUNC,EQUAL_FUNC,NE_FUNC,LT_FUNC,LE_FUNC,
GE_FUNC,GT_FUNC,FT_FUNC,
@@ -58,7 +66,7 @@ public:
NOW_FUNC, TRIG_COND_FUNC,
SUSERVAR_FUNC, GUSERVAR_FUNC, COLLATE_FUNC,
EXTRACT_FUNC, CHAR_TYPECAST_FUNC, FUNC_SP, UDF_FUNC,
- NEG_FUNC, GSYSVAR_FUNC };
+ NEG_FUNC, GSYSVAR_FUNC, DYNCOL_FUNC };
enum optimize_type { OPTIMIZE_NONE,OPTIMIZE_KEY,OPTIMIZE_OP, OPTIMIZE_NULL,
OPTIMIZE_EQUAL };
enum Type type() const { return FUNC_ITEM; }
@@ -151,7 +159,7 @@ public:
void count_decimal_length();
inline bool get_arg0_date(MYSQL_TIME *ltime, ulonglong fuzzy_date)
{
- return (null_value=args[0]->get_date(ltime, fuzzy_date));
+ return (null_value=args[0]->get_date_with_conversion(ltime, fuzzy_date));
}
void count_datetime_length(Item **item, uint nitems);
bool count_string_result_length(enum_field_types field_type,
@@ -600,6 +608,8 @@ public:
{ collation.set_numeric(); fix_char_length(21); sargable= false; }
Item_int_func(Item *a,Item *b,Item *c) :Item_func(a,b,c)
{ collation.set_numeric(); fix_char_length(21); sargable= false; }
+ Item_int_func(Item *a,Item *b,Item *c, Item *d) :Item_func(a,b,c,d)
+ { collation.set_numeric(); fix_char_length(21); sargable= false; }
Item_int_func(List<Item> &list) :Item_func(list)
{ collation.set_numeric(); fix_char_length(21); sargable= false; }
Item_int_func(THD *thd, Item_int_func *item) :Item_func(thd, item)
@@ -638,8 +648,8 @@ public:
longlong val_int_from_str(int *error);
void fix_length_and_dec()
{
- fix_char_length(min(args[0]->max_char_length(),
- MY_INT64_NUM_DECIMAL_DIGITS));
+ fix_char_length(MY_MIN(args[0]->max_char_length(),
+ MY_INT64_NUM_DECIMAL_DIGITS));
}
virtual void print(String *str, enum_query_type query_type);
uint decimal_precision() const { return args[0]->decimal_precision(); }
@@ -1335,17 +1345,20 @@ public:
};
+void item_func_sleep_init(void);
+void item_func_sleep_free(void);
+
class Item_func_sleep :public Item_int_func
{
public:
Item_func_sleep(Item *a) :Item_int_func(a) {}
bool const_item() const { return 0; }
const char *func_name() const { return "sleep"; }
- void update_used_tables()
+ table_map used_tables() const
{
- Item_int_func::update_used_tables();
- used_tables_cache|= RAND_TABLE_BIT;
+ return Item_int_func::used_tables() | RAND_TABLE_BIT;
}
+ bool is_expensive() { return 1; }
longlong val_int();
bool check_vcol_func_processor(uchar *int_arg)
{
@@ -1585,14 +1598,8 @@ public:
#endif /* HAVE_DLOPEN */
-/*
-** User level locks
-*/
-
-class User_level_lock;
-void item_user_lock_init(void);
-void item_user_lock_release(User_level_lock *ull);
-void item_user_lock_free(void);
+void mysql_ull_cleanup(THD *thd);
+void mysql_ull_set_explicit_lock_duration(THD *thd);
class Item_func_get_lock :public Item_int_func
{
@@ -1602,6 +1609,12 @@ class Item_func_get_lock :public Item_int_func
longlong val_int();
const char *func_name() const { return "get_lock"; }
void fix_length_and_dec() { max_length=1; maybe_null=1;}
+ table_map used_tables() const
+ {
+ return Item_int_func::used_tables() | RAND_TABLE_BIT;
+ }
+ bool const_item() const { return 0; }
+ bool is_expensive() { return 1; }
bool check_vcol_func_processor(uchar *int_arg)
{
return trace_unsupported_by_check_vcol_func_processor(func_name());
@@ -1615,7 +1628,13 @@ public:
Item_func_release_lock(Item *a) :Item_int_func(a) {}
longlong val_int();
const char *func_name() const { return "release_lock"; }
- void fix_length_and_dec() { max_length=1; maybe_null=1;}
+ void fix_length_and_dec() { max_length= 1; maybe_null= 1;}
+ table_map used_tables() const
+ {
+ return Item_int_func::used_tables() | RAND_TABLE_BIT;
+ }
+ bool const_item() const { return 0; }
+ bool is_expensive() { return 1; }
bool check_vcol_func_processor(uchar *int_arg)
{
return trace_unsupported_by_check_vcol_func_processor(func_name());
@@ -1630,6 +1649,7 @@ class Item_master_pos_wait :public Item_int_func
public:
Item_master_pos_wait(Item *a,Item *b) :Item_int_func(a,b) {}
Item_master_pos_wait(Item *a,Item *b,Item *c) :Item_int_func(a,b,c) {}
+ Item_master_pos_wait(Item *a,Item *b, Item *c, Item *d) :Item_int_func(a,b,c,d) {}
longlong val_int();
const char *func_name() const { return "master_pos_wait"; }
void fix_length_and_dec() { max_length=21; maybe_null=1;}
@@ -1640,6 +1660,22 @@ public:
};
+class Item_master_gtid_wait :public Item_int_func
+{
+ String value;
+public:
+ Item_master_gtid_wait(Item *a) :Item_int_func(a) {}
+ Item_master_gtid_wait(Item *a,Item *b) :Item_int_func(a,b) {}
+ longlong val_int();
+ const char *func_name() const { return "master_gtid_wait"; }
+ void fix_length_and_dec() { max_length=10+1+10+1+20+1; maybe_null=0;}
+ bool check_vcol_func_processor(uchar *int_arg)
+ {
+ return trace_unsupported_by_check_vcol_func_processor(func_name());
+ }
+};
+
+
/* Handling of user definable variables */
class user_var_entry;
@@ -1706,6 +1742,12 @@ public:
enum Item_result result_type () const { return cached_result_type; }
bool fix_fields(THD *thd, Item **ref);
void fix_length_and_dec();
+ table_map used_tables() const
+ {
+ return Item_func::used_tables() | RAND_TABLE_BIT;
+ }
+ bool const_item() const { return 0; }
+ bool is_expensive() { return 1; }
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"; }
@@ -1715,7 +1757,9 @@ public:
{
return save_in_field(field, no_conversions, 1);
}
- void save_org_in_field(Field *field) { (void)save_in_field(field, 1, 0); }
+ void save_org_in_field(Field *field,
+ fast_field_copier data __attribute__ ((__unused__)))
+ { (void)save_in_field(field, 1, 0); }
bool register_field_in_read_map(uchar *arg);
bool register_field_in_bitmap(uchar *arg);
bool set_entry(THD *thd, bool create_if_not_exists);
@@ -1847,16 +1891,6 @@ public:
};
-class Item_func_inet_aton : public Item_int_func
-{
-public:
- Item_func_inet_aton(Item *a) :Item_int_func(a) {}
- longlong val_int();
- const char *func_name() const { return "inet_aton"; }
- void fix_length_and_dec() { decimals= 0; max_length= 21; maybe_null= 1; unsigned_flag= 1;}
-};
-
-
/* for fulltext search */
class Item_func_match :public Item_real_func
@@ -1903,6 +1937,41 @@ public:
/* TODO: consider adding in support for the MATCH-based virtual columns */
return trace_unsupported_by_check_vcol_func_processor(func_name());
}
+private:
+ /**
+ Check whether storage engine for given table,
+ allows FTS Boolean search on non-indexed columns.
+
+ @todo A flag should be added to the extended fulltext API so that
+ it may be checked whether search on non-indexed columns are
+ supported. Currently, it is not possible to check for such a
+ flag since @c this->ft_handler is not yet set when this function is
+ called. The current hack is to assume that search on non-indexed
+ columns are supported for engines that does not support the extended
+ fulltext API (e.g., MyISAM), while it is not supported for other
+ engines (e.g., InnoDB)
+
+ @param table_arg Table for which storage engine to check
+
+ @retval true if BOOLEAN search on non-indexed columns is supported
+ @retval false otherwise
+ */
+ bool allows_search_on_non_indexed_columns(TABLE* table_arg)
+ {
+ // Only Boolean search may support non_indexed columns
+ if (!(flags & FT_BOOL))
+ return false;
+
+ DBUG_ASSERT(table_arg && table_arg->file);
+
+ // Assume that if extended fulltext API is not supported,
+ // non-indexed columns are allowed. This will be true for MyISAM.
+ if ((table_arg->file->ha_table_flags() & HA_CAN_FULLTEXT_EXT) == 0)
+ return true;
+
+ return false;
+ }
+
};
diff --git a/sql/item_geofunc.cc b/sql/item_geofunc.cc
index 5e1c0add54b..1d3dbaf9eeb 100644
--- a/sql/item_geofunc.cc
+++ b/sql/item_geofunc.cc
@@ -27,6 +27,7 @@
#pragma implementation // gcc: Class implementation
#endif
+#include <my_global.h>
#include "sql_priv.h"
/*
It is necessary to include set_var.h instead of item.h because there
@@ -244,7 +245,7 @@ String *Item_func_centroid::val_str(String *str)
srid= uint4korr(swkb->ptr());
str->q_append(srid);
- return (null_value= test(geom->centroid(str))) ? 0 : str;
+ return (null_value= MY_TEST(geom->centroid(str))) ? 0 : str;
}
@@ -500,7 +501,7 @@ String *Item_func_spatial_collection::val_str(String *str)
}
if (str->length() > current_thd->variables.max_allowed_packet)
{
- push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
ER_WARN_ALLOWED_PACKET_OVERFLOWED,
ER(ER_WARN_ALLOWED_PACKET_OVERFLOWED),
func_name(), current_thd->variables.max_allowed_packet);
@@ -636,10 +637,10 @@ static double count_edge_t(const Gcalc_heap::Info *ea,
double &ex, double &ey, double &vx, double &vy,
double &e_sqrlen)
{
- ex= eb->x - ea->x;
- ey= eb->y - ea->y;
- vx= v->x - ea->x;
- vy= v->y - ea->y;
+ ex= eb->node.shape.x - ea->node.shape.x;
+ ey= eb->node.shape.y - ea->node.shape.y;
+ vx= v->node.shape.x - ea->node.shape.x;
+ vy= v->node.shape.y - ea->node.shape.y;
e_sqrlen= ex * ex + ey * ey;
return (ex * vx + ey * vy) / e_sqrlen;
}
@@ -655,8 +656,8 @@ static double distance_to_line(double ex, double ey, double vx, double vy,
static double distance_points(const Gcalc_heap::Info *a,
const Gcalc_heap::Info *b)
{
- double x= a->x - b->x;
- double y= a->y - b->y;
+ double x= a->node.shape.x - b->node.shape.x;
+ double y= a->node.shape.y - b->node.shape.y;
return sqrt(x * x + y * y);
}
@@ -1697,7 +1698,7 @@ double Item_func_distance::val_real()
continue;
count_distance:
- if (cur_point->shape >= obj2_si)
+ if (cur_point->node.shape.shape >= obj2_si)
continue;
cur_point_edge= !cur_point->is_bottom();
@@ -1705,13 +1706,13 @@ count_distance:
{
/* We only check vertices of object 2 */
if (dist_point->type != Gcalc_heap::nt_shape_node ||
- dist_point->shape < obj2_si)
+ dist_point->node.shape.shape < obj2_si)
continue;
/* if we have an edge to check */
- if (dist_point->left)
+ if (dist_point->node.shape.left)
{
- t= count_edge_t(dist_point, dist_point->left, cur_point,
+ t= count_edge_t(dist_point, dist_point->node.shape.left, cur_point,
ex, ey, vx, vy, e_sqrlen);
if ((t>0.0) && (t<1.0))
{
@@ -1722,7 +1723,7 @@ count_distance:
}
if (cur_point_edge)
{
- t= count_edge_t(cur_point, cur_point->left, dist_point,
+ t= count_edge_t(cur_point, cur_point->node.shape.left, dist_point,
ex, ey, vx, vy, e_sqrlen);
if ((t>0.0) && (t<1.0))
{
@@ -1747,4 +1748,12 @@ mem_error:
}
+#ifndef DBUG_OFF
+longlong Item_func_gis_debug::val_int()
+{
+ /* For now this is just a stub. TODO: implement the internal GIS debuggign */
+ return 0;
+}
+#endif
+
#endif /*HAVE_SPATIAL*/
diff --git a/sql/item_geofunc.h b/sql/item_geofunc.h
index a2a61758617..94be38e26ee 100644
--- a/sql/item_geofunc.h
+++ b/sql/item_geofunc.h
@@ -2,7 +2,7 @@
#define ITEM_GEOFUNC_INCLUDED
/* Copyright (c) 2000, 2010 Oracle and/or its affiliates.
- Copyright (C) 2011 Monty Program Ab.
+ Copyright (C) 2011, 2015 MariaDB
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
@@ -496,6 +496,18 @@ public:
const char *func_name() const { return "st_distance"; }
};
+
+#ifndef DBUG_OFF
+class Item_func_gis_debug: public Item_int_func
+{
+ public:
+ Item_func_gis_debug(Item *a) :Item_int_func(a) { null_value= false; }
+ const char *func_name() const { return "st_gis_debug"; }
+ longlong val_int();
+};
+#endif
+
+
#define GEOM_NEW(thd, obj_constructor) new (thd->mem_root) obj_constructor
#else /*HAVE_SPATIAL*/
diff --git a/sql/item_inetfunc.cc b/sql/item_inetfunc.cc
new file mode 100644
index 00000000000..6a09747fa1a
--- /dev/null
+++ b/sql/item_inetfunc.cc
@@ -0,0 +1,831 @@
+/* Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.
+ Copyright (c) 2014 MariaDB Foundation
+
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include <my_global.h>
+#include "item_inetfunc.h"
+
+#include "my_net.h"
+
+///////////////////////////////////////////////////////////////////////////
+
+static const int IN_ADDR_SIZE= sizeof (in_addr);
+static const int IN6_ADDR_SIZE= sizeof (in6_addr);
+static const int IN6_ADDR_NUM_WORDS= IN6_ADDR_SIZE / 2;
+
+static const char HEX_DIGITS[]= "0123456789abcdef";
+
+///////////////////////////////////////////////////////////////////////////
+
+longlong Item_func_inet_aton::val_int()
+{
+ DBUG_ASSERT(fixed);
+
+ uint byte_result= 0;
+ ulonglong result= 0; // We are ready for 64 bit addresses
+ const char *p,* end;
+ char c= '.'; // we mark c to indicate invalid IP in case length is 0
+ int dot_count= 0;
+
+ StringBuffer<36> tmp;
+ String *s= args[0]->val_str_ascii(&tmp);
+
+ if (!s) // If null value
+ goto err;
+
+ null_value= 0;
+
+ end= (p = s->ptr()) + s->length();
+ while (p < end)
+ {
+ c= *p++;
+ int digit= (int) (c - '0');
+ if (digit >= 0 && digit <= 9)
+ {
+ if ((byte_result= byte_result * 10 + digit) > 255)
+ goto err; // Wrong address
+ }
+ else if (c == '.')
+ {
+ dot_count++;
+ result= (result << 8) + (ulonglong) byte_result;
+ byte_result= 0;
+ }
+ else
+ goto err; // Invalid character
+ }
+ if (c != '.') // IP number can't end on '.'
+ {
+ /*
+ Attempt to support short forms of IP-addresses. It's however pretty
+ basic one comparing to the BSD support.
+ Examples:
+ 127 -> 0.0.0.127
+ 127.255 -> 127.0.0.255
+ 127.256 -> NULL (should have been 127.0.1.0)
+ 127.2.1 -> 127.2.0.1
+ */
+ switch (dot_count) {
+ case 1: result<<= 8; /* Fall through */
+ case 2: result<<= 8; /* Fall through */
+ }
+ return (result << 8) + (ulonglong) byte_result;
+ }
+
+err:
+ null_value=1;
+ return 0;
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+String* Item_func_inet_ntoa::val_str(String* str)
+{
+ DBUG_ASSERT(fixed);
+
+ ulonglong n= (ulonglong) args[0]->val_int();
+
+ /*
+ We do not know if args[0] is NULL until we have called
+ some val function on it if args[0] is not a constant!
+
+ Also return null if n > 255.255.255.255
+ */
+ if ((null_value= (args[0]->null_value || n > 0xffffffff)))
+ return 0; // Null value
+
+ str->set_charset(collation.collation);
+ str->length(0);
+
+ uchar buf[8];
+ int4store(buf, n);
+
+ /* Now we can assume little endian. */
+
+ char num[4];
+ num[3]= '.';
+
+ for (uchar *p= buf + 4; p-- > buf;)
+ {
+ uint c= *p;
+ uint n1, n2; // Try to avoid divisions
+ n1= c / 100; // 100 digits
+ c-= n1 * 100;
+ n2= c / 10; // 10 digits
+ c-= n2 * 10; // last digit
+ num[0]= (char) n1 + '0';
+ num[1]= (char) n2 + '0';
+ num[2]= (char) c + '0';
+ uint length= (n1 ? 4 : n2 ? 3 : 2); // Remove pre-zero
+ uint dot_length= (p <= buf) ? 1 : 0;
+ (void) str->append(num + 4 - length, length - dot_length,
+ &my_charset_latin1);
+ }
+
+ return str;
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+/**
+ Check the function argument, handle errors properly.
+
+ @return The function value.
+*/
+
+longlong Item_func_inet_bool_base::val_int()
+{
+ DBUG_ASSERT(fixed);
+
+ if (args[0]->result_type() != STRING_RESULT) // String argument expected
+ return 0;
+
+ String buffer;
+ String *arg_str= args[0]->val_str(&buffer);
+
+ if (!arg_str) // Out-of memory happened. The error has been reported.
+ return 0; // Or: the underlying field is NULL
+
+ return calc_value(arg_str) ? 1 : 0;
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+/**
+ Check the function argument, handle errors properly.
+
+ @param [out] buffer Buffer for string operations.
+
+ @return The function value.
+*/
+
+String *Item_func_inet_str_base::val_str_ascii(String *buffer)
+{
+ DBUG_ASSERT(fixed);
+
+ if (args[0]->result_type() != STRING_RESULT) // String argument expected
+ {
+ null_value= true;
+ return NULL;
+ }
+
+ String *arg_str= args[0]->val_str(buffer);
+ if (!arg_str) // Out-of memory happened. The error has been reported.
+ { // Or: the underlying field is NULL
+ null_value= true;
+ return NULL;
+ }
+
+ null_value= !calc_value(arg_str, buffer);
+
+ return null_value ? NULL : buffer;
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+/**
+ Tries to convert given string to binary IPv4-address representation.
+ This is a portable alternative to inet_pton(AF_INET).
+
+ @param str String to convert.
+ @param str_len String length.
+ @param[out] ipv4_address Buffer to store IPv4-address.
+
+ @return Completion status.
+ @retval false Given string does not represent an IPv4-address.
+ @retval true The string has been converted sucessfully.
+
+ @note The problem with inet_pton() is that it treats leading zeros in
+ IPv4-part differently on different platforms.
+*/
+
+static bool str_to_ipv4(const char *str, int str_length, in_addr *ipv4_address)
+{
+ if (str_length < 7)
+ {
+ DBUG_PRINT("error", ("str_to_ipv4(%.*s): "
+ "invalid IPv4 address: too short.",
+ str_length, str));
+ return false;
+ }
+
+ if (str_length > 15)
+ {
+ DBUG_PRINT("error", ("str_to_ipv4(%.*s): "
+ "invalid IPv4 address: too long.",
+ str_length, str));
+ return false;
+ }
+
+ unsigned char *ipv4_bytes= (unsigned char *) ipv4_address;
+ const char *p= str;
+ int byte_value= 0;
+ int chars_in_group= 0;
+ int dot_count= 0;
+ char c= 0;
+
+ while (((p - str) < str_length) && *p)
+ {
+ c= *p++;
+
+ if (my_isdigit(&my_charset_latin1, c))
+ {
+ ++chars_in_group;
+
+ if (chars_in_group > 3)
+ {
+ DBUG_PRINT("error", ("str_to_ipv4(%.*s): invalid IPv4 address: "
+ "too many characters in a group.",
+ str_length, str));
+ return false;
+ }
+
+ byte_value= byte_value * 10 + (c - '0');
+
+ if (byte_value > 255)
+ {
+ DBUG_PRINT("error", ("str_to_ipv4(%.*s): invalid IPv4 address: "
+ "invalid byte value.",
+ str_length, str));
+ return false;
+ }
+ }
+ else if (c == '.')
+ {
+ if (chars_in_group == 0)
+ {
+ DBUG_PRINT("error", ("str_to_ipv4(%.*s): invalid IPv4 address: "
+ "too few characters in a group.",
+ str_length, str));
+ return false;
+ }
+
+ ipv4_bytes[dot_count]= (unsigned char) byte_value;
+
+ ++dot_count;
+ byte_value= 0;
+ chars_in_group= 0;
+
+ if (dot_count > 3)
+ {
+ DBUG_PRINT("error", ("str_to_ipv4(%.*s): invalid IPv4 address: "
+ "too many dots.", str_length, str));
+ return false;
+ }
+ }
+ else
+ {
+ DBUG_PRINT("error", ("str_to_ipv4(%.*s): invalid IPv4 address: "
+ "invalid character at pos %d.",
+ str_length, str, (int) (p - str)));
+ return false;
+ }
+ }
+
+ if (c == '.')
+ {
+ DBUG_PRINT("error", ("str_to_ipv4(%.*s): invalid IPv4 address: "
+ "ending at '.'.", str_length, str));
+ return false;
+ }
+
+ if (dot_count != 3)
+ {
+ DBUG_PRINT("error", ("str_to_ipv4(%.*s): invalid IPv4 address: "
+ "too few groups.",
+ str_length, str));
+ return false;
+ }
+
+ ipv4_bytes[3]= (unsigned char) byte_value;
+
+ DBUG_PRINT("info", ("str_to_ipv4(%.*s): valid IPv4 address: %d.%d.%d.%d",
+ str_length, str,
+ ipv4_bytes[0], ipv4_bytes[1],
+ ipv4_bytes[2], ipv4_bytes[3]));
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+/**
+ Tries to convert given string to binary IPv6-address representation.
+ This is a portable alternative to inet_pton(AF_INET6).
+
+ @param str String to convert.
+ @param str_len String length.
+ @param[out] ipv6_address Buffer to store IPv6-address.
+
+ @return Completion status.
+ @retval false Given string does not represent an IPv6-address.
+ @retval true The string has been converted sucessfully.
+
+ @note The problem with inet_pton() is that it treats leading zeros in
+ IPv4-part differently on different platforms.
+*/
+
+static bool str_to_ipv6(const char *str, int str_length, in6_addr *ipv6_address)
+{
+ if (str_length < 2)
+ {
+ DBUG_PRINT("error", ("str_to_ipv6(%.*s): invalid IPv6 address: too short.",
+ str_length, str));
+ return false;
+ }
+
+ if (str_length > 8 * 4 + 7)
+ {
+ DBUG_PRINT("error", ("str_to_ipv6(%.*s): invalid IPv6 address: too long.",
+ str_length, str));
+ return false;
+ }
+
+ memset(ipv6_address, 0, IN6_ADDR_SIZE);
+
+ const char *p= str;
+
+ if (*p == ':')
+ {
+ ++p;
+
+ if (*p != ':')
+ {
+ DBUG_PRINT("error", ("str_to_ipv6(%.*s): invalid IPv6 address: "
+ "can not start with ':x'.", str_length, str));
+ return false;
+ }
+ }
+
+ char *ipv6_bytes= (char *) ipv6_address;
+ char *ipv6_bytes_end= ipv6_bytes + IN6_ADDR_SIZE;
+ char *dst= ipv6_bytes;
+ char *gap_ptr= NULL;
+ const char *group_start_ptr= p;
+ int chars_in_group= 0;
+ int group_value= 0;
+
+ while (((p - str) < str_length) && *p)
+ {
+ char c= *p++;
+
+ if (c == ':')
+ {
+ group_start_ptr= p;
+
+ if (!chars_in_group)
+ {
+ if (gap_ptr)
+ {
+ DBUG_PRINT("error", ("str_to_ipv6(%.*s): invalid IPv6 address: "
+ "too many gaps(::).", str_length, str));
+ return false;
+ }
+
+ gap_ptr= dst;
+ continue;
+ }
+
+ if (!*p || ((p - str) >= str_length))
+ {
+ DBUG_PRINT("error", ("str_to_ipv6(%.*s): invalid IPv6 address: "
+ "ending at ':'.", str_length, str));
+ return false;
+ }
+
+ if (dst + 2 > ipv6_bytes_end)
+ {
+ DBUG_PRINT("error", ("str_to_ipv6(%.*s): invalid IPv6 address: "
+ "too many groups (1).", str_length, str));
+ return false;
+ }
+
+ dst[0]= (unsigned char) (group_value >> 8) & 0xff;
+ dst[1]= (unsigned char) group_value & 0xff;
+ dst += 2;
+
+ chars_in_group= 0;
+ group_value= 0;
+ }
+ else if (c == '.')
+ {
+ if (dst + IN_ADDR_SIZE > ipv6_bytes_end)
+ {
+ DBUG_PRINT("error", ("str_to_ipv6(%.*s): invalid IPv6 address: "
+ "unexpected IPv4-part.", str_length, str));
+ return false;
+ }
+
+ if (!str_to_ipv4(group_start_ptr,
+ str + str_length - group_start_ptr,
+ (in_addr *) dst))
+ {
+ DBUG_PRINT("error", ("str_to_ipv6(%.*s): invalid IPv6 address: "
+ "invalid IPv4-part.", str_length, str));
+ return false;
+ }
+
+ dst += IN_ADDR_SIZE;
+ chars_in_group= 0;
+
+ break;
+ }
+ else
+ {
+ const char *hdp= strchr(HEX_DIGITS, my_tolower(&my_charset_latin1, c));
+
+ if (!hdp)
+ {
+ DBUG_PRINT("error", ("str_to_ipv6(%.*s): invalid IPv6 address: "
+ "invalid character at pos %d.",
+ str_length, str, (int) (p - str)));
+ return false;
+ }
+
+ if (chars_in_group >= 4)
+ {
+ DBUG_PRINT("error", ("str_to_ipv6(%.*s): invalid IPv6 address: "
+ "too many digits in group.",
+ str_length, str));
+ return false;
+ }
+
+ group_value <<= 4;
+ group_value |= hdp - HEX_DIGITS;
+
+ DBUG_ASSERT(group_value <= 0xffff);
+
+ ++chars_in_group;
+ }
+ }
+
+ if (chars_in_group > 0)
+ {
+ if (dst + 2 > ipv6_bytes_end)
+ {
+ DBUG_PRINT("error", ("str_to_ipv6(%.*s): invalid IPv6 address: "
+ "too many groups (2).", str_length, str));
+ return false;
+ }
+
+ dst[0]= (unsigned char) (group_value >> 8) & 0xff;
+ dst[1]= (unsigned char) group_value & 0xff;
+ dst += 2;
+ }
+
+ if (gap_ptr)
+ {
+ if (dst == ipv6_bytes_end)
+ {
+ DBUG_PRINT("error", ("str_to_ipv6(%.*s): invalid IPv6 address: "
+ "no room for a gap (::).", str_length, str));
+ return false;
+ }
+
+ int bytes_to_move= dst - gap_ptr;
+
+ for (int i= 1; i <= bytes_to_move; ++i)
+ {
+ ipv6_bytes_end[-i]= gap_ptr[bytes_to_move - i];
+ gap_ptr[bytes_to_move - i]= 0;
+ }
+
+ dst= ipv6_bytes_end;
+ }
+
+ if (dst < ipv6_bytes_end)
+ {
+ DBUG_PRINT("error", ("str_to_ipv6(%.*s): invalid IPv6 address: "
+ "too few groups.", str_length, str));
+ return false;
+ }
+
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+/**
+ Converts IPv4-binary-address to a string. This function is a portable
+ alternative to inet_ntop(AF_INET).
+
+ @param[in] ipv4 IPv4-address data (byte array)
+ @param[out] str A buffer to store string representation of IPv4-address.
+ It must be at least of INET_ADDRSTRLEN.
+
+ @note The problem with inet_ntop() is that it is available starting from
+ Windows Vista, but the minimum supported version is Windows 2000.
+*/
+
+static void ipv4_to_str(const in_addr *ipv4, char *str)
+{
+ const unsigned char *ipv4_bytes= (const unsigned char *) ipv4;
+
+ sprintf(str, "%d.%d.%d.%d",
+ ipv4_bytes[0], ipv4_bytes[1], ipv4_bytes[2], ipv4_bytes[3]);
+}
+///////////////////////////////////////////////////////////////////////////
+
+/**
+ Converts IPv6-binary-address to a string. This function is a portable
+ alternative to inet_ntop(AF_INET6).
+
+ @param[in] ipv6 IPv6-address data (byte array)
+ @param[out] str A buffer to store string representation of IPv6-address.
+ It must be at least of INET6_ADDRSTRLEN.
+
+ @note The problem with inet_ntop() is that it is available starting from
+ Windows Vista, but out the minimum supported version is Windows 2000.
+*/
+
+static void ipv6_to_str(const in6_addr *ipv6, char *str)
+{
+ struct Region
+ {
+ int pos;
+ int length;
+ };
+
+ const unsigned char *ipv6_bytes= (const unsigned char *) ipv6;
+
+ // 1. Translate IPv6-address bytes to words.
+ // We can't just cast to short, because it's not guaranteed
+ // that sizeof (short) == 2. So, we have to make a copy.
+
+ uint16 ipv6_words[IN6_ADDR_NUM_WORDS];
+
+ for (int i= 0; i < IN6_ADDR_NUM_WORDS; ++i)
+ ipv6_words[i]= (ipv6_bytes[2 * i] << 8) + ipv6_bytes[2 * i + 1];
+
+ // 2. Find "the gap" -- longest sequence of zeros in IPv6-address.
+
+ Region gap= { -1, -1 };
+
+ {
+ Region rg= { -1, -1 };
+
+ for (int i = 0; i < IN6_ADDR_NUM_WORDS; ++i)
+ {
+ if (ipv6_words[i] != 0)
+ {
+ if (rg.pos >= 0)
+ {
+ if (rg.length > gap.length)
+ gap= rg;
+
+ rg.pos= -1;
+ rg.length= -1;
+ }
+ }
+ else
+ {
+ if (rg.pos >= 0)
+ {
+ ++rg.length;
+ }
+ else
+ {
+ rg.pos= i;
+ rg.length= 1;
+ }
+ }
+ }
+
+ if (rg.pos >= 0)
+ {
+ if (rg.length > gap.length)
+ gap= rg;
+ }
+ }
+
+ // 3. Convert binary data to string.
+
+ char *p= str;
+
+ for (int i = 0; i < IN6_ADDR_NUM_WORDS; ++i)
+ {
+ if (i == gap.pos)
+ {
+ // We're at the gap position. We should put trailing ':' and jump to
+ // the end of the gap.
+
+ if (i == 0)
+ {
+ // The gap starts from the beginning of the data -- leading ':'
+ // should be put additionally.
+
+ *p= ':';
+ ++p;
+ }
+
+ *p= ':';
+ ++p;
+
+ i += gap.length - 1;
+ }
+ else if (i == 6 && gap.pos == 0 &&
+ (gap.length == 6 || // IPv4-compatible
+ (gap.length == 5 && ipv6_words[5] == 0xffff) // IPv4-mapped
+ ))
+ {
+ // The data represents either IPv4-compatible or IPv4-mapped address.
+ // The IPv6-part (zeros or zeros + ffff) has been already put into
+ // the string (str). Now it's time to dump IPv4-part.
+
+ ipv4_to_str((const in_addr *) (ipv6_bytes + 12), p);
+ return;
+ }
+ else
+ {
+ // Usual IPv6-address-field. Print it out using lower-case
+ // hex-letters without leading zeros (recommended IPv6-format).
+ //
+ // If it is not the last field, append closing ':'.
+
+ p += sprintf(p, "%x", ipv6_words[i]);
+
+ if (i != IN6_ADDR_NUM_WORDS - 1)
+ {
+ *p= ':';
+ ++p;
+ }
+ }
+ }
+
+ *p= 0;
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+/**
+ Converts IP-address-string to IP-address-data.
+
+ @param arg IP-address-string.
+ @param [out] buffer Buffer to store IP-address-data.
+
+ @return Completion status.
+ @retval false Given string does not represent an IP-address.
+ @retval true The string has been converted sucessfully.
+*/
+
+bool Item_func_inet6_aton::calc_value(String *arg, String *buffer)
+{
+ // ipv4-string -> varbinary(4)
+ // ipv6-string -> varbinary(16)
+
+ in_addr ipv4_address;
+ in6_addr ipv6_address;
+
+ if (str_to_ipv4(arg->ptr(), arg->length(), &ipv4_address))
+ {
+ buffer->length(0);
+ buffer->append((char *) &ipv4_address, sizeof (in_addr), &my_charset_bin);
+
+ return true;
+ }
+
+ if (str_to_ipv6(arg->ptr(), arg->length(), &ipv6_address))
+ {
+ buffer->length(0);
+ buffer->append((char *) &ipv6_address, sizeof (in6_addr), &my_charset_bin);
+
+ return true;
+ }
+
+ return false;
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+/**
+ Converts IP-address-data to IP-address-string.
+
+ @param arg IP-address-data.
+ @param [out] buffer Buffer to store IP-address-string.
+
+ @return Completion status.
+ @retval false The argument does not correspond to IP-address.
+ @retval true The string has been converted sucessfully.
+*/
+
+bool Item_func_inet6_ntoa::calc_value(String *arg, String *buffer)
+{
+ if (arg->charset() != &my_charset_bin)
+ return false;
+
+ if ((int) arg->length() == IN_ADDR_SIZE)
+ {
+ char str[INET_ADDRSTRLEN];
+
+ ipv4_to_str((const in_addr *) arg->ptr(), str);
+
+ buffer->length(0);
+ buffer->append(str, (uint32) strlen(str), &my_charset_latin1);
+
+ return true;
+ }
+ else if ((int) arg->length() == IN6_ADDR_SIZE)
+ {
+ char str[INET6_ADDRSTRLEN];
+
+ ipv6_to_str((const in6_addr *) arg->ptr(), str);
+
+ buffer->length(0);
+ buffer->append(str, (uint32) strlen(str), &my_charset_latin1);
+
+ return true;
+ }
+
+ DBUG_PRINT("info",
+ ("INET6_NTOA(): varbinary(4) or varbinary(16) expected."));
+ return false;
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+/**
+ Checks if the passed string represents an IPv4-address.
+
+ @param arg The string to check.
+
+ @return Check status.
+ @retval false The passed string does not represent an IPv4-address.
+ @retval true The passed string represents an IPv4-address.
+*/
+
+bool Item_func_is_ipv4::calc_value(const String *arg)
+{
+ in_addr ipv4_address;
+
+ return str_to_ipv4(arg->ptr(), arg->length(), &ipv4_address);
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+/**
+ Checks if the passed string represents an IPv6-address.
+
+ @param arg The string to check.
+
+ @return Check status.
+ @retval false The passed string does not represent an IPv6-address.
+ @retval true The passed string represents an IPv6-address.
+*/
+
+bool Item_func_is_ipv6::calc_value(const String *arg)
+{
+ in6_addr ipv6_address;
+
+ return str_to_ipv6(arg->ptr(), arg->length(), &ipv6_address);
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+/**
+ Checks if the passed IPv6-address is an IPv4-compat IPv6-address.
+
+ @param arg The IPv6-address to check.
+
+ @return Check status.
+ @retval false The passed IPv6-address is not an IPv4-compatible IPv6-address.
+ @retval true The passed IPv6-address is an IPv4-compatible IPv6-address.
+*/
+
+bool Item_func_is_ipv4_compat::calc_value(const String *arg)
+{
+ if ((int) arg->length() != IN6_ADDR_SIZE || arg->charset() != &my_charset_bin)
+ return false;
+
+ return IN6_IS_ADDR_V4COMPAT((struct in6_addr *) arg->ptr());
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+/**
+ Checks if the passed IPv6-address is an IPv4-mapped IPv6-address.
+
+ @param arg The IPv6-address to check.
+
+ @return Check status.
+ @retval false The passed IPv6-address is not an IPv4-mapped IPv6-address.
+ @retval true The passed IPv6-address is an IPv4-mapped IPv6-address.
+*/
+
+bool Item_func_is_ipv4_mapped::calc_value(const String *arg)
+{
+ if ((int) arg->length() != IN6_ADDR_SIZE || arg->charset() != &my_charset_bin)
+ return false;
+
+ return IN6_IS_ADDR_V4MAPPED((struct in6_addr *) arg->ptr());
+}
diff --git a/sql/item_inetfunc.h b/sql/item_inetfunc.h
new file mode 100644
index 00000000000..3a85d367ff1
--- /dev/null
+++ b/sql/item_inetfunc.h
@@ -0,0 +1,244 @@
+#ifndef ITEM_INETFUNC_INCLUDED
+#define ITEM_INETFUNC_INCLUDED
+
+/* Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.
+ Copyright (c) 2014 MariaDB Foundation
+
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+
+#include "item.h"
+
+/*************************************************************************
+ Item_func_inet_aton implements INET_ATON() SQL-function.
+*************************************************************************/
+
+class Item_func_inet_aton : public Item_int_func
+{
+public:
+ Item_func_inet_aton(Item *a) :Item_int_func(a) {}
+ longlong val_int();
+ const char *func_name() const { return "inet_aton"; }
+ void fix_length_and_dec()
+ {
+ decimals= 0;
+ max_length= 21;
+ maybe_null= 1;
+ unsigned_flag= 1;
+ }
+};
+
+
+/*************************************************************************
+ Item_func_inet_ntoa implements INET_NTOA() SQL-function.
+*************************************************************************/
+
+class Item_func_inet_ntoa : public Item_str_func
+{
+public:
+ Item_func_inet_ntoa(Item *a)
+ : Item_str_func(a)
+ { }
+ String* val_str(String* str);
+ const char *func_name() const { return "inet_ntoa"; }
+ void fix_length_and_dec()
+ {
+ decimals= 0;
+ fix_length_and_charset(3 * 8 + 7, default_charset());
+ maybe_null= 1;
+ }
+};
+
+
+/*************************************************************************
+ Item_func_inet_bool_base implements common code for INET6/IP-related
+ functions returning boolean value.
+*************************************************************************/
+
+class Item_func_inet_bool_base : public Item_bool_func
+{
+public:
+ inline Item_func_inet_bool_base(Item *ip_addr)
+ : Item_bool_func(ip_addr)
+ {
+ null_value= false;
+ }
+
+public:
+ virtual longlong val_int();
+
+protected:
+ virtual bool calc_value(const String *arg) = 0;
+};
+
+
+/*************************************************************************
+ Item_func_inet_str_base implements common code for INET6/IP-related
+ functions returning string value.
+*************************************************************************/
+
+class Item_func_inet_str_base : public Item_str_ascii_func
+{
+public:
+ inline Item_func_inet_str_base(Item *arg)
+ : Item_str_ascii_func(arg)
+ { }
+
+public:
+ virtual String *val_str_ascii(String *buffer);
+
+protected:
+ virtual bool calc_value(String *arg, String *buffer) = 0;
+};
+
+
+/*************************************************************************
+ Item_func_inet6_aton implements INET6_ATON() SQL-function.
+*************************************************************************/
+
+class Item_func_inet6_aton : public Item_func_inet_str_base
+{
+public:
+ inline Item_func_inet6_aton(Item *ip_addr)
+ : Item_func_inet_str_base(ip_addr)
+ { }
+
+public:
+ virtual const char *func_name() const
+ { return "inet6_aton"; }
+
+ virtual void fix_length_and_dec()
+ {
+ decimals= 0;
+ fix_length_and_charset(16, &my_charset_bin);
+ maybe_null= 1;
+ }
+
+protected:
+ virtual bool calc_value(String *arg, String *buffer);
+};
+
+
+/*************************************************************************
+ Item_func_inet6_ntoa implements INET6_NTOA() SQL-function.
+*************************************************************************/
+
+class Item_func_inet6_ntoa : public Item_func_inet_str_base
+{
+public:
+ inline Item_func_inet6_ntoa(Item *ip_addr)
+ : Item_func_inet_str_base(ip_addr)
+ { }
+
+public:
+ virtual const char *func_name() const
+ { return "inet6_ntoa"; }
+
+ virtual void fix_length_and_dec()
+ {
+ decimals= 0;
+
+ // max length: IPv6-address -- 16 bytes
+ // 16 bytes / 2 bytes per group == 8 groups => 7 delimiter
+ // 4 symbols per group
+ fix_length_and_charset(8 * 4 + 7, default_charset());
+
+ maybe_null= 1;
+ }
+
+protected:
+ virtual bool calc_value(String *arg, String *buffer);
+};
+
+
+/*************************************************************************
+ Item_func_is_ipv4 implements IS_IPV4() SQL-function.
+*************************************************************************/
+
+class Item_func_is_ipv4 : public Item_func_inet_bool_base
+{
+public:
+ inline Item_func_is_ipv4(Item *ip_addr)
+ : Item_func_inet_bool_base(ip_addr)
+ { }
+
+public:
+ virtual const char *func_name() const
+ { return "is_ipv4"; }
+
+protected:
+ virtual bool calc_value(const String *arg);
+};
+
+
+/*************************************************************************
+ Item_func_is_ipv6 implements IS_IPV6() SQL-function.
+*************************************************************************/
+
+class Item_func_is_ipv6 : public Item_func_inet_bool_base
+{
+public:
+ inline Item_func_is_ipv6(Item *ip_addr)
+ : Item_func_inet_bool_base(ip_addr)
+ { }
+
+public:
+ virtual const char *func_name() const
+ { return "is_ipv6"; }
+
+protected:
+ virtual bool calc_value(const String *arg);
+};
+
+
+/*************************************************************************
+ Item_func_is_ipv4_compat implements IS_IPV4_COMPAT() SQL-function.
+*************************************************************************/
+
+class Item_func_is_ipv4_compat : public Item_func_inet_bool_base
+{
+public:
+ inline Item_func_is_ipv4_compat(Item *ip_addr)
+ : Item_func_inet_bool_base(ip_addr)
+ { }
+
+public:
+ virtual const char *func_name() const
+ { return "is_ipv4_compat"; }
+
+protected:
+ virtual bool calc_value(const String *arg);
+};
+
+
+/*************************************************************************
+ Item_func_is_ipv4_mapped implements IS_IPV4_MAPPED() SQL-function.
+*************************************************************************/
+
+class Item_func_is_ipv4_mapped : public Item_func_inet_bool_base
+{
+public:
+ inline Item_func_is_ipv4_mapped(Item *ip_addr)
+ : Item_func_inet_bool_base(ip_addr)
+ { }
+
+public:
+ virtual const char *func_name() const
+ { return "is_ipv4_mapped"; }
+
+protected:
+ virtual bool calc_value(const String *arg);
+};
+
+#endif // ITEM_INETFUNC_INCLUDED
diff --git a/sql/item_row.cc b/sql/item_row.cc
index ee1d17213ee..3548a6b9b75 100644
--- a/sql/item_row.cc
+++ b/sql/item_row.cc
@@ -14,6 +14,7 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+#include <my_global.h>
#include "sql_priv.h"
/*
It is necessary to include set_var.h instead of item.h because there
@@ -47,13 +48,13 @@ Item_row::Item_row(List<Item> &arg):
items= (Item**) sql_alloc(sizeof(Item*)*arg_count);
else
items= 0;
- List_iterator<Item> li(arg);
+ List_iterator_fast<Item> li(arg);
uint i= 0;
Item *item;
while ((item= li++))
{
items[i]= item;
- i++;
+ i++;
}
}
diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc
index 94370d45cef..3b8bc1580bb 100644
--- a/sql/item_strfunc.cc
+++ b/sql/item_strfunc.cc
@@ -31,10 +31,10 @@
#pragma implementation // gcc: Class implementation
#endif
-/* May include caustic 3rd-party defs. Use early, so it can override nothing. */
-#include "sha2.h"
-#include "my_global.h" // HAVE_*
+#include <my_global.h> // HAVE_*
+/* May include caustic 3rd-party defs. Use early, so it can override nothing */
+#include "sha2.h"
#include "sql_priv.h"
/*
@@ -51,15 +51,19 @@
#include "password.h" // my_make_scrambled_password,
// my_make_scrambled_password_323
#include <m_ctype.h>
-#include "my_md5.h"
+#include <base64.h>
+#include <my_md5.h>
#include "sha1.h"
#include "my_aes.h"
#include <zlib.h>
C_MODE_START
#include "../mysys/my_static.h" // For soundex_map
C_MODE_END
+#include "sql_show.h" // append_identifier
+#include <sql_repl.h>
+#include "sql_statistics.h"
-size_t username_char_length= 16;
+size_t username_char_length= 80;
/*
For the Items which have only val_str_ascii() method
@@ -70,7 +74,7 @@ size_t username_char_length= 16;
Normally conversion does not happen, and val_str_ascii() is immediately
returned instead.
*/
-String *Item_str_func::val_str_from_val_str_ascii(String *str, String *str2)
+String *Item_func::val_str_from_val_str_ascii(String *str, String *str2)
{
DBUG_ASSERT(fixed == 1);
@@ -98,22 +102,6 @@ String *Item_str_func::val_str_from_val_str_ascii(String *str, String *str2)
}
-/*
- Convert an array of bytes to a hexadecimal representation.
-
- Used to generate a hexadecimal representation of a message digest.
-*/
-static void array_to_hex(char *to, const unsigned char *str, uint len)
-{
- const unsigned char *str_end= str + len;
- for (; str != str_end; ++str)
- {
- *to++= _dig_vec_lower[((uchar) *str) >> 4];
- *to++= _dig_vec_lower[((uchar) *str) & 0x0F];
- }
-}
-
-
bool Item_str_func::fix_fields(THD *thd, Item **ref)
{
bool res= Item_func::fix_fields(thd, ref);
@@ -121,9 +109,7 @@ bool Item_str_func::fix_fields(THD *thd, Item **ref)
In Item_str_func::check_well_formed_result() we may set null_value
flag on the same condition as in test() below.
*/
- maybe_null= (maybe_null ||
- test(thd->variables.sql_mode &
- (MODE_STRICT_TRANS_TABLES | MODE_STRICT_ALL_TABLES)));
+ maybe_null= maybe_null || thd->is_strict_mode();
return res;
}
@@ -177,7 +163,8 @@ String *Item_func_md5::val_str_ascii(String *str)
uchar digest[16];
null_value=0;
- MY_MD5_HASH(digest,(uchar *) sptr->ptr(), sptr->length());
+ compute_md5_hash((char *) digest, (const char *) sptr->ptr(),
+ sptr->length());
if (str->alloc(32)) // Ensure that memory is free
{
null_value=1;
@@ -224,17 +211,11 @@ String *Item_func_sha::val_str_ascii(String *str)
String * sptr= args[0]->val_str(str);
if (sptr) /* If we got value different from NULL */
{
- SHA1_CONTEXT context; /* Context used to generate SHA1 hash */
/* Temporary buffer to store 160bit digest */
uint8 digest[SHA1_HASH_SIZE];
- mysql_sha1_reset(&context); /* We do not have to check for error here */
- /* No need to check error as the only case would be too long message */
- mysql_sha1_input(&context,
- (const uchar *) sptr->ptr(), sptr->length());
-
+ compute_sha1_hash(digest, (const char *) sptr->ptr(), sptr->length());
/* Ensure that memory is free and we got result */
- if (!( str->alloc(SHA1_HASH_SIZE*2) ||
- (mysql_sha1_result(&context,digest))))
+ if (!str->alloc(SHA1_HASH_SIZE*2))
{
array_to_hex((char *) str->ptr(), digest, SHA1_HASH_SIZE);
str->set_charset(&my_charset_numeric);
@@ -306,9 +287,9 @@ String *Item_func_sha2::val_str_ascii(String *str)
default:
if (!args[1]->const_item())
push_warning_printf(current_thd,
- MYSQL_ERROR::WARN_LEVEL_WARN,
- ER_WRONG_PARAMETERS_TO_NATIVE_FCT,
- ER(ER_WRONG_PARAMETERS_TO_NATIVE_FCT), "sha2");
+ Sql_condition::WARN_LEVEL_WARN,
+ ER_WRONG_PARAMETERS_TO_NATIVE_FCT,
+ ER(ER_WRONG_PARAMETERS_TO_NATIVE_FCT), "sha2");
null_value= TRUE;
return NULL;
}
@@ -330,7 +311,7 @@ String *Item_func_sha2::val_str_ascii(String *str)
#else
push_warning_printf(current_thd,
- MYSQL_ERROR::WARN_LEVEL_WARN,
+ Sql_condition::WARN_LEVEL_WARN,
ER_FEATURE_DISABLED,
ER(ER_FEATURE_DISABLED),
"sha2", "--with-ssl");
@@ -368,7 +349,7 @@ void Item_func_sha2::fix_length_and_dec()
#endif
default:
push_warning_printf(current_thd,
- MYSQL_ERROR::WARN_LEVEL_WARN,
+ Sql_condition::WARN_LEVEL_WARN,
ER_WRONG_PARAMETERS_TO_NATIVE_FCT,
ER(ER_WRONG_PARAMETERS_TO_NATIVE_FCT), "sha2");
}
@@ -378,7 +359,7 @@ void Item_func_sha2::fix_length_and_dec()
#else
push_warning_printf(current_thd,
- MYSQL_ERROR::WARN_LEVEL_WARN,
+ Sql_condition::WARN_LEVEL_WARN,
ER_FEATURE_DISABLED,
ER(ER_FEATURE_DISABLED),
"sha2", "--with-ssl");
@@ -463,6 +444,186 @@ void Item_func_aes_decrypt::fix_length_and_dec()
}
+void Item_func_to_base64::fix_length_and_dec()
+{
+ maybe_null= args[0]->maybe_null;
+ collation.set(default_charset(), DERIVATION_COERCIBLE, MY_REPERTOIRE_ASCII);
+ if (args[0]->max_length > (uint) base64_encode_max_arg_length())
+ {
+ maybe_null= 1;
+ fix_char_length_ulonglong((ulonglong) base64_encode_max_arg_length());
+ }
+ else
+ {
+ int length= base64_needed_encoded_length((int) args[0]->max_length);
+ DBUG_ASSERT(length > 0);
+ fix_char_length_ulonglong((ulonglong) length - 1);
+ }
+}
+
+
+String *Item_func_to_base64::val_str_ascii(String *str)
+{
+ String *res= args[0]->val_str(str);
+ bool too_long= false;
+ int length;
+ if (!res ||
+ res->length() > (uint) base64_encode_max_arg_length() ||
+ (too_long=
+ ((uint) (length= base64_needed_encoded_length((int) res->length())) >
+ current_thd->variables.max_allowed_packet)) ||
+ tmp_value.alloc((uint) length))
+ {
+ null_value= 1; // NULL input, too long input, or OOM.
+ if (too_long)
+ {
+ push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
+ ER_WARN_ALLOWED_PACKET_OVERFLOWED,
+ ER(ER_WARN_ALLOWED_PACKET_OVERFLOWED), func_name(),
+ current_thd->variables.max_allowed_packet);
+ }
+ return 0;
+ }
+ base64_encode(res->ptr(), (int) res->length(), (char*) tmp_value.ptr());
+ DBUG_ASSERT(length > 0);
+ tmp_value.length((uint) length - 1); // Without trailing '\0'
+ null_value= 0;
+ return &tmp_value;
+}
+
+
+void Item_func_from_base64::fix_length_and_dec()
+{
+ if (args[0]->max_length > (uint) base64_decode_max_arg_length())
+ {
+ fix_char_length_ulonglong((ulonglong) base64_decode_max_arg_length());
+ }
+ else
+ {
+ int length= base64_needed_decoded_length((int) args[0]->max_length);
+ fix_char_length_ulonglong((ulonglong) length);
+ }
+ maybe_null= 1; // Can be NULL, e.g. in case of badly formed input string
+}
+
+
+String *Item_func_from_base64::val_str(String *str)
+{
+ String *res= args[0]->val_str_ascii(str);
+ int length;
+ const char *end_ptr;
+
+ if (!res)
+ goto err;
+
+ if (res->length() > (uint) base64_decode_max_arg_length() ||
+ ((uint) (length= base64_needed_decoded_length((int) res->length())) >
+ current_thd->variables.max_allowed_packet))
+ {
+ push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
+ ER_WARN_ALLOWED_PACKET_OVERFLOWED,
+ ER(ER_WARN_ALLOWED_PACKET_OVERFLOWED), func_name(),
+ current_thd->variables.max_allowed_packet);
+ goto err;
+ }
+
+ if (tmp_value.alloc((uint) length))
+ goto err;
+
+ if ((length= base64_decode(res->ptr(), (int) res->length(),
+ (char *) tmp_value.ptr(), &end_ptr, 0)) < 0 ||
+ end_ptr < res->ptr() + res->length())
+ {
+ push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
+ ER_BAD_BASE64_DATA, ER(ER_BAD_BASE64_DATA),
+ end_ptr - res->ptr());
+ goto err;
+ }
+
+ tmp_value.length((uint) length);
+ null_value= 0;
+ return &tmp_value;
+err:
+ null_value= 1; // NULL input, too long input, OOM, or badly formed input
+ return 0;
+}
+///////////////////////////////////////////////////////////////////////////////
+
+
+const char *histogram_types[] =
+ {"SINGLE_PREC_HB", "DOUBLE_PREC_HB", 0};
+static TYPELIB hystorgam_types_typelib=
+ { array_elements(histogram_types),
+ "histogram_types",
+ histogram_types, NULL};
+const char *representation_by_type[]= {"%.3f", "%.5f"};
+
+String *Item_func_decode_histogram::val_str(String *str)
+{
+ DBUG_ASSERT(fixed == 1);
+ char buff[STRING_BUFFER_USUAL_SIZE];
+ String *res, tmp(buff, sizeof(buff), &my_charset_bin);
+ int type;
+
+ tmp.length(0);
+ if (!(res= args[0]->val_str(&tmp)) ||
+ (type= find_type(res->c_ptr_safe(),
+ &hystorgam_types_typelib, MYF(0))) <= 0)
+ {
+ null_value= 1;
+ return 0;
+ }
+ type--;
+
+ tmp.length(0);
+ if (!(res= args[1]->val_str(&tmp)))
+ {
+ null_value= 1;
+ return 0;
+ }
+ if (type == DOUBLE_PREC_HB && res->length() % 2 != 0)
+ res->length(res->length() - 1); // one byte is unused
+
+ double prev= 0.0;
+ uint i;
+ str->length(0);
+ char numbuf[32];
+ const uchar *p= (uchar*)res->c_ptr();
+ for (i= 0; i < res->length(); i++)
+ {
+ double val;
+ switch (type)
+ {
+ case SINGLE_PREC_HB:
+ val= p[i] / ((double)((1 << 8) - 1));
+ break;
+ case DOUBLE_PREC_HB:
+ val= uint2korr(p + i) / ((double)((1 << 16) - 1));
+ i++;
+ break;
+ default:
+ val= 0;
+ DBUG_ASSERT(0);
+ }
+ /* show delta with previous value */
+ int size= my_snprintf(numbuf, sizeof(numbuf),
+ representation_by_type[type], val - prev);
+ str->append(numbuf, size);
+ str->append(",");
+ prev= val;
+ }
+ /* show delta with max */
+ int size= my_snprintf(numbuf, sizeof(numbuf),
+ representation_by_type[type], 1.0 - prev);
+ str->append(numbuf, size);
+
+ null_value=0;
+ return str;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+
/**
Concatenate args with the following premises:
If only one arg (which is ok), return value of arg;
@@ -504,7 +665,7 @@ String *Item_func_concat::val_str(String *str)
if (res->length()+res2->length() >
current_thd->variables.max_allowed_packet)
{
- push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
ER_WARN_ALLOWED_PACKET_OVERFLOWED,
ER(ER_WARN_ALLOWED_PACKET_OVERFLOWED), func_name(),
current_thd->variables.max_allowed_packet);
@@ -577,7 +738,7 @@ String *Item_func_concat::val_str(String *str)
}
else
{
- uint new_len = max(tmp_value.alloced_length() * 2, concat_len);
+ uint new_len = MY_MAX(tmp_value.alloced_length() * 2, concat_len);
if (tmp_value.realloc(new_len))
goto null;
@@ -710,11 +871,11 @@ String *Item_func_des_encrypt::val_str(String *str)
return &tmp_value;
error:
- push_warning_printf(current_thd,MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(current_thd,Sql_condition::WARN_LEVEL_WARN,
code, ER(code),
"des_encrypt");
#else
- push_warning_printf(current_thd,MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(current_thd,Sql_condition::WARN_LEVEL_WARN,
ER_FEATURE_DISABLED, ER(ER_FEATURE_DISABLED),
"des_encrypt", "--with-ssl");
#endif /* defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY) */
@@ -788,12 +949,12 @@ String *Item_func_des_decrypt::val_str(String *str)
return &tmp_value;
error:
- push_warning_printf(current_thd,MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(current_thd,Sql_condition::WARN_LEVEL_WARN,
code, ER(code),
"des_decrypt");
wrong_key:
#else
- push_warning_printf(current_thd,MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(current_thd,Sql_condition::WARN_LEVEL_WARN,
ER_FEATURE_DISABLED, ER(ER_FEATURE_DISABLED),
"des_decrypt", "--with-ssl");
#endif /* defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY) */
@@ -844,7 +1005,7 @@ String *Item_func_concat_ws::val_str(String *str)
if (res->length() + sep_str->length() + res2->length() >
current_thd->variables.max_allowed_packet)
{
- push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
ER_WARN_ALLOWED_PACKET_OVERFLOWED,
ER(ER_WARN_ALLOWED_PACKET_OVERFLOWED), func_name(),
current_thd->variables.max_allowed_packet);
@@ -926,7 +1087,7 @@ String *Item_func_concat_ws::val_str(String *str)
}
else
{
- uint new_len = max(tmp_value.alloced_length() * 2, concat_len);
+ uint new_len = MY_MAX(tmp_value.alloced_length() * 2, concat_len);
if (tmp_value.realloc(new_len))
goto null;
@@ -1104,7 +1265,7 @@ redo:
if (res->length()-from_length + to_length >
current_thd->variables.max_allowed_packet)
{
- push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
ER_WARN_ALLOWED_PACKET_OVERFLOWED,
ER(ER_WARN_ALLOWED_PACKET_OVERFLOWED),
func_name(),
@@ -1133,7 +1294,7 @@ skip:
if (res->length()-from_length + to_length >
current_thd->variables.max_allowed_packet)
{
- push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
ER_WARN_ALLOWED_PACKET_OVERFLOWED,
ER(ER_WARN_ALLOWED_PACKET_OVERFLOWED), func_name(),
current_thd->variables.max_allowed_packet);
@@ -1172,6 +1333,190 @@ void Item_func_replace::fix_length_and_dec()
}
+/*********************************************************************/
+void Item_func_regexp_replace::fix_length_and_dec()
+{
+ if (agg_arg_charsets_for_string_result_with_comparison(collation, args, 3))
+ return;
+ max_length= MAX_BLOB_WIDTH;
+ re.init(collation.collation, 0, 10);
+ re.fix_owner(this, args[0], args[1]);
+}
+
+
+/*
+ Traverse through the replacement string and append to "str".
+ Sub-pattern references \0 .. \9 are recognized, which are replaced
+ to the chunks of the source string.
+*/
+bool Item_func_regexp_replace::append_replacement(String *str,
+ const LEX_CSTRING *source,
+ const LEX_CSTRING *replace)
+{
+ const char *beg= replace->str;
+ const char *end= beg + replace->length;
+ CHARSET_INFO *cs= re.library_charset();
+
+ for ( ; ; )
+ {
+ my_wc_t wc;
+ int cnv, n;
+
+ if ((cnv= cs->cset->mb_wc(cs, &wc, (const uchar *) beg,
+ (const uchar *) end)) < 1)
+ break; /* End of line */
+ beg+= cnv;
+
+ if (wc != '\\')
+ {
+ if (str->append(beg - cnv, cnv, cs))
+ return true;
+ continue;
+ }
+
+ if ((cnv= cs->cset->mb_wc(cs, &wc, (const uchar *) beg,
+ (const uchar *) end)) < 1)
+ break; /* End of line */
+ beg+= cnv;
+
+ if ((n= ((int) wc) - '0') >= 0 && n <= 9)
+ {
+ if (n < re.nsubpatterns())
+ {
+ /* A valid sub-pattern reference found */
+ int pbeg= re.subpattern_start(n), plength= re.subpattern_end(n) - pbeg;
+ if (str->append(source->str + pbeg, plength, cs))
+ return true;
+ }
+ }
+ else
+ {
+ /*
+ A non-digit character following after '\'.
+ Just add the character itself.
+ */
+ if (str->append(beg - cnv, cnv, cs))
+ return false;
+ }
+ }
+ return false;
+}
+
+
+String *Item_func_regexp_replace::val_str(String *str)
+{
+ DBUG_ASSERT(fixed == 1);
+ char buff0[MAX_FIELD_WIDTH];
+ char buff2[MAX_FIELD_WIDTH];
+ String tmp0(buff0,sizeof(buff0),&my_charset_bin);
+ String tmp2(buff2,sizeof(buff2),&my_charset_bin);
+ String *source= args[0]->val_str(&tmp0);
+ String *replace= args[2]->val_str(&tmp2);
+ LEX_CSTRING src, rpl;
+ int startoffset= 0;
+
+ if ((null_value= (args[0]->null_value || args[2]->null_value ||
+ re.recompile(args[1]))))
+ return (String *) 0;
+
+ if (!(source= re.convert_if_needed(source, &re.subject_converter)) ||
+ !(replace= re.convert_if_needed(replace, &re.replace_converter)))
+ goto err;
+
+ src= source->lex_cstring();
+ rpl= replace->lex_cstring();
+
+ str->length(0);
+ str->set_charset(collation.collation);
+
+ for ( ; ; ) // Iterate through all matches
+ {
+
+ if (re.exec(src.str, src.length, startoffset))
+ goto err;
+
+ if (!re.match() || re.subpattern_length(0) == 0)
+ {
+ /*
+ No match or an empty match.
+ Append the rest of the source string
+ starting from startoffset until the end of the source.
+ */
+ if (str->append(src.str + startoffset, src.length - startoffset, re.library_charset()))
+ goto err;
+ return str;
+ }
+
+ /*
+ Append prefix, the part before the matching pattern.
+ starting from startoffset until the next match
+ */
+ if (str->append(src.str + startoffset, re.subpattern_start(0) - startoffset, re.library_charset()))
+ goto err;
+
+ // Append replacement
+ if (append_replacement(str, &src, &rpl))
+ goto err;
+
+ // Set the new start point as the end of previous match
+ startoffset= re.subpattern_end(0);
+ }
+ return str;
+
+err:
+ null_value= true;
+ return (String *) 0;
+}
+
+
+void Item_func_regexp_substr::fix_length_and_dec()
+{
+ if (agg_arg_charsets_for_string_result_with_comparison(collation, args, 2))
+ return;
+ fix_char_length(args[0]->max_char_length());
+ re.init(collation.collation, 0, 10);
+ re.fix_owner(this, args[0], args[1]);
+}
+
+
+String *Item_func_regexp_substr::val_str(String *str)
+{
+ DBUG_ASSERT(fixed == 1);
+ char buff0[MAX_FIELD_WIDTH];
+ String tmp0(buff0,sizeof(buff0),&my_charset_bin);
+ String *source= args[0]->val_str(&tmp0);
+
+ if ((null_value= (args[0]->null_value || re.recompile(args[1]))))
+ return (String *) 0;
+
+ if (!(source= re.convert_if_needed(source, &re.subject_converter)))
+ goto err;
+
+ str->length(0);
+ str->set_charset(collation.collation);
+
+ if (re.exec(source->ptr(), source->length(), 0))
+ goto err;
+
+ if (!re.match())
+ return str;
+
+ if (str->append(source->ptr() + re.subpattern_start(0),
+ re.subpattern_end(0) - re.subpattern_start(0),
+ re.library_charset()))
+ goto err;
+
+ return str;
+
+err:
+ null_value= true;
+ return (String *) 0;
+}
+
+
+/************************************************************************/
+
+
String *Item_func_insert::val_str(String *str)
{
DBUG_ASSERT(fixed == 1);
@@ -1220,7 +1565,7 @@ String *Item_func_insert::val_str(String *str)
if ((ulonglong) (res->length() - length + res2->length()) >
(ulonglong) current_thd->variables.max_allowed_packet)
{
- push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
ER_WARN_ALLOWED_PACKET_OVERFLOWED,
ER(ER_WARN_ALLOWED_PACKET_OVERFLOWED),
func_name(), current_thd->variables.max_allowed_packet);
@@ -1419,7 +1764,7 @@ String *Item_func_substr::val_str(String *str)
length= res->charpos((int) length, (uint32) start);
tmp_length= res->length() - start;
- length= min(length, tmp_length);
+ length= MY_MIN(length, tmp_length);
if (!start && (longlong) res->length() == length)
return res;
@@ -1442,7 +1787,7 @@ void Item_func_substr::fix_length_and_dec()
else if (start < 0)
max_length= ((uint)(-start) > max_length) ? 0 : (uint)(-start);
else
- max_length-= min((uint)(start - 1), max_length);
+ max_length-= MY_MIN((uint)(start - 1), max_length);
}
if (arg_count == 3 && args[2]->const_item())
{
@@ -1831,24 +2176,28 @@ String *Item_func_password::val_str_ascii(String *str)
{
DBUG_ASSERT(fixed == 1);
String *res= args[0]->val_str(str);
- if ((null_value=args[0]->null_value))
- return 0;
- if (res->length() == 0)
+ check_password_policy(res);
+ if (args[0]->null_value || res->length() == 0)
return make_empty_result();
my_make_scrambled_password(tmp_value, res->ptr(), res->length());
str->set(tmp_value, SCRAMBLED_PASSWORD_CHAR_LENGTH, &my_charset_latin1);
return str;
}
-char *Item_func_password::alloc(THD *thd, const char *password,
- size_t pass_len)
+char *Item_func_password::alloc(THD *thd, const char *password, size_t pass_len)
{
char *buff= (char *) thd->alloc(SCRAMBLED_PASSWORD_CHAR_LENGTH+1);
if (buff)
+ {
+ String *password_str= new (thd->mem_root)String(password, thd->variables.
+ character_set_client);
+ check_password_policy(password_str);
my_make_scrambled_password(buff, password, pass_len);
+ }
return buff;
}
+
/* Item_func_old_password */
String *Item_func_old_password::val_str_ascii(String *str)
@@ -1982,32 +2331,6 @@ void Item_func_decode::crypto_transform(String *res)
}
-Item *Item_func_sysconst::safe_charset_converter(CHARSET_INFO *tocs)
-{
- Item_string *conv;
- uint conv_errors;
- String tmp, cstr, *ostr= val_str(&tmp);
- if (null_value)
- {
- Item *null_item= new Item_null((char *) fully_qualified_func_name());
- null_item->collation.set (tocs);
- return null_item;
- }
- cstr.copy(ostr->ptr(), ostr->length(), ostr->charset(), tocs, &conv_errors);
- if (conv_errors ||
- !(conv= new Item_static_string_func(fully_qualified_func_name(),
- cstr.ptr(), cstr.length(),
- cstr.charset(),
- collation.derivation)))
- {
- return NULL;
- }
- conv->str_value.copy();
- conv->str_value.mark_as_const();
- return conv;
-}
-
-
String *Item_func_database::val_str(String *str)
{
DBUG_ASSERT(fixed == 1);
@@ -2066,16 +2389,32 @@ bool Item_func_current_user::fix_fields(THD *thd, Item **ref)
if (Item_func_sysconst::fix_fields(thd, ref))
return TRUE;
- Security_context *ctx=
-#ifndef NO_EMBEDDED_ACCESS_CHECKS
- (context->security_ctx
- ? context->security_ctx : thd->security_ctx);
-#else
- thd->security_ctx;
-#endif /*NO_EMBEDDED_ACCESS_CHECKS*/
+ Security_context *ctx= context->security_ctx
+ ? context->security_ctx : thd->security_ctx;
return init(ctx->priv_user, ctx->priv_host);
}
+bool Item_func_current_role::fix_fields(THD *thd, Item **ref)
+{
+ if (Item_func_sysconst::fix_fields(thd, ref))
+ return 1;
+
+ Security_context *ctx= context->security_ctx
+ ? context->security_ctx : thd->security_ctx;
+
+ if (ctx->priv_role[0])
+ {
+ if (str_value.copy(ctx->priv_role, strlen(ctx->priv_role),
+ system_charset_info))
+ return 1;
+
+ str_value.mark_as_const();
+ return 0;
+ }
+ null_value= maybe_null= 1;
+ return 0;
+}
+
void Item_func_soundex::fix_length_and_dec()
{
@@ -2139,7 +2478,7 @@ String *Item_func_soundex::val_str(String *str)
if ((null_value= args[0]->null_value))
return 0; /* purecov: inspected */
- if (tmp_value.alloc(max(res->length(), 4 * cs->mbminlen)))
+ if (tmp_value.alloc(MY_MAX(res->length(), 4 * cs->mbminlen)))
return str; /* purecov: inspected */
char *to= (char *) tmp_value.ptr();
char *to_end= to + tmp_value.alloced_length();
@@ -2249,7 +2588,7 @@ MY_LOCALE *Item_func_format::get_locale(Item *item)
if (!locale_name ||
!(lc= my_locale_by_name(locale_name->c_ptr_safe())))
{
- push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
ER_UNKNOWN_LOCALE,
ER(ER_UNKNOWN_LOCALE),
locale_name ? locale_name->c_ptr_safe() : "NULL");
@@ -2639,7 +2978,7 @@ String *Item_func_repeat::val_str(String *str)
// Safe length check
if (length > current_thd->variables.max_allowed_packet / (uint) count)
{
- push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
ER_WARN_ALLOWED_PACKET_OVERFLOWED,
ER(ER_WARN_ALLOWED_PACKET_OVERFLOWED),
func_name(), current_thd->variables.max_allowed_packet);
@@ -2663,6 +3002,115 @@ err:
}
+void Item_func_space::fix_length_and_dec()
+{
+ collation.set(default_charset(), DERIVATION_COERCIBLE, MY_REPERTOIRE_ASCII);
+ if (args[0]->const_item())
+ {
+ /* must be longlong to avoid truncation */
+ longlong count= args[0]->val_int();
+ if (args[0]->null_value)
+ goto end;
+ /*
+ Assumes that the maximum length of a String is < INT_MAX32.
+ Set here so that rest of code sees out-of-bound value as such.
+ */
+ if (count > INT_MAX32)
+ count= INT_MAX32;
+ fix_char_length_ulonglong(count);
+ return;
+ }
+
+end:
+ max_length= MAX_BLOB_WIDTH;
+ maybe_null= 1;
+}
+
+
+String *Item_func_space::val_str(String *str)
+{
+ uint tot_length;
+ longlong count= args[0]->val_int();
+ const CHARSET_INFO *cs= collation.collation;
+
+ if (args[0]->null_value)
+ goto err; // string and/or delim are null
+ null_value= 0;
+
+ if (count <= 0 && (count == 0 || !args[0]->unsigned_flag))
+ return make_empty_result();
+ /*
+ Assumes that the maximum length of a String is < INT_MAX32.
+ Bounds check on count: If this is triggered, we will error.
+ */
+ if ((ulonglong) count > INT_MAX32)
+ count= INT_MAX32;
+
+ // Safe length check
+ tot_length= (uint) count * cs->mbminlen;
+ if (tot_length > current_thd->variables.max_allowed_packet)
+ {
+ push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
+ ER_WARN_ALLOWED_PACKET_OVERFLOWED,
+ ER(ER_WARN_ALLOWED_PACKET_OVERFLOWED),
+ func_name(),
+ current_thd->variables.max_allowed_packet);
+ goto err;
+ }
+
+ if (str->alloc(tot_length))
+ goto err;
+ str->length(tot_length);
+ str->set_charset(cs);
+ cs->cset->fill(cs, (char*) str->ptr(), tot_length, ' ');
+ return str;
+
+err:
+ null_value= 1;
+ return 0;
+}
+
+
+void Item_func_binlog_gtid_pos::fix_length_and_dec()
+{
+ collation.set(system_charset_info);
+ max_length= MAX_BLOB_WIDTH;
+ maybe_null= 1;
+}
+
+
+String *Item_func_binlog_gtid_pos::val_str(String *str)
+{
+ DBUG_ASSERT(fixed == 1);
+#ifndef HAVE_REPLICATION
+ null_value= 0;
+ str->copy("", 0, system_charset_info);
+ return str;
+#else
+ String name_str, *name;
+ longlong pos;
+
+ if (args[0]->null_value || args[1]->null_value)
+ goto err;
+
+ name= args[0]->val_str(&name_str);
+ pos= args[1]->val_int();
+
+ if (pos < 0 || pos > UINT_MAX32)
+ goto err;
+
+ if (gtid_state_from_binlog_pos(name->c_ptr_safe(), (uint32)pos, str))
+ goto err;
+ null_value= 0;
+ return str;
+
+err:
+ null_value= 1;
+ return NULL;
+#endif /* !HAVE_REPLICATION */
+}
+
+
void Item_func_rpad::fix_length_and_dec()
{
// Handle character set for args[0] and args[2].
@@ -2721,19 +3169,6 @@ String *Item_func_rpad::val_str(String *str)
res->set_charset(&my_charset_bin);
rpad->set_charset(&my_charset_bin);
}
-#if MARIADB_VERSION_ID < 1000000
- /*
- Well-formedness is handled on a higher level in 10.0,
- no needs to check it here again.
- */
- else
- {
- // This will chop off any trailing illegal characters from rpad.
- String *well_formed_pad= args[2]->check_well_formed_result(rpad, false);
- if (!well_formed_pad)
- goto err;
- }
-#endif
if (count <= (res_char_length= res->numchars()))
{ // String to pad is big enough
@@ -2745,7 +3180,7 @@ String *Item_func_rpad::val_str(String *str)
byte_count= count * collation.collation->mbmaxlen;
if ((ulonglong) byte_count > current_thd->variables.max_allowed_packet)
{
- push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
ER_WARN_ALLOWED_PACKET_OVERFLOWED,
ER(ER_WARN_ALLOWED_PACKET_OVERFLOWED),
func_name(), current_thd->variables.max_allowed_packet);
@@ -2839,18 +3274,6 @@ String *Item_func_lpad::val_str(String *str)
res->set_charset(&my_charset_bin);
pad->set_charset(&my_charset_bin);
}
-#if MARIADB_VERSION_ID < 1000000
- /*
- Well-formedness is handled on a higher level in 10.0,
- no needs to check it here again.
- */ else
- {
- // This will chop off any trailing illegal characters from pad.
- String *well_formed_pad= args[2]->check_well_formed_result(pad, false);
- if (!well_formed_pad)
- goto err;
- }
-#endif
res_char_length= res->numchars();
@@ -2865,7 +3288,7 @@ String *Item_func_lpad::val_str(String *str)
if ((ulonglong) byte_count > current_thd->variables.max_allowed_packet)
{
- push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
ER_WARN_ALLOWED_PACKET_OVERFLOWED,
ER(ER_WARN_ALLOWED_PACKET_OVERFLOWED),
func_name(), current_thd->variables.max_allowed_packet);
@@ -3001,11 +3424,8 @@ void Item_func_set_collation::fix_length_and_dec()
MY_CS_BINSORT,MYF(0));
else
{
- if (!(set_collation= get_charset_by_name(colname,MYF(0))))
- {
- my_error(ER_UNKNOWN_COLLATION, MYF(0), colname);
+ if (!(set_collation= mysqld_collation_get_by_name(colname)))
return;
- }
}
if (!set_collation ||
@@ -3049,7 +3469,7 @@ void Item_func_set_collation::print(String *str, enum_query_type query_type)
str->append(STRING_WITH_LEN(" collate "));
DBUG_ASSERT(args[1]->basic_const_item() &&
args[1]->type() == Item::STRING_ITEM);
- args[1]->str_value.print(str);
+ ((Item_string *)args[1])->print_value(str);
str->append(')');
}
@@ -3078,6 +3498,103 @@ String *Item_func_collation::val_str(String *str)
}
+void Item_func_weight_string::fix_length_and_dec()
+{
+ CHARSET_INFO *cs= args[0]->collation.collation;
+ collation.set(&my_charset_bin, args[0]->collation.derivation);
+ flags= my_strxfrm_flag_normalize(flags, cs->levels_for_order);
+ /*
+ Use result_length if it was given explicitly in constructor,
+ otherwise calculate max_length using argument's max_length
+ and "nweights".
+ */
+ if (!(max_length= result_length))
+ {
+ uint char_length;
+ char_length= ((cs->state & MY_CS_STRNXFRM_BAD_NWEIGHTS) || !nweights) ?
+ args[0]->max_char_length() : nweights;
+ max_length= cs->coll->strnxfrmlen(cs, char_length * cs->mbmaxlen);
+ }
+ maybe_null= 1;
+}
+
+
+/* Return a weight_string according to collation */
+String *Item_func_weight_string::val_str(String *str)
+{
+ String *res;
+ CHARSET_INFO *cs= args[0]->collation.collation;
+ uint tmp_length, frm_length;
+ DBUG_ASSERT(fixed == 1);
+
+ if (args[0]->result_type() != STRING_RESULT ||
+ !(res= args[0]->val_str(str)))
+ goto nl;
+
+ /*
+ Use result_length if it was given in constructor
+ explicitly, otherwise calculate result length
+ from argument and "nweights".
+ */
+ if (!(tmp_length= result_length))
+ {
+ uint char_length;
+ if (cs->state & MY_CS_STRNXFRM_BAD_NWEIGHTS)
+ {
+ /*
+ latin2_czech_cs and cp1250_czech_cs do not support
+ the "nweights" limit in strnxfrm(). Use the full length.
+ */
+ char_length= res->length();
+ }
+ else
+ {
+ /*
+ If we don't need to pad the result with spaces, then it should be
+ OK to calculate character length of the argument approximately:
+ "res->length() / cs->mbminlen" can return a number that is
+ bigger than the real number of characters in the string, so
+ we'll allocate a little bit more memory but avoid calling
+ the slow res->numchars().
+ In case if we do need to pad with spaces, we call res->numchars()
+ to know the true number of characters.
+ */
+ if (!(char_length= nweights))
+ char_length= (flags & MY_STRXFRM_PAD_WITH_SPACE) ?
+ res->numchars() : (res->length() / cs->mbminlen);
+ }
+ tmp_length= cs->coll->strnxfrmlen(cs, char_length * cs->mbmaxlen);
+ }
+
+ if(tmp_length > current_thd->variables.max_allowed_packet)
+ {
+ push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
+ ER_WARN_ALLOWED_PACKET_OVERFLOWED,
+ ER(ER_WARN_ALLOWED_PACKET_OVERFLOWED), func_name(),
+ current_thd->variables.max_allowed_packet);
+ goto nl;
+ }
+
+ if (tmp_value.alloc(tmp_length))
+ goto nl;
+
+ frm_length= cs->coll->strnxfrm(cs,
+ (uchar *) tmp_value.ptr(), tmp_length,
+ nweights ? nweights : tmp_length,
+ (const uchar *) res->ptr(), res->length(),
+ flags);
+ DBUG_ASSERT(frm_length <= tmp_length);
+
+ tmp_value.length(frm_length);
+ null_value= 0;
+ return &tmp_value;
+
+nl:
+ null_value= 1;
+ return 0;
+}
+
+
String *Item_func_hex::val_str_ascii(String *str)
{
String *res;
@@ -3245,7 +3762,7 @@ String *Item_load_file::val_str(String *str)
}
if (stat_info.st_size > (long) current_thd->variables.max_allowed_packet)
{
- push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
ER_WARN_ALLOWED_PACKET_OVERFLOWED,
ER(ER_WARN_ALLOWED_PACKET_OVERFLOWED),
func_name(), current_thd->variables.max_allowed_packet);
@@ -3331,12 +3848,12 @@ String* Item_func_export_set::val_str(String* str)
const ulong max_allowed_packet= current_thd->variables.max_allowed_packet;
const uint num_separators= num_set_values > 0 ? num_set_values - 1 : 0;
const ulonglong max_total_length=
- num_set_values * max(yes->length(), no->length()) +
+ num_set_values * MY_MAX(yes->length(), no->length()) +
num_separators * sep->length();
if (unlikely(max_total_length > max_allowed_packet))
{
- push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
ER_WARN_ALLOWED_PACKET_OVERFLOWED,
ER(ER_WARN_ALLOWED_PACKET_OVERFLOWED),
func_name(), max_allowed_packet);
@@ -3360,57 +3877,15 @@ String* Item_func_export_set::val_str(String* str)
void Item_func_export_set::fix_length_and_dec()
{
- uint32 length= max(args[1]->max_char_length(), args[2]->max_char_length());
+ uint32 length= MY_MAX(args[1]->max_char_length(), args[2]->max_char_length());
uint32 sep_length= (arg_count > 3 ? args[3]->max_char_length() : 1);
if (agg_arg_charsets_for_string_result(collation,
- args + 1, min(4, arg_count) - 1))
+ args + 1, MY_MIN(4, arg_count) - 1))
return;
fix_char_length(length * 64 + sep_length * 63);
}
-String* Item_func_inet_ntoa::val_str(String* str)
-{
- DBUG_ASSERT(fixed == 1);
- uchar buf[8], *p;
- ulonglong n = (ulonglong) args[0]->val_int();
- char num[4];
-
- /*
- We do not know if args[0] is NULL until we have called
- some val function on it if args[0] is not a constant!
-
- Also return null if n > 255.255.255.255
- */
- if ((null_value= (args[0]->null_value || n > (ulonglong) LL(4294967295))))
- return 0; // Null value
-
- str->set_charset(collation.collation);
- str->length(0);
- int4store(buf,n);
-
- /* Now we can assume little endian. */
-
- num[3]='.';
- for (p=buf+4 ; p-- > buf ; )
- {
- uint c = *p;
- uint n1,n2; // Try to avoid divisions
- n1= c / 100; // 100 digits
- c-= n1*100;
- n2= c / 10; // 10 digits
- c-=n2*10; // last digit
- num[0]=(char) n1+'0';
- num[1]=(char) n2+'0';
- num[2]=(char) c+'0';
- uint length= (n1 ? 4 : n2 ? 3 : 2); // Remove pre-zero
- uint dot_length= (p <= buf) ? 1 : 0;
- (void) str->append(num + 4 - length, length - dot_length,
- &my_charset_latin1);
- }
- return str;
-}
-
#define get_esc_bit(mask, num) (1 & (*((mask) + ((num) >> 3))) >> ((num) & 7))
@@ -3584,7 +4059,7 @@ longlong Item_func_uncompressed_length::val_int()
*/
if (res->length() <= 4)
{
- push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
ER_ZLIB_Z_DATA_ERROR,
ER(ER_ZLIB_Z_DATA_ERROR));
null_value= 1;
@@ -3661,7 +4136,7 @@ String *Item_func_compress::val_str(String *str)
res->length())) != Z_OK)
{
code= err==Z_MEM_ERROR ? ER_ZLIB_Z_MEM_ERROR : ER_ZLIB_Z_BUF_ERROR;
- push_warning(current_thd,MYSQL_ERROR::WARN_LEVEL_WARN,code,ER(code));
+ push_warning(current_thd,Sql_condition::WARN_LEVEL_WARN,code,ER(code));
null_value= 1;
return 0;
}
@@ -3699,7 +4174,7 @@ String *Item_func_uncompress::val_str(String *str)
/* If length is less than 4 bytes, data is corrupt */
if (res->length() <= 4)
{
- push_warning_printf(current_thd,MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(current_thd,Sql_condition::WARN_LEVEL_WARN,
ER_ZLIB_Z_DATA_ERROR,
ER(ER_ZLIB_Z_DATA_ERROR));
goto err;
@@ -3709,7 +4184,7 @@ String *Item_func_uncompress::val_str(String *str)
new_size= uint4korr(res->ptr()) & 0x3FFFFFFF;
if (new_size > current_thd->variables.max_allowed_packet)
{
- push_warning_printf(current_thd,MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(current_thd,Sql_condition::WARN_LEVEL_WARN,
ER_TOO_BIG_FOR_UNCOMPRESS,
ER(ER_TOO_BIG_FOR_UNCOMPRESS),
static_cast<int>(current_thd->variables.
@@ -3728,7 +4203,7 @@ String *Item_func_uncompress::val_str(String *str)
code= ((err == Z_BUF_ERROR) ? ER_ZLIB_Z_BUF_ERROR :
((err == Z_MEM_ERROR) ? ER_ZLIB_Z_MEM_ERROR : ER_ZLIB_Z_DATA_ERROR));
- push_warning(current_thd,MYSQL_ERROR::WARN_LEVEL_WARN,code,ER(code));
+ push_warning(current_thd,Sql_condition::WARN_LEVEL_WARN,code,ER(code));
err:
null_value= 1;
@@ -3754,7 +4229,8 @@ String *Item_func_uuid::val_str(String *str)
Item_func_dyncol_create::Item_func_dyncol_create(List<Item> &args,
DYNCALL_CREATE_DEF *dfs)
- : Item_str_func(args), defs(dfs), vals(0), nums(0)
+ : Item_str_func(args), defs(dfs), vals(0), keys_num(NULL), keys_str(NULL),
+ names(FALSE), force_names(FALSE)
{
DBUG_ASSERT((args.elements & 0x1) == 0); // even number of arguments
}
@@ -3762,31 +4238,85 @@ Item_func_dyncol_create::Item_func_dyncol_create(List<Item> &args,
bool Item_func_dyncol_create::fix_fields(THD *thd, Item **ref)
{
+ uint i;
bool res= Item_func::fix_fields(thd, ref); // no need Item_str_func here
- vals= (DYNAMIC_COLUMN_VALUE *) alloc_root(thd->mem_root,
- sizeof(DYNAMIC_COLUMN_VALUE) *
- (arg_count / 2));
- nums= (uint *) alloc_root(thd->mem_root,
- sizeof(uint) * (arg_count / 2));
- status_var_increment(thd->status_var.feature_dynamic_columns);
- return res || vals == 0 || nums == 0;
+ if (!res)
+ {
+ vals= (DYNAMIC_COLUMN_VALUE *) alloc_root(thd->mem_root,
+ sizeof(DYNAMIC_COLUMN_VALUE) *
+ (arg_count / 2));
+ for (i= 0;
+ i + 1 < arg_count && args[i]->result_type() == INT_RESULT;
+ i+= 2)
+ ;
+ if (i + 1 < arg_count)
+ {
+ names= TRUE;
+ }
+
+ keys_num= (uint *) alloc_root(thd->mem_root,
+ (sizeof(LEX_STRING) > sizeof(uint) ?
+ sizeof(LEX_STRING) :
+ sizeof(uint)) *
+ (arg_count / 2));
+ keys_str= (LEX_STRING *) keys_num;
+ status_var_increment(thd->status_var.feature_dynamic_columns);
+ }
+ return res || vals == 0 || keys_num == 0;
}
void Item_func_dyncol_create::fix_length_and_dec()
{
+ max_length= MAX_BLOB_WIDTH;
maybe_null= TRUE;
collation.set(&my_charset_bin);
decimals= 0;
}
-void Item_func_dyncol_create::prepare_arguments()
+bool Item_func_dyncol_create::prepare_arguments(bool force_names_arg)
{
char buff[STRING_BUFFER_USUAL_SIZE];
String *res, tmp(buff, sizeof(buff), &my_charset_bin);
uint column_count= (arg_count / 2);
uint i;
my_decimal dtmp, *dres;
+ force_names= force_names_arg;
+
+ if (!(names || force_names))
+ {
+ for (i= 0; i < column_count; i++)
+ {
+ uint valpos= i * 2 + 1;
+ DYNAMIC_COLUMN_TYPE type= defs[i].type;
+ if (type == DYN_COL_NULL)
+ switch (args[valpos]->field_type())
+ {
+ case MYSQL_TYPE_VARCHAR:
+ case MYSQL_TYPE_ENUM:
+ case MYSQL_TYPE_SET:
+ case MYSQL_TYPE_TINY_BLOB:
+ case MYSQL_TYPE_MEDIUM_BLOB:
+ case MYSQL_TYPE_LONG_BLOB:
+ case MYSQL_TYPE_BLOB:
+ case MYSQL_TYPE_VAR_STRING:
+ case MYSQL_TYPE_STRING:
+ case MYSQL_TYPE_GEOMETRY:
+ type= DYN_COL_STRING;
+ break;
+ default:
+ break;
+ }
+
+ if (type == DYN_COL_STRING &&
+ args[valpos]->type() == Item::FUNC_ITEM &&
+ ((Item_func *)args[valpos])->functype() == DYNCOL_FUNC)
+ {
+ force_names= 1;
+ break;
+ }
+ }
+ }
/* get values */
for (i= 0; i < column_count; i++)
@@ -3821,7 +4351,9 @@ void Item_func_dyncol_create::prepare_arguments()
type= DYN_COL_NULL;
break;
case MYSQL_TYPE_TIMESTAMP:
+ case MYSQL_TYPE_TIMESTAMP2:
case MYSQL_TYPE_DATETIME:
+ case MYSQL_TYPE_DATETIME2:
type= DYN_COL_DATETIME;
break;
case MYSQL_TYPE_DATE:
@@ -3829,6 +4361,7 @@ void Item_func_dyncol_create::prepare_arguments()
type= DYN_COL_DATE;
break;
case MYSQL_TYPE_TIME:
+ case MYSQL_TYPE_TIME2:
type= DYN_COL_TIME;
break;
case MYSQL_TYPE_VARCHAR:
@@ -3845,7 +4378,59 @@ void Item_func_dyncol_create::prepare_arguments()
break;
}
}
- nums[i]= (uint) args[i * 2]->val_int();
+ if (type == DYN_COL_STRING &&
+ args[valpos]->type() == Item::FUNC_ITEM &&
+ ((Item_func *)args[valpos])->functype() == DYNCOL_FUNC)
+ {
+ DBUG_ASSERT(names || force_names);
+ type= DYN_COL_DYNCOL;
+ }
+ if (names || force_names)
+ {
+ res= args[i * 2]->val_str(&tmp);
+ if (res)
+ {
+ // guaranty UTF-8 string for names
+ if (my_charset_same(res->charset(), &my_charset_utf8_general_ci))
+ {
+ keys_str[i].length= res->length();
+ keys_str[i].str= sql_strmake(res->ptr(), res->length());
+ }
+ else
+ {
+ uint strlen;
+ uint dummy_errors;
+ char *str=
+ (char *)sql_alloc((strlen= res->length() *
+ my_charset_utf8_general_ci.mbmaxlen + 1));
+ if (str)
+ {
+ keys_str[i].length=
+ copy_and_convert(str, strlen, &my_charset_utf8_general_ci,
+ res->ptr(), res->length(), res->charset(),
+ &dummy_errors);
+ keys_str[i].str= str;
+ }
+ else
+ keys_str[i].length= 0;
+
+ }
+ }
+ else
+ {
+ keys_str[i].length= 0;
+ keys_str[i].str= NULL;
+ }
+ }
+ else
+ keys_num[i]= (uint) args[i * 2]->val_int();
+ if (args[i * 2]->null_value)
+ {
+ /* to make cleanup possible */
+ for (; i < column_count; i++)
+ vals[i].type= DYN_COL_NULL;
+ return 1;
+ }
vals[i].type= type;
switch (type) {
case DYN_COL_NULL:
@@ -3860,11 +4445,13 @@ void Item_func_dyncol_create::prepare_arguments()
case DYN_COL_DOUBLE:
vals[i].x.double_value= args[valpos]->val_real();
break;
+ case DYN_COL_DYNCOL:
case DYN_COL_STRING:
res= args[valpos]->val_str(&tmp);
+ if (res && defs[i].cs)
+ res->set_charset(defs[i].cs);
if (res &&
- (vals[i].x.string.value.str= my_strndup(res->ptr(), res->length(),
- MYF(MY_WME))))
+ (vals[i].x.string.value.str= sql_strmake(res->ptr(), res->length())))
{
vals[i].x.string.value.length= res->length();
vals[i].x.string.charset= res->charset();
@@ -3879,7 +4466,7 @@ void Item_func_dyncol_create::prepare_arguments()
case DYN_COL_DECIMAL:
if ((dres= args[valpos]->val_decimal(&dtmp)))
{
- dynamic_column_prepare_decimal(&vals[i]);
+ mariadb_dyncol_prepare_decimal(&vals[i]);
DBUG_ASSERT(vals[i].x.decimal.value.len == dres->len);
vals[i].x.decimal.value.intg= dres->intg;
vals[i].x.decimal.value.frac= dres->frac;
@@ -3889,15 +4476,14 @@ void Item_func_dyncol_create::prepare_arguments()
}
else
{
- dynamic_column_prepare_decimal(&vals[i]); // just to be safe
+ mariadb_dyncol_prepare_decimal(&vals[i]); // just to be safe
DBUG_ASSERT(args[valpos]->null_value);
}
break;
case DYN_COL_DATETIME:
- args[valpos]->get_date(&vals[i].x.time_value, 0);
- break;
case DYN_COL_DATE:
- args[valpos]->get_date(&vals[i].x.time_value, 0);
+ args[valpos]->get_date(&vals[i].x.time_value,
+ sql_mode_for_dates(current_thd));
break;
case DYN_COL_TIME:
args[valpos]->get_time(&vals[i].x.time_value);
@@ -3908,24 +4494,12 @@ void Item_func_dyncol_create::prepare_arguments()
}
if (vals[i].type != DYN_COL_NULL && args[valpos]->null_value)
{
- if (vals[i].type == DYN_COL_STRING)
- my_free(vals[i].x.string.value.str);
vals[i].type= DYN_COL_NULL;
}
}
+ return FALSE;
}
-void Item_func_dyncol_create::cleanup_arguments()
-{
- uint column_count= (arg_count / 2);
- uint i;
-
- for (i= 0; i < column_count; i++)
- {
- if (vals[i].type == DYN_COL_STRING)
- my_free(vals[i].x.string.value.str);
- }
-}
String *Item_func_dyncol_create::val_str(String *str)
{
@@ -3935,30 +4509,37 @@ String *Item_func_dyncol_create::val_str(String *str)
enum enum_dyncol_func_result rc;
DBUG_ASSERT((arg_count & 0x1) == 0); // even number of arguments
- prepare_arguments();
-
- if ((rc= dynamic_column_create_many(&col, column_count, nums, vals)))
+ if (prepare_arguments(FALSE))
{
- dynamic_column_error_message(rc);
- dynamic_column_column_free(&col);
res= NULL;
- null_value= TRUE;
+ null_value= 1;
}
else
{
- /* Move result from DYNAMIC_COLUMN to str_value */
- char *ptr;
- size_t length, alloc_length;
- dynamic_column_reassociate(&col, &ptr, &length, &alloc_length);
- str_value.reassociate(ptr, (uint32) length, (uint32) alloc_length,
- &my_charset_bin);
- res= &str_value;
- null_value= FALSE;
+ if ((rc= ((names || force_names) ?
+ mariadb_dyncol_create_many_named(&col, column_count, keys_str,
+ vals, TRUE) :
+ mariadb_dyncol_create_many_num(&col, column_count, keys_num,
+ vals, TRUE))))
+ {
+ dynamic_column_error_message(rc);
+ mariadb_dyncol_free(&col);
+ res= NULL;
+ null_value= TRUE;
+ }
+ else
+ {
+ /* Move result from DYNAMIC_COLUMN to str_value */
+ char *ptr;
+ size_t length, alloc_length;
+ dynstr_reassociate(&col, &ptr, &length, &alloc_length);
+ str_value.reassociate(ptr, (uint32) length, (uint32) alloc_length,
+ &my_charset_bin);
+ res= &str_value;
+ null_value= FALSE;
+ }
}
- /* cleanup */
- cleanup_arguments();
-
return res;
}
@@ -3984,6 +4565,7 @@ void Item_func_dyncol_create::print_arguments(String *str,
case DYN_COL_DOUBLE:
str->append(STRING_WITH_LEN(" AS double"));
break;
+ case DYN_COL_DYNCOL:
case DYN_COL_STRING:
str->append(STRING_WITH_LEN(" AS char"));
if (defs[i].cs)
@@ -4021,6 +4603,40 @@ void Item_func_dyncol_create::print(String *str,
str->append(')');
}
+String *Item_func_dyncol_json::val_str(String *str)
+{
+ DYNAMIC_STRING json, col;
+ String *res;
+ enum enum_dyncol_func_result rc;
+
+ res= args[0]->val_str(str);
+ if (args[0]->null_value)
+ goto null;
+
+ col.str= (char *)res->ptr();
+ col.length= res->length();
+ if ((rc= mariadb_dyncol_json(&col, &json)))
+ {
+ dynamic_column_error_message(rc);
+ goto null;
+ }
+ bzero(&col, sizeof(col));
+ {
+ /* Move result from DYNAMIC_COLUMN to str */
+ char *ptr;
+ size_t length, alloc_length;
+ dynstr_reassociate(&json, &ptr, &length, &alloc_length);
+ str->reassociate(ptr, (uint32) length, (uint32) alloc_length,
+ &my_charset_utf8_general_ci);
+ null_value= FALSE;
+ }
+ return str;
+
+null:
+ bzero(&col, sizeof(col));
+ null_value= TRUE;
+ return NULL;
+}
String *Item_func_dyncol_add::val_str(String *str)
{
@@ -4032,21 +4648,25 @@ String *Item_func_dyncol_add::val_str(String *str)
/* We store the packed data last */
res= args[arg_count - 1]->val_str(str);
- if (args[arg_count - 1]->null_value)
+ if (args[arg_count - 1]->null_value ||
+ init_dynamic_string(&col, NULL, res->length() + STRING_BUFFER_USUAL_SIZE,
+ STRING_BUFFER_USUAL_SIZE))
goto null;
- init_dynamic_string(&col, NULL, res->length() + STRING_BUFFER_USUAL_SIZE,
- STRING_BUFFER_USUAL_SIZE);
col.length= res->length();
memcpy(col.str, res->ptr(), col.length);
- prepare_arguments();
+ if (prepare_arguments(mariadb_dyncol_has_names(&col)))
+ goto null;
- if ((rc= dynamic_column_update_many(&col, column_count, nums, vals)))
+ if ((rc= ((names || force_names) ?
+ mariadb_dyncol_update_many_named(&col, column_count,
+ keys_str, vals) :
+ mariadb_dyncol_update_many_num(&col, column_count,
+ keys_num, vals))))
{
dynamic_column_error_message(rc);
- dynamic_column_column_free(&col);
- cleanup_arguments();
+ mariadb_dyncol_free(&col);
goto null;
}
@@ -4054,16 +4674,12 @@ String *Item_func_dyncol_add::val_str(String *str)
/* Move result from DYNAMIC_COLUMN to str */
char *ptr;
size_t length, alloc_length;
- dynamic_column_reassociate(&col, &ptr, &length, &alloc_length);
+ dynstr_reassociate(&col, &ptr, &length, &alloc_length);
str->reassociate(ptr, (uint32) length, (uint32) alloc_length,
&my_charset_bin);
null_value= FALSE;
}
- /* cleanup */
- dynamic_column_column_free(&col);
- cleanup_arguments();
-
return str;
null:
@@ -4095,10 +4711,48 @@ bool Item_dyncol_get::get_dyn_value(DYNAMIC_COLUMN_VALUE *val, String *tmp)
{
DYNAMIC_COLUMN dyn_str;
String *res;
- longlong num;
+ longlong num= 0;
+ LEX_STRING buf, *name= NULL;
+ char nmstrbuf[11];
+ String nmbuf(nmstrbuf, sizeof(nmstrbuf), system_charset_info);
enum enum_dyncol_func_result rc;
- num= args[1]->val_int();
+ if (args[1]->result_type() == INT_RESULT)
+ num= args[1]->val_int();
+ else
+ {
+ String *nm= args[1]->val_str(&nmbuf);
+ if (!nm || args[1]->null_value)
+ {
+ null_value= 1;
+ return 1;
+ }
+
+ if (my_charset_same(nm->charset(), &my_charset_utf8_general_ci))
+ {
+ buf.str= (char *) nm->ptr();
+ buf.length= nm->length();
+ }
+ else
+ {
+ uint strlen;
+ uint dummy_errors;
+ buf.str= (char *)sql_alloc((strlen= nm->length() *
+ my_charset_utf8_general_ci.mbmaxlen + 1));
+ if (buf.str)
+ {
+ buf.length=
+ copy_and_convert(buf.str, strlen, &my_charset_utf8_general_ci,
+ nm->ptr(), nm->length(), nm->charset(),
+ &dummy_errors);
+ }
+ else
+ buf.length= 0;
+ }
+ name= &buf;
+ }
+
+
if (args[1]->null_value || num < 0 || num > INT_MAX)
{
null_value= 1;
@@ -4114,7 +4768,9 @@ bool Item_dyncol_get::get_dyn_value(DYNAMIC_COLUMN_VALUE *val, String *tmp)
dyn_str.str= (char*) res->ptr();
dyn_str.length= res->length();
- if ((rc= dynamic_column_get(&dyn_str, (uint) num, val)))
+ if ((rc= ((name == NULL) ?
+ mariadb_dyncol_get_num(&dyn_str, (uint) num, val) :
+ mariadb_dyncol_get_named(&dyn_str, name, val))))
{
dynamic_column_error_message(rc);
null_value= 1;
@@ -4140,12 +4796,13 @@ String *Item_dyncol_get::val_str(String *str_result)
goto null;
case DYN_COL_INT:
case DYN_COL_UINT:
- str_result->set_int(val.x.long_value, test(val.type == DYN_COL_UINT),
+ str_result->set_int(val.x.long_value, MY_TEST(val.type == DYN_COL_UINT),
&my_charset_latin1);
break;
case DYN_COL_DOUBLE:
str_result->set_real(val.x.double_value, NOT_FIXED_DEC, &my_charset_latin1);
break;
+ case DYN_COL_DYNCOL:
case DYN_COL_STRING:
if ((char*) tmp.ptr() <= val.x.string.value.str &&
(char*) tmp.ptr() + tmp.length() >= val.x.string.value.str)
@@ -4221,6 +4878,7 @@ longlong Item_dyncol_get::val_int()
return 0;
switch (val.type) {
+ case DYN_COL_DYNCOL:
case DYN_COL_NULL:
goto null;
case DYN_COL_UINT:
@@ -4239,7 +4897,7 @@ longlong Item_dyncol_get::val_int()
{
char buff[30];
sprintf(buff, "%lg", val.x.double_value);
- push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
ER_DATA_OVERFLOW,
ER(ER_DATA_OVERFLOW),
buff,
@@ -4257,9 +4915,9 @@ longlong Item_dyncol_get::val_int()
if (end != org_end || error > 0)
{
char buff[80];
- strmake(buff, val.x.string.value.str, min(sizeof(buff)-1,
+ strmake(buff, val.x.string.value.str, MY_MIN(sizeof(buff)-1,
val.x.string.value.length));
- push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
ER_BAD_DATA,
ER(ER_BAD_DATA),
buff,
@@ -4301,6 +4959,7 @@ double Item_dyncol_get::val_real()
return 0.0;
switch (val.type) {
+ case DYN_COL_DYNCOL:
case DYN_COL_NULL:
goto null;
case DYN_COL_UINT:
@@ -4320,9 +4979,9 @@ double Item_dyncol_get::val_real()
error)
{
char buff[80];
- strmake(buff, val.x.string.value.str, min(sizeof(buff)-1,
+ strmake(buff, val.x.string.value.str, MY_MIN(sizeof(buff)-1,
val.x.string.value.length));
- push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
ER_BAD_DATA,
ER(ER_BAD_DATA),
buff, "DOUBLE");
@@ -4358,6 +5017,7 @@ my_decimal *Item_dyncol_get::val_decimal(my_decimal *decimal_value)
return NULL;
switch (val.type) {
+ case DYN_COL_DYNCOL:
case DYN_COL_NULL:
goto null;
case DYN_COL_UINT:
@@ -4375,11 +5035,11 @@ my_decimal *Item_dyncol_get::val_decimal(my_decimal *decimal_value)
rc= str2my_decimal(0, val.x.string.value.str, val.x.string.value.length,
val.x.string.charset, decimal_value);
char buff[80];
- strmake(buff, val.x.string.value.str, min(sizeof(buff)-1,
+ strmake(buff, val.x.string.value.str, MY_MIN(sizeof(buff)-1,
val.x.string.value.length));
if (rc != E_DEC_OK)
{
- push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
ER_BAD_DATA,
ER(ER_BAD_DATA),
buff, "DECIMAL");
@@ -4392,10 +5052,7 @@ my_decimal *Item_dyncol_get::val_decimal(my_decimal *decimal_value)
case DYN_COL_DATETIME:
case DYN_COL_DATE:
case DYN_COL_TIME:
- decimal_value= seconds2my_decimal(val.x.time_value.neg,
- TIME_to_ulonglong(&val.x.time_value),
- val.x.time_value.second_part,
- decimal_value);
+ decimal_value= TIME_to_my_decimal(&val.x.time_value, decimal_value);
break;
}
return decimal_value;
@@ -4417,6 +5074,7 @@ bool Item_dyncol_get::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date)
return 1; // Error
switch (val.type) {
+ case DYN_COL_DYNCOL:
case DYN_COL_NULL:
goto null;
case DYN_COL_INT:
@@ -4449,7 +5107,7 @@ bool Item_dyncol_get::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date)
if (str_to_datetime_with_warn(&my_charset_numeric,
val.x.string.value.str,
val.x.string.value.length,
- ltime, fuzzy_date) <= MYSQL_TIMESTAMP_ERROR)
+ ltime, fuzzy_date))
goto null;
return 0;
case DYN_COL_DATETIME:
@@ -4464,7 +5122,6 @@ null:
return 1;
}
-
void Item_dyncol_get::print(String *str, enum_query_type query_type)
{
/*
@@ -4494,7 +5151,8 @@ String *Item_func_dyncol_list::val_str(String *str)
{
uint i;
enum enum_dyncol_func_result rc;
- DYNAMIC_ARRAY arr;
+ LEX_STRING *names= 0;
+ uint count;
DYNAMIC_COLUMN col;
String *res= args[0]->val_str(str);
@@ -4503,33 +5161,37 @@ String *Item_func_dyncol_list::val_str(String *str)
col.length= res->length();
/* We do not change the string, so could do this trick */
col.str= (char *)res->ptr();
- if ((rc= dynamic_column_list(&col, &arr)))
+ if ((rc= mariadb_dyncol_list_named(&col, &count, &names)))
{
+ bzero(&col, sizeof(col));
dynamic_column_error_message(rc);
- delete_dynamic(&arr);
goto null;
}
+ bzero(&col, sizeof(col));
/*
- We support elements from 0 - 65536, so max size for one element is
- 6 (including ,).
+ We estimate average name length as 10
*/
- if (str->alloc(arr.elements * 6))
+ if (str->alloc(count * 13))
goto null;
str->length(0);
- for (i= 0; i < arr.elements; i++)
+ str->set_charset(&my_charset_utf8_general_ci);
+ for (i= 0; i < count; i++)
{
- str->qs_append(*dynamic_element(&arr, i, uint*));
- if (i < arr.elements - 1)
+ append_identifier(current_thd, str, names[i].str, names[i].length);
+ if (i < count - 1)
str->qs_append(',');
}
-
null_value= FALSE;
- delete_dynamic(&arr);
+ if (names)
+ my_free(names);
return str;
null:
null_value= TRUE;
+ if (names)
+ my_free(names);
return NULL;
}
+
diff --git a/sql/item_strfunc.h b/sql/item_strfunc.h
index 7606c281548..2886cb68f9b 100644
--- a/sql/item_strfunc.h
+++ b/sql/item_strfunc.h
@@ -3,7 +3,7 @@
/*
Copyright (c) 2000, 2011, Oracle and/or its affiliates.
- Copyright (c) 2009, 2013, Monty Program Ab.
+ Copyright (c) 2009, 2015, MariaDB
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
@@ -62,7 +62,6 @@ public:
enum Item_result result_type () const { return STRING_RESULT; }
void left_right_max_length();
bool fix_fields(THD *thd, Item **ref);
- String *val_str_from_val_str_ascii(String *str, String *str2);
};
@@ -115,6 +114,27 @@ public:
const char *func_name() const { return "sha2"; }
};
+class Item_func_to_base64 :public Item_str_ascii_func
+{
+ String tmp_value;
+public:
+ Item_func_to_base64(Item *a) :Item_str_ascii_func(a) {}
+ String *val_str_ascii(String *);
+ void fix_length_and_dec();
+ const char *func_name() const { return "to_base64"; }
+};
+
+class Item_func_from_base64 :public Item_str_func
+{
+ String tmp_value;
+public:
+ Item_func_from_base64(Item *a) :Item_str_func(a) {}
+ String *val_str(String *);
+ void fix_length_and_dec();
+ const char *func_name() const { return "from_base64"; }
+};
+
+
class Item_func_aes_encrypt :public Item_str_func
{
public:
@@ -145,6 +165,22 @@ public:
const char *func_name() const { return "concat"; }
};
+class Item_func_decode_histogram :public Item_str_func
+{
+ String tmp_value;
+public:
+ Item_func_decode_histogram(Item *a, Item *b)
+ :Item_str_func(a, b) {}
+ String *val_str(String *);
+ void fix_length_and_dec()
+ {
+ collation.set(system_charset_info);
+ max_length= MAX_BLOB_WIDTH;
+ maybe_null= 1;
+ }
+ const char *func_name() const { return "decode_histogram"; }
+};
+
class Item_func_concat_ws :public Item_str_func
{
String tmp_value;
@@ -179,6 +215,49 @@ public:
};
+class Item_func_regexp_replace :public Item_str_func
+{
+ Regexp_processor_pcre re;
+ bool append_replacement(String *str,
+ const LEX_CSTRING *source,
+ const LEX_CSTRING *replace);
+public:
+ Item_func_regexp_replace(Item *a, Item *b, Item *c)
+ :Item_str_func(a, b, c)
+ {}
+ void cleanup()
+ {
+ DBUG_ENTER("Item_func_regex::cleanup");
+ Item_str_func::cleanup();
+ re.cleanup();
+ DBUG_VOID_RETURN;
+ }
+ String *val_str(String *str);
+ void fix_length_and_dec();
+ const char *func_name() const { return "regexp_replace"; }
+};
+
+
+class Item_func_regexp_substr :public Item_str_func
+{
+ Regexp_processor_pcre re;
+public:
+ Item_func_regexp_substr(Item *a, Item *b)
+ :Item_str_func(a, b)
+ {}
+ void cleanup()
+ {
+ DBUG_ENTER("Item_func_regex::cleanup");
+ Item_str_func::cleanup();
+ re.cleanup();
+ DBUG_VOID_RETURN;
+ }
+ String *val_str(String *str);
+ void fix_length_and_dec();
+ const char *func_name() const { return "regexp_substr"; }
+};
+
+
class Item_func_insert :public Item_str_func
{
String tmp_value;
@@ -463,7 +542,10 @@ class Item_func_sysconst :public Item_str_func
public:
Item_func_sysconst()
{ collation.set(system_charset_info,DERIVATION_SYSCONST); }
- Item *safe_charset_converter(CHARSET_INFO *tocs);
+ Item *safe_charset_converter(CHARSET_INFO *tocs)
+ {
+ return const_charset_converter(tocs, true, fully_qualified_func_name());
+ }
/*
Used to create correct Item name in new converted item in
safe_charset_converter, return string representation of this function
@@ -536,6 +618,28 @@ public:
};
+class Item_func_current_role :public Item_func_sysconst
+{
+ Name_resolution_context *context;
+
+public:
+ Item_func_current_role(Name_resolution_context *context_arg)
+ : context(context_arg) {}
+ bool fix_fields(THD *thd, Item **ref);
+ void fix_length_and_dec()
+ { max_length= username_char_length * SYSTEM_CHARSET_MBMAXLEN; }
+ int save_in_field(Field *field, bool no_conversions)
+ { return save_str_value_in_field(field, &str_value); }
+ const char *func_name() const { return "current_role"; }
+ const char *fully_qualified_func_name() const { return "current_role()"; }
+ String *val_str(String *)
+ {
+ DBUG_ASSERT(fixed == 1);
+ return (null_value ? 0 : &str_value);
+ }
+};
+
+
class Item_func_soundex :public Item_str_func
{
String tmp_value;
@@ -615,6 +719,27 @@ public:
};
+class Item_func_space :public Item_str_func
+{
+public:
+ Item_func_space(Item *arg1):Item_str_func(arg1) {}
+ String *val_str(String *);
+ void fix_length_and_dec();
+ const char *func_name() const { return "space"; }
+};
+
+
+class Item_func_binlog_gtid_pos :public Item_str_func
+{
+ String tmp_value;
+public:
+ Item_func_binlog_gtid_pos(Item *arg1,Item *arg2) :Item_str_func(arg1,arg2) {}
+ String *val_str(String *);
+ void fix_length_and_dec();
+ const char *func_name() const { return "binlog_gtid_pos"; }
+};
+
+
class Item_func_rpad :public Item_str_func
{
String tmp_value, rpad_str;
@@ -783,21 +908,6 @@ class Item_func_export_set: public Item_str_func
const char *func_name() const { return "export_set"; }
};
-class Item_func_inet_ntoa : public Item_str_func
-{
-public:
- Item_func_inet_ntoa(Item *a) :Item_str_func(a)
- {
- }
- String* val_str(String* str);
- const char *func_name() const { return "inet_ntoa"; }
- void fix_length_and_dec()
- {
- decimals= 0;
- fix_length_and_charset(3 * 8 + 7, default_charset());
- maybe_null= 1;
- }
-};
class Item_func_quote :public Item_str_func
{
@@ -811,7 +921,7 @@ public:
collation.set(args[0]->collation);
ulonglong max_result_length= (ulonglong) args[0]->max_length * 2 +
2 * collation.collation->mbmaxlen;
- max_length= (uint32) min(max_result_length, MAX_BLOB_WIDTH);
+ max_length= (uint32) MY_MIN(max_result_length, MAX_BLOB_WIDTH);
}
};
@@ -904,10 +1014,10 @@ public:
const char *func_name() const { return "collate"; }
enum Functype functype() const { return COLLATE_FUNC; }
virtual void print(String *str, enum_query_type query_type);
- Item_field *filed_for_view_update()
+ Item_field *field_for_view_update()
{
/* this function is transparent for view updating */
- return args[0]->filed_for_view_update();
+ return args[0]->field_for_view_update();
}
};
@@ -941,6 +1051,35 @@ public:
table_map not_null_tables() const { return 0; }
};
+class Item_func_weight_string :public Item_str_func
+{
+ String tmp_value;
+ uint flags;
+ uint nweights;
+ uint result_length;
+public:
+ Item_func_weight_string(Item *a, uint result_length_arg,
+ uint nweights_arg, uint flags_arg)
+ :Item_str_func(a)
+ {
+ nweights= nweights_arg;
+ flags= flags_arg;
+ result_length= result_length_arg;
+ }
+ const char *func_name() const { return "weight_string"; }
+ String *val_str(String *);
+ void fix_length_and_dec();
+ bool eq(const Item *item, bool binary_cmp) const
+ {
+ if (!Item_str_func::eq(item, binary_cmp))
+ return false;
+ Item_func_weight_string *that= (Item_func_weight_string *)item;
+ return this->flags == that->flags &&
+ this->nweights == that->nweights &&
+ this->result_length == that->result_length;
+ }
+};
+
class Item_func_crc32 :public Item_int_func
{
String value;
@@ -1012,9 +1151,10 @@ class Item_func_dyncol_create: public Item_str_func
protected:
DYNCALL_CREATE_DEF *defs;
DYNAMIC_COLUMN_VALUE *vals;
- uint *nums;
- void prepare_arguments();
- void cleanup_arguments();
+ uint *keys_num;
+ LEX_STRING *keys_str;
+ bool names, force_names;
+ bool prepare_arguments(bool force_names);
void print_arguments(String *str, enum_query_type query_type);
public:
Item_func_dyncol_create(List<Item> &args, DYNCALL_CREATE_DEF *dfs);
@@ -1023,6 +1163,7 @@ public:
const char *func_name() const{ return "column_create"; }
String *val_str(String *);
virtual void print(String *str, enum_query_type query_type);
+ virtual enum Functype functype() const { return DYNCOL_FUNC; }
};
@@ -1037,6 +1178,20 @@ public:
virtual void print(String *str, enum_query_type query_type);
};
+class Item_func_dyncol_json: public Item_str_func
+{
+public:
+ Item_func_dyncol_json(Item *str) :Item_str_func(str) {}
+ const char *func_name() const{ return "column_json"; }
+ String *val_str(String *);
+ void fix_length_and_dec()
+ {
+ max_length= MAX_BLOB_WIDTH;
+ maybe_null= 1;
+ collation.set(&my_charset_bin);
+ decimals= 0;
+ }
+};
/*
The following functions is always called from an Item_cast function
@@ -1047,11 +1202,9 @@ class Item_dyncol_get: public Item_str_func
public:
Item_dyncol_get(Item *str, Item *num)
:Item_str_func(str, num)
- {
- max_length= MAX_DYNAMIC_COLUMN_LENGTH;
- }
+ {}
void fix_length_and_dec()
- { maybe_null= 1; }
+ { maybe_null= 1;; max_length= MAX_BLOB_WIDTH; }
/* Mark that collation can change between calls */
bool dynamic_result() { return 1; }
@@ -1076,3 +1229,4 @@ public:
};
#endif /* ITEM_STRFUNC_INCLUDED */
+
diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc
index ba674743724..7d361263548 100644
--- a/sql/item_subselect.cc
+++ b/sql/item_subselect.cc
@@ -29,6 +29,7 @@
#pragma implementation // gcc: Class implementation
#endif
+#include <my_global.h>
#include "sql_priv.h"
/*
It is necessary to include set_var.h instead of item.h because there
@@ -43,6 +44,9 @@
double get_post_group_estimate(JOIN* join, double join_op_rows);
+const char *exists_outer_expr_name= "<exists outer expr>";
+
+int check_and_do_in_subquery_rewrites(JOIN *join);
Item_subselect::Item_subselect():
Item_result_field(), value_assigned(0), own_engine(0), thd(0), old_engine(0),
@@ -83,15 +87,24 @@ void Item_subselect::init(st_select_lex *select_lex,
if (unit->item)
{
- /*
- Item can be changed in JOIN::prepare while engine in JOIN::optimize
- => we do not copy old_engine here
- */
engine= unit->item->engine;
- own_engine= FALSE;
parsing_place= unit->item->parsing_place;
- thd->change_item_tree((Item**)&unit->item, this);
- engine->change_result(this, result, TRUE);
+ if (unit->item->substype() == EXISTS_SUBS &&
+ ((Item_exists_subselect *)unit->item)->exists_transformed)
+ {
+ /* it is permanent transformation of EXISTS to IN */
+ unit->item= this;
+ engine->change_result(this, result, FALSE);
+ }
+ else
+ {
+ /*
+ Item can be changed in JOIN::prepare while engine in JOIN::optimize
+ => we do not copy old_engine here
+ */
+ thd->change_item_tree((Item**)&unit->item, this);
+ engine->change_result(this, result, TRUE);
+ }
}
else
{
@@ -462,7 +475,7 @@ public:
void Item_subselect::recalc_used_tables(st_select_lex *new_parent,
bool after_pullout)
{
- List_iterator<Ref_to_outside> it(upper_refs);
+ List_iterator_fast<Ref_to_outside> it(upper_refs);
Ref_to_outside *upper;
DBUG_ENTER("recalc_used_tables");
@@ -675,9 +688,12 @@ bool Item_subselect::exec()
void Item_subselect::get_cache_parameters(List<Item> &parameters)
{
- Collect_deps_prm prm= {&parameters,
- unit->first_select()->nest_level_base,
- unit->first_select()->nest_level};
+ Collect_deps_prm prm= {&parameters, // parameters
+ unit->first_select()->nest_level_base, // nest_level_base
+ 0, // count
+ unit->first_select()->nest_level, // nest_level
+ TRUE // collect
+ };
walk(&Item::collect_outer_ref_processor, TRUE, (uchar*)&prm);
}
@@ -1077,7 +1093,7 @@ Item_singlerow_subselect::select_transformer(JOIN *join)
{
char warn_buff[MYSQL_ERRMSG_SIZE];
sprintf(warn_buff, ER(ER_SELECT_REDUCED), select_lex->select_number);
- push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
+ push_warning(thd, Sql_condition::WARN_LEVEL_NOTE,
ER_SELECT_REDUCED, warn_buff);
}
substitution= select_lex->item_list.head();
@@ -1317,10 +1333,10 @@ bool Item_singlerow_subselect::get_date(MYSQL_TIME *ltime,ulonglong fuzzydate)
Item_exists_subselect::Item_exists_subselect(st_select_lex *select_lex):
- Item_subselect()
+ Item_subselect(), upper_not(NULL), abort_on_null(0),
+ emb_on_expr_nest(NULL), optimizer(0), exists_transformed(0)
{
DBUG_ENTER("Item_exists_subselect::Item_exists_subselect");
- bool val_bool();
init(select_lex, new select_exists_subselect(this));
max_columns= UINT_MAX;
null_value= FALSE; //can't be NULL
@@ -1354,21 +1370,19 @@ bool Item_in_subselect::test_limit(st_select_lex_unit *unit_arg)
Item_in_subselect::Item_in_subselect(Item * left_exp,
st_select_lex *select_lex):
- Item_exists_subselect(),
- left_expr_cache(0), first_execution(TRUE), in_strategy(SUBS_NOT_TRANSFORMED),
- optimizer(0), pushed_cond_guards(NULL), emb_on_expr_nest(NULL),
- is_jtbm_merged(FALSE), is_jtbm_const_tab(FALSE),
- is_flattenable_semijoin(FALSE),
- is_registered_semijoin(FALSE),
+ Item_exists_subselect(), left_expr_cache(0), first_execution(TRUE),
+ in_strategy(SUBS_NOT_TRANSFORMED),
+ pushed_cond_guards(NULL), is_jtbm_merged(FALSE), is_jtbm_const_tab(FALSE),
+ is_flattenable_semijoin(FALSE), is_registered_semijoin(FALSE),
upper_item(0)
{
DBUG_ENTER("Item_in_subselect::Item_in_subselect");
+ DBUG_PRINT("info", ("in_strategy: %u", (uint)in_strategy));
left_expr_orig= left_expr= left_exp;
func= &eq_creator;
init(select_lex, new select_exists_subselect(this));
max_columns= UINT_MAX;
maybe_null= 1;
- abort_on_null= 0;
reset();
//if test_limit will fail then error will be reported to client
test_limit(select_lex->master_unit());
@@ -1755,7 +1769,7 @@ Item_in_subselect::single_value_transformer(JOIN *join)
{
char warn_buff[MYSQL_ERRMSG_SIZE];
sprintf(warn_buff, ER(ER_SELECT_REDUCED), select_lex->select_number);
- push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
+ push_warning(thd, Sql_condition::WARN_LEVEL_NOTE,
ER_SELECT_REDUCED, warn_buff);
}
DBUG_RETURN(false);
@@ -1773,8 +1787,7 @@ Item_in_subselect::single_value_transformer(JOIN *join)
SELECT_LEX *current= thd->lex->current_select;
thd->lex->current_select= current->return_after_parsing();
- //optimizer never use Item **ref => we can pass 0 as parameter
- if (!optimizer || optimizer->fix_left(thd, 0))
+ if (!optimizer || optimizer->fix_left(thd))
{
thd->lex->current_select= current;
DBUG_RETURN(true);
@@ -1951,7 +1964,7 @@ bool Item_allany_subselect::is_maxmin_applicable(JOIN *join)
WHERE condition.
*/
return (abort_on_null || (upper_item && upper_item->is_top_level_item())) &&
- !join->select_lex->master_unit()->uncacheable && !func->eqne_op();
+ !(join->select_lex->master_unit()->uncacheable & ~UNCACHEABLE_EXPLAIN) && !func->eqne_op();
}
@@ -2153,8 +2166,7 @@ Item_in_subselect::row_value_transformer(JOIN *join)
SELECT_LEX *current= thd->lex->current_select;
thd->lex->current_select= current->return_after_parsing();
- //optimizer never use Item **ref => we can pass 0 as parameter
- if (!optimizer || optimizer->fix_left(thd, 0))
+ if (!optimizer || optimizer->fix_left(thd))
{
thd->lex->current_select= current;
DBUG_RETURN(true);
@@ -2334,7 +2346,7 @@ Item_in_subselect::create_row_in_to_exists_cond(JOIN * join,
ref_pointer_array+i,
(char *)"<no matter>",
(char *)"<list ref>"));
- if (!abort_on_null)
+ if (!abort_on_null && select_lex->ref_pointer_array[i]->maybe_null)
{
Item *having_col_item=
new Item_is_not_null_test(this,
@@ -2353,10 +2365,6 @@ Item_in_subselect::create_row_in_to_exists_cond(JOIN * join,
(char *)"<no matter>",
(char *)"<list ref>"));
item= new Item_cond_or(item, item_isnull);
- /*
- TODO: why we create the above for cases where the right part
- cant be NULL?
- */
if (left_expr->element_index(i)->maybe_null)
{
if (!(item= new Item_func_trig_cond(item, get_cond_guard(i))))
@@ -2367,6 +2375,11 @@ Item_in_subselect::create_row_in_to_exists_cond(JOIN * join,
}
*having_item= and_items(*having_item, having_col_item);
}
+ if (!abort_on_null && left_expr->element_index(i)->maybe_null)
+ {
+ if (!(item= new Item_func_trig_cond(item, get_cond_guard(i))))
+ DBUG_RETURN(true);
+ }
*where_item= and_items(*where_item, item);
}
}
@@ -2397,6 +2410,12 @@ Item_in_subselect::select_transformer(JOIN *join)
return select_in_like_transformer(join);
}
+bool
+Item_exists_subselect::select_transformer(JOIN *join)
+{
+ return select_prepare_to_be_in();
+}
+
/**
Create the predicates needed to transform an IN/ALL/ANY subselect into a
@@ -2532,6 +2551,433 @@ bool Item_in_subselect::inject_in_to_exists_cond(JOIN *join_arg)
}
+/*
+ If this select can potentially be converted by EXISTS->IN conversion, wrap it
+ in an Item_in_optimizer object. Final decision whether to do the conversion
+ is done at a later phase.
+*/
+
+bool Item_exists_subselect::select_prepare_to_be_in()
+{
+ bool trans_res= FALSE;
+ DBUG_ENTER("Item_exists_subselect::select_prepare_to_be_in");
+ if (!optimizer &&
+ thd->lex->sql_command == SQLCOM_SELECT &&
+ !unit->first_select()->is_part_of_union() &&
+ optimizer_flag(thd, OPTIMIZER_SWITCH_EXISTS_TO_IN) &&
+ (is_top_level_item() ||
+ (upper_not && upper_not->is_top_level_item())))
+ {
+ Query_arena *arena, backup;
+ bool result;
+ arena= thd->activate_stmt_arena_if_needed(&backup);
+ result= (!(optimizer= new Item_in_optimizer(new Item_int(1), this)));
+ if (arena)
+ thd->restore_active_arena(arena, &backup);
+ if (result)
+ trans_res= TRUE;
+ else
+ substitution= optimizer;
+ }
+ DBUG_RETURN(trans_res);
+}
+
+/**
+ Check if 'func' is an equality in form "inner_table.column = outer_expr"
+
+ @param func Expression to check
+ @param local_field OUT Return "inner_table.column" here
+ @param outer_expr OUT Return outer_expr here
+
+ @return true - 'func' is an Equality.
+*/
+
+static bool check_equality_for_exist2in(Item_func *func,
+ Item_ident **local_field,
+ Item **outer_exp)
+{
+ Item **args;
+ if (func->functype() != Item_func::EQ_FUNC)
+ return FALSE;
+ DBUG_ASSERT(func->arg_count == 2);
+ args= func->arguments();
+ if (args[0]->real_type() == Item::FIELD_ITEM &&
+ args[0]->all_used_tables() != OUTER_REF_TABLE_BIT &&
+ args[1]->all_used_tables() == OUTER_REF_TABLE_BIT)
+ {
+ /* It is Item_field or Item_direct_view_ref) */
+ DBUG_ASSERT(args[0]->type() == Item::FIELD_ITEM ||
+ args[0]->type() == Item::REF_ITEM);
+ *local_field= (Item_ident *)args[0];
+ *outer_exp= args[1];
+ return TRUE;
+ }
+ else if (args[1]->real_type() == Item::FIELD_ITEM &&
+ args[1]->all_used_tables() != OUTER_REF_TABLE_BIT &&
+ args[0]->all_used_tables() == OUTER_REF_TABLE_BIT)
+ {
+ /* It is Item_field or Item_direct_view_ref) */
+ DBUG_ASSERT(args[0]->type() == Item::FIELD_ITEM ||
+ args[0]->type() == Item::REF_ITEM);
+ *local_field= (Item_ident *)args[1];
+ *outer_exp= args[0];
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+typedef struct st_eq_field_outer
+{
+ Item **eq_ref;
+ Item_ident *local_field;
+ Item *outer_exp;
+} EQ_FIELD_OUTER;
+
+
+/**
+ Check if 'conds' is a set of AND-ed outer_expr=inner_table.col equalities
+
+ @detail
+ Check if 'conds' has form
+
+ outer1=inner_tbl1.col1 AND ... AND outer2=inner_tbl1.col2 AND remainder_cond
+
+ @param conds Condition to be checked
+ @parm result Array to collect EQ_FIELD_OUTER elements describing
+ inner-vs-outer equalities the function has found.
+ @return
+ false - some inner-vs-outer equalities were found
+ true - otherwise.
+*/
+
+static bool find_inner_outer_equalities(Item **conds,
+ Dynamic_array<EQ_FIELD_OUTER> &result)
+{
+ bool found= FALSE;
+ EQ_FIELD_OUTER element;
+ if (is_cond_and(*conds))
+ {
+ List_iterator<Item> li(*((Item_cond*)*conds)->argument_list());
+ Item *item;
+ while ((item= li++))
+ {
+ if (item->type() == Item::FUNC_ITEM &&
+ check_equality_for_exist2in((Item_func *)item,
+ &element.local_field,
+ &element.outer_exp))
+ {
+ found= TRUE;
+ element.eq_ref= li.ref();
+ if (result.append(element))
+ goto alloc_err;
+ }
+ }
+ }
+ else if ((*conds)->type() == Item::FUNC_ITEM &&
+ check_equality_for_exist2in((Item_func *)*conds,
+ &element.local_field,
+ &element.outer_exp))
+ {
+ found= TRUE;
+ element.eq_ref= conds;
+ if (result.append(element))
+ goto alloc_err;
+ }
+
+ return !found;
+alloc_err:
+ return TRUE;
+}
+
+/**
+ Converts EXISTS subquery to IN subquery if it is possible and has sense
+
+ @param opt_arg Pointer on THD
+
+ @return TRUE in case of error and FALSE otherwise.
+*/
+
+bool Item_exists_subselect::exists2in_processor(uchar *opt_arg)
+{
+ THD *thd= (THD *)opt_arg;
+ SELECT_LEX *first_select=unit->first_select(), *save_select;
+ JOIN *join= first_select->join;
+ Item **eq_ref= NULL;
+ Item_ident *local_field= NULL;
+ Item *outer_exp= NULL;
+ Item *left_exp= NULL; Item_in_subselect *in_subs;
+ Query_arena *arena= NULL, backup;
+ int res= FALSE;
+ List<Item> outer;
+ Dynamic_array<EQ_FIELD_OUTER> eqs(5, 5);
+ bool will_be_correlated;
+ DBUG_ENTER("Item_exists_subselect::exists2in_processor");
+
+ if (!optimizer ||
+ !optimizer_flag(thd, OPTIMIZER_SWITCH_EXISTS_TO_IN) ||
+ (!is_top_level_item() && (!upper_not ||
+ !upper_not->is_top_level_item())) ||
+ first_select->is_part_of_union() ||
+ first_select->group_list.elements ||
+ first_select->order_list.elements ||
+ join->having ||
+ first_select->with_sum_func ||
+ !first_select->leaf_tables.elements||
+ !join->conds)
+ DBUG_RETURN(FALSE);
+
+ DBUG_ASSERT(first_select->order_list.elements == 0 &&
+ first_select->group_list.elements == 0 &&
+ first_select->having == NULL);
+
+ if (find_inner_outer_equalities(&join->conds, eqs))
+ DBUG_RETURN(FALSE);
+
+ DBUG_ASSERT(eqs.elements() != 0);
+
+ save_select= thd->lex->current_select;
+ thd->lex->current_select= first_select;
+
+ /* check that the subquery has only dependencies we are going pull out */
+ {
+ List<Item> unused;
+ Collect_deps_prm prm= {&unused, // parameters
+ unit->first_select()->nest_level_base, // nest_level_base
+ 0, // count
+ unit->first_select()->nest_level, // nest_level
+ FALSE // collect
+ };
+ walk(&Item::collect_outer_ref_processor, TRUE, (uchar*)&prm);
+ DBUG_ASSERT(prm.count > 0);
+ DBUG_ASSERT(prm.count >= (uint)eqs.elements());
+ will_be_correlated= prm.count > (uint)eqs.elements();
+ if (upper_not && will_be_correlated)
+ goto out;
+ }
+
+ if ((uint)eqs.elements() > (first_select->item_list.elements +
+ first_select->select_n_reserved))
+ goto out;
+ /* It is simple query */
+ DBUG_ASSERT(first_select->join->all_fields.elements ==
+ first_select->item_list.elements);
+
+ arena= thd->activate_stmt_arena_if_needed(&backup);
+
+ while (first_select->item_list.elements > (uint)eqs.elements())
+ {
+ first_select->item_list.pop();
+ first_select->join->all_fields.elements--;
+ }
+ {
+ List_iterator<Item> it(first_select->item_list);
+
+ for (uint i= 0; i < (uint)eqs.elements(); i++)
+ {
+ Item *item= it++;
+ eq_ref= eqs.at(i).eq_ref;
+ local_field= eqs.at(i).local_field;
+ outer_exp= eqs.at(i).outer_exp;
+ /* Add the field to the SELECT_LIST */
+ if (item)
+ it.replace(local_field);
+ else
+ {
+ first_select->item_list.push_back(local_field);
+ first_select->join->all_fields.elements++;
+ }
+ first_select->ref_pointer_array[i]= (Item *)local_field;
+
+ /* remove the parts from condition */
+ if (!upper_not || !local_field->maybe_null)
+ *eq_ref= new Item_int(1);
+ else
+ {
+ *eq_ref= new Item_func_isnotnull(
+ new Item_field(thd,
+ ((Item_field*)(local_field->real_item()))->context,
+ ((Item_field*)(local_field->real_item()))->field));
+ if((*eq_ref)->fix_fields(thd, (Item **)eq_ref))
+ {
+ res= TRUE;
+ goto out;
+ }
+ }
+ outer_exp->fix_after_pullout(unit->outer_select(), &outer_exp);
+ outer_exp->update_used_tables();
+ outer.push_back(outer_exp);
+ }
+ }
+
+ join->conds->update_used_tables();
+
+ /* make IN SUBQUERY and put outer_exp as left part */
+ if (eqs.elements() == 1)
+ left_exp= outer_exp;
+ else
+ {
+ if (!(left_exp= new Item_row(outer)))
+ {
+ res= TRUE;
+ goto out;
+ }
+ }
+
+ /* make EXISTS->IN permanet (see Item_subselect::init()) */
+ set_exists_transformed();
+
+ first_select->select_limit= NULL;
+ if (!(in_subs= new Item_in_subselect(left_exp, first_select)))
+ {
+ res= TRUE;
+ goto out;
+ }
+ in_subs->set_exists_transformed();
+ optimizer->arguments()[0]= left_exp;
+ optimizer->arguments()[1]= in_subs;
+ in_subs->optimizer= optimizer;
+ DBUG_ASSERT(is_top_level_item() ||
+ (upper_not && upper_not->is_top_level_item()));
+ in_subs->top_level_item();
+ {
+ SELECT_LEX *current= thd->lex->current_select;
+ optimizer->reset_cache(); // renew cache, and we will not keep it
+ thd->lex->current_select= unit->outer_select();
+ DBUG_ASSERT(optimizer);
+ if (optimizer->fix_left(thd))
+ {
+ res= TRUE;
+ /*
+ We should not restore thd->lex->current_select because it will be
+ reset on exit from this procedure
+ */
+ goto out;
+ }
+ /*
+ As far as Item_ref_in_optimizer do not substitute itself on fix_fields
+ we can use same item for all selects.
+ */
+ in_subs->expr= new Item_direct_ref(&first_select->context,
+ (Item**)optimizer->get_cache(),
+ (char *)"<no matter>",
+ (char *)in_left_expr_name);
+ if (in_subs->fix_fields(thd, optimizer->arguments() + 1))
+ {
+ res= TRUE;
+ /*
+ We should not restore thd->lex->current_select because it will be
+ reset on exit from this procedure
+ */
+ goto out;
+ }
+ {
+ /* Move dependence list */
+ List_iterator_fast<Ref_to_outside> it(upper_refs);
+ Ref_to_outside *upper;
+ while ((upper= it++))
+ {
+ uint i;
+ for (i= 0; i < (uint)eqs.elements(); i++)
+ if (eqs.at(i).outer_exp->
+ walk(&Item::find_item_processor, TRUE, (uchar*)upper->item))
+ break;
+ if (i == (uint)eqs.elements() &&
+ (in_subs->upper_refs.push_back(upper, thd->stmt_arena->mem_root)))
+ goto out;
+ }
+ }
+ in_subs->update_used_tables();
+ /*
+ The engine of the subquery is fixed so above fix_fields() is not
+ complete and should be fixed
+ */
+ in_subs->upper_refs= upper_refs;
+ upper_refs.empty();
+ thd->lex->current_select= current;
+ }
+
+ DBUG_ASSERT(unit->item == in_subs);
+ DBUG_ASSERT(join == first_select->join);
+ /*
+ Fix dependency info
+ */
+ in_subs->is_correlated= will_be_correlated;
+ if (!will_be_correlated)
+ {
+ first_select->uncacheable&= ~UNCACHEABLE_DEPENDENT_GENERATED;
+ unit->uncacheable&= ~UNCACHEABLE_DEPENDENT_GENERATED;
+ }
+ /*
+ set possible optimization strategies
+ */
+ in_subs->emb_on_expr_nest= emb_on_expr_nest;
+ res= check_and_do_in_subquery_rewrites(join);
+ first_select->join->prepare_stage2();
+
+ first_select->fix_prepare_information(thd, &join->conds, &join->having);
+
+ if (upper_not)
+ {
+ Item *exp;
+ if (eqs.elements() == 1)
+ {
+ exp= (optimizer->arguments()[0]->maybe_null ?
+ (Item*)
+ new Item_cond_and(
+ new Item_func_isnotnull(
+ new Item_direct_ref(&unit->outer_select()->context,
+ optimizer->arguments(),
+ (char *)"<no matter>",
+ (char *)exists_outer_expr_name)),
+ optimizer) :
+ (Item *)optimizer);
+ }
+ else
+ {
+ List<Item> *and_list= new List<Item>;
+ if (!and_list)
+ {
+ res= TRUE;
+ goto out;
+ }
+ for (size_t i= 0; i < eqs.elements(); i++)
+ {
+ if (optimizer->arguments()[0]->maybe_null)
+ {
+ and_list->
+ push_front(
+ new Item_func_isnotnull(
+ new Item_direct_ref(&unit->outer_select()->context,
+ optimizer->arguments()[0]->addr(i),
+ (char *)"<no matter>",
+ (char *)exists_outer_expr_name)));
+ }
+ }
+ if (and_list->elements > 0)
+ {
+ and_list->push_front(optimizer);
+ exp= new Item_cond_and(*and_list);
+ }
+ else
+ exp= optimizer;
+ }
+ upper_not->arguments()[0]= exp;
+ if (!exp->fixed && exp->fix_fields(thd, upper_not->arguments()))
+ {
+ res= TRUE;
+ goto out;
+ }
+ }
+
+out:
+ thd->lex->current_select= save_select;
+ if (arena)
+ thd->restore_active_arena(arena, &backup);
+ DBUG_RETURN(res);
+}
+
+
/**
Prepare IN/ALL/ANY/SOME subquery transformation and call the appropriate
transformation function.
@@ -2592,7 +3038,7 @@ Item_in_subselect::select_in_like_transformer(JOIN *join)
}
thd->lex->current_select= current->return_after_parsing();
- result= optimizer->fix_left(thd, optimizer->arguments());
+ result= optimizer->fix_left(thd);
thd->lex->current_select= current;
if (changed)
@@ -2646,15 +3092,24 @@ void Item_in_subselect::print(String *str, enum_query_type query_type)
Item_subselect::print(str, query_type);
}
+bool Item_exists_subselect::fix_fields(THD *thd_arg, Item **ref)
+{
+ DBUG_ENTER("Item_exists_subselect::fix_fields");
+ if (exists_transformed)
+ DBUG_RETURN( !( (*ref)= new Item_int(1)));
+ DBUG_RETURN(Item_subselect::fix_fields(thd_arg, ref));
+}
+
bool Item_in_subselect::fix_fields(THD *thd_arg, Item **ref)
{
uint outer_cols_num;
List<Item> *inner_cols;
char const *save_where= thd->where;
+ DBUG_ENTER("Item_in_subselect::fix_fields");
if (test_strategy(SUBS_SEMI_JOIN))
- return !( (*ref)= new Item_int(1));
+ DBUG_RETURN( !( (*ref)= new Item_int(1)) );
thd->where= "IN/ALL/ANY subquery";
/*
@@ -2706,15 +3161,15 @@ bool Item_in_subselect::fix_fields(THD *thd_arg, Item **ref)
left_expr->fix_fields(thd_arg, &left_expr))
goto err;
else
- if (Item_subselect::fix_fields(thd_arg, ref))
- goto err;
+ if (Item_subselect::fix_fields(thd_arg, ref))
+ goto err;
fixed= TRUE;
thd->where= save_where;
- return FALSE;
+ DBUG_RETURN(FALSE);
err:
thd->where= save_where;
- return TRUE;
+ DBUG_RETURN(TRUE);
}
@@ -2947,7 +3402,7 @@ bool subselect_union_engine::is_executed() const
bool subselect_union_engine::no_rows()
{
/* Check if we got any rows when reading UNION result from temp. table: */
- return test(!unit->fake_select_lex->join->send_records);
+ return MY_TEST(!unit->fake_select_lex->join->send_records);
}
@@ -3784,7 +4239,7 @@ void subselect_uniquesubquery_engine::print(String *str)
{
KEY *key_info= tab->table->key_info + tab->ref.key;
str->append(STRING_WITH_LEN("<primary_index_lookup>("));
- for (uint i= 0; i < key_info->key_parts; i++)
+ for (uint i= 0; i < key_info->user_defined_key_parts; i++)
tab->ref.items[i]->print(str);
str->append(STRING_WITH_LEN(" in "));
str->append(tab->table->s->table_name.str, tab->table->s->table_name.length);
@@ -3842,6 +4297,7 @@ subselect_single_select_engine::change_result(Item_subselect *si,
select_result_interceptor *res,
bool temp)
{
+ DBUG_ENTER("subselect_single_select_engine::change_result");
item= si;
if (temp)
{
@@ -3862,7 +4318,7 @@ subselect_single_select_engine::change_result(Item_subselect *si,
that would not require a lot of extra code that would be harder to manage
than the current code.
*/
- return select_lex->join->change_result(res);
+ DBUG_RETURN(select_lex->join->change_result(res));
}
@@ -4230,13 +4686,13 @@ ulonglong subselect_hash_sj_engine::rowid_merge_buff_size(
*/
static my_bool
-bitmap_init_memroot(MY_BITMAP *map, uint n_bits, MEM_ROOT *mem_root)
+my_bitmap_init_memroot(MY_BITMAP *map, uint n_bits, MEM_ROOT *mem_root)
{
my_bitmap_map *bitmap_buf;
if (!(bitmap_buf= (my_bitmap_map*) alloc_root(mem_root,
bitmap_buffer_size(n_bits))) ||
- bitmap_init(map, bitmap_buf, n_bits, FALSE))
+ my_bitmap_init(map, bitmap_buf, n_bits, FALSE))
return TRUE;
bitmap_clear_all(map);
return FALSE;
@@ -4274,9 +4730,9 @@ bool subselect_hash_sj_engine::init(List<Item> *tmp_columns, uint subquery_id)
DBUG_ENTER("subselect_hash_sj_engine::init");
- if (bitmap_init_memroot(&non_null_key_parts, tmp_columns->elements,
+ if (my_bitmap_init_memroot(&non_null_key_parts, tmp_columns->elements,
thd->mem_root) ||
- bitmap_init_memroot(&partial_match_key_parts, tmp_columns->elements,
+ my_bitmap_init_memroot(&partial_match_key_parts, tmp_columns->elements,
thd->mem_root))
DBUG_RETURN(TRUE);
@@ -4341,7 +4797,8 @@ bool subselect_hash_sj_engine::init(List<Item> *tmp_columns, uint subquery_id)
DBUG_ASSERT(
tmp_table->s->uniques ||
tmp_table->key_info->key_length >= tmp_table->file->max_key_length() ||
- tmp_table->key_info->key_parts > tmp_table->file->max_key_parts());
+ tmp_table->key_info->user_defined_key_parts >
+ tmp_table->file->max_key_parts());
free_tmp_table(thd, tmp_table);
tmp_table= NULL;
delete result;
@@ -4355,7 +4812,7 @@ bool subselect_hash_sj_engine::init(List<Item> *tmp_columns, uint subquery_id)
*/
DBUG_ASSERT(tmp_table->s->keys == 1 &&
((Item_in_subselect *) item)->left_expr->cols() ==
- tmp_table->key_info->key_parts);
+ tmp_table->key_info->user_defined_key_parts);
if (make_semi_join_conds() ||
/* A unique_engine is used both for complete and partial matching. */
@@ -4598,7 +5055,7 @@ double get_fanout_with_deps(JOIN *join, table_map tset)
!tab->emb_sj_nest &&
tab->records_read != 0)
{
- fanout *= rows2double(tab->records_read);
+ fanout *= tab->records_read;
}
}
return fanout;
@@ -4785,8 +5242,8 @@ int subselect_hash_sj_engine::exec()
/* The subquery should be optimized, and materialized only once. */
DBUG_ASSERT(materialize_join->optimized && !is_materialized);
materialize_join->exec();
- if ((res= test(materialize_join->error || thd->is_fatal_error ||
- thd->is_error())))
+ if ((res= MY_TEST(materialize_join->error || thd->is_fatal_error ||
+ thd->is_error())))
goto err;
/*
@@ -4869,7 +5326,7 @@ int subselect_hash_sj_engine::exec()
count_pm_keys= count_partial_match_columns - count_null_only_columns +
(nn_key_parts ? 1 : 0);
- choose_partial_match_strategy(test(nn_key_parts),
+ choose_partial_match_strategy(MY_TEST(nn_key_parts),
has_covering_null_row,
&partial_match_key_parts);
DBUG_ASSERT(strategy == PARTIAL_MATCH_MERGE ||
@@ -4997,7 +5454,7 @@ Ordered_key::Ordered_key(uint keyid_arg, TABLE *tbl_arg, Item *search_key_arg,
Ordered_key::~Ordered_key()
{
my_free(key_buff);
- bitmap_free(&null_key);
+ my_bitmap_free(&null_key);
}
@@ -5098,7 +5555,7 @@ bool Ordered_key::alloc_keys_buffers()
DBUG_ASSERT(key_buff_elements > 0);
if (!(key_buff= (rownum_t*) my_malloc((size_t)(key_buff_elements *
- sizeof(rownum_t)), MYF(MY_WME))))
+ sizeof(rownum_t)), MYF(MY_WME | MY_THREAD_SPECIFIC))))
return TRUE;
/*
@@ -5107,7 +5564,7 @@ bool Ordered_key::alloc_keys_buffers()
lookup offset.
*/
/* Notice that max_null_row is max array index, we need count, so +1. */
- if (bitmap_init(&null_key, NULL, (uint)(max_null_row + 1), FALSE))
+ if (my_bitmap_init(&null_key, NULL, (uint)(max_null_row + 1), FALSE))
return TRUE;
cur_key_idx= HA_POS_ERROR;
@@ -5129,7 +5586,8 @@ int
Ordered_key::cmp_keys_by_row_data(ha_rows a, ha_rows b)
{
uchar *rowid_a, *rowid_b;
- int error, cmp_res;
+ int __attribute__((unused)) error;
+ int cmp_res;
/* The length in bytes of the rowids (positions) of tmp_table. */
uint rowid_length= tbl->file->ref_length;
@@ -5225,7 +5683,8 @@ int Ordered_key::cmp_key_with_search_key(rownum_t row_num)
/* The length in bytes of the rowids (positions) of tmp_table. */
uint rowid_length= tbl->file->ref_length;
uchar *cur_rowid= row_num_to_rowid + row_num * rowid_length;
- int error, cmp_res;
+ int __attribute__((unused)) error;
+ int cmp_res;
if ((error= tbl->file->ha_rnd_pos(tbl->record[0], cur_rowid)))
{
@@ -5523,7 +5982,7 @@ subselect_rowid_merge_engine::init(MY_BITMAP *non_null_key_parts,
!(null_bitmaps= (MY_BITMAP**) thd->alloc(merge_keys_count *
sizeof(MY_BITMAP*))) ||
!(row_num_to_rowid= (uchar*) my_malloc((size_t)(row_count * rowid_length),
- MYF(MY_WME))))
+ MYF(MY_WME | MY_THREAD_SPECIFIC))))
return TRUE;
/* Create the only non-NULL key if there is any. */
@@ -5544,8 +6003,8 @@ subselect_rowid_merge_engine::init(MY_BITMAP *non_null_key_parts,
*/
if (!has_covering_null_columns)
{
- if (bitmap_init_memroot(&matching_keys, merge_keys_count, thd->mem_root) ||
- bitmap_init_memroot(&matching_outer_cols, merge_keys_count, thd->mem_root))
+ if (my_bitmap_init_memroot(&matching_keys, merge_keys_count, thd->mem_root) ||
+ my_bitmap_init_memroot(&matching_outer_cols, merge_keys_count, thd->mem_root))
return TRUE;
/*
@@ -5842,7 +6301,7 @@ bool subselect_rowid_merge_engine::partial_match()
Do not add the non_null_key, since it was already processed above.
*/
bitmap_clear_all(&matching_outer_cols);
- for (uint i= test(non_null_key); i < merge_keys_count; i++)
+ for (uint i= MY_TEST(non_null_key); i < merge_keys_count; i++)
{
DBUG_ASSERT(merge_keys[i]->get_column_count() == 1);
if (merge_keys[i]->get_search_key(0)->null_value)
@@ -5859,7 +6318,7 @@ bool subselect_rowid_merge_engine::partial_match()
nullable columns (above we guarantee there is a match for the non-null
coumns), the result is UNKNOWN.
*/
- if (count_nulls_in_search_key == merge_keys_count - test(non_null_key))
+ if (count_nulls_in_search_key == merge_keys_count - MY_TEST(non_null_key))
{
res= TRUE;
goto end;
diff --git a/sql/item_subselect.h b/sql/item_subselect.h
index 0ee5f73eb35..3c0b7bd6ade 100644
--- a/sql/item_subselect.h
+++ b/sql/item_subselect.h
@@ -244,6 +244,7 @@ public:
virtual bool expr_cache_is_needed(THD *);
virtual void get_cache_parameters(List<Item> &parameters);
virtual bool is_subquery_processor (uchar *opt_arg) { return 1; }
+ bool exists2in_processor(uchar *opt_arg) { return 0; }
bool limit_index_condition_pushdown_processor(uchar *opt_arg)
{
return TRUE;
@@ -339,13 +340,35 @@ public:
class Item_exists_subselect :public Item_subselect
{
protected:
+ Item_func_not *upper_not;
bool value; /* value of this item (boolean: exists/not-exists) */
+ bool abort_on_null;
void init_length_and_dec();
+ bool select_prepare_to_be_in();
public:
+ /*
+ Used by subquery optimizations to keep track about in which clause this
+ subquery predicate is located:
+ NO_JOIN_NEST - the predicate is an AND-part of the WHERE
+ join nest pointer - the predicate is an AND-part of ON expression
+ of a join nest
+ NULL - for all other locations
+ */
+ TABLE_LIST *emb_on_expr_nest;
+ /**
+ Reference on the Item_in_optimizer wrapper of this subquery
+ */
+ Item_in_optimizer *optimizer;
+ /* true if we got this from EXISTS or to IN */
+ bool exists_transformed;
+
Item_exists_subselect(st_select_lex *select_lex);
- Item_exists_subselect(): Item_subselect() {}
+ Item_exists_subselect()
+ :Item_subselect(), upper_not(NULL),abort_on_null(0),
+ emb_on_expr_nest(NULL), optimizer(0), exists_transformed(0)
+ {}
subs_type substype() { return EXISTS_SUBS; }
void reset()
@@ -361,11 +384,24 @@ public:
String *val_str(String*);
my_decimal *val_decimal(my_decimal *);
bool val_bool();
+ bool fix_fields(THD *thd, Item **ref);
void fix_length_and_dec();
virtual void print(String *str, enum_query_type query_type);
+ bool select_transformer(JOIN *join);
+ void top_level_item() { abort_on_null=1; }
+ inline bool is_top_level_item() { return abort_on_null; }
+ bool exists2in_processor(uchar *opt_arg);
Item* expr_cache_insert_transformer(uchar *thd_arg);
+ void mark_as_condition_AND_part(TABLE_LIST *embedding)
+ {
+ emb_on_expr_nest= embedding;
+ }
+ virtual void under_not(Item_func_not *upper) { upper_not= upper; };
+
+ void set_exists_transformed() { exists_transformed= TRUE; }
+
friend class select_exists_subselect;
friend class subselect_uniquesubquery_engine;
friend class subselect_indexsubquery_engine;
@@ -425,11 +461,8 @@ protected:
*/
Item *expr;
bool was_null;
- bool abort_on_null;
/* A bitmap of possible execution strategies for an IN predicate. */
uchar in_strategy;
-public:
- Item_in_optimizer *optimizer;
protected:
/* Used to trigger on/off conditions that were pushed down to subselect */
bool *pushed_cond_guards;
@@ -458,15 +491,6 @@ public:
/* Priority of this predicate in the convert-to-semi-join-nest process. */
int sj_convert_priority;
/*
- Used by subquery optimizations to keep track about in which clause this
- subquery predicate is located:
- NO_JOIN_NEST - the predicate is an AND-part of the WHERE
- join nest pointer - the predicate is an AND-part of ON expression
- of a join nest
- NULL - for all other locations
- */
- TABLE_LIST *emb_on_expr_nest;
- /*
Types of left_expr and subquery's select list allow to perform subquery
materialization. Currently, we set this to FALSE when it as well could
be TRUE. This is to be properly addressed with fix for BUG#36752.
@@ -534,7 +558,9 @@ public:
*/
Item *original_item()
{
- return is_flattenable_semijoin ? (Item*)this : (Item*)optimizer;
+ return (is_flattenable_semijoin && !exists_transformed ?
+ (Item*)this :
+ (Item*)optimizer);
}
bool *get_cond_guard(int i)
@@ -546,18 +572,16 @@ public:
if ( pushed_cond_guards)
pushed_cond_guards[i]= v;
}
- bool have_guarded_conds() { return test(pushed_cond_guards); }
+ bool have_guarded_conds() { return MY_TEST(pushed_cond_guards); }
Item_func_not_all *upper_item; // point on NOT/NOP before ALL/SOME subquery
Item_in_subselect(Item * left_expr, st_select_lex *select_lex);
Item_in_subselect()
:Item_exists_subselect(), left_expr_cache(0), first_execution(TRUE),
- abort_on_null(0), in_strategy(SUBS_NOT_TRANSFORMED), optimizer(0),
- pushed_cond_guards(NULL), func(NULL), emb_on_expr_nest(NULL),
- is_jtbm_merged(FALSE), is_jtbm_const_tab(FALSE),
- upper_item(0)
- {}
+ in_strategy(SUBS_NOT_TRANSFORMED),
+ pushed_cond_guards(NULL), func(NULL), is_jtbm_merged(FALSE),
+ is_jtbm_const_tab(FALSE), upper_item(0) {}
void cleanup();
subs_type substype() { return IN_SUBS; }
void reset()
@@ -578,8 +602,6 @@ public:
my_decimal *val_decimal(my_decimal *);
void update_null_value () { (void) val_bool(); }
bool val_bool();
- 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);
virtual void print(String *str, enum_query_type query_type);
bool fix_fields(THD *thd, Item **ref);
@@ -596,21 +618,16 @@ public:
void set_first_execution() { if (first_execution) first_execution= FALSE; }
bool expr_cache_is_needed(THD *thd);
inline bool left_expr_has_null();
-
+
int optimize(double *out_rows, double *cost);
- /*
+ /*
Return the identifier that we could use to identify the subquery for the
user.
*/
int get_identifier();
- void mark_as_condition_AND_part(TABLE_LIST *embedding)
- {
- emb_on_expr_nest= embedding;
- }
-
bool test_strategy(uchar strategy)
- { return test(in_strategy & strategy); }
+ { return MY_TEST(in_strategy & strategy); }
/**
Test that the IN strategy was chosen for execution. This is so
@@ -630,13 +647,16 @@ public:
}
bool is_set_strategy()
- { return test(in_strategy & SUBS_STRATEGY_CHOSEN); }
+ { return MY_TEST(in_strategy & SUBS_STRATEGY_CHOSEN); }
bool has_strategy()
{ return in_strategy != SUBS_NOT_TRANSFORMED; }
void add_strategy (uchar strategy)
{
+ DBUG_ENTER("Item_in_subselect::add_strategy");
+ DBUG_PRINT("enter", ("current: %u add: %u",
+ (uint) in_strategy, (uint) strategy));
DBUG_ASSERT(strategy != SUBS_NOT_TRANSFORMED);
DBUG_ASSERT(!(strategy & SUBS_STRATEGY_CHOSEN));
/*
@@ -646,16 +666,25 @@ public:
DBUG_ASSERT(!(in_strategy & SUBS_STRATEGY_CHOSEN));
*/
in_strategy|= strategy;
+ DBUG_VOID_RETURN;
}
void reset_strategy(uchar strategy)
{
+ DBUG_ENTER("Item_in_subselect::reset_strategy");
+ DBUG_PRINT("enter", ("current: %u new: %u",
+ (uint) in_strategy, (uint) strategy));
DBUG_ASSERT(strategy != SUBS_NOT_TRANSFORMED);
in_strategy= strategy;
+ DBUG_VOID_RETURN;
}
void set_strategy(uchar strategy)
{
+ DBUG_ENTER("Item_in_subselect::set_strategy");
+ DBUG_PRINT("enter", ("current: %u set: %u",
+ (uint) in_strategy,
+ (uint) (SUBS_STRATEGY_CHOSEN | strategy)));
/* Check that only one strategy is set for execution. */
DBUG_ASSERT(strategy == SUBS_SEMI_JOIN ||
strategy == SUBS_IN_TO_EXISTS ||
@@ -665,7 +694,12 @@ public:
strategy == SUBS_MAXMIN_INJECTED ||
strategy == SUBS_MAXMIN_ENGINE);
in_strategy= (SUBS_STRATEGY_CHOSEN | strategy);
+ DBUG_VOID_RETURN;
}
+ bool exists2in_processor(uchar *opt_arg __attribute__((unused)))
+ {
+ return 0;
+ };
friend class Item_ref_null_helper;
friend class Item_is_not_null_test;
@@ -673,6 +707,7 @@ public:
friend class subselect_indexsubquery_engine;
friend class subselect_hash_sj_engine;
friend class subselect_partial_match_engine;
+ friend class Item_exists_subselect;
};
diff --git a/sql/item_sum.cc b/sql/item_sum.cc
index adf48f6feec..e9f52a8c27b 100644
--- a/sql/item_sum.cc
+++ b/sql/item_sum.cc
@@ -26,6 +26,7 @@
#pragma implementation // gcc: Class implementation
#endif
+#include <my_global.h>
#include "sql_priv.h"
#include "sql_select.h"
@@ -36,7 +37,7 @@
ulonglong Item_sum::ram_limitation(THD *thd)
{
- return min(thd->variables.tmp_table_size,
+ return MY_MIN(thd->variables.tmp_table_size,
thd->variables.max_heap_table_size);
}
@@ -389,7 +390,12 @@ bool Item_sum::collect_outer_ref_processor(uchar *param)
if ((ds= depended_from()) &&
ds->nest_level_base == prm->nest_level_base &&
ds->nest_level < prm->nest_level)
- prm->parameters->add_unique(this, &cmp_items);
+ {
+ if (prm->collect)
+ prm->parameters->add_unique(this, &cmp_items);
+ else
+ prm->count++;
+ }
return FALSE;
}
@@ -650,13 +656,24 @@ void Item_sum::cleanup()
@retval > 0 if key1 > key2
*/
-static int simple_str_key_cmp(void* arg, uchar* key1, uchar* key2)
+int simple_str_key_cmp(void* arg, uchar* key1, uchar* key2)
{
Field *f= (Field*) arg;
return f->cmp(key1, key2);
}
+C_MODE_START
+
+int count_distinct_walk(void *elem, element_count count, void *arg)
+{
+ (*((ulonglong*)arg))++;
+ return 0;
+}
+
+C_MODE_END
+
+
/**
Correctly compare composite keys.
@@ -724,7 +741,7 @@ C_MODE_START
/* Declarations for auxilary C-callbacks */
-static int simple_raw_key_cmp(void* arg, const void* key1, const void* key2)
+int simple_raw_key_cmp(void* arg, const void* key1, const void* key2)
{
return memcmp(key1, key2, *(uint *) arg);
}
@@ -1292,16 +1309,16 @@ Field *Item_sum_hybrid::create_tmp_field(bool group, TABLE *table,
switch (args[0]->field_type()) {
case MYSQL_TYPE_DATE:
field= new Field_newdate(0, maybe_null ? (uchar*)"" : 0, 0, Field::NONE,
- name, collation.collation);
+ name);
break;
case MYSQL_TYPE_TIME:
field= new_Field_time(0, maybe_null ? (uchar*)"" : 0, 0, Field::NONE,
- name, decimals, collation.collation);
+ name, decimals);
break;
case MYSQL_TYPE_TIMESTAMP:
case MYSQL_TYPE_DATETIME:
field= new_Field_datetime(0, maybe_null ? (uchar*)"" : 0, 0, Field::NONE,
- name, decimals, collation.collation);
+ name, decimals);
break;
default:
return Item_sum::create_tmp_field(group, table, convert_blob_length);
@@ -1640,18 +1657,18 @@ void Item_sum_avg::fix_length_and_dec()
if (hybrid_type == DECIMAL_RESULT)
{
int precision= args[0]->decimal_precision() + prec_increment;
- decimals= min(args[0]->decimals + prec_increment, DECIMAL_MAX_SCALE);
+ decimals= MY_MIN(args[0]->decimals + prec_increment, DECIMAL_MAX_SCALE);
max_length= my_decimal_precision_to_length_no_truncation(precision,
decimals,
unsigned_flag);
- f_precision= min(precision+DECIMAL_LONGLONG_DIGITS, DECIMAL_MAX_PRECISION);
+ f_precision= MY_MIN(precision+DECIMAL_LONGLONG_DIGITS, DECIMAL_MAX_PRECISION);
f_scale= args[0]->decimals;
dec_bin_size= my_decimal_get_binary_size(f_precision, f_scale);
}
else
{
- decimals= min(args[0]->decimals + prec_increment, NOT_FIXED_DEC);
- max_length= min(args[0]->max_length + prec_increment, float_length(decimals));
+ decimals= MY_MIN(args[0]->decimals + prec_increment, NOT_FIXED_DEC);
+ max_length= MY_MIN(args[0]->max_length + prec_increment, float_length(decimals));
}
}
@@ -1847,13 +1864,13 @@ void Item_sum_variance::fix_length_and_dec()
switch (args[0]->result_type()) {
case REAL_RESULT:
case STRING_RESULT:
- decimals= min(args[0]->decimals + 4, NOT_FIXED_DEC);
+ decimals= MY_MIN(args[0]->decimals + 4, NOT_FIXED_DEC);
break;
case INT_RESULT:
case DECIMAL_RESULT:
{
int precision= args[0]->decimal_precision()*2 + prec_increment;
- decimals= min(args[0]->decimals + prec_increment, DECIMAL_MAX_SCALE);
+ decimals= MY_MIN(args[0]->decimals + prec_increment, DECIMAL_MAX_SCALE);
max_length= my_decimal_precision_to_length_no_truncation(precision,
decimals,
unsigned_flag);
@@ -3138,7 +3155,7 @@ int dump_leaf_key(void* key_arg, element_count count __attribute__((unused)),
&well_formed_error);
result->length(old_length + add_length);
item->warning_for_row= TRUE;
- push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
ER_CUT_VALUE_GROUP_CONCAT, ER(ER_CUT_VALUE_GROUP_CONCAT),
item->row_count);
@@ -3383,12 +3400,8 @@ bool Item_func_group_concat::add()
TREE_ELEMENT *el= 0; // Only for safety
if (row_eligible && tree)
{
- DBUG_EXECUTE_IF("trigger_OOM_in_gconcat_add",
- DBUG_SET("+d,simulate_persistent_out_of_memory"););
el= tree_insert(tree, table->record[0] + table->s->null_bytes, 0,
tree->custom_arg);
- DBUG_EXECUTE_IF("trigger_OOM_in_gconcat_add",
- DBUG_SET("-d,simulate_persistent_out_of_memory"););
/* check if there was enough memory to insert the row */
if (!el)
return 1;
@@ -3588,10 +3601,11 @@ bool Item_func_group_concat::setup(THD *thd)
syntax of this function). If there is no ORDER BY clause, we don't
create this tree.
*/
- init_tree(tree, (uint) min(thd->variables.max_heap_table_size,
+ init_tree(tree, (uint) MY_MIN(thd->variables.max_heap_table_size,
thd->variables.sortbuff_size/16), 0,
tree_key_length,
- group_concat_key_cmp_with_order , 0, NULL, (void*) this);
+ group_concat_key_cmp_with_order, NULL, (void*) this,
+ MYF(MY_THREAD_SPECIFIC));
}
if (distinct)
diff --git a/sql/item_sum.h b/sql/item_sum.h
index 86093f5d4f5..09a20487226 100644
--- a/sql/item_sum.h
+++ b/sql/item_sum.h
@@ -1125,7 +1125,7 @@ public:
class Item_sum_or :public Item_sum_bit
{
public:
- Item_sum_or(Item *item_par) :Item_sum_bit(item_par,LL(0)) {}
+ Item_sum_or(Item *item_par) :Item_sum_bit(item_par, 0) {}
Item_sum_or(THD *thd, Item_sum_or *item) :Item_sum_bit(thd, item) {}
bool add();
const char *func_name() const { return "bit_or("; }
@@ -1146,7 +1146,7 @@ class Item_sum_and :public Item_sum_bit
class Item_sum_xor :public Item_sum_bit
{
public:
- Item_sum_xor(Item *item_par) :Item_sum_bit(item_par,LL(0)) {}
+ Item_sum_xor(Item *item_par) :Item_sum_bit(item_par, 0) {}
Item_sum_xor(THD *thd, Item_sum_xor *item) :Item_sum_bit(thd, item) {}
bool add();
const char *func_name() const { return "bit_xor("; }
diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc
index 873dcdac4b9..bf3a9981943 100644
--- a/sql/item_timefunc.cc
+++ b/sql/item_timefunc.cc
@@ -30,6 +30,7 @@
#pragma implementation // gcc: Class implementation
#endif
+#include <my_global.h>
#include "sql_priv.h"
/*
It is necessary to include set_var.h instead of item.h because there
@@ -146,14 +147,14 @@ static bool extract_date_time(DATE_TIME_FORMAT *format,
switch (*++ptr) {
/* Year */
case 'Y':
- tmp= (char*) val + min(4, val_len);
+ tmp= (char*) val + MY_MIN(4, val_len);
l_time->year= (int) my_strtoll10(val, &tmp, &error);
if ((int) (tmp-val) <= 2)
l_time->year= year_2000_handling(l_time->year);
val= tmp;
break;
case 'y':
- tmp= (char*) val + min(2, val_len);
+ tmp= (char*) val + MY_MIN(2, val_len);
l_time->year= (int) my_strtoll10(val, &tmp, &error);
val= tmp;
l_time->year= year_2000_handling(l_time->year);
@@ -162,7 +163,7 @@ static bool extract_date_time(DATE_TIME_FORMAT *format,
/* Month */
case 'm':
case 'c':
- tmp= (char*) val + min(2, val_len);
+ tmp= (char*) val + MY_MIN(2, val_len);
l_time->month= (int) my_strtoll10(val, &tmp, &error);
val= tmp;
break;
@@ -179,15 +180,15 @@ static bool extract_date_time(DATE_TIME_FORMAT *format,
/* Day */
case 'd':
case 'e':
- tmp= (char*) val + min(2, val_len);
+ tmp= (char*) val + MY_MIN(2, val_len);
l_time->day= (int) my_strtoll10(val, &tmp, &error);
val= tmp;
break;
case 'D':
- tmp= (char*) val + min(2, val_len);
+ tmp= (char*) val + MY_MIN(2, val_len);
l_time->day= (int) my_strtoll10(val, &tmp, &error);
/* Skip 'st, 'nd, 'th .. */
- val= tmp + min((int) (val_end-tmp), 2);
+ val= tmp + MY_MIN((int) (val_end-tmp), 2);
break;
/* Hour */
@@ -198,14 +199,14 @@ static bool extract_date_time(DATE_TIME_FORMAT *format,
/* fall through */
case 'k':
case 'H':
- tmp= (char*) val + min(2, val_len);
+ tmp= (char*) val + MY_MIN(2, val_len);
l_time->hour= (int) my_strtoll10(val, &tmp, &error);
val= tmp;
break;
/* Minute */
case 'i':
- tmp= (char*) val + min(2, val_len);
+ tmp= (char*) val + MY_MIN(2, val_len);
l_time->minute= (int) my_strtoll10(val, &tmp, &error);
val= tmp;
break;
@@ -213,7 +214,7 @@ static bool extract_date_time(DATE_TIME_FORMAT *format,
/* Second */
case 's':
case 'S':
- tmp= (char*) val + min(2, val_len);
+ tmp= (char*) val + MY_MIN(2, val_len);
l_time->second= (int) my_strtoll10(val, &tmp, &error);
val= tmp;
break;
@@ -265,7 +266,7 @@ static bool extract_date_time(DATE_TIME_FORMAT *format,
val= tmp;
break;
case 'j':
- tmp= (char*) val + min(val_len, 3);
+ tmp= (char*) val + MY_MIN(val_len, 3);
yearday= (int) my_strtoll10(val, &tmp, &error);
val= tmp;
break;
@@ -277,7 +278,7 @@ static bool extract_date_time(DATE_TIME_FORMAT *format,
case 'u':
sunday_first_n_first_week_non_iso= (*ptr=='U' || *ptr== 'V');
strict_week_number= (*ptr=='V' || *ptr=='v');
- tmp= (char*) val + min(val_len, 2);
+ tmp= (char*) val + MY_MIN(val_len, 2);
if ((week_number= (int) my_strtoll10(val, &tmp, &error)) < 0 ||
(strict_week_number && !week_number) ||
week_number > 53)
@@ -289,7 +290,7 @@ static bool extract_date_time(DATE_TIME_FORMAT *format,
case 'X':
case 'x':
strict_week_number_year_type= (*ptr=='X');
- tmp= (char*) val + min(4, val_len);
+ tmp= (char*) val + MY_MIN(4, val_len);
strict_week_number_year= (int) my_strtoll10(val, &tmp, &error);
val= tmp;
break;
@@ -425,7 +426,7 @@ static bool extract_date_time(DATE_TIME_FORMAT *format,
{
if (!my_isspace(&my_charset_latin1,*val))
{
- make_truncated_value_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ make_truncated_value_warning(current_thd, Sql_condition::WARN_LEVEL_WARN,
val_begin, length,
cached_timestamp_type, NullS);
break;
@@ -437,8 +438,8 @@ static bool extract_date_time(DATE_TIME_FORMAT *format,
err:
{
char buff[128];
- strmake(buff, val_begin, min(length, sizeof(buff)-1));
- push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ strmake(buff, val_begin, MY_MIN(length, sizeof(buff)-1));
+ push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
ER_WRONG_VALUE_FOR_TYPE, ER(ER_WRONG_VALUE_FOR_TYPE),
date_time_type, buff, "str_to_date");
}
@@ -707,8 +708,8 @@ static bool get_interval_info(const char *str,uint length,CHARSET_INFO *cs,
{
longlong value;
const char *start= str;
- for (value=0; str != end && my_isdigit(cs,*str) ; str++)
- value= value*LL(10) + (longlong) (*str - '0');
+ for (value=0; str != end && my_isdigit(cs, *str) ; str++)
+ value= value*10 + *str - '0';
msec_length= 6 - (str - start);
values[i]= value;
while (str != end && !my_isdigit(cs,*str))
@@ -1075,7 +1076,7 @@ longlong Item_func_weekday::val_int()
return (longlong) calc_weekday(calc_daynr(ltime.year, ltime.month,
ltime.day),
- odbc_type) + test(odbc_type);
+ odbc_type) + MY_TEST(odbc_type);
}
void Item_func_dayname::fix_length_and_dec()
@@ -1298,13 +1299,11 @@ bool get_interval_value(Item *args,interval_type int_type, INTERVAL *interval)
interval->neg= my_decimal2seconds(val, &second, &second_part);
if (second == LONGLONG_MAX)
{
- char buff[DECIMAL_MAX_STR_LENGTH];
- int length= sizeof(buff);
- decimal2string(val, buff, &length, 0, 0, 0);
- push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ ErrConvDecimal err(val);
+ push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
ER_TRUNCATED_WRONG_VALUE,
ER(ER_TRUNCATED_WRONG_VALUE), "DECIMAL",
- buff);
+ err.ptr());
return true;
}
@@ -1457,25 +1456,30 @@ bool get_interval_value(Item *args,interval_type int_type, INTERVAL *interval)
void Item_temporal_func::fix_length_and_dec()
{
+ uint char_length= mysql_temporal_int_part_length(field_type());
/*
We set maybe_null to 1 as default as any bad argument with date or
time can get us to return NULL.
*/
maybe_null= 1;
- max_length= mysql_temporal_int_part_length(field_type());
if (decimals)
{
if (decimals == NOT_FIXED_DEC)
- max_length+= TIME_SECOND_PART_DIGITS + 1;
+ char_length+= TIME_SECOND_PART_DIGITS + 1;
else
{
set_if_smaller(decimals, TIME_SECOND_PART_DIGITS);
- max_length+= decimals + 1;
+ char_length+= decimals + 1;
}
}
sql_mode= current_thd->variables.sql_mode &
(MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE);
- collation.set(&my_charset_numeric, DERIVATION_NUMERIC, MY_REPERTOIRE_ASCII);
+ collation.set(field_type() == MYSQL_TYPE_STRING ?
+ default_charset() : &my_charset_numeric,
+ field_type() == MYSQL_TYPE_STRING ?
+ DERIVATION_COERCIBLE : DERIVATION_NUMERIC,
+ MY_REPERTOIRE_ASCII);
+ fix_char_length(char_length);
}
String *Item_temporal_func::val_str(String *str)
@@ -1485,6 +1489,78 @@ String *Item_temporal_func::val_str(String *str)
}
+bool Item_temporal_hybrid_func::fix_temporal_type(MYSQL_TIME *ltime)
+{
+ if (ltime->time_type < 0) /* MYSQL_TIMESTAMP_NONE, MYSQL_TIMESTAMP_ERROR */
+ return false;
+
+ if (ltime->time_type != MYSQL_TIMESTAMP_TIME)
+ goto date_or_datetime_value;
+
+ /* Convert TIME to DATE or DATETIME */
+ switch (field_type())
+ {
+ case MYSQL_TYPE_DATE:
+ case MYSQL_TYPE_DATETIME:
+ case MYSQL_TYPE_TIMESTAMP:
+ {
+ MYSQL_TIME tmp;
+ if (time_to_datetime_with_warn(current_thd, ltime, &tmp, 0))
+ return (null_value= true);
+ *ltime= tmp;
+ if (field_type() == MYSQL_TYPE_DATE)
+ datetime_to_date(ltime);
+ return false;
+ }
+ case MYSQL_TYPE_TIME:
+ case MYSQL_TYPE_STRING: /* DATE_ADD, ADDTIME can return VARCHAR */
+ return false;
+ default:
+ DBUG_ASSERT(0);
+ return (null_value= true);
+ }
+
+date_or_datetime_value:
+ /* Convert DATE or DATETIME to TIME, DATE, or DATETIME */
+ switch (field_type())
+ {
+ case MYSQL_TYPE_TIME:
+ datetime_to_time(ltime);
+ return false;
+ case MYSQL_TYPE_DATETIME:
+ case MYSQL_TYPE_TIMESTAMP:
+ date_to_datetime(ltime);
+ return false;
+ case MYSQL_TYPE_DATE:
+ datetime_to_date(ltime);
+ return false;
+ case MYSQL_TYPE_STRING: /* DATE_ADD, ADDTIME can return VARCHAR */
+ return false;
+ default:
+ DBUG_ASSERT(0);
+ return (null_value= true);
+ }
+ return false;
+}
+
+
+String *Item_temporal_hybrid_func::val_str_ascii(String *str)
+{
+ DBUG_ASSERT(fixed == 1);
+ MYSQL_TIME ltime;
+
+ if (get_date(&ltime, 0) || fix_temporal_type(&ltime) ||
+ (null_value= my_TIME_to_str(&ltime, str, decimals)))
+ return (String *) 0;
+
+ /* Check that the returned timestamp type matches to the function type */
+ DBUG_ASSERT(cached_field_type == MYSQL_TYPE_STRING ||
+ ltime.time_type == MYSQL_TIMESTAMP_NONE ||
+ mysql_type_to_time_type(cached_field_type) == ltime.time_type);
+ return str;
+}
+
+
bool Item_func_from_days::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date)
{
longlong value=args[0]->val_int();
@@ -1573,7 +1649,7 @@ static void set_sec_part(ulong sec_part, MYSQL_TIME *ltime, Item *item)
{
ltime->second_part= sec_part;
if (item->decimals < TIME_SECOND_PART_DIGITS)
- ltime->second_part= sec_part_truncate(ltime->second_part, item->decimals);
+ my_time_trunc(ltime, item->decimals);
}
}
@@ -1714,7 +1790,7 @@ overflow:
ltime->hour= TIME_MAX_HOUR+1;
check_time_range(ltime, decimals, &unused);
- make_truncated_value_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ make_truncated_value_warning(current_thd, Sql_condition::WARN_LEVEL_WARN,
err->ptr(), err->length(),
MYSQL_TIMESTAMP_TIME, NullS);
return 0;
@@ -1740,13 +1816,13 @@ void Item_func_date_format::fix_length_and_dec()
if (arg1->type() == STRING_ITEM)
{ // Optimize the normal case
fixed_length=1;
- max_length= format_length(&arg1->str_value) *
+ max_length= format_length(arg1->val_str(NULL)) *
collation.collation->mbmaxlen;
}
else
{
fixed_length=0;
- max_length=min(arg1->max_length, MAX_BLOB_WIDTH) * 10 *
+ max_length=MY_MIN(arg1->max_length, MAX_BLOB_WIDTH) * 10 *
collation.collation->mbmaxlen;
set_if_smaller(max_length,MAX_BLOB_WIDTH);
}
@@ -2009,12 +2085,12 @@ void Item_date_add_interval::fix_length_and_dec()
int_type <= INTERVAL_SECOND_MICROSECOND))
interval_dec= TIME_SECOND_PART_DIGITS;
else if (int_type == INTERVAL_SECOND && args[1]->decimals > 0)
- interval_dec= min(args[1]->decimals, TIME_SECOND_PART_DIGITS);
+ interval_dec= MY_MIN(args[1]->decimals, TIME_SECOND_PART_DIGITS);
if (arg0_field_type == MYSQL_TYPE_DATETIME ||
arg0_field_type == MYSQL_TYPE_TIMESTAMP)
{
- decimals= max(args[0]->temporal_precision(MYSQL_TYPE_DATETIME), interval_dec);
+ decimals= MY_MAX(args[0]->temporal_precision(MYSQL_TYPE_DATETIME), interval_dec);
cached_field_type= MYSQL_TYPE_DATETIME;
}
else if (arg0_field_type == MYSQL_TYPE_DATE)
@@ -2029,14 +2105,14 @@ void Item_date_add_interval::fix_length_and_dec()
}
else if (arg0_field_type == MYSQL_TYPE_TIME)
{
- decimals= max(args[0]->temporal_precision(MYSQL_TYPE_TIME), interval_dec);
+ decimals= MY_MAX(args[0]->temporal_precision(MYSQL_TYPE_TIME), interval_dec);
if (int_type >= INTERVAL_DAY && int_type != INTERVAL_YEAR_MONTH)
cached_field_type= arg0_field_type;
else
cached_field_type= MYSQL_TYPE_DATETIME;
}
else
- decimals= max(args[0]->temporal_precision(MYSQL_TYPE_DATETIME), interval_dec);
+ decimals= MY_MAX(args[0]->temporal_precision(MYSQL_TYPE_DATETIME), interval_dec);
Item_temporal_func::fix_length_and_dec();
}
@@ -2150,8 +2226,10 @@ longlong Item_extract::val_int()
long neg;
int is_time_flag = date_value ? 0 : TIME_TIME_ONLY;
- if (get_arg0_date(&ltime, is_time_flag))
+ // Not using get_arg0_date to avoid automatic TIME to DATETIME conversion
+ if ((null_value= args[0]->get_date(&ltime, is_time_flag)))
return 0;
+
neg= ltime.neg ? -1 : 1;
DBUG_ASSERT(ltime.time_type != MYSQL_TIMESTAMP_TIME || ltime.day == 0);
@@ -2289,7 +2367,7 @@ String *Item_char_typecast::val_str(String *str)
if (cast_length != ~0U &&
cast_length > current_thd->variables.max_allowed_packet)
{
- push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
ER_WARN_ALLOWED_PACKET_OVERFLOWED,
ER(ER_WARN_ALLOWED_PACKET_OVERFLOWED),
cast_cs == &my_charset_bin ?
@@ -2347,7 +2425,7 @@ String *Item_char_typecast::val_str(String *str)
res= &str_value;
}
ErrConvString err(res);
- push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
ER_TRUNCATED_WRONG_VALUE,
ER(ER_TRUNCATED_WRONG_VALUE), char_type,
err.ptr());
@@ -2369,7 +2447,7 @@ String *Item_char_typecast::val_str(String *str)
if (res->length() > current_thd->variables.max_allowed_packet)
{
- push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
ER_WARN_ALLOWED_PACKET_OVERFLOWED,
ER(ER_WARN_ALLOWED_PACKET_OVERFLOWED),
cast_cs == &my_charset_bin ?
@@ -2436,7 +2514,7 @@ bool Item_time_typecast::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date)
if (get_arg0_time(ltime))
return 1;
if (decimals < TIME_SECOND_PART_DIGITS)
- ltime->second_part= sec_part_truncate(ltime->second_part, decimals);
+ my_time_trunc(ltime, decimals);
/*
MYSQL_TIMESTAMP_TIME value can have non-zero day part,
which we should not lose.
@@ -2452,6 +2530,7 @@ bool Item_time_typecast::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date)
bool Item_date_typecast::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date)
{
+ fuzzy_date |= sql_mode_for_dates(current_thd);
if (get_arg0_date(ltime, fuzzy_date & ~TIME_TIME_ONLY))
return 1;
@@ -2464,15 +2543,15 @@ bool Item_date_typecast::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date)
bool Item_datetime_typecast::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date)
{
+ fuzzy_date |= sql_mode_for_dates(current_thd);
if (get_arg0_date(ltime, fuzzy_date & ~TIME_TIME_ONLY))
return 1;
if (decimals < TIME_SECOND_PART_DIGITS)
- ltime->second_part= sec_part_truncate(ltime->second_part, decimals);
-
- if (make_date_with_warn(ltime, fuzzy_date, MYSQL_TIMESTAMP_DATETIME))
- return (null_value= 1);
+ my_time_trunc(ltime, decimals);
+ DBUG_ASSERT(ltime->time_type != MYSQL_TIMESTAMP_TIME);
+ ltime->time_type= MYSQL_TIMESTAMP_DATETIME;
return 0;
}
@@ -2518,7 +2597,7 @@ err:
void Item_func_add_time::fix_length_and_dec()
{
enum_field_types arg0_field_type;
- decimals= max(args[0]->decimals, args[1]->decimals);
+ decimals= MY_MAX(args[0]->decimals, args[1]->decimals);
/*
The field type for the result of an Item_func_add_time function is defined
@@ -2538,14 +2617,14 @@ void Item_func_add_time::fix_length_and_dec()
is_date)
{
cached_field_type= MYSQL_TYPE_DATETIME;
- decimals= max(args[0]->temporal_precision(MYSQL_TYPE_DATETIME),
- args[1]->temporal_precision(MYSQL_TYPE_TIME));
+ decimals= MY_MAX(args[0]->temporal_precision(MYSQL_TYPE_DATETIME),
+ args[1]->temporal_precision(MYSQL_TYPE_TIME));
}
else if (arg0_field_type == MYSQL_TYPE_TIME)
{
cached_field_type= MYSQL_TYPE_TIME;
- decimals= max(args[0]->temporal_precision(MYSQL_TYPE_TIME),
- args[1]->temporal_precision(MYSQL_TYPE_TIME));
+ decimals= MY_MAX(args[0]->temporal_precision(MYSQL_TYPE_TIME),
+ args[1]->temporal_precision(MYSQL_TYPE_TIME));
}
Item_temporal_func::fix_length_and_dec();
}
@@ -2569,16 +2648,18 @@ bool Item_func_add_time::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date)
longlong seconds;
int l_sign= sign;
- if (is_date) // TIMESTAMP function
+ if (cached_field_type == MYSQL_TYPE_DATETIME)
{
+ // TIMESTAMP function OR the first argument is DATE/DATETIME/TIMESTAMP
if (get_arg0_date(&l_time1, 0) ||
args[1]->get_time(&l_time2) ||
l_time1.time_type == MYSQL_TIMESTAMP_TIME ||
l_time2.time_type != MYSQL_TIMESTAMP_TIME)
return (null_value= 1);
}
- else // ADDTIME function
+ else
{
+ // ADDTIME function AND the first argument is TIME
if (args[0]->get_time(&l_time1) ||
args[1]->get_time(&l_time2) ||
l_time2.time_type == MYSQL_TIMESTAMP_DATETIME)
@@ -2603,9 +2684,9 @@ bool Item_func_add_time::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date)
if (!is_time && ltime->neg)
return (null_value= 1);
- days= (long)(seconds/86400L);
+ days= (long) (seconds / SECONDS_IN_24H);
- calc_time_from_sec(ltime, (long)(seconds%86400L), microseconds);
+ calc_time_from_sec(ltime, (long)(seconds % SECONDS_IN_24H), microseconds);
ltime->time_type= is_time ? MYSQL_TIMESTAMP_TIME : MYSQL_TIMESTAMP_DATETIME;
@@ -2701,7 +2782,6 @@ bool Item_func_timediff::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date)
*ltime= l_time3;
return (null_value= adjust_time_range_with_warn(ltime, decimals));
-
}
/**
@@ -2753,7 +2833,7 @@ bool Item_func_maketime::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date)
char buf[28];
char *ptr= longlong10_to_str(hour, buf, args[0]->unsigned_flag ? 10 : -10);
int len = (int)(ptr - buf) + sprintf(ptr, ":%02u:%02u", (uint)minute, (uint)second);
- make_truncated_value_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ make_truncated_value_warning(current_thd, Sql_condition::WARN_LEVEL_WARN,
buf, len, MYSQL_TIMESTAMP_TIME,
NullS);
}
@@ -2789,8 +2869,12 @@ longlong Item_func_timestamp_diff::val_int()
int neg= 1;
null_value= 0;
- if (args[0]->get_date(&ltime1, TIME_NO_ZERO_DATE | TIME_NO_ZERO_IN_DATE) ||
- args[1]->get_date(&ltime2, TIME_NO_ZERO_DATE | TIME_NO_ZERO_IN_DATE))
+ if (args[0]->get_date_with_conversion(&ltime1,
+ TIME_NO_ZERO_DATE |
+ TIME_NO_ZERO_IN_DATE) ||
+ args[1]->get_date_with_conversion(&ltime2,
+ TIME_NO_ZERO_DATE |
+ TIME_NO_ZERO_IN_DATE))
goto null_date;
if (calc_time_diff(&ltime2,&ltime1, 1,
@@ -2860,9 +2944,9 @@ longlong Item_func_timestamp_diff::val_int()
case INTERVAL_MONTH:
return months*neg;
case INTERVAL_WEEK:
- return seconds/86400L/7L*neg;
+ return seconds / SECONDS_IN_24H / 7L * neg;
case INTERVAL_DAY:
- return seconds/86400L*neg;
+ return seconds / SECONDS_IN_24H * neg;
case INTERVAL_HOUR:
return seconds/3600L*neg;
case INTERVAL_MINUTE:
@@ -3071,7 +3155,7 @@ void Item_func_str_to_date::fix_length_and_dec()
}
cached_field_type= MYSQL_TYPE_DATETIME;
- decimals= NOT_FIXED_DEC;
+ decimals= TIME_SECOND_PART_DIGITS;
if ((const_item= args[1]->const_item()))
{
char format_buff[64];
@@ -3124,7 +3208,7 @@ bool Item_func_str_to_date::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date)
date_time_format.format.length= format->length();
if (extract_date_time(&date_time_format, val->ptr(), val->length(),
ltime, cached_timestamp_type, 0, "datetime",
- fuzzy_date))
+ fuzzy_date | sql_mode_for_dates(current_thd)))
return (null_value=1);
if (cached_timestamp_type == MYSQL_TIMESTAMP_TIME && ltime->day)
{
diff --git a/sql/item_timefunc.h b/sql/item_timefunc.h
index 3a03ee4b27a..839a5a4845d 100644
--- a/sql/item_timefunc.h
+++ b/sql/item_timefunc.h
@@ -113,7 +113,7 @@ public:
{
int *input_version= (int*)int_arg;
/* This function was introduced in 5.5 */
- int output_version= max(*input_version, 50500);
+ int output_version= MY_MAX(*input_version, 50500);
*input_version= output_version;
return 0;
}
@@ -489,7 +489,6 @@ public:
Item_temporal_func(Item *a, Item *b) :Item_func(a,b) {}
Item_temporal_func(Item *a, Item *b, Item *c) :Item_func(a,b,c) {}
enum Item_result result_type () const { return STRING_RESULT; }
- CHARSET_INFO *charset_for_protocol(void) const { return &my_charset_bin; }
enum_field_types field_type() const { return MYSQL_TYPE_DATETIME; }
Item_result cmp_type() const { return TIME_RESULT; }
String *val_str(String *str);
@@ -506,6 +505,55 @@ public:
};
+/**
+ Abstract class for functions returning TIME, DATE, DATETIME or string values,
+ whose data type depends on parameters and is set at fix_fields time.
+*/
+class Item_temporal_hybrid_func: public Item_temporal_func
+{
+protected:
+ enum_field_types cached_field_type; // TIME, DATE, DATETIME or STRING
+ String ascii_buf; // Conversion buffer
+public:
+ Item_temporal_hybrid_func(Item *a,Item *b)
+ :Item_temporal_func(a,b) {}
+ enum_field_types field_type() const { return cached_field_type; }
+ Item_result cmp_type() const
+ {
+ return cached_field_type == MYSQL_TYPE_STRING ?
+ STRING_RESULT : TIME_RESULT;
+ }
+ const CHARSET_INFO *charset_for_protocol() const
+ {
+ /*
+ Can return TIME, DATE, DATETIME or VARCHAR depending on arguments.
+ Send using "binary" when TIME, DATE or DATETIME,
+ or using collation.collation when VARCHAR
+ (which is fixed from @@collation_connection in fix_length_and_dec).
+ */
+ DBUG_ASSERT(fixed == 1);
+ return cached_field_type == MYSQL_TYPE_STRING ?
+ collation.collation : &my_charset_bin;
+ }
+ /**
+ Fix the returned timestamp to match field_type(),
+ which is important for val_str().
+ */
+ bool fix_temporal_type(MYSQL_TIME *ltime);
+ /**
+ Return string value in ASCII character set.
+ */
+ String *val_str_ascii(String *str);
+ /**
+ Return string value in @@character_set_connection.
+ */
+ String *val_str(String *str)
+ {
+ return val_str_from_val_str_ascii(str, &ascii_buf);
+ }
+};
+
+
class Item_datefunc :public Item_temporal_func
{
public:
@@ -756,24 +804,22 @@ public:
bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date);
void fix_length_and_dec()
{
- decimals= args[0]->decimals;
+ decimals= MY_MIN(args[0]->decimals, TIME_SECOND_PART_DIGITS);
Item_timefunc::fix_length_and_dec();
}
const char *func_name() const { return "sec_to_time"; }
};
-class Item_date_add_interval :public Item_temporal_func
+class Item_date_add_interval :public Item_temporal_hybrid_func
{
- enum_field_types cached_field_type;
public:
const interval_type int_type; // keep it public
const bool date_sub_interval; // keep it public
Item_date_add_interval(Item *a,Item *b,interval_type type_arg,bool neg_arg)
- :Item_temporal_func(a,b),int_type(type_arg), date_sub_interval(neg_arg) {}
+ :Item_temporal_hybrid_func(a,b),int_type(type_arg), date_sub_interval(neg_arg) {}
const char *func_name() const { return "date_add_interval"; }
void fix_length_and_dec();
- enum_field_types field_type() const { return cached_field_type; }
bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date);
bool eq(const Item *item, bool binary_cmp) const;
void print(String *str, enum_query_type query_type);
@@ -911,16 +957,14 @@ public:
};
-class Item_func_add_time :public Item_temporal_func
+class Item_func_add_time :public Item_temporal_hybrid_func
{
const bool is_date;
int sign;
- enum_field_types cached_field_type;
public:
Item_func_add_time(Item *a, Item *b, bool type_arg, bool neg_arg)
- :Item_temporal_func(a, b), is_date(type_arg) { sign= neg_arg ? -1 : 1; }
- enum_field_types field_type() const { return cached_field_type; }
+ :Item_temporal_hybrid_func(a, b), is_date(type_arg) { sign= neg_arg ? -1 : 1; }
void fix_length_and_dec();
bool get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date);
void print(String *str, enum_query_type query_type);
@@ -935,8 +979,8 @@ public:
const char *func_name() const { return "timediff"; }
void fix_length_and_dec()
{
- decimals= max(args[0]->temporal_precision(MYSQL_TYPE_TIME),
- args[1]->temporal_precision(MYSQL_TYPE_TIME));
+ decimals= MY_MAX(args[0]->temporal_precision(MYSQL_TYPE_TIME),
+ args[1]->temporal_precision(MYSQL_TYPE_TIME));
Item_timefunc::fix_length_and_dec();
}
bool get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date);
@@ -950,7 +994,7 @@ public:
{}
void fix_length_and_dec()
{
- decimals= min(args[2]->decimals, TIME_SECOND_PART_DIGITS);
+ decimals= MY_MIN(args[2]->decimals, TIME_SECOND_PART_DIGITS);
Item_timefunc::fix_length_and_dec();
}
const char *func_name() const { return "maketime"; }
@@ -1019,9 +1063,8 @@ public:
};
-class Item_func_str_to_date :public Item_temporal_func
+class Item_func_str_to_date :public Item_temporal_hybrid_func
{
- enum_field_types cached_field_type;
timestamp_type cached_timestamp_type;
bool const_item;
String subject_converter;
@@ -1029,12 +1072,11 @@ class Item_func_str_to_date :public Item_temporal_func
CHARSET_INFO *internal_charset;
public:
Item_func_str_to_date(Item *a, Item *b)
- :Item_temporal_func(a, b), const_item(false),
+ :Item_temporal_hybrid_func(a, b), const_item(false),
internal_charset(NULL)
{}
bool get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date);
const char *func_name() const { return "str_to_date"; }
- enum_field_types field_type() const { return cached_field_type; }
void fix_length_and_dec();
};
diff --git a/sql/item_xmlfunc.cc b/sql/item_xmlfunc.cc
index 064038a06fe..f8bf7cbf93a 100644
--- a/sql/item_xmlfunc.cc
+++ b/sql/item_xmlfunc.cc
@@ -17,6 +17,7 @@
#pragma implementation
#endif
+#include <my_global.h>
#include "sql_priv.h"
/*
It is necessary to include set_var.h instead of item.h because there
@@ -532,6 +533,32 @@ public:
};
+/**
+ A string whose value may be changed during execution.
+*/
+class Item_string_xml_non_const: public Item_string
+{
+public:
+ Item_string_xml_non_const(const char *str, uint length, CHARSET_INFO *cs)
+ :Item_string(str, length, cs)
+ { }
+ bool const_item() const { return false ; }
+ bool basic_const_item() const { return false; }
+ void set_value(const char *str, uint length, CHARSET_INFO *cs)
+ {
+ str_value.set(str, length, cs);
+ }
+ Item *safe_charset_converter(CHARSET_INFO *tocs)
+ {
+ /*
+ Item_string::safe_charset_converter() does not accept non-constants.
+ Note, conversion is not really needed here anyway.
+ */
+ return this;
+ }
+};
+
+
class Item_nodeset_to_const_comparator :public Item_bool_func
{
String *pxml;
@@ -550,7 +577,8 @@ public:
longlong val_int()
{
Item_func *comp= (Item_func*)args[1];
- Item_string *fake= (Item_string*)(comp->arguments()[0]);
+ Item_string_xml_non_const *fake=
+ (Item_string_xml_non_const*)(comp->arguments()[0]);
String *res= args[0]->val_nodeset(&tmp_nodeset);
MY_XPATH_FLT *fltbeg= (MY_XPATH_FLT*) res->ptr();
MY_XPATH_FLT *fltend= (MY_XPATH_FLT*) (res->ptr() + res->length());
@@ -568,8 +596,8 @@ public:
if ((node->parent == flt->num) &&
(node->type == MY_XML_NODE_TEXT))
{
- fake->str_value.set(node->beg, node->end - node->beg,
- collation.collation);
+ fake->set_value(node->beg, node->end - node->beg,
+ collation.collation);
if (args[1]->val_int())
return 1;
}
@@ -956,14 +984,12 @@ static Item *create_comparator(MY_XPATH *xpath,
{
/*
Compare a node set to a scalar value.
- We just create a fake Item_string() argument,
+ We just create a fake Item_string_xml_non_const() argument,
which will be filled to the partular value
in a loop through all of the nodes in the node set.
*/
- Item_string *fake= new Item_string("", 0, xpath->cs);
- /* Don't cache fake because its value will be changed during comparison.*/
- fake->set_used_tables(RAND_TABLE_BIT);
+ Item_string *fake= new Item_string_xml_non_const("", 0, xpath->cs);
Item_nodeset_func *nodeset;
Item *scalar, *comp;
if (a->type() == Item::XPATH_NODESET)
@@ -1040,7 +1066,7 @@ static char simpletok[128]=
/*
! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ?
@ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _
- ` a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~ €
+ ` a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~ \200
*/
0,1,0,0,1,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,1,0,1,1,1,0,
1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,
@@ -2504,12 +2530,12 @@ my_xpath_parse_VariableReference(MY_XPATH *xpath)
xpath->item= new Item_func_get_user_var(name);
else
{
- sp_variable_t *spv;
+ sp_variable *spv;
sp_pcontext *spc;
LEX *lex;
if ((lex= current_thd->lex) &&
(spc= lex->spcont) &&
- (spv= spc->find_variable(&name)))
+ (spv= spc->find_variable(name, false)))
{
Item_splocal *splocal= new Item_splocal(name, spv->offset, spv->type, 0);
#ifndef DBUG_OFF
@@ -2600,16 +2626,24 @@ my_xpath_parse(MY_XPATH *xpath, const char *str, const char *strend)
void Item_xml_str_func::fix_length_and_dec()
{
+ max_length= MAX_BLOB_WIDTH;
+ agg_arg_charsets_for_comparison(collation, args, arg_count);
+}
+
+
+bool Item_xml_str_func::fix_fields(THD *thd, Item **ref)
+{
String *xp, tmp;
MY_XPATH xpath;
int rc;
+ if (Item_str_func::fix_fields(thd, ref))
+ return true;
+
status_var_increment(current_thd->status_var.feature_xml);
nodeset_func= 0;
- if (agg_arg_charsets_for_comparison(collation, args, arg_count))
- return;
if (collation.collation->mbminlen > 1)
{
@@ -2617,23 +2651,23 @@ void Item_xml_str_func::fix_length_and_dec()
my_printf_error(ER_UNKNOWN_ERROR,
"Character set '%s' is not supported by XPATH",
MYF(0), collation.collation->csname);
- return;
+ return true;
}
if (!args[1]->const_item())
{
my_printf_error(ER_UNKNOWN_ERROR,
"Only constant XPATH queries are supported", MYF(0));
- return;
+ return true;
}
if (!(xp= args[1]->val_str(&tmp)))
- return;
+ return false; // Will return NULL
my_xpath_init(&xpath);
xpath.cs= collation.collation;
xpath.debug= 0;
- xpath.pxml= &pxml;
- pxml.set_charset(collation.collation);
+ xpath.pxml= xml.parsed();
+ xml.set_charset(collation.collation);
rc= my_xpath_parse(&xpath, xp->ptr(), xp->ptr() + xp->length());
@@ -2643,13 +2677,24 @@ void Item_xml_str_func::fix_length_and_dec()
set_if_smaller(clen, 32);
my_printf_error(ER_UNKNOWN_ERROR, "XPATH syntax error: '%.*s'",
MYF(0), clen, xpath.lasttok.beg);
- return;
+ return true;
}
- nodeset_func= xpath.item;
- if (nodeset_func)
- nodeset_func->fix_fields(current_thd, &nodeset_func);
- max_length= MAX_BLOB_WIDTH;
+ /*
+ Parsing XML is a heavy operation, so if the first argument is constant,
+ then parse XML only one time and cache the parsed representation
+ together with raw text representation.
+
+ Note, we cannot cache the entire function result even if
+ the first and the second arguments are constants, because
+ the XPath expression may have user and SP variable references,
+ so the function result can vary between executions.
+ */
+ if ((args[0]->const_item() && get_xml(&xml, true)) ||
+ !(nodeset_func= xpath.item))
+ return false; // Will return NULL
+
+ return nodeset_func->fix_fields(thd, &nodeset_func);
}
@@ -2780,25 +2825,24 @@ int xml_leave(MY_XML_PARSER *st,const char *attr, size_t len)
Parse raw XML
SYNOPSYS
-
RETURN
- Currently pointer to parsed XML on success
- 0 on parse error
+ false on success
+ true on error
*/
-String *Item_xml_str_func::parse_xml(String *raw_xml, String *parsed_xml_buf)
+bool Item_xml_str_func::XML::parse()
{
MY_XML_PARSER p;
MY_XML_USER_DATA user_data;
int rc;
- parsed_xml_buf->length(0);
+ m_parsed_buf.length(0);
/* Prepare XML parser */
my_xml_parser_create(&p);
p.flags= MY_XML_FLAG_RELATIVE_NAMES | MY_XML_FLAG_SKIP_TEXT_NORMALIZATION;
user_data.level= 0;
- user_data.pxml= parsed_xml_buf;
+ user_data.pxml= &m_parsed_buf;
user_data.parent= 0;
my_xml_set_enter_handler(&p, xml_enter);
my_xml_set_value_handler(&p, xml_value);
@@ -2807,23 +2851,54 @@ String *Item_xml_str_func::parse_xml(String *raw_xml, String *parsed_xml_buf)
/* Add root node */
p.current_node_type= MY_XML_NODE_TAG;
- xml_enter(&p, raw_xml->ptr(), 0);
+ xml_enter(&p, m_raw_ptr->ptr(), 0);
/* Execute XML parser */
- if ((rc= my_xml_parse(&p, raw_xml->ptr(), raw_xml->length())) != MY_XML_OK)
+ if ((rc= my_xml_parse(&p, m_raw_ptr->ptr(), m_raw_ptr->length())) != MY_XML_OK)
{
char buf[128];
my_snprintf(buf, sizeof(buf)-1, "parse error at line %d pos %lu: %s",
my_xml_error_lineno(&p) + 1,
(ulong) my_xml_error_pos(&p) + 1,
my_xml_error_string(&p));
- push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
ER_WRONG_VALUE,
ER(ER_WRONG_VALUE), "XML", buf);
+ m_raw_ptr= (String *) 0;
}
my_xml_parser_free(&p);
- return rc == MY_XML_OK ? parsed_xml_buf : 0;
+ return rc != MY_XML_OK;
+}
+
+
+/*
+ Parse the raw XML from the given source,
+ optionally cache the raw XML,
+ remember the pointer to the raw XML.
+*/
+bool Item_xml_str_func::XML::parse(String *raw_xml, bool cache)
+{
+ m_raw_ptr= raw_xml;
+ if (cache)
+ {
+ m_cached= true;
+ if (m_raw_ptr != &m_raw_buf && m_raw_buf.copy(*m_raw_ptr))
+ {
+ m_raw_ptr= (String *) 0;
+ return true;
+ }
+ m_raw_ptr= &m_raw_buf;
+ }
+ return parse();
+}
+
+
+const MY_XML_NODE *Item_xml_str_func::XML::node(uint idx)
+{
+ const MY_XML_NODE *nodebeg= (MY_XML_NODE*) m_parsed_buf.ptr();
+ DBUG_ASSERT(idx < m_parsed_buf.length() / sizeof (MY_XML_NODE));
+ return nodebeg + idx;
}
@@ -2831,10 +2906,8 @@ String *Item_func_xml_extractvalue::val_str(String *str)
{
String *res;
null_value= 0;
- if (!nodeset_func ||
- !(res= args[0]->val_str(str)) ||
- !parse_xml(res, &pxml) ||
- !(res= nodeset_func->val_str(&tmp_value)))
+ if (!nodeset_func || get_xml(&xml) ||
+ !(res= nodeset_func->val_str(str)))
{
null_value= 1;
return 0;
@@ -2843,22 +2916,37 @@ String *Item_func_xml_extractvalue::val_str(String *str)
}
+bool Item_func_xml_update::collect_result(String *str,
+ const MY_XML_NODE *cut,
+ const String *replace)
+{
+ uint offs= cut->type == MY_XML_NODE_TAG ? 1 : 0;
+ const char *end= cut->tagend + offs;
+ str->length(0);
+ str->set_charset(collation.collation);
+ return
+ /* Put the XML part preceding the replaced piece */
+ str->append(xml.raw()->ptr(), cut->beg - xml.raw()->ptr() - offs) ||
+ /* Put the replacement */
+ str->append(replace->ptr(), replace->length()) ||
+ /* Put the XML part following the replaced piece */
+ str->append(end, xml.raw()->ptr() + xml.raw()->length() - end);
+}
+
+
String *Item_func_xml_update::val_str(String *str)
{
- String *res, *nodeset, *rep;
+ String *nodeset, *rep;
null_value= 0;
- if (!nodeset_func ||
- !(res= args[0]->val_str(str)) ||
+ if (!nodeset_func || get_xml(&xml) ||
!(rep= args[2]->val_str(&tmp_value3)) ||
- !parse_xml(res, &pxml) ||
!(nodeset= nodeset_func->val_nodeset(&tmp_value2)))
{
null_value= 1;
return 0;
}
- MY_XML_NODE *nodebeg= (MY_XML_NODE*) pxml.ptr();
MY_XPATH_FLT *fltbeg= (MY_XPATH_FLT*) nodeset->ptr();
MY_XPATH_FLT *fltend= (MY_XPATH_FLT*) (nodeset->ptr() + nodeset->length());
@@ -2866,10 +2954,10 @@ String *Item_func_xml_update::val_str(String *str)
if (fltend - fltbeg != 1)
{
/* TODO: perhaps add a warning that more than one tag selected */
- return res;
+ return xml.raw();
}
- nodebeg+= fltbeg->num;
+ const MY_XML_NODE *nodebeg= xml.node(fltbeg->num);
if (!nodebeg->level)
{
@@ -2881,12 +2969,5 @@ String *Item_func_xml_update::val_str(String *str)
return rep;
}
- tmp_value.length(0);
- tmp_value.set_charset(collation.collation);
- uint offs= nodebeg->type == MY_XML_NODE_TAG ? 1 : 0;
- tmp_value.append(res->ptr(), nodebeg->beg - res->ptr() - offs);
- tmp_value.append(rep->ptr(), rep->length());
- const char *end= nodebeg->tagend + offs;
- tmp_value.append(end, res->ptr() + res->length() - end);
- return &tmp_value;
+ return collect_result(str, nodebeg, rep) ? (String *) NULL : str;
}
diff --git a/sql/item_xmlfunc.h b/sql/item_xmlfunc.h
index 800cf6ed760..637f505e12e 100644
--- a/sql/item_xmlfunc.h
+++ b/sql/item_xmlfunc.h
@@ -21,16 +21,60 @@
/* This file defines all XML functions */
-#ifdef __GNUC__
+#ifdef USE_PRAGMA_INTERFACE
#pragma interface /* gcc class implementation */
#endif
+typedef struct my_xml_node_st MY_XML_NODE;
+
+
class Item_xml_str_func: public Item_str_func
{
protected:
- String tmp_value, pxml;
+ /*
+ A helper class to store raw and parsed XML.
+ */
+ class XML
+ {
+ bool m_cached;
+ String *m_raw_ptr; // Pointer to text representation
+ String m_raw_buf; // Cached text representation
+ String m_parsed_buf; // Array of MY_XML_NODEs, pointing to raw_buffer
+ bool parse();
+ void reset()
+ {
+ m_cached= false;
+ m_raw_ptr= (String *) 0;
+ }
+ public:
+ XML() { reset(); }
+ void set_charset(CHARSET_INFO *cs) { m_parsed_buf.set_charset(cs); }
+ String *raw() { return m_raw_ptr; }
+ String *parsed() { return &m_parsed_buf; }
+ const MY_XML_NODE *node(uint idx);
+ bool cached() { return m_cached; }
+ bool parse(String *raw, bool cache);
+ bool parse(Item *item, bool cache)
+ {
+ String *res;
+ if (!(res= item->val_str(&m_raw_buf)))
+ {
+ m_raw_ptr= (String *) 0;
+ m_cached= cache;
+ return true;
+ }
+ return parse(res, cache);
+ }
+ };
Item *nodeset_func;
+ XML xml;
+ bool get_xml(XML *xml, bool cache= false)
+ {
+ if (!cache && xml->cached())
+ return xml->raw() == 0;
+ return xml->parse(args[0], cache);
+ }
public:
Item_xml_str_func(Item *a, Item *b):
Item_str_func(a,b)
@@ -42,8 +86,12 @@ public:
{
maybe_null= TRUE;
}
+ bool fix_fields(THD *thd, Item **ref);
void fix_length_and_dec();
- String *parse_xml(String *raw_xml, String *parsed_xml_buf);
+ bool const_item() const
+ {
+ return const_item_cache && (!nodeset_func || nodeset_func->const_item());
+ }
bool check_vcol_func_processor(uchar *int_arg)
{
return trace_unsupported_by_check_vcol_func_processor(func_name());
@@ -63,6 +111,9 @@ public:
class Item_func_xml_update: public Item_xml_str_func
{
String tmp_value2, tmp_value3;
+ bool collect_result(String *str,
+ const MY_XML_NODE *cut,
+ const String *replace);
public:
Item_func_xml_update(Item *a,Item *b,Item *c) :Item_xml_str_func(a,b,c) {}
const char *func_name() const { return "updatexml"; }
diff --git a/sql/key.cc b/sql/key.cc
index 110b13000ed..aaaea9391c6 100644
--- a/sql/key.cc
+++ b/sql/key.cc
@@ -16,11 +16,14 @@
/* Functions to handle keys and fields in forms */
+#include <my_global.h>
#include "sql_priv.h"
-#include "unireg.h" // REQUIRED: by includes later
#include "key.h" // key_rec_cmp
#include "field.h" // Field
+using std::min;
+using std::max;
+
/*
Search after a key that starts with 'field'
@@ -78,7 +81,7 @@ int find_ref_key(KEY *key, uint key_count, uchar *record, Field *field,
KEY_PART_INFO *key_part;
*key_length=0;
for (j=0, key_part=key_info->key_part ;
- j < key_info->key_parts ;
+ j < key_info->user_defined_key_parts ;
j++, key_part++)
{
if (key_part->offset == fieldpos)
@@ -123,8 +126,8 @@ void key_copy(uchar *to_key, uchar *from_record, KEY *key_info,
{
if (key_part->null_bit)
{
- *to_key++= test(from_record[key_part->null_offset] &
- key_part->null_bit);
+ *to_key++= MY_TEST(from_record[key_part->null_offset] &
+ key_part->null_bit);
key_length--;
if (to_key[-1])
{
@@ -132,7 +135,7 @@ void key_copy(uchar *to_key, uchar *from_record, KEY *key_info,
Don't copy data for null values
The -1 below is to subtract the null byte which is already handled
*/
- length= min(key_length, (uint) key_part->store_length-1);
+ length= min<uint>(key_length, key_part->store_length-1);
if (with_zerofill)
bzero((char*) to_key, length);
continue;
@@ -142,7 +145,7 @@ void key_copy(uchar *to_key, uchar *from_record, KEY *key_info,
key_part->key_part_flag & HA_VAR_LENGTH_PART)
{
key_length-= HA_KEY_BLOB_LENGTH;
- length= min(key_length, key_part->length);
+ length= min<uint>(key_length, key_part->length);
uint bytes= key_part->field->get_key_image(to_key, length, Field::itRAW);
if (with_zerofill && bytes < length)
bzero((char*) to_key + bytes, length - bytes);
@@ -150,7 +153,7 @@ void key_copy(uchar *to_key, uchar *from_record, KEY *key_info,
}
else
{
- length= min(key_length, key_part->length);
+ length= min<uint>(key_length, key_part->length);
Field *field= key_part->field;
CHARSET_INFO *cs= field->charset();
uint bytes= field->get_key_image(to_key, length, Field::itRAW);
@@ -202,7 +205,7 @@ void key_restore(uchar *to_record, uchar *from_key, KEY *key_info,
Don't copy data for null bytes
The -1 below is to subtract the null byte which is already handled
*/
- length= min(key_length, (uint) key_part->store_length-1);
+ length= min<uint>(key_length, key_part->store_length-1);
continue;
}
}
@@ -244,7 +247,7 @@ void key_restore(uchar *to_record, uchar *from_key, KEY *key_info,
my_ptrdiff_t ptrdiff= to_record - field->table->record[0];
field->move_field_offset(ptrdiff);
key_length-= HA_KEY_BLOB_LENGTH;
- length= min(key_length, key_part->length);
+ length= min<uint>(key_length, key_part->length);
old_map= dbug_tmp_use_all_columns(field->table, field->table->write_set);
field->set_key_image(from_key, length);
dbug_tmp_restore_column_map(field->table->write_set, old_map);
@@ -253,7 +256,7 @@ void key_restore(uchar *to_record, uchar *from_key, KEY *key_info,
}
else
{
- length= min(key_length, key_part->length);
+ length= min<uint>(key_length, key_part->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);
@@ -297,8 +300,8 @@ bool key_cmp_if_same(TABLE *table,const uchar *key,uint idx,uint key_length)
if (key_part->null_bit)
{
- if (*key != test(table->record[0][key_part->null_offset] &
- key_part->null_bit))
+ if (*key != MY_TEST(table->record[0][key_part->null_offset] &
+ key_part->null_bit))
return 1;
if (*key)
continue;
@@ -410,18 +413,17 @@ void field_unpack(String *to, Field *field, const uchar *rec, uint max_length,
@param
table Table to use
@param
- idx Key number
+ key Key
*/
-void key_unpack(String *to,TABLE *table,uint idx)
+void key_unpack(String *to, TABLE *table, KEY *key)
{
- KEY_PART_INFO *key_part,*key_part_end;
my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->read_set);
DBUG_ENTER("key_unpack");
to->length(0);
- for (key_part=table->key_info[idx].key_part,key_part_end=key_part+
- table->key_info[idx].key_parts ;
+ KEY_PART_INFO *key_part_end= key->key_part + key->user_defined_key_parts;
+ for (KEY_PART_INFO *key_part= key->key_part;
key_part < key_part_end;
key_part++)
{
@@ -431,12 +433,12 @@ void key_unpack(String *to,TABLE *table,uint idx)
{
if (table->record[0][key_part->null_offset] & key_part->null_bit)
{
- to->append(STRING_WITH_LEN("NULL"));
- continue;
+ to->append(STRING_WITH_LEN("NULL"));
+ continue;
}
}
field_unpack(to, key_part->field, table->record[0], key_part->length,
- test(key_part->key_part_flag & HA_PART_KEY_SEG));
+ MY_TEST(key_part->key_part_flag & HA_PART_KEY_SEG));
}
dbug_tmp_restore_column_map(table->read_set, old_map);
DBUG_VOID_RETURN;
@@ -574,7 +576,7 @@ int key_rec_cmp(void *key_p, uchar *first_rec, uchar *second_rec)
/* loop over all given keys */
do
{
- key_parts= key_info->key_parts;
+ key_parts= key_info->user_defined_key_parts;
key_part= key_info->key_part;
key_part_num= 0;
@@ -586,8 +588,8 @@ int key_rec_cmp(void *key_p, uchar *first_rec, uchar *second_rec)
if (key_part->null_bit)
{
/* The key_part can contain NULL values */
- bool first_is_null= field->is_null_in_record_with_offset(first_diff);
- bool sec_is_null= field->is_null_in_record_with_offset(sec_diff);
+ bool first_is_null= field->is_real_null(first_diff);
+ bool sec_is_null= field->is_real_null(sec_diff);
/*
NULL is smaller then everything so if first is NULL and the other
not then we know that we should return -1 and for the opposite
diff --git a/sql/key.h b/sql/key.h
index f5f9a19178c..47b981f5298 100644
--- a/sql/key.h
+++ b/sql/key.h
@@ -32,7 +32,7 @@ void key_copy(uchar *to_key, uchar *from_record, KEY *key_info, uint key_length,
void key_restore(uchar *to_record, uchar *from_key, KEY *key_info,
uint key_length);
bool key_cmp_if_same(TABLE *form,const uchar *key,uint index,uint key_length);
-void key_unpack(String *to,TABLE *form,uint index);
+void key_unpack(String *to, TABLE *table, KEY *key);
void field_unpack(String *to, Field *field, const uchar *rec, uint max_length,
bool prefix_key);
bool is_key_used(TABLE *table, uint idx, const MY_BITMAP *fields);
diff --git a/sql/keycaches.cc b/sql/keycaches.cc
index 26a39808c56..a559c99bbd8 100644
--- a/sql/keycaches.cc
+++ b/sql/keycaches.cc
@@ -20,6 +20,7 @@
****************************************************************************/
NAMED_ILIST key_caches;
+NAMED_ILIST rpl_filters;
/**
ilink (intrusive list element) with a name
@@ -66,6 +67,23 @@ uchar* find_named(I_List<NAMED_ILINK> *list, const char *name, uint length,
}
+bool NAMED_ILIST::delete_element(const char *name, uint length, void (*free_element)(const char *name, uchar*))
+{
+ I_List_iterator<NAMED_ILINK> it(*this);
+ NAMED_ILINK *element;
+ DBUG_ENTER("NAMED_ILIST::delete_element");
+ while ((element= it++))
+ {
+ if (element->cmp(name, length))
+ {
+ (*free_element)(element->name, element->data);
+ delete element;
+ DBUG_RETURN(0);
+ }
+ }
+ DBUG_RETURN(1);
+}
+
void NAMED_ILIST::delete_elements(void (*free_element)(const char *name, uchar*))
{
NAMED_ILINK *element;
@@ -159,7 +177,56 @@ bool process_key_caches(process_key_cache_t func, void *param)
return res != 0;
}
-#ifdef HAVE_EXPLICIT_TEMPLATE_INSTANTIATION
-template class I_List_iterator<NAMED_ILINK>;
-#endif
+/* Rpl_filter functions */
+
+LEX_STRING default_rpl_filter_base= {C_STRING_WITH_LEN("")};
+
+Rpl_filter *get_rpl_filter(LEX_STRING *filter_name)
+{
+ if (!filter_name->length)
+ filter_name= &default_rpl_filter_base;
+ return ((Rpl_filter*) find_named(&rpl_filters,
+ filter_name->str, filter_name->length, 0));
+}
+
+Rpl_filter *create_rpl_filter(const char *name, uint length)
+{
+ Rpl_filter *filter;
+ DBUG_ENTER("create_rpl_filter");
+ DBUG_PRINT("enter",("name: %.*s", length, name));
+
+ filter= new Rpl_filter;
+ if (filter)
+ {
+ if (!new NAMED_ILINK(&rpl_filters, name, length, (uchar*) filter))
+ {
+ delete filter;
+ filter= 0;
+ }
+ }
+ DBUG_RETURN(filter);
+}
+
+Rpl_filter *get_or_create_rpl_filter(const char *name, uint length)
+{
+ LEX_STRING rpl_filter_name;
+ Rpl_filter *filter;
+
+ rpl_filter_name.str= (char *) name;
+ rpl_filter_name.length= length;
+ if (!(filter= get_rpl_filter(&rpl_filter_name)))
+ filter= create_rpl_filter(name, length);
+ return filter;
+}
+
+void free_rpl_filter(const char *name, Rpl_filter *filter)
+{
+ delete filter;
+ filter= 0;
+}
+
+void free_all_rpl_filters()
+{
+ rpl_filters.delete_elements((void (*)(const char*, uchar*)) free_rpl_filter);
+}
diff --git a/sql/keycaches.h b/sql/keycaches.h
index 04d3f6145e7..32537339e2e 100644
--- a/sql/keycaches.h
+++ b/sql/keycaches.h
@@ -18,6 +18,7 @@
#include "sql_list.h"
#include <keycache.h>
+#include <rpl_filter.h>
extern "C"
{
@@ -30,8 +31,10 @@ class NAMED_ILIST: public I_List<NAMED_ILINK>
{
public:
void delete_elements(void (*free_element)(const char*, uchar*));
+ bool delete_element(const char *name, uint length, void (*free_element)(const char*, uchar*));
};
+/* For key cache */
extern LEX_STRING default_key_cache_base;
extern KEY_CACHE zero_key_cache;
extern NAMED_ILIST key_caches;
@@ -42,4 +45,14 @@ KEY_CACHE *get_or_create_key_cache(const char *name, uint length);
void free_key_cache(const char *name, KEY_CACHE *key_cache);
bool process_key_caches(process_key_cache_t func, void *param);
+/* For Rpl_filter */
+extern LEX_STRING default_rpl_filter_base;
+extern NAMED_ILIST rpl_filters;
+
+Rpl_filter *create_rpl_filter(const char *name, uint length);
+Rpl_filter *get_rpl_filter(LEX_STRING *filter_name);
+Rpl_filter *get_or_create_rpl_filter(const char *name, uint length);
+void free_rpl_filter(const char *name, Rpl_filter *filter);
+void free_all_rpl_filters(void);
+
#endif /* KEYCACHES_INCLUDED */
diff --git a/sql/lex.h b/sql/lex.h
index 65411402f6a..a272504c0f2 100644
--- a/sql/lex.h
+++ b/sql/lex.h
@@ -1,7 +1,8 @@
#ifndef LEX_INCLUDED
#define LEX_INCLUDED
-/* Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
+/* Copyright (c) 2000, 2010, Oracle and/or its affiliates.
+ Copyright (c) 2009, 2015, MariaDB
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
@@ -59,6 +60,7 @@ static SYMBOL symbols[] = {
{ "ACCESSIBLE", SYM(ACCESSIBLE_SYM)},
{ "ACTION", SYM(ACTION)},
{ "ADD", SYM(ADD)},
+ { "ADMIN", SYM(ADMIN_SYM)},
{ "AFTER", SYM(AFTER_SYM)},
{ "AGAINST", SYM(AGAINST)},
{ "AGGREGATE", SYM(AGGREGATE_SYM)},
@@ -77,6 +79,7 @@ static SYMBOL symbols[] = {
{ "AUTHORS", SYM(AUTHORS_SYM)},
{ "AUTO_INCREMENT", SYM(AUTO_INC)},
{ "AUTOEXTEND_SIZE", SYM(AUTOEXTEND_SIZE_SYM)},
+ { "AUTO", SYM(AUTO_SYM)},
{ "AVG", SYM(AVG_SYM)},
{ "AVG_ROW_LENGTH", SYM(AVG_ROW_LENGTH)},
{ "BACKUP", SYM(BACKUP_SYM)},
@@ -123,11 +126,10 @@ static SYMBOL symbols[] = {
{ "COLUMN_NAME", SYM(COLUMN_NAME_SYM)},
{ "COLUMNS", SYM(COLUMNS)},
{ "COLUMN_ADD", SYM(COLUMN_ADD_SYM)},
+ { "COLUMN_CHECK", SYM(COLUMN_CHECK_SYM)},
{ "COLUMN_CREATE", SYM(COLUMN_CREATE_SYM)},
{ "COLUMN_DELETE", SYM(COLUMN_DELETE_SYM)},
- { "COLUMN_EXISTS", SYM(COLUMN_EXISTS_SYM)},
{ "COLUMN_GET", SYM(COLUMN_GET_SYM)},
- { "COLUMN_LIST", SYM(COLUMN_LIST_SYM)},
{ "COMMENT", SYM(COMMENT_SYM)},
{ "COMMIT", SYM(COMMIT_SYM)},
{ "COMMITTED", SYM(COMMITTED_SYM)},
@@ -151,7 +153,10 @@ static SYMBOL symbols[] = {
{ "CREATE", SYM(CREATE)},
{ "CROSS", SYM(CROSS)},
{ "CUBE", SYM(CUBE_SYM)},
+ { "CURRENT", SYM(CURRENT_SYM)},
{ "CURRENT_DATE", SYM(CURDATE)},
+ { "CURRENT_POS", SYM(CURRENT_POS_SYM)},
+ { "CURRENT_ROLE", SYM(CURRENT_ROLE)},
{ "CURRENT_TIME", SYM(CURTIME)},
{ "CURRENT_TIMESTAMP", SYM(NOW_SYM)},
{ "CURRENT_USER", SYM(CURRENT_USER)},
@@ -181,6 +186,7 @@ static SYMBOL symbols[] = {
{ "DESCRIBE", SYM(DESCRIBE)},
{ "DES_KEY_FILE", SYM(DES_KEY_FILE)},
{ "DETERMINISTIC", SYM(DETERMINISTIC_SYM)},
+ { "DIAGNOSTICS", SYM(DIAGNOSTICS_SYM)},
{ "DIRECTORY", SYM(DIRECTORY_SYM)},
{ "DISABLE", SYM(DISABLE_SYM)},
{ "DISCARD", SYM(DISCARD)},
@@ -213,10 +219,12 @@ static SYMBOL symbols[] = {
{ "EVENTS", SYM(EVENTS_SYM)},
{ "EVERY", SYM(EVERY_SYM)},
{ "EXAMINED", SYM(EXAMINED_SYM)},
+ { "EXCHANGE", SYM(EXCHANGE_SYM)},
{ "EXECUTE", SYM(EXECUTE_SYM)},
{ "EXISTS", SYM(EXISTS)},
{ "EXIT", SYM(EXIT_SYM)},
{ "EXPANSION", SYM(EXPANSION_SYM)},
+ { "EXPORT", SYM(EXPORT_SYM)},
{ "EXPLAIN", SYM(DESCRIBE)},
{ "EXTENDED", SYM(EXTENDED_SYM)},
{ "EXTENT_SIZE", SYM(EXTENT_SIZE_SYM)},
@@ -245,6 +253,7 @@ static SYMBOL symbols[] = {
{ "GEOMETRY", SYM(GEOMETRY_SYM)},
{ "GEOMETRYCOLLECTION",SYM(GEOMETRYCOLLECTION)},
{ "GET_FORMAT", SYM(GET_FORMAT)},
+ { "GET", SYM(GET_SYM)},
{ "GLOBAL", SYM(GLOBAL_SYM)},
{ "GRANT", SYM(GRANT)},
{ "GRANTS", SYM(GRANTS)},
@@ -261,6 +270,7 @@ static SYMBOL symbols[] = {
{ "HOUR_MICROSECOND", SYM(HOUR_MICROSECOND_SYM)},
{ "HOUR_MINUTE", SYM(HOUR_MINUTE_SYM)},
{ "HOUR_SECOND", SYM(HOUR_SECOND_SYM)},
+ { "ID", SYM(ID_SYM)},
{ "IDENTIFIED", SYM(IDENTIFIED_SYM)},
{ "IF", SYM(IF)},
{ "IGNORE", SYM(IGNORE_SYM)},
@@ -330,6 +340,7 @@ static SYMBOL symbols[] = {
{ "LOW_PRIORITY", SYM(LOW_PRIORITY)},
{ "MASTER", SYM(MASTER_SYM)},
{ "MASTER_CONNECT_RETRY", SYM(MASTER_CONNECT_RETRY_SYM)},
+ { "MASTER_GTID_POS", SYM(MASTER_GTID_POS_SYM)},
{ "MASTER_HOST", SYM(MASTER_HOST_SYM)},
{ "MASTER_LOG_FILE", SYM(MASTER_LOG_FILE_SYM)},
{ "MASTER_LOG_POS", SYM(MASTER_LOG_POS_SYM)},
@@ -341,9 +352,12 @@ static SYMBOL symbols[] = {
{ "MASTER_SSL_CAPATH",SYM(MASTER_SSL_CAPATH_SYM)},
{ "MASTER_SSL_CERT", SYM(MASTER_SSL_CERT_SYM)},
{ "MASTER_SSL_CIPHER",SYM(MASTER_SSL_CIPHER_SYM)},
+ { "MASTER_SSL_CRL", SYM(MASTER_SSL_CRL_SYM)},
+ { "MASTER_SSL_CRLPATH",SYM(MASTER_SSL_CRLPATH_SYM)},
{ "MASTER_SSL_KEY", SYM(MASTER_SSL_KEY_SYM)},
{ "MASTER_SSL_VERIFY_SERVER_CERT", SYM(MASTER_SSL_VERIFY_SERVER_CERT_SYM)},
{ "MASTER_USER", SYM(MASTER_USER_SYM)},
+ { "MASTER_USE_GTID", SYM(MASTER_USE_GTID_SYM)},
{ "MASTER_HEARTBEAT_PERIOD", SYM(MASTER_HEARTBEAT_PERIOD_SYM)},
{ "MATCH", SYM(MATCH)},
{ "MAX_CONNECTIONS_PER_HOUR", SYM(MAX_CONNECTIONS_PER_HOUR)},
@@ -394,14 +408,15 @@ static SYMBOL symbols[] = {
{ "NOT", SYM(NOT_SYM)},
{ "NO_WRITE_TO_BINLOG", SYM(NO_WRITE_TO_BINLOG)},
{ "NULL", SYM(NULL_SYM)},
+ { "NUMBER", SYM(NUMBER_SYM)},
{ "NUMERIC", SYM(NUMERIC_SYM)},
{ "NVARCHAR", SYM(NVARCHAR_SYM)},
{ "OFFSET", SYM(OFFSET_SYM)},
{ "OLD_PASSWORD", SYM(OLD_PASSWORD)},
{ "ON", SYM(ON)},
{ "ONE", SYM(ONE_SYM)},
- { "ONE_SHOT", SYM(ONE_SHOT_SYM)},
{ "ONLINE", SYM(ONLINE_SYM)},
+ { "ONLY", SYM(ONLY_SYM)},
{ "OPEN", SYM(OPEN_SYM)},
{ "OPTIMIZE", SYM(OPTIMIZE)},
{ "OPTIONS", SYM(OPTIONS_SYM)},
@@ -480,15 +495,20 @@ static SYMBOL symbols[] = {
{ "RESTORE", SYM(RESTORE_SYM)},
{ "RESTRICT", SYM(RESTRICT)},
{ "RESUME", SYM(RESUME_SYM)},
+ { "RETURNED_SQLSTATE",SYM(RETURNED_SQLSTATE_SYM)},
{ "RETURN", SYM(RETURN_SYM)},
+ { "RETURNING", SYM(RETURNING_SYM)},
{ "RETURNS", SYM(RETURNS_SYM)},
+ { "REVERSE", SYM(REVERSE_SYM)},
{ "REVOKE", SYM(REVOKE)},
{ "RIGHT", SYM(RIGHT)},
{ "RLIKE", SYM(REGEXP)}, /* Like in mSQL2 */
+ { "ROLE", SYM(ROLE_SYM)},
{ "ROLLBACK", SYM(ROLLBACK_SYM)},
{ "ROLLUP", SYM(ROLLUP_SYM)},
{ "ROUTINE", SYM(ROUTINE_SYM)},
{ "ROW", SYM(ROW_SYM)},
+ { "ROW_COUNT", SYM(ROW_COUNT_SYM)},
{ "ROWS", SYM(ROWS_SYM)},
{ "ROW_FORMAT", SYM(ROW_FORMAT_SYM)},
{ "RTREE", SYM(RTREE_SYM)},
@@ -515,6 +535,8 @@ static SYMBOL symbols[] = {
{ "SIGNED", SYM(SIGNED_SYM)},
{ "SIMPLE", SYM(SIMPLE_SYM)},
{ "SLAVE", SYM(SLAVE)},
+ { "SLAVES", SYM(SLAVES)},
+ { "SLAVE_POS", SYM(SLAVE_POS_SYM)},
{ "SLOW", SYM(SLOW)},
{ "SNAPSHOT", SYM(SNAPSHOT_SYM)},
{ "SMALLINT", SYM(SMALLINT)},
@@ -549,6 +571,9 @@ static SYMBOL symbols[] = {
{ "START", SYM(START_SYM)},
{ "STARTING", SYM(STARTING)},
{ "STARTS", SYM(STARTS_SYM)},
+ { "STATS_AUTO_RECALC",SYM(STATS_AUTO_RECALC_SYM)},
+ { "STATS_PERSISTENT", SYM(STATS_PERSISTENT_SYM)},
+ { "STATS_SAMPLE_PAGES",SYM(STATS_SAMPLE_PAGES_SYM)},
{ "STATUS", SYM(STATUS_SYM)},
{ "STOP", SYM(STOP_SYM)},
{ "STORAGE", SYM(STORAGE_SYM)},
@@ -629,6 +654,7 @@ static SYMBOL symbols[] = {
{ "WAIT", SYM(WAIT_SYM)},
{ "WARNINGS", SYM(WARNINGS)},
{ "WEEK", SYM(WEEK_SYM)},
+ { "WEIGHT_STRING", SYM(WEIGHT_STRING_SYM)},
{ "WHEN", SYM(WHEN_SYM)},
{ "WHERE", SYM(WHERE)},
{ "WHILE", SYM(WHILE_SYM)},
diff --git a/sql/lock.cc b/sql/lock.cc
index 7747414ee48..07ea0b1f6dc 100644
--- a/sql/lock.cc
+++ b/sql/lock.cc
@@ -11,8 +11,8 @@
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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+ along with this program; if not, write to the Free Software Foundation,
+ 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */
/**
@@ -74,15 +74,14 @@
we are forced to use mysql_lock_merge.
*/
+#include <my_global.h>
#include "sql_priv.h"
#include "debug_sync.h"
-#include "unireg.h" // REQUIRED: for other includes
#include "lock.h"
#include "sql_base.h" // close_tables_for_reopen
#include "sql_parse.h" // is_log_table_write_query
#include "sql_acl.h" // SUPER_ACL
#include <hash.h>
-#include <assert.h>
#ifdef WITH_WSREP
#include "wsrep_mysqld.h"
@@ -97,7 +96,7 @@ extern HASH open_cache;
static int lock_external(THD *thd, TABLE **table,uint count);
static int unlock_external(THD *thd, TABLE **table,uint count);
-static void print_lock_error(int error, const char *);
+static void print_lock_error(int error, TABLE *);
/* Map the return value of thr_lock to an error from errmsg.txt */
static int thr_lock_errno_to_mysql[]=
@@ -305,22 +304,24 @@ bool mysql_lock_tables(THD *thd, MYSQL_LOCK *sql_lock, uint flags)
int rc= 1;
ulong timeout= (flags & MYSQL_LOCK_IGNORE_TIMEOUT) ?
LONG_TIMEOUT : thd->variables.lock_wait_timeout;
-
+ PSI_stage_info org_stage;
DBUG_ENTER("mysql_lock_tables(sql_lock)");
- thd_proc_info(thd, "System lock");
+ thd->enter_stage(&stage_system_lock, &org_stage, __func__, __FILE__,
+ __LINE__);
if (sql_lock->table_count && lock_external(thd, sql_lock->table,
sql_lock->table_count))
goto end;
- thd_proc_info(thd, "Table lock");
+ THD_STAGE_INFO(thd, stage_table_lock);
/* Copy the lock data array. thr_multi_lock() reorders its contents. */
- memcpy(sql_lock->locks + sql_lock->lock_count, sql_lock->locks,
- sql_lock->lock_count * sizeof(*sql_lock->locks));
+ memmove(sql_lock->locks + sql_lock->lock_count, sql_lock->locks,
+ sql_lock->lock_count * sizeof(*sql_lock->locks));
#ifdef WITH_WSREP
thd->lock_info.in_lock_tables= thd->in_lock_tables;
-#endif /* Lock on the copied half of the lock data array. */
+#endif
+
/* Lock on the copied half of the lock data array. */
rc= thr_lock_errno_to_mysql[(int) thr_multi_lock(sql_lock->locks +
sql_lock->lock_count,
@@ -330,7 +331,12 @@ bool mysql_lock_tables(THD *thd, MYSQL_LOCK *sql_lock, uint flags)
(void) unlock_external(thd, sql_lock->table, sql_lock->table_count);
end:
- thd_proc_info(thd, "After table lock");
+ THD_STAGE_INFO(thd, org_stage);
+#ifdef WITH_WSREP
+ thd_proc_info(thd, "mysql_lock_tables(): unlocking tables II");
+#else /* WITH_WSREP */
+ thd_proc_info(thd, 0);
+#endif /* WITH_WSREP */
if (thd->killed)
{
@@ -368,7 +374,7 @@ static int lock_external(THD *thd, TABLE **tables, uint count)
if ((error=(*tables)->file->ha_external_lock(thd,lock_type)))
{
- print_lock_error(error, (*tables)->file->table_type());
+ print_lock_error(error, *tables);
while (--i)
{
tables--;
@@ -683,7 +689,7 @@ static int unlock_external(THD *thd, TABLE **table,uint count)
if ((error=(*table)->file->ha_external_lock(thd, F_UNLCK)))
{
error_code=error;
- print_lock_error(error_code, (*table)->file->table_type());
+ print_lock_error(error_code, *table);
}
}
table++;
@@ -704,7 +710,7 @@ static int unlock_external(THD *thd, TABLE **table,uint count)
MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count, uint flags)
{
- uint i,tables,lock_count;
+ uint i,lock_count,table_count;
MYSQL_LOCK *sql_lock;
THR_LOCK_DATA **locks, **locks_buf;
TABLE **to, **table_buf;
@@ -713,16 +719,15 @@ MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count, uint flags)
DBUG_ASSERT((flags == GET_LOCK_UNLOCK) || (flags == GET_LOCK_STORE_LOCKS));
DBUG_PRINT("info", ("count %d", count));
- for (i=tables=lock_count=0 ; i < count ; i++)
+ for (i=lock_count=table_count=0 ; i < count ; i++)
{
TABLE *t= table_ptr[i];
-
if (t->s->tmp_table != NON_TRANSACTIONAL_TMP_TABLE &&
t->s->tmp_table != INTERNAL_TMP_TABLE)
{
- tables+= t->file->lock_count();
- lock_count++;
+ lock_count+= t->file->lock_count();
+ table_count++;
}
}
@@ -734,13 +739,13 @@ MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count, uint flags)
*/
if (!(sql_lock= (MYSQL_LOCK*)
my_malloc(sizeof(*sql_lock) +
- sizeof(THR_LOCK_DATA*) * tables * 2 +
- sizeof(table_ptr) * lock_count,
+ sizeof(THR_LOCK_DATA*) * lock_count * 2 +
+ sizeof(table_ptr) * table_count,
MYF(0))))
DBUG_RETURN(0);
locks= locks_buf= sql_lock->locks= (THR_LOCK_DATA**) (sql_lock + 1);
- to= table_buf= sql_lock->table= (TABLE**) (locks + tables * 2);
- sql_lock->table_count=lock_count;
+ to= table_buf= sql_lock->table= (TABLE**) (locks + lock_count * 2);
+ sql_lock->table_count= table_count;
for (i=0 ; i < count ; i++)
{
@@ -769,13 +774,14 @@ MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count, uint flags)
for ( ; locks_start != locks ; locks_start++)
{
(*locks_start)->debug_print_param= (void *) table;
+ (*locks_start)->m_psi= table->file->m_psi;
(*locks_start)->lock->name= table->alias.c_ptr();
(*locks_start)->org_type= (*locks_start)->type;
}
}
}
/*
- We do not use 'tables', because there are cases where store_lock()
+ We do not use 'lock_count', because there are cases where store_lock()
returns less locks than lock_count() claimed. This can happen when
a FLUSH TABLES tries to abort locks from a MERGE table of another
thread. When that thread has just opened the table, but not yet
@@ -789,6 +795,7 @@ MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count, uint flags)
And in the FLUSH case, the memory is released quickly anyway.
*/
sql_lock->lock_count= locks - locks_buf;
+ DBUG_ASSERT(sql_lock->lock_count <= lock_count);
DBUG_PRINT("info", ("sql_lock->table_count %d sql_lock->lock_count %d",
sql_lock->table_count, sql_lock->lock_count));
DBUG_RETURN(sql_lock);
@@ -801,7 +808,6 @@ MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count, uint flags)
@param thd Thread handle.
@param db The database name.
- This function cannot be called while holding LOCK_open mutex.
To avoid deadlocks, we do not try to obtain exclusive metadata
locks in LOCK TABLES mode, since in this mode there may be
other metadata locks already taken by the current connection,
@@ -853,9 +859,7 @@ bool lock_schema_name(THD *thd, const char *db)
@param name Object name in the schema.
This function assumes that no metadata locks were acquired
- before calling it. Additionally, it cannot be called while
- holding LOCK_open mutex. Both these invariants are enforced by
- asserts in MDL_context::acquire_locks().
+ before calling it. It is enforced by asserts in MDL_context::acquire_locks().
To avoid deadlocks, we do not try to obtain exclusive metadata
locks in LOCK TABLES mode, since in this mode there may be
other metadata locks already taken by the current connection,
@@ -874,6 +878,8 @@ bool lock_object_name(THD *thd, MDL_key::enum_mdl_namespace mdl_type,
MDL_request schema_request;
MDL_request mdl_request;
+ DBUG_ASSERT(ok_for_lower_case_names(db));
+
if (thd->locked_tables_mode)
{
my_message(ER_LOCK_OR_ACTIVE_TRANSACTION,
@@ -905,7 +911,7 @@ bool lock_object_name(THD *thd, MDL_key::enum_mdl_namespace mdl_type,
}
-static void print_lock_error(int error, const char *table)
+static void print_lock_error(int error, TABLE *table)
{
int textno;
DBUG_ENTER("print_lock_error");
@@ -921,17 +927,15 @@ static void print_lock_error(int error, const char *table)
textno=ER_LOCK_DEADLOCK;
break;
case HA_ERR_WRONG_COMMAND:
- textno=ER_ILLEGAL_HA;
- break;
+ my_error(ER_ILLEGAL_HA, MYF(0), table->file->table_type(),
+ table->s->db.str, table->s->table_name.str);
+ DBUG_VOID_RETURN;
default:
textno=ER_CANT_LOCK;
break;
}
- if ( textno == ER_ILLEGAL_HA )
- my_error(textno, MYF(ME_BELL+ME_OLDWIN+ME_WAITTANG), table);
- else
- my_error(textno, MYF(ME_BELL+ME_OLDWIN+ME_WAITTANG), error);
+ my_error(textno, MYF(0), error);
DBUG_VOID_RETURN;
}
diff --git a/sql/log.cc b/sql/log.cc
index 9194838d424..745b7747aab 100644
--- a/sql/log.cc
+++ b/sql/log.cc
@@ -25,7 +25,7 @@
Abort logging when we get an error in reading or writing log files
*/
-#include "my_global.h" /* NO_EMBEDDED_ACCESS_CHECKS */
+#include <my_global.h> /* NO_EMBEDDED_ACCESS_CHECKS */
#include "sql_priv.h"
#include "log.h"
#include "sql_base.h" // open_log_table
@@ -40,6 +40,7 @@
#include "rpl_rli.h"
#include "sql_audit.h"
#include "log_slow.h"
+#include "mysqld.h"
#include <my_dir.h>
#include <stdarg.h>
@@ -56,6 +57,7 @@
#endif /* WITH_WSREP */
#include "debug_sync.h"
#include "sql_show.h"
+#include "my_pthread.h"
/* max size of the log message */
#define MAX_LOG_BUFFER_SIZE 1024
@@ -74,6 +76,8 @@ static int binlog_init(void *p);
static int binlog_close_connection(handlerton *hton, THD *thd);
static int binlog_savepoint_set(handlerton *hton, THD *thd, void *sv);
static int binlog_savepoint_rollback(handlerton *hton, THD *thd, void *sv);
+static bool binlog_savepoint_rollback_can_release_mdl(handlerton *hton,
+ THD *thd);
static int binlog_commit(handlerton *hton, THD *thd, bool all);
static int binlog_rollback(handlerton *hton, THD *thd, bool all);
static int binlog_prepare(handlerton *hton, THD *thd, bool all);
@@ -89,10 +93,14 @@ ulong opt_binlog_dbug_fsync_sleep= 0;
#endif
mysql_mutex_t LOCK_prepare_ordered;
+mysql_cond_t COND_prepare_ordered;
mysql_mutex_t LOCK_commit_ordered;
static ulonglong binlog_status_var_num_commits;
static ulonglong binlog_status_var_num_group_commits;
+static ulonglong binlog_status_group_commit_trigger_count;
+static ulonglong binlog_status_group_commit_trigger_lock_wait;
+static ulonglong binlog_status_group_commit_trigger_timeout;
static char binlog_snapshot_file[FN_REFLEN];
static ulonglong binlog_snapshot_position;
@@ -102,6 +110,12 @@ static SHOW_VAR binlog_status_vars_detail[]=
(char *)&binlog_status_var_num_commits, SHOW_LONGLONG},
{"group_commits",
(char *)&binlog_status_var_num_group_commits, SHOW_LONGLONG},
+ {"group_commit_trigger_count",
+ (char *)&binlog_status_group_commit_trigger_count, SHOW_LONGLONG},
+ {"group_commit_trigger_lock_wait",
+ (char *)&binlog_status_group_commit_trigger_lock_wait, SHOW_LONGLONG},
+ {"group_commit_trigger_timeout",
+ (char *)&binlog_status_group_commit_trigger_timeout, SHOW_LONGLONG},
{"snapshot_file",
(char *)&binlog_snapshot_file, SHOW_CHAR},
{"snapshot_position",
@@ -109,6 +123,18 @@ static SHOW_VAR binlog_status_vars_detail[]=
{NullS, NullS, SHOW_LONG}
};
+/*
+ Variables for the binlog background thread.
+ Protected by the MYSQL_BIN_LOG::LOCK_binlog_background_thread mutex.
+ */
+static bool binlog_background_thread_started= false;
+static bool binlog_background_thread_stop= false;
+static MYSQL_BIN_LOG::xid_count_per_binlog *
+ binlog_background_thread_queue= NULL;
+
+static bool start_binlog_background_thread();
+
+static rpl_binlog_state rpl_global_gtid_binlog_state;
/**
purge logs, master and slave sides both, related error code
@@ -160,9 +186,9 @@ public:
virtual bool handle_condition(THD *thd,
uint sql_errno,
const char* sql_state,
- MYSQL_ERROR::enum_warning_level level,
+ Sql_condition::enum_warning_level level,
const char* msg,
- MYSQL_ERROR ** cond_hdl);
+ Sql_condition ** cond_hdl);
const char *message() const { return m_message; }
};
@@ -170,9 +196,9 @@ bool
Silence_log_table_errors::handle_condition(THD *,
uint,
const char*,
- MYSQL_ERROR::enum_warning_level,
+ Sql_condition::enum_warning_level,
const char* msg,
- MYSQL_ERROR ** cond_hdl)
+ Sql_condition ** cond_hdl)
{
*cond_hdl= NULL;
strmake_buf(m_message, msg);
@@ -482,6 +508,14 @@ public:
*/
bool using_xa;
my_xid xa_xid;
+ bool need_unlog;
+ /*
+ Id of binlog that transaction was written to; only needed if need_unlog is
+ true.
+ */
+ ulong binlog_id;
+ /* Set if we get an error during commit that must be returned from unlog(). */
+ bool delayed_error;
private:
@@ -490,10 +524,24 @@ private:
};
handlerton *binlog_hton;
+#ifdef WITH_WSREP
+extern handlerton *wsrep_hton;
+#endif
-#if WITH_WSREP
-/* the functions below depend on the definition of binlog_cache_manager class,
- * so have to stay in this unit. */
+bool LOGGER::is_log_table_enabled(uint log_table_type)
+{
+ switch (log_table_type) {
+ case QUERY_LOG_SLOW:
+ return (table_log_handler != NULL) && opt_slow_log;
+ case QUERY_LOG_GENERAL:
+ return (table_log_handler != NULL) && opt_log ;
+ default:
+ DBUG_ASSERT(0);
+ return FALSE; /* make compiler happy */
+ }
+}
+
+#ifdef WITH_WSREP
IO_CACHE * get_trans_log(THD * thd)
{
binlog_cache_mngr *cache_mngr = (binlog_cache_mngr*)
@@ -501,7 +549,7 @@ IO_CACHE * get_trans_log(THD * thd)
if (cache_mngr)
{
return cache_mngr->get_binlog_cache_log(true);
- }
+ }
else
{
WSREP_DEBUG("binlog cache not initialized, conn :%ld", thd->thread_id);
@@ -509,6 +557,7 @@ IO_CACHE * get_trans_log(THD * thd)
}
}
+
bool wsrep_trans_cache_is_empty(THD *thd)
{
binlog_cache_mngr *const cache_mngr=
@@ -520,7 +569,6 @@ void thd_binlog_flush_pending_rows_event(THD *thd, bool stmt_end)
{
thd->binlog_flush_pending_rows_event(stmt_end);
}
-
void thd_binlog_trx_reset(THD * thd)
{
/*
@@ -530,8 +578,16 @@ void thd_binlog_trx_reset(THD * thd)
{
binlog_cache_mngr *const cache_mngr=
(binlog_cache_mngr*) thd_get_ha_data(thd, binlog_hton);
- if (cache_mngr) cache_mngr->reset(TRUE, TRUE);
- }
+ if (cache_mngr)
+ {
+ cache_mngr->reset(false, true);
+ if (!cache_mngr->stmt_cache.empty())
+ {
+ WSREP_DEBUG("pending events in stmt cache, sql: %s", thd->query());
+ cache_mngr->stmt_cache.reset();
+ }
+ }
+ }
thd->clear_binlog_table_maps();
}
@@ -542,51 +598,55 @@ void thd_binlog_rollback_stmt(THD * thd)
(binlog_cache_mngr*) thd_get_ha_data(thd, binlog_hton);
if (cache_mngr) cache_mngr->trx_cache.set_prev_position(MY_OFF_T_UNDEF);
}
-#endif /* WITH_WSREP */
-bool LOGGER::is_log_table_enabled(uint log_table_type)
-{
- switch (log_table_type) {
- case QUERY_LOG_SLOW:
- return (table_log_handler != NULL) && opt_slow_log;
- case QUERY_LOG_GENERAL:
- return (table_log_handler != NULL) && opt_log ;
- default:
- DBUG_ASSERT(0);
- return FALSE; /* make compiler happy */
- }
-}
+#endif
+
+/**
+ Check if a given table is opened log table
+
+ @param table Table to check
+ @param check_if_opened Only fail if it's a log table in use
+ @param error_msg String to put in error message if not ok.
+ No error message if 0
+ @return 0 ok
+ @return # Type of log file
+ */
-/* Check if a given table is opened log table */
-int check_if_log_table(size_t db_len, const char *db, size_t table_name_len,
- const char *table_name, bool check_if_opened)
+int check_if_log_table(const TABLE_LIST *table,
+ bool check_if_opened,
+ const char *error_msg)
{
- if (db_len == 5 &&
- !(lower_case_table_names ?
- my_strcasecmp(system_charset_info, db, "mysql") :
- strcmp(db, "mysql")))
+ int result= 0;
+ if (table->db_length == 5 &&
+ !my_strcasecmp(table_alias_charset, table->db, "mysql"))
{
- if (table_name_len == 11 && !(lower_case_table_names ?
- my_strcasecmp(system_charset_info,
- table_name, "general_log") :
- strcmp(table_name, "general_log")))
+ const char *table_name= table->table_name;
+
+ if (table->table_name_length == 11 &&
+ !my_strcasecmp(table_alias_charset, table_name, "general_log"))
{
- if (!check_if_opened || logger.is_log_table_enabled(QUERY_LOG_GENERAL))
- return QUERY_LOG_GENERAL;
- return 0;
+ result= QUERY_LOG_GENERAL;
+ goto end;
}
- if (table_name_len == 8 && !(lower_case_table_names ?
- my_strcasecmp(system_charset_info, table_name, "slow_log") :
- strcmp(table_name, "slow_log")))
+ if (table->table_name_length == 8 &&
+ !my_strcasecmp(table_alias_charset, table_name, "slow_log"))
{
- if (!check_if_opened || logger.is_log_table_enabled(QUERY_LOG_SLOW))
- return QUERY_LOG_SLOW;
- return 0;
+ result= QUERY_LOG_SLOW;
+ goto end;
}
}
return 0;
+
+end:
+ if (!check_if_opened || logger.is_log_table_enabled(result))
+ {
+ if (error_msg)
+ my_error(ER_BAD_LOG_STATEMENT, MYF(0), error_msg);
+ return result;
+ }
+ return 0;
}
@@ -721,7 +781,8 @@ bool Log_to_csv_event_handler::
/* do a write */
if (table->field[1]->store(user_host, user_host_len, client_cs) ||
table->field[2]->store((longlong) thread_id, TRUE) ||
- table->field[3]->store((longlong) server_id, TRUE) ||
+ table->field[3]->store((longlong) global_system_variables.server_id,
+ TRUE) ||
table->field[4]->store(command_type, command_type_len, client_cs))
goto err;
@@ -816,8 +877,8 @@ bool Log_to_csv_event_handler::
Open_tables_backup open_tables_backup;
CHARSET_INFO *client_cs= thd->variables.character_set_client;
bool save_time_zone_used;
- long query_time= (long) min(query_utime/1000000, TIME_MAX_VALUE_SECONDS);
- long lock_time= (long) min(lock_utime/1000000, TIME_MAX_VALUE_SECONDS);
+ long query_time= (long) MY_MIN(query_utime/1000000, TIME_MAX_VALUE_SECONDS);
+ long lock_time= (long) MY_MIN(lock_utime/1000000, TIME_MAX_VALUE_SECONDS);
long query_time_micro= (long) (query_utime % 1000000);
long lock_time_micro= (long) (lock_utime % 1000000);
@@ -879,10 +940,10 @@ bool Log_to_csv_event_handler::
if (table->field[3]->store_time(&t))
goto err;
/* rows_sent */
- if (table->field[4]->store((longlong) thd->sent_row_count, TRUE))
+ if (table->field[4]->store((longlong) thd->get_sent_row_count(), TRUE))
goto err;
/* rows_examined */
- if (table->field[5]->store((longlong) thd->examined_row_count, TRUE))
+ if (table->field[5]->store((longlong) thd->get_examined_row_count(), TRUE))
goto err;
/* fill database field */
@@ -918,7 +979,7 @@ bool Log_to_csv_event_handler::
table->field[8]->set_notnull();
}
- if (table->field[9]->store((longlong) server_id, TRUE))
+ if (table->field[9]->store((longlong)global_system_variables.server_id, TRUE))
goto err;
table->field[9]->set_notnull();
@@ -930,6 +991,9 @@ bool Log_to_csv_event_handler::
if (table->field[10]->store(sql_text, sql_text_len, client_cs) < 0)
goto err;
+ if (table->field[11]->store((longlong) thd->thread_id, TRUE))
+ goto err;
+
/* log table entries are not replicated */
if (table->file->ha_write_row(table->record[0]))
goto err;
@@ -1309,8 +1373,8 @@ bool LOGGER::slow_log_print(THD *thd, const char *query, uint query_length,
if (!query)
{
is_command= TRUE;
- query= command_name[thd->command].str;
- query_length= command_name[thd->command].length;
+ query= command_name[thd->get_command()].str;
+ query_length= command_name[thd->get_command()].length;
}
for (current_handler= slow_log_handler_list; *current_handler ;)
@@ -1655,6 +1719,8 @@ int binlog_init(void *p)
binlog_hton->close_connection= binlog_close_connection;
binlog_hton->savepoint_set= binlog_savepoint_set;
binlog_hton->savepoint_rollback= binlog_savepoint_rollback;
+ binlog_hton->savepoint_rollback_can_release_mdl=
+ binlog_savepoint_rollback_can_release_mdl;
binlog_hton->commit= binlog_commit;
binlog_hton->rollback= binlog_rollback;
binlog_hton->prepare= binlog_prepare;
@@ -1663,15 +1729,36 @@ int binlog_init(void *p)
return 0;
}
+#ifdef WITH_WSREP
+#include "wsrep_binlog.h"
+#endif /* WITH_WSREP */
static int binlog_close_connection(handlerton *hton, THD *thd)
{
+ DBUG_ENTER("binlog_close_connection");
binlog_cache_mngr *const cache_mngr=
(binlog_cache_mngr*) thd_get_ha_data(thd, binlog_hton);
+#ifdef WITH_WSREP
+ if (cache_mngr && !cache_mngr->trx_cache.empty()) {
+ IO_CACHE* cache= get_trans_log(thd);
+ uchar *buf;
+ size_t len=0;
+ wsrep_write_cache_buf(cache, &buf, &len);
+ WSREP_WARN("binlog trx cache not empty (%lu bytes) @ connection close %lu",
+ len, thd->thread_id);
+ if (len > 0) wsrep_dump_rbr_buf(thd, buf, len);
+
+ cache = cache_mngr->get_binlog_cache_log(false);
+ wsrep_write_cache_buf(cache, &buf, &len);
+ WSREP_WARN("binlog stmt cache not empty (%lu bytes) @ connection close %lu",
+ len, thd->thread_id);
+ if (len > 0) wsrep_dump_rbr_buf(thd, buf, len);
+ }
+#endif /* WITH_WSREP */
DBUG_ASSERT(cache_mngr->trx_cache.empty() && cache_mngr->stmt_cache.empty());
thd_set_ha_data(thd, binlog_hton, NULL);
cache_mngr->~binlog_cache_mngr();
my_free(cache_mngr);
- return 0;
+ DBUG_RETURN(0);
}
/*
@@ -1699,6 +1786,7 @@ static int binlog_close_connection(handlerton *hton, THD *thd)
contain updates to non-transactional tables. Or it can be a flush of
a statement cache.
*/
+
static int
binlog_flush_cache(THD *thd, binlog_cache_mngr *cache_mngr,
Log_event *end_ev, bool all, bool using_stmt,
@@ -1706,6 +1794,7 @@ binlog_flush_cache(THD *thd, binlog_cache_mngr *cache_mngr,
{
int error= 0;
DBUG_ENTER("binlog_flush_cache");
+ DBUG_PRINT("enter", ("end_ev: %p", end_ev));
if ((using_stmt && !cache_mngr->stmt_cache.empty()) ||
(using_trx && !cache_mngr->trx_cache.empty()))
@@ -1729,6 +1818,20 @@ binlog_flush_cache(THD *thd, binlog_cache_mngr *cache_mngr,
end_ev, all,
using_stmt, using_trx);
}
+ else
+ {
+ /*
+ This can happen in row-format binlog with something like
+ BEGIN; INSERT INTO nontrans_table; INSERT IGNORE INTO trans_table;
+ The nontrans_table is written directly into the binlog before commit,
+ and if the trans_table is ignored there will be no rows to write when
+ we get here.
+
+ So there is no work to do. Therefore, we will not increment any XID
+ count, so we must not decrement any XID count in unlog().
+ */
+ cache_mngr->need_unlog= 0;
+ }
cache_mngr->reset(using_stmt, using_trx);
DBUG_ASSERT((!using_stmt || cache_mngr->stmt_cache.empty()) &&
@@ -1750,9 +1853,18 @@ static inline int
binlog_commit_flush_stmt_cache(THD *thd, bool all,
binlog_cache_mngr *cache_mngr)
{
+ DBUG_ENTER("binlog_commit_flush_stmt_cache");
+#ifdef WITH_WSREP
+ if (thd->wsrep_mysql_replicated > 0)
+ {
+ WSREP_DEBUG("avoiding binlog_commit_flush_trx_cache: %d", thd->wsrep_mysql_replicated);
+ return 0;
+ }
+#endif
+
Query_log_event end_evt(thd, STRING_WITH_LEN("COMMIT"),
FALSE, TRUE, TRUE, 0);
- return (binlog_flush_cache(thd, cache_mngr, &end_evt, all, TRUE, FALSE));
+ DBUG_RETURN(binlog_flush_cache(thd, cache_mngr, &end_evt, all, TRUE, FALSE));
}
/**
@@ -1767,16 +1879,10 @@ binlog_commit_flush_stmt_cache(THD *thd, bool all,
static inline int
binlog_commit_flush_trx_cache(THD *thd, bool all, binlog_cache_mngr *cache_mngr)
{
-#ifdef WITH_WSREP
- if (thd->wsrep_mysql_replicated > 0)
- {
- WSREP_DEBUG("avoiding binlog_commit_flush_trx_cache: %d", thd->wsrep_mysql_replicated);
- return 0;
- }
-#endif
+ DBUG_ENTER("binlog_commit_flush_trx_cache");
Query_log_event end_evt(thd, STRING_WITH_LEN("COMMIT"),
TRUE, TRUE, TRUE, 0);
- return (binlog_flush_cache(thd, cache_mngr, &end_evt, all, FALSE, TRUE));
+ DBUG_RETURN(binlog_flush_cache(thd, cache_mngr, &end_evt, all, FALSE, TRUE));
}
/**
@@ -1894,6 +2000,32 @@ static int binlog_prepare(handlerton *hton, THD *thd, bool all)
return 0;
}
+/*
+ We flush the cache wrapped in a beging/rollback if:
+ . aborting a single or multi-statement transaction and;
+ . the OPTION_KEEP_LOG is active or;
+ . the format is STMT and a non-trans table was updated or;
+ . the format is MIXED and a temporary non-trans table was
+ updated or;
+ . the format is MIXED, non-trans table was updated and
+ aborting a single statement transaction;
+*/
+static bool trans_cannot_safely_rollback(THD *thd, bool all)
+{
+ binlog_cache_mngr *const cache_mngr=
+ (binlog_cache_mngr*) thd_get_ha_data(thd, binlog_hton);
+
+ return ((thd->variables.option_bits & OPTION_KEEP_LOG) ||
+ (trans_has_updated_non_trans_table(thd) &&
+ WSREP_FORMAT(thd->variables.binlog_format) == BINLOG_FORMAT_STMT) ||
+ (cache_mngr->trx_cache.changes_to_non_trans_temp_table() &&
+ WSREP_FORMAT(thd->variables.binlog_format) == BINLOG_FORMAT_MIXED) ||
+ (trans_has_updated_non_trans_table(thd) &&
+ ending_single_stmt_trans(thd,all) &&
+ WSREP_FORMAT(thd->variables.binlog_format) == BINLOG_FORMAT_MIXED));
+}
+
+
/**
This function is called once after each statement.
@@ -2023,26 +2155,8 @@ static int binlog_rollback(handlerton *hton, THD *thd, bool all)
error |= binlog_truncate_trx_cache(thd, cache_mngr, all);
}
else if (!error)
- {
- /*
- We flush the cache wrapped in a beging/rollback if:
- . aborting a single or multi-statement transaction and;
- . the OPTION_KEEP_LOG is active or;
- . the format is STMT and a non-trans table was updated or;
- . the format is MIXED and a temporary non-trans table was
- updated or;
- . the format is MIXED, non-trans table was updated and
- aborting a single statement transaction;
- */
- if (ending_trans(thd, all) &&
- ((thd->variables.option_bits & OPTION_KEEP_LOG) ||
- (trans_has_updated_non_trans_table(thd) &&
- WSREP_BINLOG_FORMAT(thd->variables.binlog_format) == BINLOG_FORMAT_STMT) ||
- (cache_mngr->trx_cache.changes_to_non_trans_temp_table() &&
- WSREP_BINLOG_FORMAT(thd->variables.binlog_format) == BINLOG_FORMAT_MIXED) ||
- (trans_has_updated_non_trans_table(thd) &&
- ending_single_stmt_trans(thd,all) &&
- WSREP_BINLOG_FORMAT(thd->variables.binlog_format) == BINLOG_FORMAT_MIXED)))
+ {
+ if (ending_trans(thd, all) && trans_cannot_safely_rollback(thd, all))
error= binlog_rollback_flush_trx_cache(thd, all, cache_mngr);
/*
Truncate the cache if:
@@ -2056,9 +2170,9 @@ static int binlog_rollback(handlerton *hton, THD *thd, bool all)
else if (ending_trans(thd, all) ||
(!(thd->variables.option_bits & OPTION_KEEP_LOG) &&
(!stmt_has_updated_non_trans_table(thd) ||
- WSREP_BINLOG_FORMAT(thd->variables.binlog_format) != BINLOG_FORMAT_STMT) &&
+ WSREP_FORMAT(thd->variables.binlog_format) != BINLOG_FORMAT_STMT) &&
(!cache_mngr->trx_cache.changes_to_non_trans_temp_table() ||
- WSREP_BINLOG_FORMAT(thd->variables.binlog_format) != BINLOG_FORMAT_MIXED)))
+ WSREP_FORMAT(thd->variables.binlog_format) != BINLOG_FORMAT_MIXED)))
error= binlog_truncate_trx_cache(thd, cache_mngr, all);
}
@@ -2071,6 +2185,21 @@ static int binlog_rollback(handlerton *hton, THD *thd, bool all)
DBUG_RETURN(error);
}
+
+void binlog_reset_cache(THD *thd)
+{
+ binlog_cache_mngr *const cache_mngr= opt_bin_log ?
+ (binlog_cache_mngr*) thd_get_ha_data(thd, binlog_hton) : 0;
+ DBUG_ENTER("binlog_reset_cache");
+ if (cache_mngr)
+ {
+ thd->binlog_remove_pending_rows_event(TRUE, TRUE);
+ cache_mngr->reset(true, true);
+ }
+ DBUG_VOID_RETURN;
+}
+
+
void MYSQL_BIN_LOG::set_write_error(THD *thd, bool is_transactional)
{
DBUG_ENTER("MYSQL_BIN_LOG::set_write_error");
@@ -2108,7 +2237,7 @@ bool MYSQL_BIN_LOG::check_write_error(THD *thd)
if (!thd->is_error())
DBUG_RETURN(checked);
- switch (thd->stmt_da->sql_errno())
+ switch (thd->get_stmt_da()->sql_errno())
{
case ER_TRANS_CACHE_FULL:
case ER_STMT_CACHE_FULL:
@@ -2149,32 +2278,41 @@ bool MYSQL_BIN_LOG::check_write_error(THD *thd)
static int binlog_savepoint_set(handlerton *hton, THD *thd, void *sv)
{
DBUG_ENTER("binlog_savepoint_set");
-
-#ifdef WITH_WSREP
- /*
- If wsrep_emulate_bin_log is true, (i.e opt_bin_log == false),
- we should return from here if wsrep_on is off.
- */
- if (wsrep_emulate_bin_log && !WSREP(thd)) DBUG_RETURN(0);
-#endif /* WITH_WSREP */
-
- binlog_trans_log_savepos(thd, (my_off_t*) sv);
- /* Write it to the binary log */
+ int error= 1;
+ char buf[1024];
#ifdef WITH_WSREP
if (wsrep_emulate_bin_log) DBUG_RETURN(0);
+ /*
+ Clear table maps before writing SAVEPOINT event. This enforces
+ recreation of table map events for the following row event.
+ */
+ thd->clear_binlog_table_maps();
#endif /* WITH_WSREP */
-
- char buf[1024];
String log_query(buf, sizeof(buf), &my_charset_bin);
if (log_query.copy(STRING_WITH_LEN("SAVEPOINT "), &my_charset_bin) ||
append_identifier(thd, &log_query,
thd->lex->ident.str, thd->lex->ident.length))
DBUG_RETURN(1);
int errcode= query_error_code(thd, thd->killed == NOT_KILLED);
- Query_log_event qinfo(thd, log_query.ptr(), log_query.length(),
+ Query_log_event qinfo(thd, log_query.c_ptr_safe(), log_query.length(),
TRUE, FALSE, TRUE, errcode);
- int ret= mysql_bin_log.write(&qinfo);
- DBUG_RETURN(ret);
+ /*
+ We cannot record the position before writing the statement
+ because a rollback to a savepoint (.e.g. consider it "S") would
+ prevent the savepoint statement (i.e. "SAVEPOINT S") from being
+ written to the binary log despite the fact that the server could
+ still issue other rollback statements to the same savepoint (i.e.
+ "S").
+ Given that the savepoint is valid until the server releases it,
+ ie, until the transaction commits or it is released explicitly,
+ we need to log it anyway so that we don't have "ROLLBACK TO S"
+ or "RELEASE S" without the preceding "SAVEPOINT S" in the binary
+ log.
+ */
+ if (!(error= mysql_bin_log.write(&qinfo)))
+ binlog_trans_log_savepos(thd, (my_off_t*) sv);
+
+ DBUG_RETURN(error);
}
static int binlog_savepoint_rollback(handlerton *hton, THD *thd, void *sv)
@@ -2205,11 +2343,38 @@ static int binlog_savepoint_rollback(handlerton *hton, THD *thd, void *sv)
TRUE, FALSE, TRUE, errcode);
DBUG_RETURN(mysql_bin_log.write(&qinfo));
}
- binlog_trans_log_truncate(thd, *(my_off_t*)sv);
+#ifdef WITH_WSREP
+ if (!wsrep_emulate_bin_log)
+#endif
+ binlog_trans_log_truncate(thd, *(my_off_t*)sv);
DBUG_RETURN(0);
}
+/**
+ Check whether binlog state allows to safely release MDL locks after
+ rollback to savepoint.
+
+ @param hton The binlog handlerton.
+ @param thd The client thread that executes the transaction.
+
+ @return true - It is safe to release MDL locks.
+ false - If it is not.
+*/
+static bool binlog_savepoint_rollback_can_release_mdl(handlerton *hton,
+ THD *thd)
+{
+ DBUG_ENTER("binlog_savepoint_rollback_can_release_mdl");
+ /*
+ If we have not updated any non-transactional tables rollback
+ to savepoint will simply truncate binlog cache starting from
+ SAVEPOINT command. So it should be safe to release MDL acquired
+ after SAVEPOINT command in this case.
+ */
+ DBUG_RETURN(!trans_cannot_safely_rollback(thd, true));
+}
+
+
int check_binlog_magic(IO_CACHE* log, const char** errmsg)
{
uchar magic[4];
@@ -2344,7 +2509,7 @@ static int find_uniq_filename(char *name)
DBUG_RETURN(1);
}
file_info= dir_info->dir_entry;
- for (i= dir_info->number_off_files ; i-- ; file_info++)
+ for (i= dir_info->number_of_files ; i-- ; file_info++)
{
if (strncmp(file_info->name, start, length) == 0 &&
test_if_number(file_info->name+length, &number,0))
@@ -2637,7 +2802,8 @@ int MYSQL_LOG::generate_new_name(char *new_name, const char *log_name)
{
if (!fn_ext(log_name)[0])
{
- if (find_uniq_filename(new_name))
+ if (DBUG_EVALUATE_IF("binlog_inject_new_name_error", TRUE, FALSE) ||
+ find_uniq_filename(new_name))
{
if (current_thd)
my_printf_error(ER_NO_UNIQUE_LOGFILE, ER(ER_NO_UNIQUE_LOGFILE),
@@ -2885,8 +3051,8 @@ bool MYSQL_QUERY_LOG::write(THD *thd, time_t current_time,
(ulong) thd->thread_id, (thd->db ? thd->db : ""),
((thd->query_plan_flags & QPLAN_QC) ? "Yes" : "No"),
query_time_buff, lock_time_buff,
- (ulong) thd->sent_row_count,
- (ulong) thd->examined_row_count) == (size_t) -1)
+ (ulong) thd->get_sent_row_count(),
+ (ulong) thd->get_examined_row_count()) == (size_t) -1)
tmp_errno= errno;
if ((thd->variables.log_slow_verbosity & LOG_SLOW_VERBOSITY_QUERY_PLAN) &&
(thd->query_plan_flags &
@@ -2895,7 +3061,8 @@ bool MYSQL_QUERY_LOG::write(THD *thd, time_t current_time,
my_b_printf(&log_file,
"# Full_scan: %s Full_join: %s "
"Tmp_table: %s Tmp_table_on_disk: %s\n"
- "# Filesort: %s Filesort_on_disk: %s Merge_passes: %lu\n",
+ "# Filesort: %s Filesort_on_disk: %s Merge_passes: %lu "
+ "Priority_queue: %s\n",
((thd->query_plan_flags & QPLAN_FULL_SCAN) ? "Yes" : "No"),
((thd->query_plan_flags & QPLAN_FULL_JOIN) ? "Yes" : "No"),
((thd->query_plan_flags & QPLAN_TMP_TABLE) ? "Yes" : "No"),
@@ -2903,8 +3070,20 @@ bool MYSQL_QUERY_LOG::write(THD *thd, time_t current_time,
((thd->query_plan_flags & QPLAN_FILESORT) ? "Yes" : "No"),
((thd->query_plan_flags & QPLAN_FILESORT_DISK) ?
"Yes" : "No"),
- thd->query_plan_fsort_passes) == (size_t) -1)
+ thd->query_plan_fsort_passes,
+ ((thd->query_plan_flags & QPLAN_FILESORT_PRIORITY_QUEUE) ?
+ "Yes" : "No")
+ ) == (size_t) -1)
tmp_errno= errno;
+ if (thd->variables.log_slow_verbosity & LOG_SLOW_VERBOSITY_EXPLAIN &&
+ thd->lex->explain)
+ {
+ StringBuffer<128> buf;
+ DBUG_ASSERT(!thd->free_list);
+ if (!print_explain_query(thd->lex, thd, &buf))
+ my_b_printf(&log_file, "%s", buf.c_ptr_safe());
+ thd->free_items();
+ }
if (thd->db && strcmp(thd->db, db))
{ // Database changed
if (my_b_printf(&log_file,"use %s;\n",thd->db) == (size_t) -1)
@@ -2994,7 +3173,7 @@ const char *MYSQL_LOG::generate_name(const char *log_name,
{
char *p= fn_ext(log_name);
uint length= (uint) (p - log_name);
- strmake(buff, log_name, min(length, FN_REFLEN-1));
+ strmake(buff, log_name, MY_MIN(length, FN_REFLEN-1));
return (const char*)buff;
}
return log_name;
@@ -3003,15 +3182,19 @@ const char *MYSQL_LOG::generate_name(const char *log_name,
MYSQL_BIN_LOG::MYSQL_BIN_LOG(uint *sync_period)
- :bytes_written(0), prepared_xids(0), file_id(1), open_count(1),
- need_start_event(TRUE),
+ :reset_master_pending(0), mark_xid_done_waiting(0),
+ bytes_written(0), file_id(1), open_count(1),
group_commit_queue(0), group_commit_queue_busy(FALSE),
num_commits(0), num_group_commits(0),
+ group_commit_trigger_count(0), group_commit_trigger_timeout(0),
+ group_commit_trigger_lock_wait(0),
sync_period_ptr(sync_period), sync_counter(0),
+ state_file_deleted(false), binlog_state_recover_done(false),
is_relay_log(0), signal_cnt(0),
checksum_alg_reset(BINLOG_CHECKSUM_ALG_UNDEF),
relay_log_checksum_alg(BINLOG_CHECKSUM_ALG_UNDEF),
- description_event_for_exec(0), description_event_for_queue(0)
+ description_event_for_exec(0), description_event_for_queue(0),
+ current_binlog_id(0)
{
/*
We don't want to initialize locks here as such initialization depends on
@@ -3031,23 +3214,63 @@ void MYSQL_BIN_LOG::cleanup()
DBUG_ENTER("cleanup");
if (inited)
{
+ xid_count_per_binlog *b;
+
+ /* Wait for the binlog background thread to stop. */
+ if (!is_relay_log && binlog_background_thread_started)
+ {
+ mysql_mutex_lock(&LOCK_binlog_background_thread);
+ binlog_background_thread_stop= true;
+ mysql_cond_signal(&COND_binlog_background_thread);
+ while (binlog_background_thread_stop)
+ mysql_cond_wait(&COND_binlog_background_thread_end,
+ &LOCK_binlog_background_thread);
+ mysql_mutex_unlock(&LOCK_binlog_background_thread);
+ binlog_background_thread_started= false;
+ }
+
inited= 0;
close(LOG_CLOSE_INDEX|LOG_CLOSE_STOP_EVENT);
delete description_event_for_queue;
delete description_event_for_exec;
+
+ while ((b= binlog_xid_count_list.get()))
+ {
+ /*
+ There should be no pending XIDs at shutdown, and only one entry (for
+ the active binlog file) in the list.
+ */
+ DBUG_ASSERT(b->xid_count == 0);
+ DBUG_ASSERT(!binlog_xid_count_list.head());
+ my_free(b);
+ }
+
mysql_mutex_destroy(&LOCK_log);
mysql_mutex_destroy(&LOCK_index);
+ mysql_mutex_destroy(&LOCK_xid_list);
+ mysql_mutex_destroy(&LOCK_binlog_background_thread);
mysql_cond_destroy(&update_cond);
+ mysql_cond_destroy(&COND_queue_busy);
+ mysql_cond_destroy(&COND_xid_list);
+ mysql_cond_destroy(&COND_binlog_background_thread);
+ mysql_cond_destroy(&COND_binlog_background_thread_end);
}
+
+ /*
+ Free data for global binlog state.
+ We can't do that automaticly as we need to do this before
+ safemalloc is shut down
+ */
+ if (!is_relay_log)
+ rpl_global_gtid_binlog_state.free();
DBUG_VOID_RETURN;
}
/* Init binlog-specific vars */
-void MYSQL_BIN_LOG::init(bool no_auto_events_arg, ulong max_size_arg)
+void MYSQL_BIN_LOG::init(ulong max_size_arg)
{
DBUG_ENTER("MYSQL_BIN_LOG::init");
- no_auto_events= no_auto_events_arg;
max_size= max_size_arg;
DBUG_PRINT("info",("max_size: %lu", max_size));
DBUG_VOID_RETURN;
@@ -3059,8 +3282,18 @@ void MYSQL_BIN_LOG::init_pthread_objects()
MYSQL_LOG::init_pthread_objects();
mysql_mutex_init(m_key_LOCK_index, &LOCK_index, MY_MUTEX_INIT_SLOW);
mysql_mutex_setflags(&LOCK_index, MYF_NO_DEADLOCK_DETECTION);
+ mysql_mutex_init(key_BINLOG_LOCK_xid_list,
+ &LOCK_xid_list, MY_MUTEX_INIT_FAST);
mysql_cond_init(m_key_update_cond, &update_cond, 0);
mysql_cond_init(m_key_COND_queue_busy, &COND_queue_busy, 0);
+ mysql_cond_init(key_BINLOG_COND_xid_list, &COND_xid_list, 0);
+
+ mysql_mutex_init(key_BINLOG_LOCK_binlog_background_thread,
+ &LOCK_binlog_background_thread, MY_MUTEX_INIT_FAST);
+ mysql_cond_init(key_BINLOG_COND_binlog_background_thread,
+ &COND_binlog_background_thread, 0);
+ mysql_cond_init(key_BINLOG_COND_binlog_background_thread_end,
+ &COND_binlog_background_thread_end, 0);
}
@@ -3148,16 +3381,30 @@ bool MYSQL_BIN_LOG::open(const char *log_name,
enum_log_type log_type_arg,
const char *new_name,
enum cache_type io_cache_type_arg,
- bool no_auto_events_arg,
ulong max_size_arg,
bool null_created_arg,
bool need_mutex)
{
File file= -1;
+ xid_count_per_binlog *new_xid_list_entry= NULL, *b;
DBUG_ENTER("MYSQL_BIN_LOG::open");
DBUG_PRINT("enter",("log_type: %d",(int) log_type_arg));
+ if (!is_relay_log)
+ {
+ if (!binlog_state_recover_done)
+ {
+ binlog_state_recover_done= true;
+ if (do_binlog_recovery(opt_bin_logname, false))
+ DBUG_RETURN(1);
+ }
+
+ if (!binlog_background_thread_started &&
+ start_binlog_background_thread())
+ DBUG_RETURN(1);
+ }
+
if (init_and_set_log_file_name(log_name, new_name, log_type_arg,
io_cache_type_arg))
{
@@ -3177,7 +3424,7 @@ bool MYSQL_BIN_LOG::open(const char *log_name,
it may be good to consider what actually happens when
open_purge_index_file succeeds but register or sync fails.
- Perhaps we might need the code below in MYSQL_LOG_BIN::cleanup
+ Perhaps we might need the code below in MYSQL_BIN_LOG::cleanup
for "real life" purposes as well?
*/
DBUG_EXECUTE_IF("fault_injection_registering_index", {
@@ -3209,7 +3456,7 @@ bool MYSQL_BIN_LOG::open(const char *log_name,
DBUG_RETURN(1); /* all warnings issued */
}
- init(no_auto_events_arg, max_size_arg);
+ init(max_size_arg);
open_count++;
@@ -3233,11 +3480,10 @@ bool MYSQL_BIN_LOG::open(const char *log_name,
write_file_name_to_index_file= 1;
}
- if (need_start_event && !no_auto_events)
{
/*
- In 4.x we set need_start_event=0 here, but in 5.0 we want a Start event
- even if this is not the very first binlog.
+ In 4.x we put Start event only in the first binlog. But from 5.0 we
+ want a Start event even if this is not the very first binlog.
*/
Format_description_log_event s(BINLOG_VERSION);
/*
@@ -3264,6 +3510,101 @@ bool MYSQL_BIN_LOG::open(const char *log_name,
if (s.write(&log_file))
goto err;
bytes_written+= s.data_written;
+
+ if (!is_relay_log)
+ {
+ char buf[FN_REFLEN];
+
+ /*
+ Output a Gtid_list_log_event at the start of the binlog file.
+
+ This is used to quickly determine which GTIDs are found in binlog
+ files earlier than this one, and which are found in this (or later)
+ binlogs.
+
+ The list gives a mapping from (domain_id, server_id) -> seq_no (so
+ this means that there is at most one entry for every unique pair
+ (domain_id, server_id) in the list). It indicates that this seq_no is
+ the last one found in an earlier binlog file for this (domain_id,
+ server_id) combination - so any higher seq_no should be search for
+ from this binlog file, or a later one.
+
+ This allows to locate the binlog file containing a given GTID by
+ scanning backwards, reading just the Gtid_list_log_event at the
+ start of each file, and scanning only the relevant binlog file when
+ found, not all binlog files.
+
+ The existence of a given entry (domain_id, server_id, seq_no)
+ guarantees only that this seq_no will not be found in this or any
+ later binlog file. It does not guarantee that it can be found it an
+ earlier binlog file, for example the file may have been purged.
+
+ If there is no entry for a given (domain_id, server_id) pair, then
+ it means that no such GTID exists in any earlier binlog. It is
+ permissible to remove such pair from future Gtid_list_log_events
+ if all previous binlog files containing such GTIDs have been purged
+ (though such optimization is not performed at the time of this
+ writing). So if there is no entry for given GTID it means that such
+ GTID should be search for in this or later binlog file, same as if
+ there had been an entry (domain_id, server_id, 0).
+ */
+
+ Gtid_list_log_event gl_ev(&rpl_global_gtid_binlog_state, 0);
+ if (gl_ev.write(&log_file))
+ goto err;
+
+ /* Output a binlog checkpoint event at the start of the binlog file. */
+
+ /*
+ Construct an entry in the binlog_xid_count_list for the new binlog
+ file (we will not link it into the list until we know the new file
+ is successfully created; otherwise we would have to remove it again
+ if creation failed, which gets tricky since other threads may have
+ seen the entry in the meantime - and we do not want to hold
+ LOCK_xid_list for long periods of time).
+
+ Write the current binlog checkpoint into the log, so XA recovery will
+ know from where to start recovery.
+ */
+ uint off= dirname_length(log_file_name);
+ uint len= strlen(log_file_name) - off;
+ char *entry_mem, *name_mem;
+ if (!(new_xid_list_entry = (xid_count_per_binlog *)
+ my_multi_malloc(MYF(MY_WME),
+ &entry_mem, sizeof(xid_count_per_binlog),
+ &name_mem, len,
+ NULL)))
+ goto err;
+ memcpy(name_mem, log_file_name+off, len);
+ new_xid_list_entry->binlog_name= name_mem;
+ new_xid_list_entry->binlog_name_len= len;
+ new_xid_list_entry->xid_count= 0;
+
+ /*
+ Find the name for the Initial binlog checkpoint.
+
+ Normally this will just be the first entry, as we delete entries
+ when their count drops to zero. But we scan the list to handle any
+ corner case, eg. for the first binlog file opened after startup, the
+ list will be empty.
+ */
+ mysql_mutex_lock(&LOCK_xid_list);
+ I_List_iterator<xid_count_per_binlog> it(binlog_xid_count_list);
+ while ((b= it++) && b->xid_count == 0)
+ ;
+ mysql_mutex_unlock(&LOCK_xid_list);
+ if (!b)
+ b= new_xid_list_entry;
+ strmake(buf, b->binlog_name, b->binlog_name_len);
+ Binlog_checkpoint_log_event ev(buf, len);
+ DBUG_EXECUTE_IF("crash_before_write_checkpoint_event",
+ flush_io_cache(&log_file);
+ mysql_file_sync(log_file.file, MYF(MY_WME));
+ DBUG_SUICIDE(););
+ if (ev.write(&log_file))
+ goto err;
+ bytes_written+= ev.data_written;
+ }
}
if (description_event_for_queue &&
description_event_for_queue->binlog_version>=4)
@@ -3334,6 +3675,42 @@ bool MYSQL_BIN_LOG::open(const char *log_name,
#endif
}
}
+
+ if (!is_relay_log)
+ {
+ /*
+ Now the file was created successfully, so we can link in the entry for
+ the new binlog file in binlog_xid_count_list.
+ */
+ mysql_mutex_lock(&LOCK_xid_list);
+ ++current_binlog_id;
+ new_xid_list_entry->binlog_id= current_binlog_id;
+ /* Remove any initial entries with no pending XIDs. */
+ while ((b= binlog_xid_count_list.head()) && b->xid_count == 0)
+ my_free(binlog_xid_count_list.get());
+ binlog_xid_count_list.push_back(new_xid_list_entry);
+ mysql_mutex_unlock(&LOCK_xid_list);
+
+ /*
+ Now that we have synced a new binlog file with an initial Gtid_list
+ event, it is safe to delete the binlog state file. We will write out
+ a new, updated file at shutdown, and if we crash before we can recover
+ the state from the newly written binlog file.
+
+ Since the state file will contain out-of-date data as soon as the first
+ new GTID is binlogged, it is better to remove it, to avoid any risk of
+ accidentally reading incorrect data later.
+ */
+ if (!state_file_deleted)
+ {
+ char buf[FN_REFLEN];
+ fn_format(buf, opt_bin_logname, mysql_data_home, ".state",
+ MY_UNPACK_FILENAME);
+ my_delete(buf, MY_SYNC_DIR);
+ state_file_deleted= true;
+ }
+ }
+
log_state= LOG_OPENED;
#ifdef HAVE_REPLICATION
@@ -3352,6 +3729,8 @@ err:
Turning logging off for the whole duration of the MySQL server process. \
To turn it on again: fix the cause, \
shutdown the MySQL server and restart it.", name, errno);
+ if (new_xid_list_entry)
+ my_free(new_xid_list_entry);
if (file >= 0)
mysql_file_close(file, MYF(0));
close(LOG_CLOSE_INDEX);
@@ -3411,7 +3790,8 @@ static bool copy_up_file_and_fill(IO_CACHE *index_file, my_off_t offset)
if (!bytes_read)
break; // end of file
mysql_file_seek(file, offset-init_offset, MY_SEEK_SET, MYF(0));
- if (mysql_file_write(file, io_buf, bytes_read, MYF(MY_WME | MY_NABP)))
+ if (mysql_file_write(file, io_buf, bytes_read,
+ MYF(MY_WME | MY_NABP | MY_WAIT_IF_FULL)))
goto err;
}
/* The following will either truncate the file or fill the end with \n' */
@@ -3597,11 +3977,11 @@ err:
/**
Delete all logs refered to in the index file.
- Start writing to a new log file.
The new index file will only contain this file.
- @param thd Thread
+ @param thd Thread
+ @param create_new_log 1 if we should start writing to a new log file
@note
If not called from slave thread, write start event to new log
@@ -3612,7 +3992,8 @@ err:
1 error
*/
-bool MYSQL_BIN_LOG::reset_logs(THD* thd)
+bool MYSQL_BIN_LOG::reset_logs(THD* thd, bool create_new_log,
+ rpl_gtid *init_state, uint32 init_state_len)
{
LOG_INFO linfo;
bool error=0;
@@ -3620,7 +4001,34 @@ bool MYSQL_BIN_LOG::reset_logs(THD* thd)
const char* save_name;
DBUG_ENTER("reset_logs");
- ha_reset_logs(thd);
+ if (!is_relay_log)
+ {
+ if (init_state && !is_empty_state())
+ {
+ my_error(ER_BINLOG_MUST_BE_EMPTY, MYF(0));
+ DBUG_RETURN(1);
+ }
+
+ /*
+ Mark that a RESET MASTER is in progress.
+ This ensures that a binlog checkpoint will not try to write binlog
+ checkpoint events, which would be useless (as we are deleting the binlog
+ anyway) and could deadlock, as we are holding LOCK_log.
+
+ Wait for any mark_xid_done() calls that might be already running to
+ complete (mark_xid_done_waiting counter to drop to zero); we need to
+ do this before we take the LOCK_log to not deadlock.
+ */
+ mysql_mutex_lock(&LOCK_xid_list);
+ reset_master_pending++;
+ while (mark_xid_done_waiting > 0)
+ mysql_cond_wait(&COND_xid_list, &LOCK_xid_list);
+ mysql_mutex_unlock(&LOCK_xid_list);
+ }
+
+ DEBUG_SYNC(thd, "reset_logs_after_set_reset_master_pending");
+ if (thd)
+ ha_reset_logs(thd);
/*
We need to get both locks to be sure that no one is trying to
write to the index log file.
@@ -3628,6 +4036,50 @@ bool MYSQL_BIN_LOG::reset_logs(THD* thd)
mysql_mutex_lock(&LOCK_log);
mysql_mutex_lock(&LOCK_index);
+ if (!is_relay_log)
+ {
+ /*
+ We are going to nuke all binary log files.
+ Without binlog, we cannot XA recover prepared-but-not-committed
+ transactions in engines. So force a commit checkpoint first.
+
+ Note that we take and immediately release LOCK_commit_ordered. This has
+ the effect to ensure that any on-going group commit (in
+ trx_group_commit_leader()) has completed before we request the checkpoint,
+ due to the chaining of LOCK_log and LOCK_commit_ordered in that function.
+ (We are holding LOCK_log, so no new group commit can start).
+
+ Without this, it is possible (though perhaps unlikely) that the RESET
+ MASTER could run in-between the write to the binlog and the
+ commit_ordered() in the engine of some transaction, and then a crash
+ later would leave such transaction not recoverable.
+ */
+ mysql_mutex_lock(&LOCK_commit_ordered);
+ mysql_mutex_unlock(&LOCK_commit_ordered);
+
+ mark_xids_active(current_binlog_id, 1);
+ do_checkpoint_request(current_binlog_id);
+
+ /* Now wait for all checkpoint requests and pending unlog() to complete. */
+ mysql_mutex_lock(&LOCK_xid_list);
+ for (;;)
+ {
+ if (is_xidlist_idle_nolock())
+ break;
+ /*
+ Wait until signalled that one more binlog dropped to zero, then check
+ again.
+ */
+ mysql_cond_wait(&COND_xid_list, &LOCK_xid_list);
+ }
+
+ /*
+ Now all XIDs are fully flushed to disk, and we are holding LOCK_log so
+ no new ones will be written. So we can proceed to delete the logs.
+ */
+ mysql_mutex_unlock(&LOCK_xid_list);
+ }
+
/*
The following mutex is needed to ensure that no threads call
'delete thd' as we would then risk missing a 'rollback' from this
@@ -3666,7 +4118,7 @@ bool MYSQL_BIN_LOG::reset_logs(THD* thd)
{
if (my_errno == ENOENT)
{
- push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(current_thd, Sql_condition::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'",
@@ -3676,7 +4128,7 @@ bool MYSQL_BIN_LOG::reset_logs(THD* thd)
}
else
{
- push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
ER_BINLOG_PURGE_FATAL_ERR,
"a problem with deleting %s; "
"consider examining correspondence "
@@ -3691,13 +4143,21 @@ bool MYSQL_BIN_LOG::reset_logs(THD* thd)
break;
}
+ if (!is_relay_log)
+ {
+ if (init_state)
+ rpl_global_gtid_binlog_state.load(init_state, init_state_len);
+ else
+ rpl_global_gtid_binlog_state.reset();
+ }
+
/* Start logging with a new file */
close(LOG_CLOSE_INDEX | LOG_CLOSE_TO_BE_OPENED);
if ((error= my_delete(index_file_name, MYF(0)))) // Reset (open will update)
{
if (my_errno == ENOENT)
{
- push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(current_thd, Sql_condition::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'",
@@ -3707,7 +4167,7 @@ bool MYSQL_BIN_LOG::reset_logs(THD* thd)
}
else
{
- push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
ER_BINLOG_PURGE_FATAL_ERR,
"a problem with deleting %s; "
"consider examining correspondence "
@@ -3718,10 +4178,8 @@ bool MYSQL_BIN_LOG::reset_logs(THD* thd)
goto err;
}
}
- if (!thd->slave_thread)
- need_start_event=1;
- if (!open_index_file(index_file_name, 0, FALSE))
- if ((error= open(save_name, log_type, 0, io_cache_type, no_auto_events, max_size, 0, FALSE)))
+ if (create_new_log && !open_index_file(index_file_name, 0, FALSE))
+ if ((error= open(save_name, log_type, 0, io_cache_type, max_size, 0, FALSE)))
goto err;
my_free((void *) save_name);
@@ -3729,6 +4187,31 @@ err:
if (error == 1)
name= const_cast<char*>(save_name);
mysql_mutex_unlock(&LOCK_thread_count);
+
+ if (!is_relay_log)
+ {
+ xid_count_per_binlog *b;
+ /*
+ Remove all entries in the xid_count list except the last.
+ Normally we will just be deleting all the entries that we waited for to
+ drop to zero above. But if we fail during RESET MASTER for some reason
+ then we will not have created any new log file, and we may keep the last
+ of the old entries.
+ */
+ mysql_mutex_lock(&LOCK_xid_list);
+ for (;;)
+ {
+ b= binlog_xid_count_list.head();
+ DBUG_ASSERT(b /* List can never become empty. */);
+ if (b->binlog_id == current_binlog_id)
+ break;
+ DBUG_ASSERT(b->xid_count == 0);
+ my_free(binlog_xid_count_list.get());
+ }
+ reset_master_pending--;
+ mysql_mutex_unlock(&LOCK_xid_list);
+ }
+
mysql_mutex_unlock(&LOCK_index);
mysql_mutex_unlock(&LOCK_log);
DBUG_RETURN(error);
@@ -3778,15 +4261,39 @@ int MYSQL_BIN_LOG::purge_first_log(Relay_log_info* rli, bool included)
{
int error;
char *to_purge_if_included= NULL;
+ inuse_relaylog *ir;
ulonglong log_space_reclaimed= 0;
DBUG_ENTER("purge_first_log");
DBUG_ASSERT(is_open());
- DBUG_ASSERT(rli->slave_running == 1);
+ DBUG_ASSERT(rli->slave_running == MYSQL_SLAVE_RUN_NOT_CONNECT);
DBUG_ASSERT(!strcmp(rli->linfo.log_file_name,rli->event_relay_log_name));
mysql_mutex_lock(&LOCK_index);
- to_purge_if_included= my_strdup(rli->group_relay_log_name, MYF(0));
+
+ ir= rli->inuse_relaylog_list;
+ while (ir)
+ {
+ inuse_relaylog *next= ir->next;
+ if (!ir->completed || ir->dequeued_count < ir->queued_count)
+ {
+ included= false;
+ break;
+ }
+ if (!included && !strcmp(ir->name, rli->group_relay_log_name))
+ break;
+ if (!next)
+ {
+ rli->last_inuse_relaylog= NULL;
+ included= 1;
+ to_purge_if_included= my_strdup(ir->name, MYF(0));
+ }
+ rli->free_inuse_relaylog(ir);
+ ir= next;
+ }
+ rli->inuse_relaylog_list= ir;
+ if (ir)
+ to_purge_if_included= my_strdup(ir->name, MYF(0));
/*
Read the next log file name from the index file and pass it back to
@@ -3933,8 +4440,7 @@ int MYSQL_BIN_LOG::purge_logs(const char *to_log,
if ((error=find_log_pos(&log_info, NullS, 0 /*no mutex*/)))
goto err;
while ((strcmp(to_log,log_info.log_file_name) || (exit_loop=included)) &&
- !is_active(log_info.log_file_name) &&
- !log_in_use(log_info.log_file_name))
+ can_purge_log(log_info.log_file_name))
{
if ((error= register_purge_index_entry(log_info.log_file_name)))
{
@@ -4122,7 +4628,7 @@ int MYSQL_BIN_LOG::purge_index_entry(THD *thd, ulonglong *reclaimed_space,
*/
if (thd)
{
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_LOG_PURGE_NO_FILE, ER(ER_LOG_PURGE_NO_FILE),
log_info.log_file_name);
}
@@ -4137,7 +4643,7 @@ int MYSQL_BIN_LOG::purge_index_entry(THD *thd, ulonglong *reclaimed_space,
*/
if (thd)
{
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_BINLOG_PURGE_FATAL_ERR,
"a problem with getting info on being purged %s; "
"consider examining correspondence "
@@ -4165,7 +4671,7 @@ int MYSQL_BIN_LOG::purge_index_entry(THD *thd, ulonglong *reclaimed_space,
{
if (thd)
{
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_BINLOG_PURGE_FATAL_ERR,
"a problem with deleting %s and "
"reading the binlog index file",
@@ -4201,7 +4707,7 @@ int MYSQL_BIN_LOG::purge_index_entry(THD *thd, ulonglong *reclaimed_space,
{
if (thd)
{
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_LOG_PURGE_NO_FILE, ER(ER_LOG_PURGE_NO_FILE),
log_info.log_file_name);
}
@@ -4213,7 +4719,7 @@ int MYSQL_BIN_LOG::purge_index_entry(THD *thd, ulonglong *reclaimed_space,
{
if (thd)
{
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_BINLOG_PURGE_FATAL_ERR,
"a problem with deleting %s; "
"consider examining correspondence "
@@ -4284,8 +4790,7 @@ int MYSQL_BIN_LOG::purge_logs_before_date(time_t purge_time)
goto err;
while (strcmp(log_file_name, log_info.log_file_name) &&
- !is_active(log_info.log_file_name) &&
- !log_in_use(log_info.log_file_name))
+ can_purge_log(log_info.log_file_name))
{
if (!mysql_file_stat(m_key_file_log,
log_info.log_file_name, &stat_area, MYF(0)))
@@ -4304,7 +4809,7 @@ int MYSQL_BIN_LOG::purge_logs_before_date(time_t purge_time)
*/
if (thd)
{
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_BINLOG_PURGE_FATAL_ERR,
"a problem with getting info on being purged %s; "
"consider examining correspondence "
@@ -4338,9 +4843,57 @@ err:
mysql_mutex_unlock(&LOCK_index);
DBUG_RETURN(error);
}
+
+
+bool
+MYSQL_BIN_LOG::can_purge_log(const char *log_file_name)
+{
+ xid_count_per_binlog *b;
+
+ if (is_active(log_file_name))
+ return false;
+ mysql_mutex_lock(&LOCK_xid_list);
+ {
+ I_List_iterator<xid_count_per_binlog> it(binlog_xid_count_list);
+ while ((b= it++) &&
+ 0 != strncmp(log_file_name+dirname_length(log_file_name),
+ b->binlog_name, b->binlog_name_len))
+ ;
+ }
+ mysql_mutex_unlock(&LOCK_xid_list);
+ if (b)
+ return false;
+ return !log_in_use(log_file_name);
+}
#endif /* HAVE_REPLICATION */
+bool
+MYSQL_BIN_LOG::is_xidlist_idle()
+{
+ bool res;
+ mysql_mutex_lock(&LOCK_xid_list);
+ res= is_xidlist_idle_nolock();
+ mysql_mutex_unlock(&LOCK_xid_list);
+ return res;
+}
+
+
+bool
+MYSQL_BIN_LOG::is_xidlist_idle_nolock()
+{
+ xid_count_per_binlog *b;
+
+ I_List_iterator<xid_count_per_binlog> it(binlog_xid_count_list);
+ while ((b= it++))
+ {
+ if (b->xid_count > 0)
+ return false;
+ }
+ return true;
+}
+
+
/**
Create a new log file name.
@@ -4431,26 +4984,6 @@ int MYSQL_BIN_LOG::new_file_impl(bool need_lock)
mysql_mutex_assert_owner(&LOCK_log);
mysql_mutex_assert_owner(&LOCK_index);
- /*
- if binlog is used as tc log, be sure all xids are "unlogged",
- so that on recover we only need to scan one - latest - binlog file
- for prepared xids. As this is expected to be a rare event,
- simple wait strategy is enough. We're locking LOCK_log to be sure no
- new Xid_log_event's are added to the log (and prepared_xids is not
- increased), and waiting on COND_prep_xids for late threads to
- catch up.
- */
- if (prepared_xids)
- {
- tc_log_page_waits++;
- mysql_mutex_lock(&LOCK_prep_xids);
- while (prepared_xids) {
- DBUG_PRINT("info", ("prepared_xids=%lu", prepared_xids));
- mysql_cond_wait(&COND_prep_xids, &LOCK_prep_xids);
- }
- mysql_mutex_unlock(&LOCK_prep_xids);
- }
-
/* Reuse old name if not binlog and not update log */
new_name_ptr= name;
@@ -4465,7 +4998,6 @@ int MYSQL_BIN_LOG::new_file_impl(bool need_lock)
if (log_type == LOG_BIN)
{
- if (!no_auto_events)
{
/*
We log the whole file name for log file as the user may decide
@@ -4540,7 +5072,7 @@ int MYSQL_BIN_LOG::new_file_impl(bool need_lock)
/* reopen the binary log file. */
file_to_open= new_name_ptr;
error= open(old_name, log_type, new_name_ptr, io_cache_type,
- no_auto_events, max_size, 1, FALSE);
+ max_size, 1, FALSE);
}
/* handle reopening errors */
@@ -4592,12 +5124,23 @@ end:
}
-bool MYSQL_BIN_LOG::append(Log_event* ev)
+bool
+MYSQL_BIN_LOG::append(Log_event *ev)
{
- bool error = 0;
+ bool res;
mysql_mutex_lock(&LOCK_log);
+ res= append_no_lock(ev);
+ mysql_mutex_unlock(&LOCK_log);
+ return res;
+}
+
+
+bool MYSQL_BIN_LOG::append_no_lock(Log_event* ev)
+{
+ bool error = 0;
DBUG_ENTER("MYSQL_BIN_LOG::append");
+ mysql_mutex_assert_owner(&LOCK_log);
DBUG_ASSERT(log_file.type == SEQ_READ_APPEND);
/*
Log_event::write() is smart enough to use my_b_write() or
@@ -4612,10 +5155,9 @@ bool MYSQL_BIN_LOG::append(Log_event* ev)
DBUG_PRINT("info",("max_size: %lu",max_size));
if (flush_and_sync(0))
goto err;
- if ((uint) my_b_append_tell(&log_file) > max_size)
+ if (my_b_append_tell(&log_file) > max_size)
error= new_file_without_locking();
err:
- mysql_mutex_unlock(&LOCK_log);
signal_update(); // Safe as we don't call close
DBUG_RETURN(error);
}
@@ -4643,7 +5185,7 @@ bool MYSQL_BIN_LOG::appendv(const char* buf, uint len,...)
DBUG_PRINT("info",("max_size: %lu",max_size));
if (flush_and_sync(0))
goto err;
- if ((uint) my_b_append_tell(&log_file) > max_size)
+ if (my_b_append_tell(&log_file) > max_size)
error= new_file_without_locking();
err:
if (!error)
@@ -4972,6 +5514,10 @@ int THD::binlog_write_table_map(TABLE *table, bool is_transactional,
(long) table, table->s->table_name.str,
table->s->table_map_id));
+ /* Ensure that all events in a GTID group are in the same cache */
+ if (variables.option_bits & OPTION_GTID_BEGIN)
+ is_transactional= 1;
+
/* Pre-conditions */
#ifdef WITH_WSREP
DBUG_ASSERT(is_current_stmt_binlog_format_row() &&
@@ -4994,7 +5540,7 @@ int THD::binlog_write_table_map(TABLE *table, bool is_transactional,
cache_mngr->get_binlog_cache_log(use_trans_cache(this, is_transactional));
if (with_annotate && *with_annotate)
{
- Annotate_rows_log_event anno(current_thd, is_transactional, false);
+ Annotate_rows_log_event anno(table->in_use, is_transactional, false);
/* Annotate event should be written not more than once */
*with_annotate= 0;
if ((error= anno.write(file)))
@@ -5159,6 +5705,244 @@ MYSQL_BIN_LOG::flush_and_set_pending_rows_event(THD *thd,
DBUG_RETURN(error);
}
+
+/* Generate a new global transaction ID, and write it to the binlog */
+
+bool
+MYSQL_BIN_LOG::write_gtid_event(THD *thd, bool standalone,
+ bool is_transactional, uint64 commit_id)
+{
+ rpl_gtid gtid;
+ uint32 domain_id= thd->variables.gtid_domain_id;
+ uint32 server_id= thd->variables.server_id;
+ uint64 seq_no= thd->variables.gtid_seq_no;
+ int err;
+ DBUG_ENTER("write_gtid_event");
+ DBUG_PRINT("enter", ("standalone: %d", standalone));
+
+ if (thd->variables.option_bits & OPTION_GTID_BEGIN)
+ {
+ DBUG_PRINT("error", ("OPTION_GTID_BEGIN is set. "
+ "Master and slave will have different GTID values"));
+ /* Reset the flag, as we will write out a GTID anyway */
+ thd->variables.option_bits&= ~OPTION_GTID_BEGIN;
+ }
+
+ /*
+ Reset the session variable gtid_seq_no, to reduce the risk of accidentally
+ producing a duplicate GTID.
+ */
+ thd->variables.gtid_seq_no= 0;
+ if (seq_no != 0)
+ {
+ /* Use the specified sequence number. */
+ gtid.domain_id= domain_id;
+ gtid.server_id= server_id;
+ gtid.seq_no= seq_no;
+ err= rpl_global_gtid_binlog_state.update(&gtid, opt_gtid_strict_mode);
+ if (err && thd->get_stmt_da()->sql_errno()==ER_GTID_STRICT_OUT_OF_ORDER)
+ errno= ER_GTID_STRICT_OUT_OF_ORDER;
+ }
+ else
+ {
+ /* Allocate the next sequence number for the GTID. */
+ err= rpl_global_gtid_binlog_state.update_with_next_gtid(domain_id,
+ server_id, &gtid);
+ seq_no= gtid.seq_no;
+ }
+ if (err)
+ DBUG_RETURN(true);
+ thd->last_commit_gtid= gtid;
+
+ Gtid_log_event gtid_event(thd, seq_no, domain_id, standalone,
+ LOG_EVENT_SUPPRESS_USE_F, is_transactional,
+ commit_id);
+
+ /* Write the event to the binary log. */
+ if (gtid_event.write(&mysql_bin_log.log_file))
+ DBUG_RETURN(true);
+ status_var_add(thd->status_var.binlog_bytes_written, gtid_event.data_written);
+
+ DBUG_RETURN(false);
+}
+
+
+int
+MYSQL_BIN_LOG::write_state_to_file()
+{
+ File file_no;
+ IO_CACHE cache;
+ char buf[FN_REFLEN];
+ int err;
+ bool opened= false;
+ bool inited= false;
+
+ fn_format(buf, opt_bin_logname, mysql_data_home, ".state",
+ MY_UNPACK_FILENAME);
+ if ((file_no= mysql_file_open(key_file_binlog_state, buf,
+ O_RDWR|O_CREAT|O_TRUNC|O_BINARY,
+ MYF(MY_WME))) < 0)
+ {
+ err= 1;
+ goto err;
+ }
+ opened= true;
+ if ((err= init_io_cache(&cache, file_no, IO_SIZE, WRITE_CACHE, 0, 0,
+ MYF(MY_WME|MY_WAIT_IF_FULL))))
+ goto err;
+ inited= true;
+ if ((err= rpl_global_gtid_binlog_state.write_to_iocache(&cache)))
+ goto err;
+ inited= false;
+ if ((err= end_io_cache(&cache)))
+ goto err;
+ if ((err= mysql_file_sync(file_no, MYF(MY_WME|MY_SYNC_FILESIZE))))
+ goto err;
+ goto end;
+
+err:
+ sql_print_error("Error writing binlog state to file '%s'.\n", buf);
+ if (inited)
+ end_io_cache(&cache);
+end:
+ if (opened)
+ mysql_file_close(file_no, MYF(0));
+
+ return err;
+}
+
+
+/*
+ Initialize the binlog state from the master-bin.state file, at server startup.
+
+ Returns:
+ 0 for success.
+ 2 for when .state file did not exist.
+ 1 for other error.
+*/
+int
+MYSQL_BIN_LOG::read_state_from_file()
+{
+ File file_no;
+ IO_CACHE cache;
+ char buf[FN_REFLEN];
+ int err;
+ bool opened= false;
+ bool inited= false;
+
+ fn_format(buf, opt_bin_logname, mysql_data_home, ".state",
+ MY_UNPACK_FILENAME);
+ if ((file_no= mysql_file_open(key_file_binlog_state, buf,
+ O_RDONLY|O_BINARY, MYF(0))) < 0)
+ {
+ if (my_errno != ENOENT)
+ {
+ err= 1;
+ goto err;
+ }
+ else
+ {
+ /*
+ If the state file does not exist, this is the first server startup
+ with GTID enabled. So initialize to empty state.
+ */
+ rpl_global_gtid_binlog_state.reset();
+ err= 2;
+ goto end;
+ }
+ }
+ opened= true;
+ if ((err= init_io_cache(&cache, file_no, IO_SIZE, READ_CACHE, 0, 0,
+ MYF(MY_WME|MY_WAIT_IF_FULL))))
+ goto err;
+ inited= true;
+ if ((err= rpl_global_gtid_binlog_state.read_from_iocache(&cache)))
+ goto err;
+ goto end;
+
+err:
+ sql_print_error("Error reading binlog GTID state from file '%s'.\n", buf);
+end:
+ if (inited)
+ end_io_cache(&cache);
+ if (opened)
+ mysql_file_close(file_no, MYF(0));
+
+ return err;
+}
+
+
+int
+MYSQL_BIN_LOG::get_most_recent_gtid_list(rpl_gtid **list, uint32 *size)
+{
+ return rpl_global_gtid_binlog_state.get_most_recent_gtid_list(list, size);
+}
+
+
+bool
+MYSQL_BIN_LOG::append_state_pos(String *str)
+{
+ return rpl_global_gtid_binlog_state.append_pos(str);
+}
+
+
+bool
+MYSQL_BIN_LOG::append_state(String *str)
+{
+ return rpl_global_gtid_binlog_state.append_state(str);
+}
+
+
+bool
+MYSQL_BIN_LOG::is_empty_state()
+{
+ return (rpl_global_gtid_binlog_state.count() == 0);
+}
+
+
+bool
+MYSQL_BIN_LOG::find_in_binlog_state(uint32 domain_id, uint32 server_id,
+ rpl_gtid *out_gtid)
+{
+ rpl_gtid *gtid;
+ if ((gtid= rpl_global_gtid_binlog_state.find(domain_id, server_id)))
+ *out_gtid= *gtid;
+ return gtid != NULL;
+}
+
+
+bool
+MYSQL_BIN_LOG::lookup_domain_in_binlog_state(uint32 domain_id,
+ rpl_gtid *out_gtid)
+{
+ rpl_gtid *found_gtid;
+
+ if ((found_gtid= rpl_global_gtid_binlog_state.find_most_recent(domain_id)))
+ {
+ *out_gtid= *found_gtid;
+ return true;
+ }
+
+ return false;
+}
+
+
+int
+MYSQL_BIN_LOG::bump_seq_no_counter_if_needed(uint32 domain_id, uint64 seq_no)
+{
+ return rpl_global_gtid_binlog_state.bump_seq_no_if_needed(domain_id, seq_no);
+}
+
+
+bool
+MYSQL_BIN_LOG::check_strict_gtid_sequence(uint32 domain_id, uint32 server_id,
+ uint64 seq_no)
+{
+ return rpl_global_gtid_binlog_state.check_strict_sequence(domain_id,
+ server_id, seq_no);
+}
+
+
/**
Write an event to the binary log. If with_annotate != NULL and
*with_annotate = TRUE write also Annotate_rows before the event
@@ -5169,11 +5953,13 @@ bool MYSQL_BIN_LOG::write(Log_event *event_info, my_bool *with_annotate)
{
THD *thd= event_info->thd;
bool error= 1;
- DBUG_ENTER("MYSQL_BIN_LOG::write(Log_event *)");
binlog_cache_data *cache_data= 0;
bool is_trans_cache= FALSE;
bool using_trans= event_info->use_trans_cache();
bool direct;
+ ulong prev_binlog_id;
+ DBUG_ENTER("MYSQL_BIN_LOG::write(Log_event *)");
+ LINT_INIT(prev_binlog_id);
#ifdef WITH_WSREP
/*
@@ -5187,6 +5973,14 @@ bool MYSQL_BIN_LOG::write(Log_event *event_info, my_bool *with_annotate)
#endif /* WITH_WSREP */
direct= event_info->use_direct_logging();
+ if (thd->variables.option_bits & OPTION_GTID_BEGIN)
+ {
+ DBUG_PRINT("info", ("OPTION_GTID_BEGIN was set"));
+ /* Wait for commit from binary log before we commit */
+ direct= 0;
+ using_trans= 1;
+ }
+
if (thd->binlog_evt_union.do_union)
{
/*
@@ -5214,7 +6008,9 @@ bool MYSQL_BIN_LOG::write(Log_event *event_info, my_bool *with_annotate)
could have changed since.
*/
#ifdef WITH_WSREP
- if ((WSREP(thd) && wsrep_emulate_bin_log) || is_open())
+ /* applier and replayer can skip writing binlog events */
+ if ((WSREP_EMULATE_BINLOG(thd) && (thd->wsrep_exec_mode != REPL_RECV)) ||
+ is_open())
#else
if (likely(is_open()))
#endif
@@ -5238,9 +6034,26 @@ bool MYSQL_BIN_LOG::write(Log_event *event_info, my_bool *with_annotate)
if (direct)
{
+ int res;
+ uint64 commit_id= 0;
+ DBUG_PRINT("info", ("direct is set"));
+ if ((res= thd->wait_for_prior_commit()))
+ DBUG_RETURN(res);
file= &log_file;
my_org_b_tell= my_b_tell(file);
mysql_mutex_lock(&LOCK_log);
+ prev_binlog_id= current_binlog_id;
+ DBUG_EXECUTE_IF("binlog_force_commit_id",
+ {
+ const LEX_STRING name= { C_STRING_WITH_LEN("commit_id") };
+ bool null_value;
+ user_var_entry *entry=
+ (user_var_entry*) my_hash_search(&thd->user_vars,
+ (uchar*) name.str, name.length);
+ commit_id= entry->val_int(&null_value);
+ });
+ if (write_gtid_event(thd, true, using_trans, commit_id))
+ goto err;
}
else
{
@@ -5384,7 +6197,7 @@ err:
mysql_mutex_unlock(&LOCK_log);
if (check_purge)
- purge();
+ checkpoint_and_purge(prev_binlog_id);
}
if (error)
@@ -5469,6 +6282,60 @@ bool general_log_write(THD *thd, enum enum_server_command command,
return FALSE;
}
+
+static void
+binlog_checkpoint_callback(void *cookie)
+{
+ MYSQL_BIN_LOG::xid_count_per_binlog *entry=
+ (MYSQL_BIN_LOG::xid_count_per_binlog *)cookie;
+ /*
+ For every supporting engine, we increment the xid_count and issue a
+ commit_checkpoint_request(). Then we can count when all
+ commit_checkpoint_notify() callbacks have occured, and then log a new
+ binlog checkpoint event.
+ */
+ mysql_bin_log.mark_xids_active(entry->binlog_id, 1);
+}
+
+
+/*
+ Request a commit checkpoint from each supporting engine.
+ This must be called after each binlog rotate, and after LOCK_log has been
+ released. The xid_count value in the xid_count_per_binlog entry was
+ incremented by 1 and will be decremented in this function; this ensures
+ that the entry will not go away early despite LOCK_log not being held.
+*/
+void
+MYSQL_BIN_LOG::do_checkpoint_request(ulong binlog_id)
+{
+ xid_count_per_binlog *entry;
+
+ /*
+ Find the binlog entry, and invoke commit_checkpoint_request() on it in
+ each supporting storage engine.
+ */
+ mysql_mutex_lock(&LOCK_xid_list);
+ I_List_iterator<xid_count_per_binlog> it(binlog_xid_count_list);
+ do {
+ entry= it++;
+ DBUG_ASSERT(entry /* binlog_id is always somewhere in the list. */);
+ } while (entry->binlog_id != binlog_id);
+ mysql_mutex_unlock(&LOCK_xid_list);
+
+ ha_commit_checkpoint_request(entry, binlog_checkpoint_callback);
+ /*
+ When we rotated the binlog, we incremented xid_count to make sure the
+ entry would not go away until this point, where we have done all necessary
+ commit_checkpoint_request() calls.
+ So now we can (and must) decrease the count - when it reaches zero, we
+ will know that both all pending unlog() and all pending
+ commit_checkpoint_notify() calls are done, and we can log a new binlog
+ checkpoint.
+ */
+ mark_xid_done(binlog_id, true);
+}
+
+
/**
The method executes rotation when LOCK_log is already acquired
by the caller.
@@ -5477,6 +6344,15 @@ bool general_log_write(THD *thd, enum enum_server_command command,
@param check_purge is set to true if rotation took place
@note
+ Caller _must_ check the check_purge variable. If this is set, it means
+ that the binlog was rotated, and caller _must_ ensure that
+ do_checkpoint_request() is called later with the binlog_id of the rotated
+ binlog file. The call to do_checkpoint_request() must happen after
+ LOCK_log is released (which is why we cannot simply do it here).
+ Usually, checkpoint_and_purge() is appropriate, as it will both handle
+ the checkpointing and any needed purging of old logs.
+
+ @note
If rotation fails, for instance the server was unable
to create a new log file, we still try to write an
incident event to the current log.
@@ -5503,7 +6379,27 @@ int MYSQL_BIN_LOG::rotate(bool force_rotate, bool* check_purge)
if (force_rotate || (my_b_tell(&log_file) >= (my_off_t) max_size))
{
+ ulong binlog_id= current_binlog_id;
+ /*
+ We rotate the binlog, so we need to start a commit checkpoint in all
+ supporting engines - when it finishes, we can log a new binlog checkpoint
+ event.
+
+ But we cannot start the checkpoint here - there could be a group commit
+ still in progress which needs to be included in the checkpoint, and
+ besides we do not want to do the (possibly expensive) checkpoint while
+ LOCK_log is held.
+
+ On the other hand, we must be sure that the xid_count entry for the
+ previous log does not go away until we start the checkpoint - which it
+ could do as it is no longer the most recent. So we increment xid_count
+ (to count the pending checkpoint request) - this will fix the entry in
+ place until we decrement again in do_checkpoint_request().
+ */
+ mark_xids_active(binlog_id, 1);
+
if ((error= new_file_without_locking()))
+ {
/**
Be conservative... There are possible lost events (eg,
failing to log the Execute_load_query_log_event
@@ -5516,7 +6412,14 @@ int MYSQL_BIN_LOG::rotate(bool force_rotate, bool* check_purge)
if (!write_incident_already_locked(current_thd))
flush_and_sync(0);
- *check_purge= true;
+ /*
+ We failed to rotate - so we have to decrement the xid_count back that
+ we incremented before attempting the rotate.
+ */
+ mark_xid_done(binlog_id, false);
+ }
+ else
+ *check_purge= true;
}
DBUG_RETURN(error);
}
@@ -5544,6 +6447,13 @@ void MYSQL_BIN_LOG::purge()
#endif
}
+
+void MYSQL_BIN_LOG::checkpoint_and_purge(ulong binlog_id)
+{
+ do_checkpoint_request(binlog_id);
+ purge();
+}
+
/**
The method is a shortcut of @c rotate() and @c purge().
LOCK_log is acquired prior to rotate and is released after it.
@@ -5556,11 +6466,13 @@ void MYSQL_BIN_LOG::purge()
int MYSQL_BIN_LOG::rotate_and_purge(bool force_rotate)
{
int error= 0;
+ ulong prev_binlog_id;
DBUG_ENTER("MYSQL_BIN_LOG::rotate_and_purge");
bool check_purge= false;
//todo: fix the macro def and restore safe_mutex_assert_not_owner(&LOCK_log);
mysql_mutex_lock(&LOCK_log);
+ prev_binlog_id= current_binlog_id;
if ((error= rotate(force_rotate, &check_purge)))
check_purge= false;
/*
@@ -5570,7 +6482,7 @@ int MYSQL_BIN_LOG::rotate_and_purge(bool force_rotate)
mysql_mutex_unlock(&LOCK_log);
if (check_purge)
- purge();
+ checkpoint_and_purge(prev_binlog_id);
DBUG_RETURN(error);
}
@@ -5639,9 +6551,10 @@ int MYSQL_BIN_LOG::write_cache(THD *thd, IO_CACHE *cache)
long val;
ulong end_log_pos_inc= 0; // each event processed adds BINLOG_CHECKSUM_LEN 2 t
uchar header[LOG_EVENT_HEADER_LEN];
- ha_checksum crc= 0, crc_0= 0; // assignments to keep compiler happy
+ ha_checksum crc= 0, crc_0= 0;
my_bool do_checksum= (binlog_checksum_options != BINLOG_CHECKSUM_ALG_OFF);
uchar buf[BINLOG_CHECKSUM_LEN];
+ DBUG_ENTER("MYSQL_BIN_LOG::write_cache");
// while there is just one alg the following must hold:
DBUG_ASSERT(!do_checksum ||
@@ -5663,9 +6576,7 @@ int MYSQL_BIN_LOG::write_cache(THD *thd, IO_CACHE *cache)
group= (uint)my_b_tell(&log_file);
hdr_offs= carry= 0;
- if (do_checksum)
- crc= crc_0= my_checksum(0L, NULL, 0);
-
+
do
{
/*
@@ -5694,7 +6605,7 @@ int MYSQL_BIN_LOG::write_cache(THD *thd, IO_CACHE *cache)
/* write the first half of the split header */
if (my_b_write(&log_file, header, carry))
- return ER_ERROR_ON_WRITE;
+ DBUG_RETURN(ER_ERROR_ON_WRITE);
status_var_add(thd->status_var.binlog_bytes_written, carry);
/*
@@ -5738,12 +6649,12 @@ int MYSQL_BIN_LOG::write_cache(THD *thd, IO_CACHE *cache)
crc= my_checksum(crc, cache->read_pos, length);
remains -= length;
if (my_b_write(&log_file, cache->read_pos, length))
- return ER_ERROR_ON_WRITE;
+ DBUG_RETURN(ER_ERROR_ON_WRITE);
if (remains == 0)
{
int4store(buf, crc);
if (my_b_write(&log_file, buf, BINLOG_CHECKSUM_LEN))
- return ER_ERROR_ON_WRITE;
+ DBUG_RETURN(ER_ERROR_ON_WRITE);
crc= crc_0;
}
}
@@ -5770,7 +6681,7 @@ int MYSQL_BIN_LOG::write_cache(THD *thd, IO_CACHE *cache)
DBUG_ASSERT(remains == 0);
if (my_b_write(&log_file, cache->read_pos, hdr_offs) ||
my_b_write(&log_file, buf, BINLOG_CHECKSUM_LEN))
- return ER_ERROR_ON_WRITE;
+ DBUG_RETURN(ER_ERROR_ON_WRITE);
crc= crc_0;
}
}
@@ -5802,12 +6713,12 @@ int MYSQL_BIN_LOG::write_cache(THD *thd, IO_CACHE *cache)
length, &crc);
if (my_b_write(&log_file, ev,
remains == 0 ? event_len : length - hdr_offs))
- return ER_ERROR_ON_WRITE;
+ DBUG_RETURN(ER_ERROR_ON_WRITE);
if (remains == 0)
{
int4store(buf, crc);
if (my_b_write(&log_file, buf, BINLOG_CHECKSUM_LEN))
- return ER_ERROR_ON_WRITE;
+ DBUG_RETURN(ER_ERROR_ON_WRITE);
crc= crc_0; // crc is complete
}
}
@@ -5832,10 +6743,10 @@ int MYSQL_BIN_LOG::write_cache(THD *thd, IO_CACHE *cache)
/* Write data to the binary log file */
DBUG_EXECUTE_IF("fail_binlog_write_1",
- errno= 28; return ER_ERROR_ON_WRITE;);
+ errno= 28; DBUG_RETURN(ER_ERROR_ON_WRITE););
if (!do_checksum)
if (my_b_write(&log_file, cache->read_pos, length))
- return ER_ERROR_ON_WRITE;
+ DBUG_RETURN(ER_ERROR_ON_WRITE);
status_var_add(thd->status_var.binlog_bytes_written, length);
cache->read_pos=cache->read_end; // Mark buffer used up
@@ -5845,7 +6756,7 @@ int MYSQL_BIN_LOG::write_cache(THD *thd, IO_CACHE *cache)
DBUG_ASSERT(!do_checksum || remains == 0);
DBUG_ASSERT(!do_checksum || crc == crc_0);
- return 0; // All OK
+ DBUG_RETURN(0); // All OK
}
/*
@@ -5857,9 +6768,9 @@ int query_error_code(THD *thd, bool not_killed)
if (not_killed || (killed_mask_hard(thd->killed) == KILL_BAD_DATA))
{
- error= thd->is_error() ? thd->stmt_da->sql_errno() : 0;
+ error= thd->is_error() ? thd->get_stmt_da()->sql_errno() : 0;
- /* thd->stmt_da->sql_errno() might be ER_SERVER_SHUTDOWN or
+ /* thd->get_get_stmt_da()->sql_errno() might be ER_SERVER_SHUTDOWN or
ER_QUERY_INTERRUPTED, So here we need to make sure that error
is not set to these errors when specified not_killed by the
caller.
@@ -5901,11 +6812,13 @@ bool MYSQL_BIN_LOG::write_incident(THD *thd)
uint error= 0;
my_off_t offset;
bool check_purge= false;
+ ulong prev_binlog_id;
DBUG_ENTER("MYSQL_BIN_LOG::write_incident");
mysql_mutex_lock(&LOCK_log);
if (likely(is_open()))
{
+ prev_binlog_id= current_binlog_id;
if (!(error= write_incident_already_locked(thd)) &&
!(error= flush_and_sync(0)))
{
@@ -5925,7 +6838,7 @@ bool MYSQL_BIN_LOG::write_incident(THD *thd)
mysql_mutex_unlock(&LOCK_log);
if (check_purge)
- purge();
+ checkpoint_and_purge(prev_binlog_id);
}
else
{
@@ -5935,6 +6848,45 @@ bool MYSQL_BIN_LOG::write_incident(THD *thd)
DBUG_RETURN(error);
}
+void
+MYSQL_BIN_LOG::write_binlog_checkpoint_event_already_locked(const char *name,
+ uint len)
+{
+ my_off_t offset;
+ Binlog_checkpoint_log_event ev(name, len);
+ /*
+ Note that we must sync the binlog checkpoint to disk.
+ Otherwise a subsequent log purge could delete binlogs that XA recovery
+ thinks are needed (even though they are not really).
+ */
+ if (!ev.write(&log_file) && !flush_and_sync(0))
+ {
+ signal_update();
+ }
+ else
+ {
+ /*
+ If we fail to write the checkpoint event, something is probably really
+ bad with the binlog. We complain in the error log.
+
+ Note that failure to write binlog checkpoint does not compromise the
+ ability to do crash recovery - crash recovery will just have to scan a
+ bit more of the binlog than strictly necessary.
+ */
+ sql_print_error("Failed to write binlog checkpoint event to binary log\n");
+ }
+
+ offset= my_b_tell(&log_file);
+ /*
+ Take mutex to protect against a reader seeing partial writes of 64-bit
+ offset on 32-bit CPUs.
+ */
+ mysql_mutex_lock(&LOCK_commit_ordered);
+ last_commit_pos_offset= offset;
+ mysql_mutex_unlock(&LOCK_commit_ordered);
+}
+
+
/**
Write a cached log entry to the binary log.
- To support transaction over replication, we wrap the transaction
@@ -5967,6 +6919,7 @@ MYSQL_BIN_LOG::write_transaction_to_binlog(THD *thd,
bool using_trx_cache)
{
group_commit_entry entry;
+ Ha_trx_info *ha_info;
DBUG_ENTER("MYSQL_BIN_LOG::write_transaction_to_binlog");
#ifdef WITH_WSREP
@@ -5982,20 +6935,17 @@ MYSQL_BIN_LOG::write_transaction_to_binlog(THD *thd,
entry.all= all;
entry.using_stmt_cache= using_stmt_cache;
entry.using_trx_cache= using_trx_cache;
+ entry.need_unlog= false;
+ ha_info= all ? thd->transaction.all.ha_list : thd->transaction.stmt.ha_list;
- /*
- Log "BEGIN" at the beginning of every transaction. Here, a transaction is
- either a BEGIN..COMMIT block or a single statement in autocommit mode.
-
- Create the necessary events here, where we have the correct THD (and
- thread context).
+ for (; ha_info; ha_info= ha_info->next())
+ {
+ if (ha_info->is_started() && ha_info->ht() != binlog_hton &&
+ !ha_info->ht()->commit_checkpoint_request)
+ entry.need_unlog= true;
+ break;
+ }
- Due to group commit the actual writing to binlog may happen in a different
- thread.
- */
- Query_log_event qinfo(thd, STRING_WITH_LEN("BEGIN"), using_trx_cache, TRUE,
- TRUE, 0);
- entry.begin_event= &qinfo;
entry.end_event= end_ev;
if (cache_mngr->stmt_cache.has_incident() ||
cache_mngr->trx_cache.has_incident())
@@ -6011,45 +6961,343 @@ MYSQL_BIN_LOG::write_transaction_to_binlog(THD *thd,
}
}
-bool
-MYSQL_BIN_LOG::write_transaction_to_binlog_events(group_commit_entry *entry)
+
+/*
+ Put a transaction that is ready to commit in the group commit queue.
+ The transaction is identified by the ENTRY object passed into this function.
+
+ To facilitate group commit for the binlog, we first queue up ourselves in
+ this function. Then later the first thread to enter the queue waits for
+ the LOCK_log mutex, and commits for everyone in the queue once it gets the
+ lock. Any other threads in the queue just wait for the first one to finish
+ the commit and wake them up. This way, all transactions in the queue get
+ committed in a single disk operation.
+
+ The main work in this function is when the commit in one transaction has
+ been marked to wait for the commit of another transaction to happen
+ first. This is used to support in-order parallel replication, where
+ transactions can execute out-of-order but need to be committed in-order with
+ how they happened on the master. The waiting of one commit on another needs
+ to be integrated with the group commit queue, to ensure that the waiting
+ transaction can participate in the same group commit as the waited-for
+ transaction.
+
+ So when we put a transaction in the queue, we check if there were other
+ transactions already prepared to commit but just waiting for the first one
+ to commit. If so, we add those to the queue as well, transitively for all
+ waiters.
+
+ And if a transaction is marked to wait for a prior transaction, but that
+ prior transaction is already queued for group commit, then we can queue the
+ new transaction directly to participate in the group commit.
+
+ @retval < 0 Error
+ @retval > 0 If queued as the first entry in the queue (meaning this
+ is the leader)
+ @retval 0 Otherwise (queued as participant, leader handles the commit)
+*/
+
+int
+MYSQL_BIN_LOG::queue_for_group_commit(group_commit_entry *orig_entry)
{
+ group_commit_entry *entry, *orig_queue, *last;
+ wait_for_commit *cur;
+ wait_for_commit *wfc;
+ DBUG_ENTER("MYSQL_BIN_LOG::queue_for_group_commit");
+
/*
- To facilitate group commit for the binlog, we first queue up ourselves in
- the group commit queue. Then the first thread to enter the queue waits for
- the LOCK_log mutex, and commits for everyone in the queue once it gets the
- lock. Any other threads in the queue just wait for the first one to finish
- the commit and wake them up.
+ Check if we need to wait for another transaction to commit before us.
+
+ It is safe to do a quick check without lock first in the case where we do
+ not have to wait. But if the quick check shows we need to wait, we must do
+ another safe check under lock, to avoid the race where the other
+ transaction wakes us up between the check and the wait.
*/
+ wfc= orig_entry->thd->wait_for_commit_ptr;
+ orig_entry->queued_by_other= false;
+ if (wfc && wfc->waitee)
+ {
+ mysql_mutex_lock(&wfc->LOCK_wait_commit);
+ /*
+ Do an extra check here, this time safely under lock.
+
+ If waitee->commit_started is set, it means that the transaction we need
+ to wait for has already queued up for group commit. In this case it is
+ safe for us to queue up immediately as well, increasing the opprtunities
+ for group commit. Because waitee has taken the LOCK_prepare_ordered
+ before setting the flag, so there is no risk that we can queue ahead of
+ it.
+ */
+ if (wfc->waitee && !wfc->waitee->commit_started)
+ {
+ PSI_stage_info old_stage;
+ wait_for_commit *loc_waitee;
+
+ /*
+ By setting wfc->opaque_pointer to our own entry, we mark that we are
+ ready to commit, but waiting for another transaction to commit before
+ us.
+
+ This other transaction may then take over the commit process for us to
+ get us included in its own group commit. If this happens, the
+ queued_by_other flag is set.
+
+ Setting this flag may or may not be seen by the other thread, but we
+ are safe in any case: The other thread will set queued_by_other under
+ its LOCK_wait_commit, and we will not check queued_by_other only after
+ we have been woken up.
+ */
+ wfc->opaque_pointer= orig_entry;
+ DEBUG_SYNC(orig_entry->thd, "group_commit_waiting_for_prior");
+ orig_entry->thd->ENTER_COND(&wfc->COND_wait_commit,
+ &wfc->LOCK_wait_commit,
+ &stage_waiting_for_prior_transaction_to_commit,
+ &old_stage);
+ while ((loc_waitee= wfc->waitee) && !orig_entry->thd->check_killed())
+ mysql_cond_wait(&wfc->COND_wait_commit, &wfc->LOCK_wait_commit);
+ wfc->opaque_pointer= NULL;
+ DBUG_PRINT("info", ("After waiting for prior commit, queued_by_other=%d",
+ orig_entry->queued_by_other));
+
+ if (loc_waitee)
+ {
+ /* Wait terminated due to kill. */
+ mysql_mutex_lock(&loc_waitee->LOCK_wait_commit);
+ if (loc_waitee->wakeup_subsequent_commits_running ||
+ orig_entry->queued_by_other)
+ {
+ /* Our waitee is already waking us up, so ignore the kill. */
+ mysql_mutex_unlock(&loc_waitee->LOCK_wait_commit);
+ do
+ {
+ mysql_cond_wait(&wfc->COND_wait_commit, &wfc->LOCK_wait_commit);
+ } while (wfc->waitee);
+ }
+ else
+ {
+ /* We were killed, so remove us from the list of waitee. */
+ wfc->remove_from_list(&loc_waitee->subsequent_commits_list);
+ mysql_mutex_unlock(&loc_waitee->LOCK_wait_commit);
+ wfc->waitee= NULL;
+
+ orig_entry->thd->EXIT_COND(&old_stage);
+ /* Interrupted by kill. */
+ DEBUG_SYNC(orig_entry->thd, "group_commit_waiting_for_prior_killed");
+ wfc->wakeup_error= orig_entry->thd->killed_errno();
+ if (!wfc->wakeup_error)
+ wfc->wakeup_error= ER_QUERY_INTERRUPTED;
+ my_message(wfc->wakeup_error, ER(wfc->wakeup_error), MYF(0));
+ DBUG_RETURN(-1);
+ }
+ }
+ orig_entry->thd->EXIT_COND(&old_stage);
+ }
+ else
+ mysql_mutex_unlock(&wfc->LOCK_wait_commit);
+ }
+ /*
+ If the transaction we were waiting for has already put us into the group
+ commit queue (and possibly already done the entire binlog commit for us),
+ then there is nothing else to do.
+ */
+ if (orig_entry->queued_by_other)
+ DBUG_RETURN(0);
- entry->thd->clear_wakeup_ready();
+ if (wfc && wfc->wakeup_error)
+ {
+ my_error(ER_PRIOR_COMMIT_FAILED, MYF(0));
+ DBUG_RETURN(-1);
+ }
+
+ /* Now enqueue ourselves in the group commit queue. */
+ DEBUG_SYNC(orig_entry->thd, "commit_before_enqueue");
+ orig_entry->thd->clear_wakeup_ready();
mysql_mutex_lock(&LOCK_prepare_ordered);
- group_commit_entry *orig_queue= group_commit_queue;
- entry->next= orig_queue;
- group_commit_queue= entry;
+ orig_queue= group_commit_queue;
+
+ /*
+ Iteratively process everything added to the queue, looking for waiters,
+ and their waiters, and so on. If a waiter is ready to commit, we
+ immediately add it to the queue, and mark it as queued_by_other.
+
+ This would be natural to do with recursion, but we want to avoid
+ potentially unbounded recursion blowing the C stack, so we use the list
+ approach instead.
+
+ We keep a list of the group_commit_entry of all the waiters that need to
+ be processed. Initially this list contains only the entry passed into this
+ function.
+
+ We process entries in the list one by one. The element currently being
+ processed is pointed to by `entry`, and the element at the end of the list
+ is pointed to by `last` (we do not use NULL to terminate the list).
+
+ As we process an entry, any waiters for that entry are added at the end of
+ the list, to be processed in subsequent iterations. The the entry is added
+ to the group_commit_queue. This continues until the list is exhausted,
+ with all entries ever added eventually processed.
+
+ The end result is a breath-first traversal of the tree of waiters,
+ re-using the `next' pointers of the group_commit_entry objects in place of
+ extra stack space in a recursive traversal.
+
+ The temporary list linked through these `next' pointers is not used by the
+ caller or any other function; it only exists while doing the iterative
+ tree traversal. After, all the processed entries are linked into the
+ group_commit_queue.
+ */
- if (entry->cache_mngr->using_xa)
+ cur= wfc;
+ last= orig_entry;
+ entry= orig_entry;
+ for (;;)
{
- DEBUG_SYNC(entry->thd, "commit_before_prepare_ordered");
- run_prepare_ordered(entry->thd, entry->all);
- DEBUG_SYNC(entry->thd, "commit_after_prepare_ordered");
+ group_commit_entry *next_entry;
+
+ if (entry->cache_mngr->using_xa)
+ {
+ DEBUG_SYNC(entry->thd, "commit_before_prepare_ordered");
+ run_prepare_ordered(entry->thd, entry->all);
+ DEBUG_SYNC(entry->thd, "commit_after_prepare_ordered");
+ }
+
+ if (cur)
+ {
+ /*
+ Now that we have taken LOCK_prepare_ordered and will queue up in the
+ group commit queue, it is safe for following transactions to queue
+ themselves. We will grab here any transaction that is now ready to
+ queue up, but after that, more transactions may become ready while the
+ leader is waiting to start the group commit. So set the flag
+ `commit_started', so that later transactions can still participate in
+ the group commit..
+ */
+ cur->commit_started= true;
+
+ /*
+ Check if this transaction has other transaction waiting for it to
+ commit.
+
+ If so, process the waiting transactions, and their waiters and so on,
+ transitively.
+ */
+ if (cur->subsequent_commits_list)
+ {
+ wait_for_commit *waiter, **waiter_ptr;
+
+ mysql_mutex_lock(&cur->LOCK_wait_commit);
+ /*
+ Grab the list, now safely under lock, and process it if still
+ non-empty.
+ */
+ waiter= cur->subsequent_commits_list;
+ waiter_ptr= &cur->subsequent_commits_list;
+ while (waiter)
+ {
+ wait_for_commit *next_waiter= waiter->next_subsequent_commit;
+ group_commit_entry *entry2=
+ (group_commit_entry *)waiter->opaque_pointer;
+ if (entry2)
+ {
+ /*
+ This is another transaction ready to be written to the binary
+ log. We can put it into the queue directly, without needing a
+ separate context switch to the other thread. We just set a flag
+ so that the other thread will know when it wakes up that it was
+ already processed.
+
+ So remove it from the list of our waiters, and instead put it at
+ the end of the list to be processed in a subsequent iteration of
+ the outer loop.
+ */
+ *waiter_ptr= next_waiter;
+ entry2->queued_by_other= true;
+ last->next= entry2;
+ last= entry2;
+ /*
+ As a small optimisation, we do not actually need to set
+ entry2->next to NULL, as we can use the pointer `last' to check
+ for end-of-list.
+ */
+ }
+ else
+ {
+ /*
+ This transaction is not ready to participate in the group commit
+ yet, so leave it in the waiter list. It might join the group
+ commit later, if it completes soon enough to do so (it will see
+ our wfc->commit_started flag set), or it might commit later in a
+ later group commit.
+ */
+ waiter_ptr= &waiter->next_subsequent_commit;
+ }
+ waiter= next_waiter;
+ }
+ mysql_mutex_unlock(&cur->LOCK_wait_commit);
+ }
+ }
+
+ /*
+ Handle the heuristics that if another transaction is waiting for this
+ transaction (or if it does so later), then we want to trigger group
+ commit immediately, without waiting for the binlog_commit_wait_usec
+ timeout to expire.
+ */
+ entry->thd->waiting_on_group_commit= true;
+
+ /* Add the entry to the group commit queue. */
+ next_entry= entry->next;
+ entry->next= group_commit_queue;
+ group_commit_queue= entry;
+ if (entry == last)
+ break;
+ /*
+ Move to the next entry in the flattened list of waiting transactions
+ that still need to be processed transitively.
+ */
+ entry= next_entry;
+ DBUG_ASSERT(entry != NULL);
+ cur= entry->thd->wait_for_commit_ptr;
}
+
+ if (opt_binlog_commit_wait_count > 0 && orig_queue != NULL)
+ mysql_cond_signal(&COND_prepare_ordered);
mysql_mutex_unlock(&LOCK_prepare_ordered);
- DEBUG_SYNC(entry->thd, "commit_after_release_LOCK_prepare_ordered");
+ DEBUG_SYNC(orig_entry->thd, "commit_after_release_LOCK_prepare_ordered");
+
+ DBUG_PRINT("info", ("Queued for group commit as %s\n",
+ (orig_queue == NULL) ? "leader" : "participant"));
+ DBUG_RETURN(orig_queue == NULL);
+}
+
+bool
+MYSQL_BIN_LOG::write_transaction_to_binlog_events(group_commit_entry *entry)
+{
+ int is_leader= queue_for_group_commit(entry);
/*
- The first in the queue handle group commit for all; the others just wait
+ The first in the queue handles group commit for all; the others just wait
to be signalled when group commit is done.
*/
- if (orig_queue != NULL)
+ if (is_leader < 0)
+ return true; /* Error */
+ else if (is_leader)
+ trx_group_commit_leader(entry);
+ else if (!entry->queued_by_other)
entry->thd->wait_for_wakeup_ready();
else
- trx_group_commit_leader(entry);
+ {
+ /*
+ If we were queued by another prior commit, then we are woken up
+ only when the leader has already completed the commit for us.
+ So nothing to do here then.
+ */
+ }
if (!opt_optimize_thread_scheduling)
{
/* For the leader, trx_group_commit_leader() already took the lock. */
- if (orig_queue != NULL)
+ if (!is_leader)
mysql_mutex_lock(&LOCK_commit_ordered);
DEBUG_SYNC(entry->thd, "commit_loop_entry_commit_ordered");
@@ -6065,15 +7313,41 @@ MYSQL_BIN_LOG::write_transaction_to_binlog_events(group_commit_entry *entry)
DEBUG_SYNC(entry->thd, "commit_after_group_run_commit_ordered");
}
mysql_mutex_unlock(&LOCK_commit_ordered);
+ entry->thd->wakeup_subsequent_commits(entry->error);
if (next)
{
- next->thd->signal_wakeup_ready();
+ /*
+ Wake up the next thread in the group commit.
+
+ The next thread can be waiting in two different ways, depending on
+ whether it put itself in the queue, or if it was put in queue by us
+ because it had to wait for us to commit first.
+
+ So execute the appropriate wakeup, identified by the queued_by_other
+ field.
+ */
+ if (next->queued_by_other)
+ next->thd->wait_for_commit_ptr->wakeup(entry->error);
+ else
+ next->thd->signal_wakeup_ready();
+ }
+ else
+ {
+ /*
+ If we rotated the binlog, and if we are using the unoptimized thread
+ scheduling where every thread runs its own commit_ordered(), then we
+ must do the commit checkpoint and log purge here, after all
+ commit_ordered() calls have finished, and locks have been released.
+ */
+ if (entry->check_purge)
+ checkpoint_and_purge(entry->binlog_id);
}
+
}
if (likely(!entry->error))
- return 0;
+ return entry->thd->wait_for_prior_commit();
switch (entry->error)
{
@@ -6100,8 +7374,9 @@ MYSQL_BIN_LOG::write_transaction_to_binlog_events(group_commit_entry *entry)
we need to mark it as not needed for recovery (unlog() is not called
for a transaction if log_xid() fails).
*/
- if (entry->cache_mngr->using_xa && entry->cache_mngr->xa_xid)
- mark_xid_done();
+ if (entry->cache_mngr->using_xa && entry->cache_mngr->xa_xid &&
+ entry->cache_mngr->need_unlog)
+ mark_xid_done(entry->cache_mngr->binlog_id, true);
return 1;
}
@@ -6121,32 +7396,49 @@ MYSQL_BIN_LOG::trx_group_commit_leader(group_commit_entry *leader)
{
uint xid_count= 0;
my_off_t UNINIT_VAR(commit_offset);
- group_commit_entry *current;
- group_commit_entry *last_in_queue;
+ group_commit_entry *current, *last_in_queue;
group_commit_entry *queue= NULL;
bool check_purge= false;
+ ulong binlog_id;
+ uint64 commit_id;
DBUG_ENTER("MYSQL_BIN_LOG::trx_group_commit_leader");
+ LINT_INIT(binlog_id);
- DBUG_ASSERT(is_open());
- if (likely(is_open())) // Should always be true
{
+ DBUG_EXECUTE_IF("inject_binlog_commit_before_get_LOCK_log",
+ DBUG_ASSERT(!debug_sync_set_action(leader->thd, STRING_WITH_LEN
+ ("commit_before_get_LOCK_log SIGNAL waiting WAIT_FOR cont TIMEOUT 1")));
+ );
/*
Lock the LOCK_log(), and once we get it, collect any additional writes
that queued up while we were waiting.
*/
+ DEBUG_SYNC(leader->thd, "commit_before_get_LOCK_log");
mysql_mutex_lock(&LOCK_log);
DEBUG_SYNC(leader->thd, "commit_after_get_LOCK_log");
mysql_mutex_lock(&LOCK_prepare_ordered);
+ if (opt_binlog_commit_wait_count)
+ wait_for_sufficient_commits();
+ /*
+ Note that wait_for_sufficient_commits() may have released and
+ re-acquired the LOCK_log and LOCK_prepare_ordered if it needed to wait.
+ */
current= group_commit_queue;
group_commit_queue= NULL;
mysql_mutex_unlock(&LOCK_prepare_ordered);
+ binlog_id= current_binlog_id;
/* As the queue is in reverse order of entering, reverse it. */
last_in_queue= current;
while (current)
{
group_commit_entry *next= current->next;
+ /*
+ Now that group commit is started, we can clear the flag; there is no
+ longer any use in waiters on this commit trying to trigger it early.
+ */
+ current->thd->waiting_on_group_commit= false;
current->next= queue;
queue= current;
current= next;
@@ -6154,7 +7446,21 @@ MYSQL_BIN_LOG::trx_group_commit_leader(group_commit_entry *leader)
DBUG_ASSERT(leader == queue /* the leader should be first in queue */);
/* Now we have in queue the list of transactions to be committed in order. */
+ }
+ DBUG_ASSERT(is_open());
+ if (likely(is_open())) // Should always be true
+ {
+ commit_id= (last_in_queue == leader ? 0 : (uint64)leader->thd->query_id);
+ DBUG_EXECUTE_IF("binlog_force_commit_id",
+ {
+ const LEX_STRING name= { C_STRING_WITH_LEN("commit_id") };
+ bool null_value;
+ user_var_entry *entry=
+ (user_var_entry*) my_hash_search(&leader->thd->user_vars,
+ (uchar*) name.str, name.length);
+ commit_id= entry->val_int(&null_value);
+ });
/*
Commit every transaction in the queue.
@@ -6175,13 +7481,31 @@ MYSQL_BIN_LOG::trx_group_commit_leader(group_commit_entry *leader)
*/
DBUG_ASSERT(!cache_mngr->stmt_cache.empty() || !cache_mngr->trx_cache.empty());
- current->error= write_transaction_or_stmt(current);
+ if ((current->error= write_transaction_or_stmt(current, commit_id)))
+ current->commit_errno= errno;
strmake_buf(cache_mngr->last_commit_pos_file, log_file_name);
commit_offset= my_b_write_tell(&log_file);
cache_mngr->last_commit_pos_offset= commit_offset;
if (cache_mngr->using_xa && cache_mngr->xa_xid)
- xid_count++;
+ {
+ /*
+ If all storage engines support commit_checkpoint_request(), then we
+ do not need to keep track of when this XID is durably committed.
+ Instead we will just ask the storage engine to durably commit all its
+ XIDs when we rotate a binlog file.
+ */
+ if (current->need_unlog)
+ {
+ xid_count++;
+ cache_mngr->need_unlog= true;
+ cache_mngr->binlog_id= binlog_id;
+ }
+ else
+ cache_mngr->need_unlog= false;
+
+ cache_mngr->delayed_error= false;
+ }
}
bool synced= 0;
@@ -6224,33 +7548,37 @@ MYSQL_BIN_LOG::trx_group_commit_leader(group_commit_entry *leader)
}
/*
- if any commit_events are Xid_log_event, increase the number of
- prepared_xids (it's decreased in ::unlog()). Binlog cannot be rotated
- if there're prepared xids in it - see the comment in new_file() for
- an explanation.
- If no Xid_log_events (then it's all Query_log_event) rotate binlog,
- if necessary.
+ If any commit_events are Xid_log_event, increase the number of pending
+ XIDs in current binlog (it's decreased in ::unlog()). When the count in
+ a (not active) binlog file reaches zero, we know that it is no longer
+ needed in XA recovery, and we can log a new binlog checkpoint event.
*/
if (xid_count > 0)
{
- mark_xids_active(xid_count);
+ mark_xids_active(binlog_id, xid_count);
}
- else
+
+ if (rotate(false, &check_purge))
{
- if (rotate(false, &check_purge))
- {
- /*
- If we fail to rotate, which thread should get the error?
- We give the error to the *last* transaction thread; that seems to
- make the most sense, as it was the last to write to the log.
- */
- last_in_queue->error= ER_ERROR_ON_WRITE;
- last_in_queue->commit_errno= errno;
- check_purge= false;
- }
- /* In case of binlog rotate, update the correct current binlog offset. */
- commit_offset= my_b_write_tell(&log_file);
+ /*
+ If we fail to rotate, which thread should get the error?
+ We give the error to the leader, as any my_error() thrown inside
+ rotate() will have been registered for the leader THD.
+
+ However we must not return error from here - that would cause
+ ha_commit_trans() to abort and rollback the transaction, which would
+ leave an inconsistent state with the transaction committed in the
+ binlog but rolled back in the engine.
+
+ Instead set a flag so that we can return error later, from unlog(),
+ when the transaction has been safely committed in the engine.
+ */
+ leader->cache_mngr->delayed_error= true;
+ my_error(ER_ERROR_ON_WRITE, MYF(ME_NOREFRESH), name, errno);
+ check_purge= false;
}
+ /* In case of binlog rotate, update the correct current binlog offset. */
+ commit_offset= my_b_write_tell(&log_file);
}
DEBUG_SYNC(leader->thd, "commit_before_get_LOCK_commit_ordered");
@@ -6264,9 +7592,6 @@ MYSQL_BIN_LOG::trx_group_commit_leader(group_commit_entry *leader)
*/
mysql_mutex_unlock(&LOCK_log);
- if (check_purge)
- purge();
-
DEBUG_SYNC(leader->thd, "commit_after_release_LOCK_log");
++num_group_commits;
@@ -6284,6 +7609,15 @@ MYSQL_BIN_LOG::trx_group_commit_leader(group_commit_entry *leader)
mysql_cond_wait(&COND_queue_busy, &LOCK_commit_ordered);
group_commit_queue_busy= TRUE;
+ /*
+ Set these so parent can run checkpoint_and_purge() in last thread.
+ (When using optimized thread scheduling, we run checkpoint_and_purge()
+ in this function, so parent does not need to and we need not set these
+ values).
+ */
+ last_in_queue->check_purge= check_purge;
+ last_in_queue->binlog_id= binlog_id;
+
/* Note that we return with LOCK_commit_ordered locked! */
DBUG_VOID_RETURN;
}
@@ -6299,8 +7633,10 @@ MYSQL_BIN_LOG::trx_group_commit_leader(group_commit_entry *leader)
DEBUG_SYNC(leader->thd, "commit_loop_entry_commit_ordered");
++num_commits;
- if (current->cache_mngr->using_xa && !current->error)
+ if (current->cache_mngr->using_xa && !current->error &&
+ DBUG_EVALUATE_IF("skip_commit_ordered", 0, 1))
run_commit_ordered(current->thd, current->all);
+ current->thd->wakeup_subsequent_commits(current->error);
/*
Careful not to access current->next after waking up the other thread! As
@@ -6308,32 +7644,40 @@ MYSQL_BIN_LOG::trx_group_commit_leader(group_commit_entry *leader)
*/
next= current->next;
if (current != leader) // Don't wake up ourself
- current->thd->signal_wakeup_ready();
+ {
+ if (current->queued_by_other)
+ current->thd->wait_for_commit_ptr->wakeup(current->error);
+ else
+ current->thd->signal_wakeup_ready();
+ }
current= next;
}
DEBUG_SYNC(leader->thd, "commit_after_group_run_commit_ordered");
mysql_mutex_unlock(&LOCK_commit_ordered);
+ DEBUG_SYNC(leader->thd, "commit_after_group_release_commit_ordered");
+
+ if (check_purge)
+ checkpoint_and_purge(binlog_id);
DBUG_VOID_RETURN;
}
int
-MYSQL_BIN_LOG::write_transaction_or_stmt(group_commit_entry *entry)
+MYSQL_BIN_LOG::write_transaction_or_stmt(group_commit_entry *entry,
+ uint64 commit_id)
{
binlog_cache_mngr *mngr= entry->cache_mngr;
+ DBUG_ENTER("MYSQL_BIN_LOG::write_transaction_or_stmt");
- if (entry->begin_event->write(&log_file))
- return ER_ERROR_ON_WRITE;
- status_var_add(entry->thd->status_var.binlog_bytes_written,
- entry->begin_event->data_written);
+ if (write_gtid_event(entry->thd, false, entry->using_trx_cache, commit_id))
+ DBUG_RETURN(ER_ERROR_ON_WRITE);
if (entry->using_stmt_cache && !mngr->stmt_cache.empty() &&
write_cache(entry->thd, mngr->get_binlog_cache_log(FALSE)))
{
entry->error_cache= &mngr->stmt_cache.cache_log;
- entry->commit_errno= errno;
- return ER_ERROR_ON_WRITE;
+ DBUG_RETURN(ER_ERROR_ON_WRITE);
}
if (entry->using_trx_cache && !mngr->trx_cache.empty())
@@ -6353,16 +7697,21 @@ MYSQL_BIN_LOG::write_transaction_or_stmt(group_commit_entry *entry)
if (write_cache(entry->thd, mngr->get_binlog_cache_log(TRUE)))
{
entry->error_cache= &mngr->trx_cache.cache_log;
- entry->commit_errno= errno;
- return ER_ERROR_ON_WRITE;
+ DBUG_RETURN(ER_ERROR_ON_WRITE);
}
}
+ DBUG_EXECUTE_IF("inject_error_writing_xid",
+ {
+ entry->error_cache= NULL;
+ errno= 28;
+ DBUG_RETURN(ER_ERROR_ON_WRITE);
+ });
+
if (entry->end_event->write(&log_file))
{
entry->error_cache= NULL;
- entry->commit_errno= errno;
- return ER_ERROR_ON_WRITE;
+ DBUG_RETURN(ER_ERROR_ON_WRITE);
}
status_var_add(entry->thd->status_var.binlog_bytes_written,
entry->end_event->data_written);
@@ -6372,27 +7721,156 @@ MYSQL_BIN_LOG::write_transaction_or_stmt(group_commit_entry *entry)
if (entry->incident_event->write(&log_file))
{
entry->error_cache= NULL;
- entry->commit_errno= errno;
- return ER_ERROR_ON_WRITE;
+ DBUG_RETURN(ER_ERROR_ON_WRITE);
}
}
if (mngr->get_binlog_cache_log(FALSE)->error) // Error on read
{
entry->error_cache= &mngr->stmt_cache.cache_log;
- entry->commit_errno= errno;
- return ER_ERROR_ON_READ;
+ DBUG_RETURN(ER_ERROR_ON_WRITE);
}
if (mngr->get_binlog_cache_log(TRUE)->error) // Error on read
{
entry->error_cache= &mngr->trx_cache.cache_log;
- entry->commit_errno= errno;
- return ER_ERROR_ON_READ;
+ DBUG_RETURN(ER_ERROR_ON_WRITE);
}
- return 0;
+ DBUG_RETURN(0);
+}
+
+
+/*
+ Wait for sufficient commits to queue up for group commit, according to the
+ values of binlog_commit_wait_count and binlog_commit_wait_usec.
+
+ Note that this function may release and re-acquire LOCK_log and
+ LOCK_prepare_ordered if it needs to wait.
+*/
+
+void
+MYSQL_BIN_LOG::wait_for_sufficient_commits()
+{
+ size_t count;
+ group_commit_entry *e;
+ group_commit_entry *last_head;
+ struct timespec wait_until;
+
+ mysql_mutex_assert_owner(&LOCK_log);
+ mysql_mutex_assert_owner(&LOCK_prepare_ordered);
+
+ for (e= last_head= group_commit_queue, count= 0; e; e= e->next)
+ {
+ if (++count >= opt_binlog_commit_wait_count)
+ {
+ group_commit_trigger_count++;
+ return;
+ }
+ if (unlikely(e->thd->has_waiter))
+ {
+ group_commit_trigger_lock_wait++;
+ return;
+ }
+ }
+
+ mysql_mutex_unlock(&LOCK_log);
+ set_timespec_nsec(wait_until, (ulonglong)1000*opt_binlog_commit_wait_usec);
+
+ for (;;)
+ {
+ int err;
+ group_commit_entry *head;
+
+ err= mysql_cond_timedwait(&COND_prepare_ordered, &LOCK_prepare_ordered,
+ &wait_until);
+ if (err == ETIMEDOUT)
+ {
+ group_commit_trigger_timeout++;
+ break;
+ }
+ if (unlikely(last_head->thd->has_waiter))
+ {
+ group_commit_trigger_lock_wait++;
+ break;
+ }
+ head= group_commit_queue;
+ for (e= head; e && e != last_head; e= e->next)
+ {
+ ++count;
+ if (unlikely(e->thd->has_waiter))
+ {
+ group_commit_trigger_lock_wait++;
+ goto after_loop;
+ }
+ }
+ if (count >= opt_binlog_commit_wait_count)
+ {
+ group_commit_trigger_count++;
+ break;
+ }
+ last_head= head;
+ }
+after_loop:
+
+ /*
+ We must not wait for LOCK_log while holding LOCK_prepare_ordered.
+ LOCK_log can be held for long periods (eg. we do I/O under it), while
+ LOCK_prepare_ordered must only be held for short periods.
+
+ In addition, waiting for LOCK_log while holding LOCK_prepare_ordered would
+ violate locking order of LOCK_log-before-LOCK_prepare_ordered. This could
+ cause SAFEMUTEX warnings (even if it cannot actually deadlock with current
+ code, as there can be at most one group commit leader thread at a time).
+
+ So release and re-acquire LOCK_prepare_ordered if we need to wait for the
+ LOCK_log.
+ */
+ if (mysql_mutex_trylock(&LOCK_log))
+ {
+ mysql_mutex_unlock(&LOCK_prepare_ordered);
+ mysql_mutex_lock(&LOCK_log);
+ mysql_mutex_lock(&LOCK_prepare_ordered);
+ }
+}
+
+
+void
+MYSQL_BIN_LOG::binlog_trigger_immediate_group_commit()
+{
+ group_commit_entry *head;
+ mysql_mutex_assert_owner(&LOCK_prepare_ordered);
+ head= group_commit_queue;
+ if (head)
+ {
+ head->thd->has_waiter= true;
+ mysql_cond_signal(&COND_prepare_ordered);
+ }
}
+
+/*
+ This function is called when a transaction T1 goes to wait for another
+ transaction T2. It is used to cut short any binlog group commit delay from
+ --binlog-commit-wait-count in the case where another transaction is stalled
+ on the wait due to conflicting row locks.
+
+ If T2 is already ready to group commit, any waiting group commit will be
+ signalled to proceed immediately. Otherwise, a flag will be set in T2, and
+ when T2 later becomes ready, immediate group commit will be triggered.
+*/
+void
+binlog_report_wait_for(THD *thd1, THD *thd2)
+{
+ if (opt_binlog_commit_wait_count == 0)
+ return;
+ mysql_mutex_lock(&LOCK_prepare_ordered);
+ thd2->has_waiter= true;
+ if (thd2->waiting_on_group_commit)
+ mysql_bin_log.binlog_trigger_immediate_group_commit();
+ mysql_mutex_unlock(&LOCK_prepare_ordered);
+}
+
+
/**
Wait until we get a signal that the relay log has been updated.
@@ -6406,15 +7884,14 @@ MYSQL_BIN_LOG::write_transaction_or_stmt(group_commit_entry *entry)
void MYSQL_BIN_LOG::wait_for_update_relay_log(THD* thd)
{
- const char *old_msg;
+ PSI_stage_info old_stage;
DBUG_ENTER("wait_for_update_relay_log");
- old_msg= thd->enter_cond(&update_cond, &LOCK_log,
- "Slave has read all relay log; "
- "waiting for the slave I/O "
- "thread to update it" );
+ thd->ENTER_COND(&update_cond, &LOCK_log,
+ &stage_slave_has_read_all_relay_log,
+ &old_stage);
mysql_cond_wait(&update_cond, &LOCK_log);
- thd->exit_cond(old_msg);
+ thd->EXIT_COND(&old_stage);
DBUG_VOID_RETURN;
}
@@ -6469,12 +7946,14 @@ int MYSQL_BIN_LOG::wait_for_update_bin_log(THD* thd,
void MYSQL_BIN_LOG::close(uint exiting)
{ // One can't set log_type here!
+ bool failed_to_save_state= false;
+
DBUG_ENTER("MYSQL_BIN_LOG::close");
DBUG_PRINT("enter",("exiting: %d", (int) exiting));
if (log_state == LOG_OPENED)
{
#ifdef HAVE_REPLICATION
- if (log_type == LOG_BIN && !no_auto_events &&
+ if (log_type == LOG_BIN &&
(exiting & LOG_CLOSE_STOP_EVENT))
{
Stop_log_event s;
@@ -6486,6 +7965,27 @@ void MYSQL_BIN_LOG::close(uint exiting)
s.write(&log_file);
bytes_written+= s.data_written;
signal_update();
+
+ /*
+ When we shut down server, write out the binlog state to a separate
+ file so we do not have to scan an entire binlog file to recover it
+ at next server start.
+
+ Note that this must be written and synced to disk before marking the
+ last binlog file as "not crashed".
+ */
+ if (!is_relay_log && write_state_to_file())
+ {
+ sql_print_error("Failed to save binlog GTID state during shutdown. "
+ "Binlog will be marked as crashed, so that crash "
+ "recovery can recover the state at next server "
+ "startup.");
+ /*
+ Leave binlog file marked as crashed, so we can recover state by
+ scanning it now that we failed to write out the state properly.
+ */
+ failed_to_save_state= true;
+ }
}
#endif /* HAVE_REPLICATION */
@@ -6494,7 +7994,8 @@ void MYSQL_BIN_LOG::close(uint exiting)
&& !(exiting & LOG_CLOSE_DELAYED_CLOSE))
{
my_off_t org_position= mysql_file_tell(log_file.file, MYF(0));
- clear_inuse_flag_when_closing(log_file.file);
+ if (!failed_to_save_state)
+ clear_inuse_flag_when_closing(log_file.file);
/*
Restore position so that anything we have in the IO_cache is written
to the correct position.
@@ -6698,7 +8199,7 @@ static void print_buffer_to_nt_eventlog(enum loglevel level, char *buff,
DBUG_ENTER("print_buffer_to_nt_eventlog");
/* Add ending CR/LF's to string, overwrite last chars if necessary */
- strmov(buffptr+min(length, buffLen-5), "\r\n\r\n");
+ strmov(buffptr+MY_MIN(length, buffLen-5), "\r\n\r\n");
setup_windows_event_source();
if ((event= RegisterEventSource(NULL,"MySQL")))
@@ -6732,16 +8233,33 @@ static void print_buffer_to_file(enum loglevel level, const char *buffer,
time_t skr;
struct tm tm_tmp;
struct tm *start;
+ THD *thd;
+ int tag_length= 0;
+ char tag[NAME_LEN];
DBUG_ENTER("print_buffer_to_file");
DBUG_PRINT("enter",("buffer: %s", buffer));
+ if (mysqld_server_initialized && (thd= current_thd))
+ {
+ if (thd->connection_name.length)
+ {
+ /*
+ Add tag for slaves so that the user can see from which connection
+ the error originates.
+ */
+ tag_length= my_snprintf(tag, sizeof(tag), ER(ER_MASTER_LOG_PREFIX),
+ (int) thd->connection_name.length,
+ thd->connection_name.str);
+ }
+ }
+
mysql_mutex_lock(&LOCK_error_log);
skr= my_time(0);
localtime_r(&skr, &tm_tmp);
start=&tm_tmp;
- fprintf(stderr, "%02d%02d%02d %2d:%02d:%02d [%s] %.*s\n",
+ fprintf(stderr, "%02d%02d%02d %2d:%02d:%02d [%s] %.*s%.*s\n",
start->tm_year % 100,
start->tm_mon+1,
start->tm_mday,
@@ -6750,6 +8268,7 @@ static void print_buffer_to_file(enum loglevel level, const char *buffer,
start->tm_sec,
(level == ERROR_LEVEL ? "ERROR" : level == WARNING_LEVEL ?
"Warning" : "Note"),
+ tag_length, tag,
(int) length, buffer);
fflush(stderr);
@@ -6895,6 +8414,9 @@ int TC_LOG_MMAP::log_and_order(THD *thd, my_xid xid, bool all,
mysql_mutex_unlock(&LOCK_prepare_ordered);
}
+ if (thd->wait_for_prior_commit())
+ return 0;
+
cookie= 0;
if (xid)
cookie= log_one_transaction(xid);
@@ -7019,7 +8541,7 @@ ulong tc_log_page_waits= 0;
static const uchar tc_log_magic[]={(uchar) 254, 0x23, 0x05, 0x74};
-ulong opt_tc_log_size= TC_LOG_MIN_SIZE;
+ulong opt_tc_log_size;
ulong tc_log_max_pages_used=0, tc_log_page_size=0, tc_log_cur_pages_used=0;
int TC_LOG_MMAP::open(const char *opt_name)
@@ -7032,7 +8554,6 @@ int TC_LOG_MMAP::open(const char *opt_name)
DBUG_ASSERT(opt_name && opt_name[0]);
tc_log_page_size= my_getpagesize();
- DBUG_ASSERT(TC_LOG_PAGE_SIZE % tc_log_page_size == 0);
fn_format(logname,opt_name,mysql_data_home,"",MY_UNPACK_FILENAME);
if ((fd= mysql_file_open(key_file_tclog, logname, O_RDWR, MYF(0))) < 0)
@@ -7108,6 +8629,8 @@ int TC_LOG_MMAP::open(const char *opt_name)
mysql_mutex_init(key_LOCK_sync, &LOCK_sync, MY_MUTEX_INIT_FAST);
mysql_mutex_init(key_LOCK_active, &LOCK_active, MY_MUTEX_INIT_FAST);
mysql_mutex_init(key_LOCK_pool, &LOCK_pool, MY_MUTEX_INIT_FAST);
+ mysql_mutex_init(key_LOCK_pending_checkpoint, &LOCK_pending_checkpoint,
+ MY_MUTEX_INIT_FAST);
mysql_cond_init(key_COND_active, &COND_active, 0);
mysql_cond_init(key_COND_pool, &COND_pool, 0);
mysql_cond_init(key_TC_LOG_MMAP_COND_queue_busy, &COND_queue_busy, 0);
@@ -7358,17 +8881,93 @@ int TC_LOG_MMAP::sync()
return err;
}
+static void
+mmap_do_checkpoint_callback(void *data)
+{
+ TC_LOG_MMAP::pending_cookies *pending=
+ static_cast<TC_LOG_MMAP::pending_cookies *>(data);
+ ++pending->pending_count;
+}
+
+int TC_LOG_MMAP::unlog(ulong cookie, my_xid xid)
+{
+ pending_cookies *full_buffer= NULL;
+ uint32 ncookies= tc_log_page_size / sizeof(my_xid);
+ DBUG_ASSERT(*(my_xid *)(data+cookie) == xid);
+
+ /*
+ Do not delete the entry immediately, as there may be participating storage
+ engines which implement commit_checkpoint_request(), and thus have not yet
+ flushed the commit durably to disk.
+
+ Instead put it in a queue - and periodically, we will request a checkpoint
+ from all engines and delete a whole batch at once.
+ */
+ mysql_mutex_lock(&LOCK_pending_checkpoint);
+ if (pending_checkpoint == NULL)
+ {
+ uint32 size= sizeof(*pending_checkpoint) + sizeof(ulong) * (ncookies - 1);
+ if (!(pending_checkpoint=
+ (pending_cookies *)my_malloc(size, MYF(MY_ZEROFILL))))
+ {
+ my_error(ER_OUTOFMEMORY, MYF(0), size);
+ mysql_mutex_unlock(&LOCK_pending_checkpoint);
+ return 1;
+ }
+ }
+
+ pending_checkpoint->cookies[pending_checkpoint->count++]= cookie;
+ if (pending_checkpoint->count == ncookies)
+ {
+ full_buffer= pending_checkpoint;
+ pending_checkpoint= NULL;
+ }
+ mysql_mutex_unlock(&LOCK_pending_checkpoint);
+
+ if (full_buffer)
+ {
+ /*
+ We do an extra increment and notify here - this ensures that
+ things work also if there are no engines at all that support
+ commit_checkpoint_request.
+ */
+ ++full_buffer->pending_count;
+ ha_commit_checkpoint_request(full_buffer, mmap_do_checkpoint_callback);
+ commit_checkpoint_notify(full_buffer);
+ }
+ return 0;
+}
+
+
+void
+TC_LOG_MMAP::commit_checkpoint_notify(void *cookie)
+{
+ uint count;
+ pending_cookies *pending= static_cast<pending_cookies *>(cookie);
+ mysql_mutex_lock(&LOCK_pending_checkpoint);
+ DBUG_ASSERT(pending->pending_count > 0);
+ count= --pending->pending_count;
+ mysql_mutex_unlock(&LOCK_pending_checkpoint);
+ if (count == 0)
+ {
+ uint i;
+ for (i= 0; i < tc_log_page_size / sizeof(my_xid); ++i)
+ delete_entry(pending->cookies[i]);
+ my_free(pending);
+ }
+}
+
+
/**
erase xid from the page, update page free space counters/pointers.
cookie points directly to the memory where xid was logged.
*/
-int TC_LOG_MMAP::unlog(ulong cookie, my_xid xid)
+int TC_LOG_MMAP::delete_entry(ulong cookie)
{
PAGE *p=pages+(cookie/tc_log_page_size);
my_xid *x=(my_xid *)(data+cookie);
- DBUG_ASSERT(*x == xid);
DBUG_ASSERT(x >= p->start && x < p->end);
mysql_mutex_lock(&p->lock);
@@ -7392,6 +8991,7 @@ void TC_LOG_MMAP::close()
mysql_mutex_destroy(&LOCK_sync);
mysql_mutex_destroy(&LOCK_active);
mysql_mutex_destroy(&LOCK_pool);
+ mysql_mutex_destroy(&LOCK_pending_checkpoint);
mysql_cond_destroy(&COND_pool);
mysql_cond_destroy(&COND_active);
mysql_cond_destroy(&COND_queue_busy);
@@ -7414,9 +9014,12 @@ void TC_LOG_MMAP::close()
}
if (inited>=5) // cannot do in the switch because of Windows
mysql_file_delete(key_file_tclog, logname, MYF(MY_WME));
+ if (pending_checkpoint)
+ my_free(pending_checkpoint);
inited=0;
}
+
int TC_LOG_MMAP::recover()
{
HASH xids;
@@ -7502,26 +9105,13 @@ int TC_LOG::using_heuristic_recover()
/****** transaction coordinator log for 2pc - binlog() based solution ******/
#define TC_LOG_BINLOG MYSQL_BIN_LOG
-/**
- @todo
- keep in-memory list of prepared transactions
- (add to list in log(), remove on unlog())
- and copy it to the new binlog if rotated
- but let's check the behaviour of tc_log_page_waits first!
-*/
-
int TC_LOG_BINLOG::open(const char *opt_name)
{
- LOG_INFO log_info;
int error= 1;
DBUG_ASSERT(total_ha_2pc > 1);
DBUG_ASSERT(opt_name && opt_name[0]);
- mysql_mutex_init(key_BINLOG_LOCK_prep_xids,
- &LOCK_prep_xids, MY_MUTEX_INIT_FAST);
- mysql_cond_init(key_BINLOG_COND_prep_xids, &COND_prep_xids, 0);
-
if (!my_b_inited(&index_file))
{
/* There was a failure to open the index file, can't open the binlog */
@@ -7532,77 +9122,19 @@ int TC_LOG_BINLOG::open(const char *opt_name)
if (using_heuristic_recover())
{
/* generate a new binlog to mask a corrupted one */
- open(opt_name, LOG_BIN, 0, WRITE_CACHE, 0, max_binlog_size, 0, TRUE);
+ open(opt_name, LOG_BIN, 0, WRITE_CACHE, max_binlog_size, 0, TRUE);
cleanup();
return 1;
}
- if ((error= find_log_pos(&log_info, NullS, 1)))
- {
- if (error != LOG_INFO_EOF)
- sql_print_error("find_log_pos() failed (error: %d)", error);
- else
- error= 0;
- goto err;
- }
-
- {
- const char *errmsg;
- IO_CACHE log;
- File file;
- Log_event *ev=0;
- Format_description_log_event fdle(BINLOG_VERSION);
- char log_name[FN_REFLEN];
-
- if (! fdle.is_valid())
- goto err;
-
- do
- {
- strmake_buf(log_name, log_info.log_file_name);
- } while (!(error= find_next_log(&log_info, 1)));
-
- if (error != LOG_INFO_EOF)
- {
- sql_print_error("find_log_pos() failed (error: %d)", error);
- goto err;
- }
-
- if ((file= open_binlog(&log, log_name, &errmsg)) < 0)
- {
- sql_print_error("%s", errmsg);
- goto err;
- }
-
- if ((ev= Log_event::read_log_event(&log, 0, &fdle,
- opt_master_verify_checksum)) &&
- ev->get_type_code() == FORMAT_DESCRIPTION_EVENT &&
- ev->flags & LOG_EVENT_BINLOG_IN_USE_F)
- {
- sql_print_information("Recovering after a crash using %s", opt_name);
- error= recover(&log, (Format_description_log_event *)ev);
- }
- else
- error=0;
-
- delete ev;
- end_io_cache(&log);
- mysql_file_close(file, MYF(MY_WME));
-
- if (error)
- goto err;
- }
-
-err:
+ error= do_binlog_recovery(opt_name, true);
+ binlog_state_recover_done= true;
return error;
}
/** This is called on shutdown, after ha_panic. */
void TC_LOG_BINLOG::close()
{
- DBUG_ASSERT(prepared_xids==0);
- mysql_mutex_destroy(&LOCK_prep_xids);
- mysql_cond_destroy(&COND_prep_xids);
}
/*
@@ -7634,7 +9166,17 @@ TC_LOG_BINLOG::log_and_order(THD *thd, my_xid xid, bool all,
DEBUG_SYNC(thd, "binlog_after_log_and_order");
- DBUG_RETURN(!err);
+ if (err)
+ DBUG_RETURN(0);
+ /*
+ If using explicit user XA, we will not have XID. We must still return a
+ non-zero cookie (as zero cookie signals error).
+ */
+ if (!xid || !cache_mngr->need_unlog)
+ DBUG_RETURN(BINLOG_COOKIE_DUMMY(cache_mngr->delayed_error));
+ else
+ DBUG_RETURN(BINLOG_COOKIE_MAKE(cache_mngr->binlog_id,
+ cache_mngr->delayed_error));
}
/*
@@ -7649,121 +9191,540 @@ TC_LOG_BINLOG::log_and_order(THD *thd, my_xid xid, bool all,
binary log.
*/
void
-TC_LOG_BINLOG::mark_xids_active(uint xid_count)
+TC_LOG_BINLOG::mark_xids_active(ulong binlog_id, uint xid_count)
{
+ xid_count_per_binlog *b;
+
DBUG_ENTER("TC_LOG_BINLOG::mark_xids_active");
- DBUG_PRINT("info", ("xid_count=%u", xid_count));
- mysql_mutex_lock(&LOCK_prep_xids);
- prepared_xids+= xid_count;
- mysql_mutex_unlock(&LOCK_prep_xids);
+ DBUG_PRINT("info", ("binlog_id=%lu xid_count=%u", binlog_id, xid_count));
+
+ mysql_mutex_lock(&LOCK_xid_list);
+ I_List_iterator<xid_count_per_binlog> it(binlog_xid_count_list);
+ while ((b= it++))
+ {
+ if (b->binlog_id == binlog_id)
+ {
+ b->xid_count += xid_count;
+ break;
+ }
+ }
+ /*
+ As we do not delete elements until count reach zero, elements should always
+ be found.
+ */
+ DBUG_ASSERT(b);
+ mysql_mutex_unlock(&LOCK_xid_list);
DBUG_VOID_RETURN;
}
/*
- Once an XID is committed, it is safe to rotate the binary log, as it can no
- longer be needed during crash recovery.
+ Once an XID is committed, it can no longer be needed during crash recovery,
+ as it has been durably recorded on disk as "committed".
This function is called to mark an XID this way. It needs to decrease the
- count of pending XIDs, and signal the log rotator thread when it reaches zero.
+ count of pending XIDs in the corresponding binlog. When the count reaches
+ zero (for an "old" binlog that is not the active one), that binlog file no
+ longer need to be scanned during crash recovery, so we can log a new binlog
+ checkpoint.
*/
void
-TC_LOG_BINLOG::mark_xid_done()
+TC_LOG_BINLOG::mark_xid_done(ulong binlog_id, bool write_checkpoint)
{
- my_bool send_signal;
+ xid_count_per_binlog *b;
+ bool first;
+ ulong current;
DBUG_ENTER("TC_LOG_BINLOG::mark_xid_done");
- mysql_mutex_lock(&LOCK_prep_xids);
- // prepared_xids can be 0 if the transaction had ignorable errors.
- DBUG_ASSERT(prepared_xids >= 0);
- if (prepared_xids > 0)
- prepared_xids--;
- send_signal= (prepared_xids == 0);
- mysql_mutex_unlock(&LOCK_prep_xids);
- if (send_signal) {
- DBUG_PRINT("info", ("prepared_xids=%lu", prepared_xids));
- mysql_cond_signal(&COND_prep_xids);
+
+ mysql_mutex_lock(&LOCK_xid_list);
+ current= current_binlog_id;
+ I_List_iterator<xid_count_per_binlog> it(binlog_xid_count_list);
+ first= true;
+ while ((b= it++))
+ {
+ if (b->binlog_id == binlog_id)
+ {
+ --b->xid_count;
+ break;
+ }
+ first= false;
}
+ /* Binlog is always found, as we do not remove until count reaches 0 */
+ DBUG_ASSERT(b);
+ /*
+ If a RESET MASTER is pending, we are about to remove all log files, and
+ the RESET MASTER thread is waiting for all pending unlog() calls to
+ complete while holding LOCK_log. In this case we should not log a binlog
+ checkpoint event (it would be deleted immediately anyway and we would
+ deadlock on LOCK_log) but just signal the thread.
+ */
+ if (unlikely(reset_master_pending))
+ {
+ mysql_cond_signal(&COND_xid_list);
+ mysql_mutex_unlock(&LOCK_xid_list);
+ DBUG_VOID_RETURN;
+ }
+
+ if (likely(binlog_id == current) || b->xid_count != 0 || !first ||
+ !write_checkpoint)
+ {
+ /* No new binlog checkpoint reached yet. */
+ mysql_mutex_unlock(&LOCK_xid_list);
+ DBUG_VOID_RETURN;
+ }
+
+ /*
+ Now log a binlog checkpoint for the first binlog file with a non-zero count.
+
+ Note that it is possible (though perhaps unlikely) that when count of
+ binlog (N-2) drops to zero, binlog (N-1) is already at zero. So we may
+ need to skip several entries before we find the one to log in the binlog
+ checkpoint event.
+
+ We chain the locking of LOCK_xid_list and LOCK_log, so that we ensure that
+ Binlog_checkpoint_events are logged in order. This simplifies recovery a
+ bit, as it can just take the last binlog checkpoint in the log, rather
+ than compare all found against each other to find the one pointing to the
+ most recent binlog.
+
+ Note also that we need to first release LOCK_xid_list, then aquire
+ LOCK_log, then re-aquire LOCK_xid_list. If we were to take LOCK_log while
+ holding LOCK_xid_list, we might deadlock with other threads that take the
+ locks in the opposite order.
+ */
+
+ ++mark_xid_done_waiting;
+ mysql_mutex_unlock(&LOCK_xid_list);
+ mysql_mutex_lock(&LOCK_log);
+ mysql_mutex_lock(&LOCK_xid_list);
+ --mark_xid_done_waiting;
+ if (unlikely(reset_master_pending))
+ mysql_cond_signal(&COND_xid_list);
+ /* We need to reload current_binlog_id due to release/re-take of lock. */
+ current= current_binlog_id;
+
+ for (;;)
+ {
+ /* Remove initial element(s) with zero count. */
+ b= binlog_xid_count_list.head();
+ /*
+ We must not remove all elements in the list - the entry for the current
+ binlog must be present always.
+ */
+ DBUG_ASSERT(b);
+ if (b->binlog_id == current || b->xid_count > 0)
+ break;
+ my_free(binlog_xid_count_list.get());
+ }
+
+ mysql_mutex_unlock(&LOCK_xid_list);
+ write_binlog_checkpoint_event_already_locked(b->binlog_name,
+ b->binlog_name_len);
+ mysql_mutex_unlock(&LOCK_log);
DBUG_VOID_RETURN;
}
int TC_LOG_BINLOG::unlog(ulong cookie, my_xid xid)
{
DBUG_ENTER("TC_LOG_BINLOG::unlog");
- if (xid)
- mark_xid_done();
- /* As ::write_transaction_to_binlog() did not rotate, do it here. */
- DBUG_RETURN(rotate_and_purge(0));
+ if (!xid)
+ DBUG_RETURN(0);
+
+ if (!BINLOG_COOKIE_IS_DUMMY(cookie))
+ mark_xid_done(BINLOG_COOKIE_GET_ID(cookie), true);
+ /*
+ See comment in trx_group_commit_leader() - if rotate() gave a failure,
+ we delay the return of error code to here.
+ */
+ DBUG_RETURN(BINLOG_COOKIE_GET_ERROR_FLAG(cookie));
}
-int TC_LOG_BINLOG::recover(IO_CACHE *log, Format_description_log_event *fdle)
+void
+TC_LOG_BINLOG::commit_checkpoint_notify(void *cookie)
{
- Log_event *ev;
- HASH xids;
- MEM_ROOT mem_root;
+ xid_count_per_binlog *entry= static_cast<xid_count_per_binlog *>(cookie);
+ mysql_mutex_lock(&LOCK_binlog_background_thread);
+ entry->next_in_queue= binlog_background_thread_queue;
+ binlog_background_thread_queue= entry;
+ mysql_cond_signal(&COND_binlog_background_thread);
+ mysql_mutex_unlock(&LOCK_binlog_background_thread);
+}
+
+/*
+ Binlog background thread.
+
+ This thread is used to log binlog checkpoints in the background, rather than
+ in the context of random storage engine threads that happen to call
+ commit_checkpoint_notify_ha() and may not like the delays while syncing
+ binlog to disk or may not be setup with all my_thread_init() and other
+ necessary stuff.
+
+ In the future, this thread could also be used to do log rotation in the
+ background, which could elimiate all stalls around binlog rotations.
+*/
+pthread_handler_t
+binlog_background_thread(void *arg __attribute__((unused)))
+{
+ bool stop;
+ MYSQL_BIN_LOG::xid_count_per_binlog *queue, *next;
+ THD *thd;
+ my_thread_init();
+ DBUG_ENTER("binlog_background_thread");
+
+ thd= new THD;
+ thd->system_thread= SYSTEM_THREAD_BINLOG_BACKGROUND;
+ thd->thread_stack= (char*) &thd; /* Set approximate stack start */
+ mysql_mutex_lock(&LOCK_thread_count);
+ thd->thread_id= thread_id++;
+ mysql_mutex_unlock(&LOCK_thread_count);
+ thd->store_globals();
+ thd->security_ctx->skip_grants();
+ thd->set_command(COM_DAEMON);
-#ifdef WITH_WSREP
/*
- Read current wsrep position from storage engines to have consistent
- end position for binlog scan.
+ Load the slave replication GTID state from the mysql.gtid_slave_pos
+ table.
+
+ This is mostly so that we can start our seq_no counter from the highest
+ seq_no seen by a slave. This way, we have a way to tell if a transaction
+ logged by ourselves as master is newer or older than a replicated
+ transaction.
*/
- wsrep_uuid_t uuid;
- wsrep_seqno_t seqno;
- wsrep_get_SE_checkpoint(uuid, seqno);
- char uuid_str[40];
- wsrep_uuid_print(&uuid, uuid_str, sizeof(uuid_str));
- WSREP_INFO("Binlog recovery, found wsrep position %s:%lld", uuid_str,
- (long long)seqno);
- const wsrep_seqno_t last_xid_seqno= seqno;
- wsrep_seqno_t cur_xid_seqno= WSREP_SEQNO_UNDEFINED;
-#endif /* WITH_WSREP */
+#ifdef HAVE_REPLICATION
+ if (rpl_load_gtid_slave_state(thd))
+ sql_print_warning("Failed to load slave replication state from table "
+ "%s.%s: %u: %s", "mysql",
+ rpl_gtid_slave_state_table_name.str,
+ thd->get_stmt_da()->sql_errno(),
+ thd->get_stmt_da()->message());
+#endif
+
+ mysql_mutex_lock(&mysql_bin_log.LOCK_binlog_background_thread);
+ binlog_background_thread_started= true;
+ mysql_cond_signal(&mysql_bin_log.COND_binlog_background_thread_end);
+ mysql_mutex_unlock(&mysql_bin_log.LOCK_binlog_background_thread);
+
+ for (;;)
+ {
+ /*
+ Wait until there is something in the queue to process, or we are asked
+ to shut down.
+ */
+ THD_STAGE_INFO(thd, stage_binlog_waiting_background_tasks);
+ mysql_mutex_lock(&mysql_bin_log.LOCK_binlog_background_thread);
+ for (;;)
+ {
+ stop= binlog_background_thread_stop;
+ queue= binlog_background_thread_queue;
+ if (stop && !mysql_bin_log.is_xidlist_idle())
+ {
+ /*
+ Delay stop until all pending binlog checkpoints have been processed.
+ */
+ stop= false;
+ }
+ if (stop || queue)
+ break;
+ mysql_cond_wait(&mysql_bin_log.COND_binlog_background_thread,
+ &mysql_bin_log.LOCK_binlog_background_thread);
+ }
+ /* Grab the queue, if any. */
+ binlog_background_thread_queue= NULL;
+ mysql_mutex_unlock(&mysql_bin_log.LOCK_binlog_background_thread);
+
+ /* Process any incoming commit_checkpoint_notify() calls. */
+ DBUG_EXECUTE_IF("inject_binlog_background_thread_before_mark_xid_done",
+ DBUG_ASSERT(!debug_sync_set_action(
+ thd,
+ STRING_WITH_LEN("binlog_background_thread_before_mark_xid_done "
+ "SIGNAL injected_binlog_background_thread "
+ "WAIT_FOR something_that_will_never_happen "
+ "TIMEOUT 2")));
+ );
+ while (queue)
+ {
+ THD_STAGE_INFO(thd, stage_binlog_processing_checkpoint_notify);
+ DEBUG_SYNC(current_thd, "binlog_background_thread_before_mark_xid_done");
+ /* Grab next pointer first, as mark_xid_done() may free the element. */
+ next= queue->next_in_queue;
+ mysql_bin_log.mark_xid_done(queue->binlog_id, true);
+ queue= next;
+
+ DBUG_EXECUTE_IF("binlog_background_checkpoint_processed",
+ DBUG_ASSERT(!debug_sync_set_action(
+ thd,
+ STRING_WITH_LEN("now SIGNAL binlog_background_checkpoint_processed")));
+ );
+ }
+
+ if (stop)
+ break;
+ }
+
+ THD_STAGE_INFO(thd, stage_binlog_stopping_background_thread);
+
+ mysql_mutex_lock(&LOCK_thread_count);
+ delete thd;
+ mysql_mutex_unlock(&LOCK_thread_count);
+
+ my_thread_end();
+
+ /* Signal that we are (almost) stopped. */
+ mysql_mutex_lock(&mysql_bin_log.LOCK_binlog_background_thread);
+ binlog_background_thread_stop= false;
+ mysql_cond_signal(&mysql_bin_log.COND_binlog_background_thread_end);
+ mysql_mutex_unlock(&mysql_bin_log.LOCK_binlog_background_thread);
+
+ DBUG_RETURN(0);
+}
+
+#ifdef HAVE_PSI_INTERFACE
+static PSI_thread_key key_thread_binlog;
+
+static PSI_thread_info all_binlog_threads[]=
+{
+ { &key_thread_binlog, "binlog_background", PSI_FLAG_GLOBAL},
+};
+#endif /* HAVE_PSI_INTERFACE */
+
+static bool
+start_binlog_background_thread()
+{
+ pthread_t th;
+
+#ifdef HAVE_PSI_INTERFACE
+ if (PSI_server)
+ PSI_server->register_thread("sql", all_binlog_threads,
+ array_elements(all_binlog_threads));
+#endif
+
+ if (mysql_thread_create(key_thread_binlog, &th, &connection_attrib,
+ binlog_background_thread, NULL))
+ return 1;
+
+ /*
+ Wait for the thread to have started (so we know that the slave replication
+ state is loaded and we have correct global_gtid_counter).
+ */
+ mysql_mutex_lock(&mysql_bin_log.LOCK_binlog_background_thread);
+ while (!binlog_background_thread_started)
+ mysql_cond_wait(&mysql_bin_log.COND_binlog_background_thread_end,
+ &mysql_bin_log.LOCK_binlog_background_thread);
+ mysql_mutex_unlock(&mysql_bin_log.LOCK_binlog_background_thread);
+
+ return 0;
+}
+
+
+int TC_LOG_BINLOG::recover(LOG_INFO *linfo, const char *last_log_name,
+ IO_CACHE *first_log,
+ Format_description_log_event *fdle, bool do_xa)
+{
+ Log_event *ev= NULL;
+ HASH xids;
+ MEM_ROOT mem_root;
+ char binlog_checkpoint_name[FN_REFLEN];
+ bool binlog_checkpoint_found;
+ bool first_round;
+ IO_CACHE log;
+ File file= -1;
+ const char *errmsg;
+#ifdef HAVE_REPLICATION
+ rpl_gtid last_gtid;
+ bool last_gtid_standalone= false;
+ bool last_gtid_valid= false;
+#endif
if (! fdle->is_valid() ||
- my_hash_init(&xids, &my_charset_bin, TC_LOG_PAGE_SIZE/3, 0,
- sizeof(my_xid), 0, 0, MYF(0)))
+ (do_xa && my_hash_init(&xids, &my_charset_bin, TC_LOG_PAGE_SIZE/3, 0,
+ sizeof(my_xid), 0, 0, MYF(0))))
goto err1;
- init_alloc_root(&mem_root, TC_LOG_PAGE_SIZE, TC_LOG_PAGE_SIZE);
+ if (do_xa)
+ init_alloc_root(&mem_root, TC_LOG_PAGE_SIZE, TC_LOG_PAGE_SIZE, MYF(0));
fdle->flags&= ~LOG_EVENT_BINLOG_IN_USE_F; // abort on the first error
- while ((ev= Log_event::read_log_event(log, 0, fdle,
- opt_master_verify_checksum))
- && ev->is_valid()
-#ifdef WITH_WSREP
- && (last_xid_seqno == WSREP_SEQNO_UNDEFINED ||
- last_xid_seqno != cur_xid_seqno)
-#endif
- )
+ /*
+ Scan the binlog for XIDs that need to be committed if still in the
+ prepared stage.
+
+ Start with the latest binlog file, then continue with any other binlog
+ files if the last found binlog checkpoint indicates it is needed.
+ */
+
+ binlog_checkpoint_found= false;
+ first_round= true;
+ for (;;)
{
- if (ev->get_type_code() == XID_EVENT)
+ while ((ev= Log_event::read_log_event(first_round ? first_log : &log,
+ 0, fdle, opt_master_verify_checksum))
+ && ev->is_valid())
{
- Xid_log_event *xev=(Xid_log_event *)ev;
- uchar *x= (uchar *) memdup_root(&mem_root, (uchar*) &xev->xid,
- sizeof(xev->xid));
- if (!x || my_hash_insert(&xids, x))
+ enum Log_event_type typ= ev->get_type_code();
+ switch (typ)
+ {
+ case XID_EVENT:
+ {
+ if (do_xa)
+ {
+ Xid_log_event *xev=(Xid_log_event *)ev;
+ uchar *x= (uchar *) memdup_root(&mem_root, (uchar*) &xev->xid,
+ sizeof(xev->xid));
+ if (!x || my_hash_insert(&xids, x))
+ goto err2;
+ break;
+ }
+ }
+ case BINLOG_CHECKPOINT_EVENT:
+ if (first_round && do_xa)
+ {
+ uint dir_len;
+ Binlog_checkpoint_log_event *cev= (Binlog_checkpoint_log_event *)ev;
+ if (cev->binlog_file_len >= FN_REFLEN)
+ sql_print_warning("Incorrect binlog checkpoint event with too "
+ "long file name found.");
+ else
+ {
+ /*
+ Note that we cannot use make_log_name() here, as we have not yet
+ initialised MYSQL_BIN_LOG::log_file_name.
+ */
+ dir_len= dirname_length(last_log_name);
+ strmake(strnmov(binlog_checkpoint_name, last_log_name, dir_len),
+ cev->binlog_file_name, FN_REFLEN - 1 - dir_len);
+ binlog_checkpoint_found= true;
+ }
+ }
+ break;
+ case GTID_LIST_EVENT:
+ if (first_round)
+ {
+ Gtid_list_log_event *glev= (Gtid_list_log_event *)ev;
+
+ /* Initialise the binlog state from the Gtid_list event. */
+ if (rpl_global_gtid_binlog_state.load(glev->list, glev->count))
+ goto err2;
+ }
+ break;
+
+#ifdef HAVE_REPLICATION
+ case GTID_EVENT:
+ if (first_round)
+ {
+ Gtid_log_event *gev= (Gtid_log_event *)ev;
+
+ /* Update the binlog state with any GTID logged after Gtid_list. */
+ last_gtid.domain_id= gev->domain_id;
+ last_gtid.server_id= gev->server_id;
+ last_gtid.seq_no= gev->seq_no;
+ last_gtid_standalone=
+ ((gev->flags2 & Gtid_log_event::FL_STANDALONE) ? true : false);
+ last_gtid_valid= true;
+ }
+ break;
+#endif
+
+ default:
+ /* Nothing. */
+ break;
+ }
+
+#ifdef HAVE_REPLICATION
+ if (last_gtid_valid &&
+ ((last_gtid_standalone && !ev->is_part_of_group(typ)) ||
+ (!last_gtid_standalone &&
+ (typ == XID_EVENT ||
+ (typ == QUERY_EVENT &&
+ (((Query_log_event *)ev)->is_commit() ||
+ ((Query_log_event *)ev)->is_rollback()))))))
+ {
+ if (rpl_global_gtid_binlog_state.update_nolock(&last_gtid, false))
+ goto err2;
+ last_gtid_valid= false;
+ }
+#endif
+
+ delete ev;
+ ev= NULL;
+ }
+
+ if (!do_xa)
+ break;
+ /*
+ If the last binlog checkpoint event points to an older log, we have to
+ scan all logs from there also, to get all possible XIDs to recover.
+
+ If there was no binlog checkpoint event at all, this means the log was
+ written by an older version of MariaDB (or MySQL) - these always have an
+ (implicit) binlog checkpoint event at the start of the last binlog file.
+ */
+ if (first_round)
+ {
+ if (!binlog_checkpoint_found)
+ break;
+ first_round= false;
+ DBUG_EXECUTE_IF("xa_recover_expect_master_bin_000004",
+ if (0 != strcmp("./master-bin.000004", binlog_checkpoint_name) &&
+ 0 != strcmp(".\\master-bin.000004", binlog_checkpoint_name))
+ DBUG_SUICIDE();
+ );
+ if (find_log_pos(linfo, binlog_checkpoint_name, 1))
+ {
+ sql_print_error("Binlog file '%s' not found in binlog index, needed "
+ "for recovery. Aborting.", binlog_checkpoint_name);
goto err2;
-#ifdef WITH_WSREP
- cur_xid_seqno= xev->xid;
-#endif /* WITH_WSREP */
+ }
+ }
+ else
+ {
+ end_io_cache(&log);
+ mysql_file_close(file, MYF(MY_WME));
+ file= -1;
}
- delete ev;
- }
-#ifdef WITH_WSREP
- WSREP_INFO("Binlog recovery scan stopped at Xid event %lld",
- (long long)cur_xid_seqno);
-#endif /* WITH_WSREP */
+ if (!strcmp(linfo->log_file_name, last_log_name))
+ break; // No more files to do
+ if ((file= open_binlog(&log, linfo->log_file_name, &errmsg)) < 0)
+ {
+ sql_print_error("%s", errmsg);
+ goto err2;
+ }
+ /*
+ We do not need to read the Format_description_log_event of other binlog
+ files. It is not possible for a binlog checkpoint to span multiple
+ binlog files written by different versions of the server. So we can use
+ the first one read for reading from all binlog files.
+ */
+ if (find_next_log(linfo, 1))
+ {
+ sql_print_error("Error reading binlog files during recovery. Aborting.");
+ goto err2;
+ }
+ }
- if (ha_recover(&xids))
- goto err2;
+ if (do_xa)
+ {
+ if (ha_recover(&xids))
+ goto err2;
- free_root(&mem_root, MYF(0));
- my_hash_free(&xids);
+ free_root(&mem_root, MYF(0));
+ my_hash_free(&xids);
+ }
return 0;
err2:
- free_root(&mem_root, MYF(0));
- my_hash_free(&xids);
+ delete ev;
+ if (file >= 0)
+ {
+ end_io_cache(&log);
+ mysql_file_close(file, MYF(MY_WME));
+ }
+ if (do_xa)
+ {
+ free_root(&mem_root, MYF(0));
+ my_hash_free(&xids);
+ }
err1:
sql_print_error("Crash recovery failed. Either correct the problem "
"(if it's, for example, out of memory error) and restart, "
@@ -7773,6 +9734,110 @@ err1:
}
+int
+MYSQL_BIN_LOG::do_binlog_recovery(const char *opt_name, bool do_xa_recovery)
+{
+ LOG_INFO log_info;
+ const char *errmsg;
+ IO_CACHE log;
+ File file;
+ Log_event *ev= 0;
+ Format_description_log_event fdle(BINLOG_VERSION);
+ char log_name[FN_REFLEN];
+ int error;
+
+ if ((error= find_log_pos(&log_info, NullS, 1)))
+ {
+ /*
+ If there are no binlog files (LOG_INFO_EOF), then we still try to read
+ the .state file to restore the binlog state. This allows to copy a server
+ to provision a new one without copying the binlog files (except the
+ master-bin.state file) and still preserve the correct binlog state.
+ */
+ if (error != LOG_INFO_EOF)
+ sql_print_error("find_log_pos() failed (error: %d)", error);
+ else
+ {
+ error= read_state_from_file();
+ if (error == 2)
+ {
+ /*
+ No binlog files and no binlog state is not an error (eg. just initial
+ server start after fresh installation).
+ */
+ error= 0;
+ }
+ }
+ return error;
+ }
+
+ if (! fdle.is_valid())
+ return 1;
+
+ do
+ {
+ strmake_buf(log_name, log_info.log_file_name);
+ } while (!(error= find_next_log(&log_info, 1)));
+
+ if (error != LOG_INFO_EOF)
+ {
+ sql_print_error("find_log_pos() failed (error: %d)", error);
+ return error;
+ }
+
+ if ((file= open_binlog(&log, log_name, &errmsg)) < 0)
+ {
+ sql_print_error("%s", errmsg);
+ return 1;
+ }
+
+ if ((ev= Log_event::read_log_event(&log, 0, &fdle,
+ opt_master_verify_checksum)) &&
+ ev->get_type_code() == FORMAT_DESCRIPTION_EVENT)
+ {
+ if (ev->flags & LOG_EVENT_BINLOG_IN_USE_F)
+ {
+ sql_print_information("Recovering after a crash using %s", opt_name);
+ error= recover(&log_info, log_name, &log,
+ (Format_description_log_event *)ev, do_xa_recovery);
+ }
+ else
+ {
+ error= read_state_from_file();
+ if (error == 2)
+ {
+ /*
+ The binlog exists, but the .state file is missing. This is normal if
+ this is the first master start after a major upgrade to 10.0 (with
+ GTID support).
+
+ However, it could also be that the .state file was lost somehow, and
+ in this case it could be a serious issue, as we would set the wrong
+ binlog state in the next binlog file to be created, and GTID
+ processing would be corrupted. A common way would be copying files
+ from an old server to a new one and forgetting the .state file.
+
+ So in this case, we want to try to recover the binlog state by
+ scanning the last binlog file (but we do not need any XA recovery).
+
+ ToDo: We could avoid one scan at first start after major upgrade, by
+ detecting that there is no GTID_LIST event at the start of the
+ binlog file, and stopping the scan in that case.
+ */
+ error= recover(&log_info, log_name, &log,
+ (Format_description_log_event *)ev, false);
+ }
+ }
+ }
+
+ delete ev;
+ end_io_cache(&log);
+ mysql_file_close(file, MYF(MY_WME));
+
+ return error;
+}
+
+
#ifdef INNODB_COMPATIBILITY_HOOKS
/**
Get the file name of the MySQL binlog.
@@ -7827,10 +9892,13 @@ binlog_checksum_update(MYSQL_THD thd, struct st_mysql_sys_var *var,
{
ulong value= *((ulong *)save);
bool check_purge= false;
+ ulong prev_binlog_id;
+ LINT_INIT(prev_binlog_id);
mysql_mutex_lock(mysql_bin_log.get_log_lock());
if(mysql_bin_log.is_open())
{
+ prev_binlog_id= mysql_bin_log.current_binlog_id;
if (binlog_checksum_options != value)
mysql_bin_log.checksum_alg_reset= (uint8) value;
if (mysql_bin_log.rotate(true, &check_purge))
@@ -7844,7 +9912,7 @@ binlog_checksum_update(MYSQL_THD thd, struct st_mysql_sys_var *var,
mysql_bin_log.checksum_alg_reset= BINLOG_CHECKSUM_ALG_UNDEF;
mysql_mutex_unlock(mysql_bin_log.get_log_lock());
if (check_purge)
- mysql_bin_log.purge();
+ mysql_bin_log.checkpoint_and_purge(prev_binlog_id);
}
@@ -7910,7 +9978,7 @@ set_binlog_snapshot_file(const char *src)
Copy out current values of status variables, for SHOW STATUS or
information_schema.global_status.
- This is called only under LOCK_status, so we can fill in a static array.
+ This is called only under LOCK_show_status, so we can fill in a static array.
*/
void
TC_LOG_BINLOG::set_status_variables(THD *thd)
@@ -7932,6 +10000,11 @@ TC_LOG_BINLOG::set_status_variables(THD *thd)
binlog_snapshot_position= last_commit_pos_offset;
}
mysql_mutex_unlock(&LOCK_commit_ordered);
+ mysql_mutex_lock(&LOCK_prepare_ordered);
+ binlog_status_group_commit_trigger_count= this->group_commit_trigger_count;
+ binlog_status_group_commit_trigger_timeout= this->group_commit_trigger_timeout;
+ binlog_status_group_commit_trigger_lock_wait= this->group_commit_trigger_lock_wait;
+ mysql_mutex_unlock(&LOCK_prepare_ordered);
if (have_snapshot)
{
diff --git a/sql/log.h b/sql/log.h
index 4abefc55b90..14389963aad 100644
--- a/sql/log.h
+++ b/sql/log.h
@@ -17,7 +17,6 @@
#ifndef LOG_H
#define LOG_H
-#include "unireg.h" // REQUIRED: for other includes
#include "handler.h" /* my_xid */
class Relay_log_info;
@@ -45,10 +44,20 @@ class TC_LOG
virtual int open(const char *opt_name)=0;
virtual void close()=0;
+ /*
+ Transaction coordinator 2-phase commit.
+
+ Must invoke the run_prepare_ordered and run_commit_ordered methods, as
+ described below for these methods.
+
+ In addition, must invoke THD::wait_for_prior_commit(), or equivalent
+ wait, to ensure that one commit waits for another if registered to do so.
+ */
virtual int log_and_order(THD *thd, my_xid xid, bool all,
bool need_prepare_ordered,
bool need_commit_ordered) = 0;
virtual int unlog(ulong cookie, my_xid xid)=0;
+ virtual void commit_checkpoint_notify(void *cookie)= 0;
protected:
/*
@@ -75,9 +84,11 @@ protected:
prepare_ordered() or commit_ordered() methods.
*/
extern mysql_mutex_t LOCK_prepare_ordered;
+extern mysql_cond_t COND_prepare_ordered;
extern mysql_mutex_t LOCK_commit_ordered;
#ifdef HAVE_PSI_INTERFACE
extern PSI_mutex_key key_LOCK_prepare_ordered, key_LOCK_commit_ordered;
+extern PSI_cond_key key_COND_prepare_ordered;
#endif
class TC_LOG_DUMMY: public TC_LOG // use it to disable the logging
@@ -98,8 +109,11 @@ public:
return 1;
}
int unlog(ulong cookie, my_xid xid) { return 0; }
+ void commit_checkpoint_notify(void *cookie) { DBUG_ASSERT(0); };
};
+#define TC_LOG_PAGE_SIZE 8192
+
#ifdef HAVE_MMAP
class TC_LOG_MMAP: public TC_LOG
{
@@ -110,6 +124,12 @@ class TC_LOG_MMAP: public TC_LOG
PS_DIRTY // new xids added since last sync
} PAGE_STATE;
+ struct pending_cookies {
+ uint count;
+ uint pending_count;
+ ulong cookies[1];
+ };
+
private:
typedef struct st_page {
struct st_page *next; // page a linked in a fifo queue
@@ -141,7 +161,7 @@ class TC_LOG_MMAP: public TC_LOG
one has to use active->lock.
Same for LOCK_pool and LOCK_sync
*/
- mysql_mutex_t LOCK_active, LOCK_pool, LOCK_sync;
+ mysql_mutex_t LOCK_active, LOCK_pool, LOCK_sync, LOCK_pending_checkpoint;
mysql_cond_t COND_pool, COND_active;
/*
Queue of threads that need to call commit_ordered().
@@ -163,14 +183,16 @@ class TC_LOG_MMAP: public TC_LOG
*/
mysql_cond_t COND_queue_busy;
my_bool commit_ordered_queue_busy;
+ pending_cookies* pending_checkpoint;
public:
- TC_LOG_MMAP(): inited(0) {}
+ TC_LOG_MMAP(): inited(0), pending_checkpoint(0) {}
int open(const char *opt_name);
void close();
int log_and_order(THD *thd, my_xid xid, bool all,
bool need_prepare_ordered, bool need_commit_ordered);
int unlog(ulong cookie, my_xid xid);
+ void commit_checkpoint_notify(void *cookie);
int recover();
private:
@@ -178,6 +200,7 @@ class TC_LOG_MMAP: public TC_LOG
void get_active_from_pool();
int sync();
int overflow();
+ int delete_entry(ulong cookie);
};
#else
#define TC_LOG_MMAP TC_LOG_DUMMY
@@ -354,7 +377,36 @@ private:
time_t last_time;
};
+/*
+ We assign each binlog file an internal ID, used to identify them for unlog().
+ The IDs start from 0 and increment for each new binlog created.
+
+ In unlog() we need to know the ID of the binlog file that the corresponding
+ transaction was written into. We also need a special value for a corner
+ case where there is no corresponding binlog id (since nothing was logged).
+ And we need an error flag to mark that unlog() must return failure.
+
+ We use the following macros to pack all of this information into the single
+ ulong available with log_and_order() / unlog().
+
+ Note that we cannot use the value 0 for cookie, as that is reserved as error
+ return value from log_and_order().
+ */
+#define BINLOG_COOKIE_ERROR_RETURN 0
+#define BINLOG_COOKIE_DUMMY_ID 1
+#define BINLOG_COOKIE_BASE 2
+#define BINLOG_COOKIE_DUMMY(error_flag) \
+ ( (BINLOG_COOKIE_DUMMY_ID<<1) | ((error_flag)&1) )
+#define BINLOG_COOKIE_MAKE(id, error_flag) \
+ ( (((id)+BINLOG_COOKIE_BASE)<<1) | ((error_flag)&1) )
+#define BINLOG_COOKIE_GET_ERROR_FLAG(c) ((c) & 1)
+#define BINLOG_COOKIE_GET_ID(c) ( ((ulong)(c)>>1) - BINLOG_COOKIE_BASE )
+#define BINLOG_COOKIE_IS_DUMMY(c) \
+ ( ((ulong)(c)>>1) == BINLOG_COOKIE_DUMMY_ID )
+
class binlog_cache_mngr;
+struct rpl_gtid;
+struct wait_for_commit;
class MYSQL_BIN_LOG: public TC_LOG, private MYSQL_LOG
{
private:
@@ -379,11 +431,10 @@ class MYSQL_BIN_LOG: public TC_LOG, private MYSQL_LOG
bool using_stmt_cache;
bool using_trx_cache;
/*
- Extra events (BEGIN, COMMIT/ROLLBACK/XID, and possibly INCIDENT) to be
+ Extra events (COMMIT/ROLLBACK/XID, and possibly INCIDENT) to be
written during group commit. The incident_event is only valid if
trx_data->has_incident() is true.
*/
- Log_event *begin_event;
Log_event *end_event;
Log_event *incident_event;
/* Set during group commit to record any per-thread error. */
@@ -392,12 +443,38 @@ class MYSQL_BIN_LOG: public TC_LOG, private MYSQL_LOG
IO_CACHE *error_cache;
/* This is the `all' parameter for ha_commit_ordered(). */
bool all;
+ /*
+ True if we need to increment xid_count in trx_group_commit_leader() and
+ decrement in unlog() (this is needed if there is a participating engine
+ that does not implement the commit_checkpoint_request() handlerton
+ method).
+ */
+ bool need_unlog;
+ /*
+ Fields used to pass the necessary information to the last thread in a
+ group commit, only used when opt_optimize_thread_scheduling is not set.
+ */
+ bool check_purge;
+ /* Flag used to optimise around wait_for_prior_commit. */
+ bool queued_by_other;
+ ulong binlog_id;
};
+ /*
+ When this is set, a RESET MASTER is in progress.
+
+ Then we should not write any binlog checkpoints into the binlog (that
+ could result in deadlock on LOCK_log, and we will delete all binlog files
+ anyway). Instead we should signal COND_xid_list whenever a new binlog
+ checkpoint arrives - when all have arrived, RESET MASTER will complete.
+ */
+ uint reset_master_pending;
+ ulong mark_xid_done_waiting;
+
/* LOCK_log and LOCK_index are inited by init_pthread_objects() */
mysql_mutex_t LOCK_index;
- mysql_mutex_t LOCK_prep_xids;
- mysql_cond_t COND_prep_xids;
+ mysql_mutex_t LOCK_xid_list;
+ mysql_cond_t COND_xid_list;
mysql_cond_t update_cond;
ulonglong bytes_written;
IO_CACHE index_file;
@@ -414,27 +491,14 @@ class MYSQL_BIN_LOG: public TC_LOG, private MYSQL_LOG
The max size before rotation (usable only if log_type == LOG_BIN: binary
logs and relay logs).
For a binlog, max_size should be max_binlog_size.
- For a relay log, it should be max_relay_log_size if this is non-zero,
- max_binlog_size otherwise.
max_size is set in init(), and dynamically changed (when one does SET
- GLOBAL MAX_BINLOG_SIZE|MAX_RELAY_LOG_SIZE) by fix_max_binlog_size and
- fix_max_relay_log_size).
+ GLOBAL MAX_BINLOG_SIZE|MAX_RELAY_LOG_SIZE) from sys_vars.cc
*/
ulong max_size;
- long prepared_xids; /* for tc log - number of xids to remember */
// current file sequence number for load data infile binary logging
uint file_id;
uint open_count; // For replication
int readers_count;
- bool need_start_event;
- /*
- no_auto_events means we don't want any of these automatic events :
- Start/Rotate/Stop. That is, in 4.x when we rotate a relay log, we don't
- want a Rotate_log event to be written to the relay log. When we start a
- relay log etc. So in 4.x this is 1 for relay logs, 0 for binlogs.
- In 5.0 it's 0 for relay logs too!
- */
- bool no_auto_events;
/* Queue of transactions queued up to participate in group commit. */
group_commit_entry *group_commit_queue;
/*
@@ -449,6 +513,9 @@ class MYSQL_BIN_LOG: public TC_LOG, private MYSQL_LOG
ulonglong num_commits;
/* Number of group commits done. */
ulonglong num_group_commits;
+ /* The reason why the group commit was grouped */
+ ulonglong group_commit_trigger_count, group_commit_trigger_timeout;
+ ulonglong group_commit_trigger_lock_wait;
/* pointer to the sync period variable, for binlog this will be
sync_binlog_period, for relay log this will be
@@ -456,6 +523,8 @@ class MYSQL_BIN_LOG: public TC_LOG, private MYSQL_LOG
*/
uint *sync_period_ptr;
uint sync_counter;
+ bool state_file_deleted;
+ bool binlog_state_recover_done;
inline uint get_sync_period()
{
@@ -470,13 +539,42 @@ class MYSQL_BIN_LOG: public TC_LOG, private MYSQL_LOG
*/
int new_file_without_locking();
int new_file_impl(bool need_lock);
- int write_transaction_or_stmt(group_commit_entry *entry);
+ void do_checkpoint_request(ulong binlog_id);
+ void purge();
+ int write_transaction_or_stmt(group_commit_entry *entry, uint64 commit_id);
+ int queue_for_group_commit(group_commit_entry *entry);
bool write_transaction_to_binlog_events(group_commit_entry *entry);
void trx_group_commit_leader(group_commit_entry *leader);
- void mark_xid_done();
- void mark_xids_active(uint xid_count);
+ bool is_xidlist_idle_nolock();
public:
+ /*
+ A list of struct xid_count_per_binlog is used to keep track of how many
+ XIDs are in prepared, but not committed, state in each binlog. And how
+ many commit_checkpoint_request()'s are pending.
+
+ When count drops to zero in a binlog after rotation, it means that there
+ are no more XIDs in prepared state, so that binlog is no longer needed
+ for XA crash recovery, and we can log a new binlog checkpoint event.
+
+ The list is protected against simultaneous access from multiple
+ threads by LOCK_xid_list.
+ */
+ struct xid_count_per_binlog : public ilink {
+ char *binlog_name;
+ uint binlog_name_len;
+ ulong binlog_id;
+ /* Total prepared XIDs and pending checkpoint requests in this binlog. */
+ long xid_count;
+ /* For linking in requests to the binlog background thread. */
+ xid_count_per_binlog *next_in_queue;
+ xid_count_per_binlog(); /* Give link error if constructor used. */
+ };
+ I_List<xid_count_per_binlog> binlog_xid_count_list;
+ mysql_mutex_t LOCK_binlog_background_thread;
+ mysql_cond_t COND_binlog_background_thread;
+ mysql_cond_t COND_binlog_background_thread_end;
+
using MYSQL_LOG::generate_name;
using MYSQL_LOG::is_open;
@@ -534,6 +632,7 @@ public:
*/
char last_commit_pos_file[FN_REFLEN];
my_off_t last_commit_pos_offset;
+ ulong current_binlog_id;
MYSQL_BIN_LOG(uint *sync_period);
/*
@@ -562,7 +661,10 @@ public:
int log_and_order(THD *thd, my_xid xid, bool all,
bool need_prepare_ordered, bool need_commit_ordered);
int unlog(ulong cookie, my_xid xid);
- int recover(IO_CACHE *log, Format_description_log_event *fdle);
+ void commit_checkpoint_notify(void *cookie);
+ int recover(LOG_INFO *linfo, const char *last_log_name, IO_CACHE *first_log,
+ Format_description_log_event *fdle, bool do_xa);
+ int do_binlog_recovery(const char *opt_name, bool do_xa_recovery);
#if !defined(MYSQL_CLIENT)
int flush_and_set_pending_rows_event(THD *thd, Rows_log_event* event,
@@ -588,17 +690,18 @@ public:
}
void set_max_size(ulong max_size_arg);
void signal_update();
+ void wait_for_sufficient_commits();
+ void binlog_trigger_immediate_group_commit();
void wait_for_update_relay_log(THD* thd);
int wait_for_update_bin_log(THD* thd, const struct timespec * timeout);
- void set_need_start_event() { need_start_event = 1; }
- void init(bool no_auto_events_arg, ulong max_size);
+ void init(ulong max_size);
void init_pthread_objects();
void cleanup();
bool open(const char *log_name,
enum_log_type log_type,
const char *new_name,
enum cache_type io_cache_type_arg,
- bool no_auto_events_arg, ulong max_size,
+ ulong max_size,
bool null_created,
bool need_mutex);
bool open_index_file(const char *index_file_name_arg,
@@ -614,6 +717,7 @@ public:
bool write_incident_already_locked(THD *thd);
bool write_incident(THD *thd);
+ void write_binlog_checkpoint_event_already_locked(const char *name, uint len);
int write_cache(THD *thd, IO_CACHE *cache);
void set_write_error(THD *thd, bool is_transactional);
bool check_write_error(THD *thd);
@@ -628,12 +732,16 @@ public:
*/
bool appendv(const char* buf,uint len,...);
bool append(Log_event* ev);
+ bool append_no_lock(Log_event* ev);
+ void mark_xids_active(ulong cookie, uint xid_count);
+ void mark_xid_done(ulong cookie, bool write_checkpoint);
void make_log_name(char* buf, const char* log_ident);
bool is_active(const char* log_file_name);
+ bool can_purge_log(const char *log_file_name);
int update_log_index(LOG_INFO* linfo, bool need_update_threads);
int rotate(bool force_rotate, bool* check_purge);
- void purge();
+ void checkpoint_and_purge(ulong binlog_id);
int rotate_and_purge(bool force_rotate);
/**
Flush binlog cache and synchronize to disk.
@@ -664,7 +772,8 @@ public:
int register_create_index_entry(const char* entry);
int purge_index_entry(THD *thd, ulonglong *decrease_log_space,
bool need_mutex);
- bool reset_logs(THD* thd);
+ bool reset_logs(THD* thd, bool create_new_log,
+ rpl_gtid *init_state, uint32 init_state_len);
void close(uint exiting);
void clear_inuse_flag_when_closing(File file);
@@ -687,6 +796,21 @@ public:
inline IO_CACHE *get_index_file() { return &index_file;}
inline uint32 get_open_count() { return open_count; }
void set_status_variables(THD *thd);
+ bool is_xidlist_idle();
+ bool write_gtid_event(THD *thd, bool standalone, bool is_transactional,
+ uint64 commit_id);
+ int read_state_from_file();
+ int write_state_to_file();
+ int get_most_recent_gtid_list(rpl_gtid **list, uint32 *size);
+ bool append_state_pos(String *str);
+ bool append_state(String *str);
+ bool is_empty_state();
+ bool find_in_binlog_state(uint32 domain_id, uint32 server_id,
+ rpl_gtid *out_gtid);
+ bool lookup_domain_in_binlog_state(uint32 domain_id, rpl_gtid *out_gtid);
+ int bump_seq_no_counter_if_needed(uint32 domain_id, uint64 seq_no);
+ bool check_strict_gtid_sequence(uint32 domain_id, uint32 server_id,
+ uint64 seq_no);
};
class Log_event_handler
@@ -712,8 +836,8 @@ public:
};
-int check_if_log_table(size_t db_len, const char *db, size_t table_name_len,
- const char *table_name, bool check_if_opened);
+int check_if_log_table(const TABLE_LIST *table, bool check_if_opened,
+ const char *errmsg);
class Log_to_csv_event_handler: public Log_event_handler
{
@@ -864,17 +988,17 @@ enum enum_binlog_format {
};
#ifdef WITH_WSREP
-IO_CACHE* get_trans_log(THD * thd);
+IO_CACHE * get_trans_log(THD * thd);
bool wsrep_trans_cache_is_empty(THD *thd);
void thd_binlog_flush_pending_rows_event(THD *thd, bool stmt_end);
void thd_binlog_trx_reset(THD * thd);
void thd_binlog_rollback_stmt(THD * thd);
-#define WSREP_BINLOG_FORMAT(my_format) \
- ((wsrep_forced_binlog_format != BINLOG_FORMAT_UNSPEC) ? \
+#define WSREP_FORMAT(my_format) \
+ ((wsrep_forced_binlog_format != BINLOG_FORMAT_UNSPEC) ? \
wsrep_forced_binlog_format : my_format)
#else
-#define WSREP_BINLOG_FORMAT(my_format) my_format
+#define WSREP_FORMAT(my_format) my_format
#endif
int query_error_code(THD *thd, bool not_killed);
uint purge_log_get_error_code(int res);
@@ -898,6 +1022,7 @@ bool general_log_print(THD *thd, enum enum_server_command command,
bool general_log_write(THD *thd, enum enum_server_command command,
const char *query, uint query_length);
+void binlog_report_wait_for(THD *thd, THD *other_thd);
void sql_perror(const char *message);
bool flush_error_log();
@@ -905,6 +1030,7 @@ File open_binlog(IO_CACHE *log, const char *log_file_name,
const char **errmsg);
void make_default_log_name(char **out, const char* log_ext, bool once);
+void binlog_reset_cache(THD *thd);
extern MYSQL_PLUGIN_IMPORT MYSQL_BIN_LOG mysql_bin_log;
extern LOGGER logger;
diff --git a/sql/log_event.cc b/sql/log_event.cc
index f71b245b942..bffaa9ee554 100644
--- a/sql/log_event.cc
+++ b/sql/log_event.cc
@@ -16,20 +16,12 @@
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
-#ifdef MYSQL_CLIENT
-
+#include <my_global.h>
#include "sql_priv.h"
#include "mysqld_error.h"
-#else
-
-#ifdef USE_PRAGMA_IMPLEMENTATION
-#pragma implementation // gcc: Class implementation
-#endif
-
-#include "sql_priv.h"
+#ifndef MYSQL_CLIENT
#include "unireg.h"
-#include "my_global.h" // REQUIRED by log_event.h > m_string.h > my_bitmap.h
#include "log_event.h"
#include "sql_base.h" // close_thread_tables
#include "sql_cache.h" // QUERY_CACHE_FLAGS_SIZE
@@ -48,6 +40,9 @@
#include "transaction.h"
#include <my_dir.h>
#include "sql_show.h" // append_identifier
+#include <mysql/psi/mysql_statement.h>
+#include <strfunc.h>
+#include "compat56.h"
#if WITH_WSREP
#include "wsrep_mysqld.h"
@@ -57,7 +52,11 @@
#include <base64.h>
#include <my_bitmap.h>
#include "rpl_utility.h"
+#include "sql_digest.h"
+
+#define my_b_write_string(A, B) my_b_write((A), (B), (uint) (sizeof(B) - 1))
+using std::max;
/**
BINLOG_CHECKSUM variable.
@@ -87,7 +86,6 @@ TYPELIB binlog_checksum_typelib=
#define FLAGSTR(V,F) ((V)&(F)?#F" ":"")
-
/*
Size of buffer for printing a double in format %.<PREC>g
@@ -96,23 +94,6 @@ TYPELIB binlog_checksum_typelib=
*/
#define FMT_G_BUFSIZE(PREC) (3 + (PREC) + 5 + 1)
-/*
- Explicit instantiation to unsigned int of template available_buffer
- function.
-*/
-template unsigned int available_buffer<unsigned int>(const char*,
- const char*,
- unsigned int);
-
-/*
- Explicit instantiation to unsigned int of template valid_buffer_range
- function.
-*/
-template bool valid_buffer_range<unsigned int>(unsigned int,
- const char*,
- const char*,
- unsigned int);
-
/*
replication event checksum is introduced in the following "checksum-home" version.
The checksum-aware servers extract FD's version to decide whether the FD event
@@ -133,7 +114,7 @@ const ulong checksum_version_product_mariadb=
checksum_version_split_mariadb[2];
#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
-static int rows_event_stmt_cleanup(Relay_log_info const *rli, THD* thd);
+static int rows_event_stmt_cleanup(rpl_group_info *rgi, THD* thd);
static const char *HA_ERR(int i)
{
@@ -197,6 +178,28 @@ static const char *HA_ERR(int i)
return "No Error!";
}
+
+/*
+ Return true if an error caught during event execution is a temporary error
+ that will cause automatic retry of the event group during parallel
+ replication, false otherwise.
+
+ In parallel replication, conflicting transactions can occasionally cause
+ deadlocks; such errors are handled automatically by rolling back re-trying
+ the transactions, so should not pollute the error log.
+*/
+static bool
+is_parallel_retry_error(rpl_group_info *rgi, int err)
+{
+ if (!rgi->is_parallel_exec)
+ return false;
+ if (rgi->killed_for_retry &&
+ (err == ER_QUERY_INTERRUPTED || err == ER_CONNECTION_KILLED))
+ return true;
+ return has_temporary_error(rgi->thd);
+}
+
+
/**
Error reporting facility for Rows_log_event::do_apply_event
@@ -211,7 +214,7 @@ static const char *HA_ERR(int i)
*/
static void inline slave_rows_error_report(enum loglevel level, int ha_error,
- Relay_log_info const *rli, THD *thd,
+ rpl_group_info *rgi, THD *thd,
TABLE *table, const char * type,
const char *log_name, ulong pos)
{
@@ -219,9 +222,21 @@ static void inline slave_rows_error_report(enum loglevel level, int ha_error,
char buff[MAX_SLAVE_ERRMSG], *slider;
const char *buff_end= buff + sizeof(buff);
uint len;
- List_iterator_fast<MYSQL_ERROR> it(thd->warning_info->warn_list());
- MYSQL_ERROR *err;
+ Diagnostics_area::Sql_condition_iterator it=
+ thd->get_stmt_da()->sql_conditions();
+ Relay_log_info const *rli= rgi->rli;
+ const Sql_condition *err;
buff[0]= 0;
+ int errcode= thd->is_error() ? thd->get_stmt_da()->sql_errno() : 0;
+
+ /*
+ In parallel replication, deadlocks or other temporary errors can happen
+ occasionally in normal operation, they will be handled correctly and
+ automatically by re-trying the transactions. So do not pollute the error
+ log with messages about them.
+ */
+ if (is_parallel_retry_error(rgi, errcode))
+ return;
for (err= it++, slider= buff; err && slider < buff_end - 1;
slider += len, err= it++)
@@ -232,7 +247,7 @@ static void inline slave_rows_error_report(enum loglevel level, int ha_error,
}
if (ha_error != 0)
- rli->report(level, thd->is_error() ? thd->stmt_da->sql_errno() : 0,
+ rli->report(level, errcode, rgi->gtid_info(),
"Could not execute %s event on table %s.%s;"
"%s handler error %s; "
"the event's master log %s, end_log_pos %lu",
@@ -240,7 +255,7 @@ static void inline slave_rows_error_report(enum loglevel level, int ha_error,
buff, handler_error == NULL ? "<unknown>" : handler_error,
log_name, pos);
else
- rli->report(level, thd->is_error() ? thd->stmt_da->sql_errno() : 0,
+ rli->report(level, errcode, rgi->gtid_info(),
"Could not execute %s event on table %s.%s;"
"%s the event's master log %s, end_log_pos %lu",
type, table->s->db.str, table->s->table_name.str,
@@ -293,7 +308,7 @@ public:
~Write_on_release_cache()
{
copy_event_cache_to_file_and_reinit(m_cache, m_file);
- if (m_flags | FLUSH_F)
+ if (m_flags & FLUSH_F)
fflush(m_file);
}
@@ -326,10 +341,6 @@ private:
flag_set m_flags;
};
-#ifndef DBUG_OFF
-uint debug_not_change_ts_if_art_event= 1; // bug#29309 simulation
-#endif
-
/*
pretty_print_str()
*/
@@ -338,24 +349,24 @@ uint debug_not_change_ts_if_art_event= 1; // bug#29309 simulation
static void pretty_print_str(IO_CACHE* cache, const char* str, int len)
{
const char* end = str + len;
- my_b_printf(cache, "\'");
+ my_b_write_byte(cache, '\'');
while (str < end)
{
char c;
switch ((c=*str++)) {
- case '\n': my_b_printf(cache, "\\n"); break;
- case '\r': my_b_printf(cache, "\\r"); break;
- case '\\': my_b_printf(cache, "\\\\"); break;
- case '\b': my_b_printf(cache, "\\b"); break;
- case '\t': my_b_printf(cache, "\\t"); break;
- case '\'': my_b_printf(cache, "\\'"); break;
- case 0 : my_b_printf(cache, "\\0"); break;
+ case '\n': my_b_write(cache, "\\n", 2); break;
+ case '\r': my_b_write(cache, "\\r", 2); break;
+ case '\\': my_b_write(cache, "\\\\", 2); break;
+ case '\b': my_b_write(cache, "\\b", 2); break;
+ case '\t': my_b_write(cache, "\\t", 2); break;
+ case '\'': my_b_write(cache, "\\'", 2); break;
+ case 0 : my_b_write(cache, "\\0", 2); break;
default:
- my_b_printf(cache, "%c", c);
+ my_b_write_byte(cache, c);
break;
}
}
- my_b_printf(cache, "\'");
+ my_b_write_byte(cache, '\'');
}
#endif /* MYSQL_CLIENT */
@@ -365,7 +376,6 @@ static void clear_all_errors(THD *thd, Relay_log_info *rli)
{
thd->is_slave_error = 0;
thd->clear_error();
- rli->clear_error();
}
inline int idempotent_error_code(int err_code)
@@ -444,13 +454,13 @@ inline int ignored_error_code(int err_code)
*/
int convert_handler_error(int error, THD* thd, TABLE *table)
{
- uint actual_error= (thd->is_error() ? thd->stmt_da->sql_errno() :
+ uint actual_error= (thd->is_error() ? thd->get_stmt_da()->sql_errno() :
0);
if (actual_error == 0)
{
table->file->print_error(error, MYF(0));
- actual_error= (thd->is_error() ? thd->stmt_da->sql_errno() :
+ actual_error= (thd->is_error() ? thd->get_stmt_da()->sql_errno() :
ER_UNKNOWN_ERROR);
if (actual_error == ER_UNKNOWN_ERROR)
if (global_system_variables.log_warnings)
@@ -522,11 +532,58 @@ pretty_print_str(String *packet, const char *str, int len)
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
/**
- Creates a temporary name for load data infile:.
+ Create a prefix for the temporary files that is to be used for
+ load data file name for this master
+
+ @param name Store prefix of name here
+ @param connection_name Connection name
+
+ @return pointer to end of name
+
+ @description
+ We assume that FN_REFLEN is big enough to hold
+ MAX_CONNECTION_NAME * MAX_FILENAME_MBWIDTH characters + 2 numbers +
+ a short extension.
+
+ The resulting file name has the following parts, each separated with a '-'
+ - PREFIX_SQL_LOAD (SQL_LOAD-)
+ - If a connection name is given (multi-master setup):
+ - Add an extra '-' to mark that this is a multi-master file
+ - connection name in lower case, converted to safe file characters.
+ (see create_logfile_name_with_suffix()).
+ - server_id
+ - A last '-' (after server_id).
+*/
+
+static char *load_data_tmp_prefix(char *name,
+ LEX_STRING *connection_name)
+{
+ name= strmov(name, PREFIX_SQL_LOAD);
+ if (connection_name->length)
+ {
+ uint buf_length;
+ uint errors;
+ /* Add marker that this is a multi-master-file */
+ *name++='-';
+ /* Convert connection_name to a safe filename */
+ buf_length= strconvert(system_charset_info, connection_name->str, FN_REFLEN,
+ &my_charset_filename, name, FN_REFLEN, &errors);
+ name+= buf_length;
+ *name++= '-';
+ }
+ name= int10_to_str(global_system_variables.server_id, name, 10);
+ *name++ = '-';
+ *name= '\0'; // For testing prefixes
+ return name;
+}
+
+
+/**
+ Creates a temporary name for LOAD DATA INFILE
@param buf Store new filename here
@param file_id File_id (part of file name)
- @param event_server_id Event_id (part of file name)
+ @param event_server_id Event_id (part of file name)
@param ext Extension for file name
@return
@@ -534,16 +591,14 @@ pretty_print_str(String *packet, const char *str, int len)
*/
static char *slave_load_file_stem(char *buf, uint file_id,
- int event_server_id, const char *ext)
+ int event_server_id, const char *ext,
+ LEX_STRING *connection_name)
{
char *res;
- fn_format(buf,PREFIX_SQL_LOAD,slave_load_tmpdir, "", MY_UNPACK_FILENAME);
+ res= buf+ unpack_dirname(buf, slave_load_tmpdir);
to_unix_path(buf);
-
- buf = strend(buf);
- buf = int10_to_str(::server_id, buf, 10);
- *buf++ = '-';
- buf = int10_to_str(event_server_id, buf, 10);
+ buf= load_data_tmp_prefix(res, connection_name);
+ buf= int10_to_str(event_server_id, buf, 10);
*buf++ = '-';
res= int10_to_str(file_id, buf, 10);
strmov(res, ext); // Add extension last
@@ -558,14 +613,17 @@ static char *slave_load_file_stem(char *buf, uint file_id,
Delete all temporary files used for SQL_LOAD.
*/
-static void cleanup_load_tmpdir()
+static void cleanup_load_tmpdir(LEX_STRING *connection_name)
{
MY_DIR *dirp;
FILEINFO *file;
uint i;
- char fname[FN_REFLEN], prefbuf[31], *p;
+ char dir[FN_REFLEN], fname[FN_REFLEN];
+ char prefbuf[31 + MAX_CONNECTION_NAME* MAX_FILENAME_MBWIDTH + 1];
+ DBUG_ENTER("cleanup_load_tmpdir");
- if (!(dirp=my_dir(slave_load_tmpdir,MYF(0))))
+ unpack_dirname(dir, slave_load_tmpdir);
+ if (!(dirp=my_dir(dir, MYF(MY_WME))))
return;
/*
@@ -576,12 +634,11 @@ static void cleanup_load_tmpdir()
we cannot meet Start_log event in the middle of events from one
LOAD DATA.
*/
- p= strmake(prefbuf, STRING_WITH_LEN(PREFIX_SQL_LOAD));
- p= int10_to_str(::server_id, p, 10);
- *(p++)= '-';
- *p= 0;
- for (i=0 ; i < (uint)dirp->number_off_files; i++)
+ load_data_tmp_prefix(prefbuf, connection_name);
+ DBUG_PRINT("enter", ("dir: '%s' prefix: '%s'", dir, prefbuf));
+
+ for (i=0 ; i < (uint)dirp->number_of_files; i++)
{
file=dirp->dir_entry+i;
if (is_prefix(file->name, prefbuf))
@@ -592,6 +649,7 @@ static void cleanup_load_tmpdir()
}
my_dirend(dirp);
+ DBUG_VOID_RETURN;
}
#endif
@@ -647,37 +705,35 @@ char *str_to_hex(char *to, const char *from, uint len)
#ifndef MYSQL_CLIENT
/**
- Append a version of the 'from' string suitable for use in a query to
+ Append a version of the 'str' string suitable for use in a query to
the 'to' string. To generate a correct escaping, the character set
information in 'csinfo' is used.
*/
-int
-append_query_string(THD *thd, CHARSET_INFO *csinfo,
- String const *from, String *to)
+int append_query_string(CHARSET_INFO *csinfo, String *to,
+ const char *str, size_t len, bool no_backslash)
{
char *beg, *ptr;
uint32 const orig_len= to->length();
- if (to->reserve(orig_len + from->length() * 2 + 4))
+ if (to->reserve(orig_len + len * 2 + 4))
return 1;
beg= (char*) to->ptr() + to->length();
ptr= beg;
if (csinfo->escape_with_backslash_is_dangerous)
- ptr= str_to_hex(ptr, from->ptr(), from->length());
+ ptr= str_to_hex(ptr, str, len);
else
{
*ptr++= '\'';
- if (!(thd->variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES))
+ if (!no_backslash)
{
- ptr+= escape_string_for_mysql(csinfo, ptr, 0,
- from->ptr(), from->length());
+ ptr+= escape_string_for_mysql(csinfo, ptr, 0, str, len);
}
else
{
- const char *frm_str= from->ptr();
+ const char *frm_str= str;
- for (; frm_str < (from->ptr() + from->length()); frm_str++)
+ for (; frm_str < (str + len); frm_str++)
{
/* Using '' way to represent "'" */
if (*frm_str == '\'')
@@ -709,8 +765,8 @@ static void print_set_option(IO_CACHE* file, uint32 bits_changed,
if (bits_changed & option)
{
if (*need_comma)
- my_b_printf(file,", ");
- my_b_printf(file,"%s=%d", name, test(flags & option));
+ my_b_write(file, ", ", 2);
+ my_b_printf(file, "%s=%d", name, MY_TEST(flags & option));
*need_comma= 1;
}
}
@@ -747,6 +803,9 @@ const char* Log_event::get_type_str(Log_event_type type)
case PRE_GA_WRITE_ROWS_EVENT: return "Write_rows_event_old";
case PRE_GA_UPDATE_ROWS_EVENT: return "Update_rows_event_old";
case PRE_GA_DELETE_ROWS_EVENT: return "Delete_rows_event_old";
+ case WRITE_ROWS_EVENT_V1: return "Write_rows_v1";
+ case UPDATE_ROWS_EVENT_V1: return "Update_rows_v1";
+ case DELETE_ROWS_EVENT_V1: return "Delete_rows_v1";
case WRITE_ROWS_EVENT: return "Write_rows";
case UPDATE_ROWS_EVENT: return "Update_rows";
case DELETE_ROWS_EVENT: return "Delete_rows";
@@ -754,6 +813,18 @@ const char* Log_event::get_type_str(Log_event_type type)
case EXECUTE_LOAD_QUERY_EVENT: return "Execute_load_query";
case INCIDENT_EVENT: return "Incident";
case ANNOTATE_ROWS_EVENT: return "Annotate_rows";
+ case BINLOG_CHECKPOINT_EVENT: return "Binlog_checkpoint";
+ case GTID_EVENT: return "Gtid";
+ case GTID_LIST_EVENT: return "Gtid_list";
+
+ /* The following is only for mysqlbinlog */
+ case IGNORABLE_LOG_EVENT: return "Ignorable log event";
+ case ROWS_QUERY_LOG_EVENT: return "MySQL Rows_query";
+ case GTID_LOG_EVENT: return "MySQL Gtid";
+ case ANONYMOUS_GTID_LOG_EVENT: return "MySQL Anonymous_Gtid";
+ case PREVIOUS_GTIDS_LOG_EVENT: return "MySQL Previous_gtids";
+ case HEARTBEAT_LOG_EVENT: return "Heartbeat";
+
default: return "Unknown"; /* impossible */
}
}
@@ -774,7 +845,7 @@ Log_event::Log_event(THD* thd_arg, uint16 flags_arg, bool using_trans)
crc(0), thd(thd_arg),
checksum_alg(BINLOG_CHECKSUM_ALG_UNDEF)
{
- server_id= thd->server_id;
+ server_id= thd->variables.server_id;
when= thd->start_time;
when_sec_part=thd->start_time_sec_part;
@@ -799,7 +870,7 @@ Log_event::Log_event()
cache_type(Log_event::EVENT_INVALID_CACHE), crc(0),
thd(0), checksum_alg(BINLOG_CHECKSUM_ALG_UNDEF)
{
- server_id= ::server_id;
+ server_id= global_system_variables.server_id;
/*
We can't call my_time() here as this would cause a call before
my_init() is called
@@ -817,7 +888,7 @@ Log_event::Log_event()
Log_event::Log_event(const char* buf,
const Format_description_log_event* description_event)
- :temp_buf(0), cache_type(Log_event::EVENT_INVALID_CACHE),
+ :temp_buf(0), exec_time(0), cache_type(Log_event::EVENT_INVALID_CACHE),
crc(0), checksum_alg(BINLOG_CHECKSUM_ALG_UNDEF)
{
#ifndef MYSQL_CLIENT
@@ -888,8 +959,11 @@ Log_event::Log_event(const char* buf,
#ifndef MYSQL_CLIENT
#ifdef HAVE_REPLICATION
-int Log_event::do_update_pos(Relay_log_info *rli)
+int Log_event::do_update_pos(rpl_group_info *rgi)
{
+ Relay_log_info *rli= rgi->rli;
+ DBUG_ENTER("Log_event::do_update_pos");
+
/*
rli is null when (as far as I (Guilhem) know) the caller is
Load_log_event::do_apply_event *and* that one is called from
@@ -905,36 +979,30 @@ int Log_event::do_update_pos(Relay_log_info *rli)
if (rli)
{
/*
- bug#29309 simulation: resetting the flag to force
- wrong behaviour of artificial event to update
- rli->last_master_timestamp for only one time -
- the first FLUSH LOGS in the test.
+ In parallel execution, delay position update for the events that are
+ not part of event groups (format description, rotate, and such) until
+ the actual event execution reaches that point.
*/
- DBUG_EXECUTE_IF("let_first_flush_log_change_timestamp",
- if (debug_not_change_ts_if_art_event == 1
- && is_artificial_event())
- debug_not_change_ts_if_art_event= 0; );
- rli->stmt_done(log_pos, is_artificial_event() &&
- IF_DBUG(debug_not_change_ts_if_art_event > 0, 1) ?
- 0 : when);
- DBUG_EXECUTE_IF("let_first_flush_log_change_timestamp",
- if (debug_not_change_ts_if_art_event == 0)
- debug_not_change_ts_if_art_event= 2; );
+ if (!rgi->is_parallel_exec || is_group_event(get_type_code()))
+ rli->stmt_done(log_pos, thd, rgi);
}
- return 0; // Cannot fail currently
+ DBUG_RETURN(0); // Cannot fail currently
}
Log_event::enum_skip_reason
-Log_event::do_shall_skip(Relay_log_info *rli)
-{
- DBUG_PRINT("info", ("ev->server_id=%lu, ::server_id=%lu,"
- " rli->replicate_same_server_id=%d,"
- " rli->slave_skip_counter=%d",
- (ulong) server_id, (ulong) ::server_id,
+Log_event::do_shall_skip(rpl_group_info *rgi)
+{
+ Relay_log_info *rli= rgi->rli;
+ DBUG_PRINT("info", ("ev->server_id: %lu, ::server_id: %lu,"
+ " rli->replicate_same_server_id: %d,"
+ " rli->slave_skip_counter: %llu",
+ (ulong) server_id,
+ (ulong) global_system_variables.server_id,
rli->replicate_same_server_id,
rli->slave_skip_counter));
- if ((server_id == ::server_id && !rli->replicate_same_server_id) ||
+ if ((server_id == global_system_variables.server_id &&
+ !rli->replicate_same_server_id) ||
(rli->slave_skip_counter == 1 && rli->is_in_group()) ||
(flags & LOG_EVENT_SKIP_REPLICATION_F &&
opt_replicate_events_marked_for_skip != RPL_SKIP_REPLICATE))
@@ -1027,12 +1095,22 @@ my_bool Log_event::need_checksum()
and Stop event)
provides their checksum alg preference through Log_event::checksum_alg.
*/
- ret= ((checksum_alg != BINLOG_CHECKSUM_ALG_UNDEF) ?
- (checksum_alg != BINLOG_CHECKSUM_ALG_OFF) :
- ((binlog_checksum_options != BINLOG_CHECKSUM_ALG_OFF) &&
- (cache_type == Log_event::EVENT_NO_CACHE)) ?
- test(binlog_checksum_options) : FALSE);
-
+ if (checksum_alg != BINLOG_CHECKSUM_ALG_UNDEF)
+ ret= (checksum_alg != BINLOG_CHECKSUM_ALG_OFF);
+ else
+ {
+ if (binlog_checksum_options != BINLOG_CHECKSUM_ALG_OFF &&
+ cache_type == Log_event::EVENT_NO_CACHE)
+ {
+ checksum_alg= binlog_checksum_options;
+ ret= MY_TEST(binlog_checksum_options);
+ }
+ else
+ {
+ ret= FALSE;
+ checksum_alg= (uint8) BINLOG_CHECKSUM_ALG_OFF;
+ }
+ }
/*
FD calls the methods before data_written has been calculated.
The following invariant claims if the current is not the first
@@ -1043,10 +1121,6 @@ my_bool Log_event::need_checksum()
DBUG_ASSERT(get_type_code() != FORMAT_DESCRIPTION_EVENT || ret ||
data_written == 0);
- if (checksum_alg == BINLOG_CHECKSUM_ALG_UNDEF)
- checksum_alg= ret ? // calculated value stored
- (uint8) binlog_checksum_options : (uint8) BINLOG_CHECKSUM_ALG_OFF;
-
DBUG_ASSERT(!ret ||
((checksum_alg == binlog_checksum_options ||
/*
@@ -1086,17 +1160,19 @@ bool Log_event::wrapper_my_b_safe_write(IO_CACHE* file, const uchar* buf, ulong
bool Log_event::write_footer(IO_CACHE* file)
{
+ DBUG_ENTER("write_footer");
/*
footer contains the checksum-algorithm descriptor
followed by the checksum value
*/
if (need_checksum())
{
+ DBUG_PRINT("info", ("Writing checksum"));
uchar buf[BINLOG_CHECKSUM_LEN];
int4store(buf, crc);
- return (my_b_safe_write(file, (uchar*) buf, sizeof(buf)));
+ DBUG_RETURN(my_b_safe_write(file, (uchar*) buf, sizeof(buf)));
}
- return 0;
+ DBUG_RETURN(0);
}
/*
@@ -1118,7 +1194,7 @@ bool Log_event::write_header(IO_CACHE* file, ulong event_data_length)
if (need_checksum())
{
- crc= my_checksum(0L, NULL, 0);
+ crc= 0;
data_written += BINLOG_CHECKSUM_LEN;
}
@@ -1352,6 +1428,8 @@ Log_event* Log_event::read_log_event(IO_CACHE* file,
DBUG_ENTER("Log_event::read_log_event");
DBUG_ASSERT(description_event != 0);
char head[LOG_EVENT_MINIMAL_HEADER_LEN];
+ my_off_t position= my_b_tell(file);
+
/*
First we only want to read at most LOG_EVENT_MINIMAL_HEADER_LEN, just to
check the event for sanity and to know its length; no need to really parse
@@ -1359,11 +1437,11 @@ Log_event* Log_event::read_log_event(IO_CACHE* file,
of 13 bytes, whereas LOG_EVENT_MINIMAL_HEADER_LEN is 19 bytes (it's
"minimal" over the set {MySQL >=4.0}).
*/
- uint header_size= min(description_event->common_header_len,
+ uint header_size= MY_MIN(description_event->common_header_len,
LOG_EVENT_MINIMAL_HEADER_LEN);
LOCK_MUTEX;
- DBUG_PRINT("info", ("my_b_tell: %lu", (ulong) my_b_tell(file)));
+ DBUG_PRINT("info", ("my_b_tell: %llu", (ulonglong) position));
if (my_b_read(file, (uchar *) head, header_size))
{
DBUG_PRINT("info", ("Log_event::read_log_event(IO_CACHE*,Format_desc*) \
@@ -1385,8 +1463,8 @@ failed my_b_read"));
uint max_allowed_packet= thd ? slave_max_allowed_packet:~(uint)0;
#endif
- if (data_len > max(max_allowed_packet,
- opt_binlog_rows_event_max_size + MAX_LOG_EVENT_HEADER))
+ if (data_len > max<ulong>(max_allowed_packet,
+ opt_binlog_rows_event_max_size + MAX_LOG_EVENT_HEADER))
{
error = "Event too big";
goto err;
@@ -1420,8 +1498,9 @@ err:
{
DBUG_ASSERT(error != 0);
sql_print_error("Error in Log_event::read_log_event(): "
- "'%s', data_len: %lu, event_type: %d",
- error,data_len,head[EVENT_TYPE_OFFSET]);
+ "'%s' at offset: %llu data_len: %lu event_type: %d",
+ error, position, data_len,
+ (uchar)(head[EVENT_TYPE_OFFSET]));
my_free(buf);
/*
The SQL slave thread will check if file->error<0 to know
@@ -1454,10 +1533,12 @@ Log_event* Log_event::read_log_event(const char* buf, uint event_len,
DBUG_PRINT("info", ("binlog_version: %d", description_event->binlog_version));
DBUG_DUMP("data", (unsigned char*) buf, event_len);
- /* Check the integrity */
+ /*
+ Check the integrity; This is needed because handle_slave_io() doesn't
+ check if packet is of proper length.
+ */
if (event_len < EVENT_LEN_OFFSET ||
- (uchar)buf[EVENT_TYPE_OFFSET] >= ENUM_END_EVENT ||
- (uint) event_len != uint4korr(buf+EVENT_LEN_OFFSET))
+ event_len != uint4korr(buf+EVENT_LEN_OFFSET))
{
*error="Sanity check failed"; // Needed to free buffer
DBUG_RETURN(NULL); // general sanity check - will fail on a partial read
@@ -1570,6 +1651,15 @@ Log_event* Log_event::read_log_event(const char* buf, uint event_len,
case ROTATE_EVENT:
ev = new Rotate_log_event(buf, event_len, description_event);
break;
+ case BINLOG_CHECKPOINT_EVENT:
+ ev = new Binlog_checkpoint_log_event(buf, event_len, description_event);
+ break;
+ case GTID_EVENT:
+ ev = new Gtid_log_event(buf, event_len, description_event);
+ break;
+ case GTID_LIST_EVENT:
+ ev = new Gtid_list_log_event(buf, event_len, description_event);
+ break;
#ifdef HAVE_REPLICATION
case SLAVE_EVENT: /* can never happen (unused event) */
ev = new Slave_log_event(buf, event_len, description_event);
@@ -1618,15 +1708,27 @@ Log_event* Log_event::read_log_event(const char* buf, uint event_len,
case PRE_GA_DELETE_ROWS_EVENT:
ev = new Delete_rows_log_event_old(buf, event_len, description_event);
break;
+ case WRITE_ROWS_EVENT_V1:
case WRITE_ROWS_EVENT:
ev = new Write_rows_log_event(buf, event_len, description_event);
break;
+ case UPDATE_ROWS_EVENT_V1:
case UPDATE_ROWS_EVENT:
ev = new Update_rows_log_event(buf, event_len, description_event);
break;
+ case DELETE_ROWS_EVENT_V1:
case DELETE_ROWS_EVENT:
ev = new Delete_rows_log_event(buf, event_len, description_event);
break;
+
+ /* MySQL GTID events are ignored */
+ case GTID_LOG_EVENT:
+ case ANONYMOUS_GTID_LOG_EVENT:
+ case PREVIOUS_GTIDS_LOG_EVENT:
+ ev= new Ignorable_log_event(buf, description_event,
+ get_type_str((Log_event_type) event_type));
+ break;
+
case TABLE_MAP_EVENT:
ev = new Table_map_log_event(buf, event_len, description_event);
break;
@@ -1644,10 +1746,22 @@ Log_event* Log_event::read_log_event(const char* buf, uint event_len,
ev = new Annotate_rows_log_event(buf, event_len, description_event);
break;
default:
- DBUG_PRINT("error",("Unknown event code: %d",
- (int) buf[EVENT_TYPE_OFFSET]));
- ev= NULL;
- break;
+ /*
+ Create an object of Ignorable_log_event for unrecognized sub-class.
+ So that SLAVE SQL THREAD will only update the position and continue.
+ */
+ if (uint2korr(buf + FLAGS_OFFSET) & LOG_EVENT_IGNORABLE_F)
+ {
+ ev= new Ignorable_log_event(buf, description_event,
+ get_type_str((Log_event_type) event_type));
+ }
+ else
+ {
+ DBUG_PRINT("error",("Unknown event code: %d",
+ (int) buf[EVENT_TYPE_OFFSET]));
+ ev= NULL;
+ break;
+ }
}
}
@@ -1696,6 +1810,165 @@ Log_event* Log_event::read_log_event(const char* buf, uint event_len,
#ifdef MYSQL_CLIENT
+static void hexdump_minimal_header_to_io_cache(IO_CACHE *file,
+ my_off_t offset,
+ uchar *ptr)
+{
+ DBUG_ASSERT(LOG_EVENT_MINIMAL_HEADER_LEN == 19);
+
+ /*
+ Pretty-print the first LOG_EVENT_MINIMAL_HEADER_LEN (19) bytes of the
+ common header, which contains the basic information about the log event.
+ Every event will have at least this much header, but events could contain
+ more headers (which must be printed by other methods, if desired).
+ */
+ char emit_buf[120]; // Enough for storing one line
+ my_b_printf(file,
+ "# "
+ "|Timestamp "
+ "|Type "
+ "|Master ID "
+ "|Size "
+ "|Master Pos "
+ "|Flags\n");
+ size_t const emit_buf_written=
+ my_snprintf(emit_buf, sizeof(emit_buf),
+ "# %8llx " /* Position */
+ "|%02x %02x %02x %02x " /* Timestamp */
+ "|%02x " /* Type */
+ "|%02x %02x %02x %02x " /* Master ID */
+ "|%02x %02x %02x %02x " /* Size */
+ "|%02x %02x %02x %02x " /* Master Pos */
+ "|%02x %02x\n", /* Flags */
+ (ulonglong) offset, /* Position */
+ ptr[0], ptr[1], ptr[2], ptr[3], /* Timestamp */
+ ptr[4], /* Type */
+ ptr[5], ptr[6], ptr[7], ptr[8], /* Master ID */
+ ptr[9], ptr[10], ptr[11], ptr[12], /* Size */
+ ptr[13], ptr[14], ptr[15], ptr[16], /* Master Pos */
+ ptr[17], ptr[18]); /* Flags */
+
+ DBUG_ASSERT(static_cast<size_t>(emit_buf_written) < sizeof(emit_buf));
+ my_b_write(file, reinterpret_cast<uchar*>(emit_buf), emit_buf_written);
+ my_b_write(file, "#\n", 2);
+}
+
+
+/*
+ The number of bytes to print per line. Should be an even number,
+ and "hexdump -C" uses 16, so we'll duplicate that here.
+*/
+#define HEXDUMP_BYTES_PER_LINE 16
+
+static void format_hex_line(char *emit_buff)
+{
+ memset(emit_buff + 1, ' ',
+ 1 + 8 + 2 + (HEXDUMP_BYTES_PER_LINE * 3 + 1) + 2 +
+ HEXDUMP_BYTES_PER_LINE);
+ emit_buff[0]= '#';
+ emit_buff[2 + 8 + 2 + (HEXDUMP_BYTES_PER_LINE * 3 + 1) + 1]= '|';
+ emit_buff[2 + 8 + 2 + (HEXDUMP_BYTES_PER_LINE * 3 + 1) + 2 +
+ HEXDUMP_BYTES_PER_LINE]= '|';
+ emit_buff[2 + 8 + 2 + (HEXDUMP_BYTES_PER_LINE * 3 + 1) + 2 +
+ HEXDUMP_BYTES_PER_LINE + 1]= '\n';
+ emit_buff[2 + 8 + 2 + (HEXDUMP_BYTES_PER_LINE * 3 + 1) + 2 +
+ HEXDUMP_BYTES_PER_LINE + 2]= '\0';
+}
+
+static void hexdump_data_to_io_cache(IO_CACHE *file,
+ my_off_t offset,
+ uchar *ptr,
+ my_off_t size)
+{
+ /*
+ 2 = '# '
+ 8 = address
+ 2 = ' '
+ (HEXDUMP_BYTES_PER_LINE * 3 + 1) = Each byte prints as two hex digits,
+ plus a space
+ 2 = ' |'
+ HEXDUMP_BYTES_PER_LINE = text representation
+ 2 = '|\n'
+ 1 = '\0'
+ */
+ char emit_buffer[2 + 8 + 2 + (HEXDUMP_BYTES_PER_LINE * 3 + 1) + 2 +
+ HEXDUMP_BYTES_PER_LINE + 2 + 1 ];
+ char *h,*c;
+ my_off_t i;
+
+ if (size == 0)
+ return;
+
+ format_hex_line(emit_buffer);
+ /*
+ Print the rest of the event (without common header)
+ */
+ my_off_t starting_offset = offset;
+ for (i= 0,
+ c= emit_buffer + 2 + 8 + 2 + (HEXDUMP_BYTES_PER_LINE * 3 + 1) + 2,
+ h= emit_buffer + 2 + 8 + 2;
+ i < size;
+ i++, ptr++)
+ {
+ my_snprintf(h, 4, "%02x ", *ptr);
+ h+= 3;
+
+ *c++= my_isprint(&my_charset_bin, *ptr) ? *ptr : '.';
+
+ /* Print in groups of HEXDUMP_BYTES_PER_LINE characters. */
+ if ((i % HEXDUMP_BYTES_PER_LINE) == (HEXDUMP_BYTES_PER_LINE - 1))
+ {
+ /* remove \0 left after printing hex byte representation */
+ *h= ' ';
+ /* prepare space to print address */
+ memset(emit_buffer + 2, ' ', 8);
+ /* print address */
+ size_t const emit_buf_written= my_snprintf(emit_buffer + 2, 9, "%8llx",
+ (ulonglong) starting_offset);
+ /* remove \0 left after printing address */
+ emit_buffer[2 + emit_buf_written]= ' ';
+ my_b_write(file, reinterpret_cast<uchar*>(emit_buffer),
+ sizeof(emit_buffer) - 1);
+ c= emit_buffer + 2 + 8 + 2 + (HEXDUMP_BYTES_PER_LINE * 3 + 1) + 2;
+ h= emit_buffer + 2 + 8 + 2;
+ format_hex_line(emit_buffer);
+ starting_offset+= HEXDUMP_BYTES_PER_LINE;
+ }
+ else if ((i % (HEXDUMP_BYTES_PER_LINE / 2))
+ == ((HEXDUMP_BYTES_PER_LINE / 2) - 1))
+ {
+ /*
+ In the middle of the group of HEXDUMP_BYTES_PER_LINE, emit an extra
+ space in the hex string, to make two groups.
+ */
+ *h++= ' ';
+ }
+
+ }
+
+ /*
+ There is still data left in our buffer, which means that the previous
+ line was not perfectly HEXDUMP_BYTES_PER_LINE characters, so write an
+ incomplete line, with spaces to pad out to the same length as a full
+ line would be, to make things more readable.
+ */
+ if (h != emit_buffer + 2 + 8 + 2)
+ {
+ *h= ' ';
+ *c++= '|'; *c++= '\n';
+ memset(emit_buffer + 2, ' ', 8);
+ size_t const emit_buf_written= my_snprintf(emit_buffer + 2, 9, "%8llx",
+ (ulonglong) starting_offset);
+ emit_buffer[2 + emit_buf_written]= ' ';
+ /* pad unprinted area */
+ memset(h, ' ',
+ (HEXDUMP_BYTES_PER_LINE * 3 + 1) - (h - (emit_buffer + 2 + 8 + 2)));
+ my_b_write(file, reinterpret_cast<uchar*>(emit_buffer),
+ c - emit_buffer);
+ }
+ my_b_write(file, "#\n", 2);
+}
+
/*
Log_event::print_header()
*/
@@ -1708,7 +1981,7 @@ void Log_event::print_header(IO_CACHE* file,
my_off_t hexdump_from= print_event_info->hexdump_from;
DBUG_ENTER("Log_event::print_header");
- my_b_printf(file, "#");
+ my_b_write_byte(file, '#');
print_timestamp(file);
my_b_printf(file, " server id %lu end_log_pos %s ", (ulong) server_id,
llstr(log_pos,llbuff));
@@ -1728,88 +2001,29 @@ void Log_event::print_header(IO_CACHE* file,
/* mysqlbinlog --hexdump */
if (print_event_info->hexdump_from)
{
- my_b_printf(file, "\n");
+ my_b_write_byte(file, '\n');
uchar *ptr= (uchar*)temp_buf;
- my_off_t size=
- uint4korr(ptr + EVENT_LEN_OFFSET) - LOG_EVENT_MINIMAL_HEADER_LEN;
- my_off_t i;
-
- /* Header len * 4 >= header len * (2 chars + space + extra space) */
- char *h, hex_string[LOG_EVENT_MINIMAL_HEADER_LEN*4]= {0};
- char *c, char_string[16+1]= {0};
-
- /* Pretty-print event common header if header is exactly 19 bytes */
- if (print_event_info->common_header_len == LOG_EVENT_MINIMAL_HEADER_LEN)
- {
- char emit_buf[256]; // Enough for storing one line
- my_b_printf(file, "# Position Timestamp Type Master ID "
- "Size Master Pos Flags \n");
- size_t const bytes_written=
- my_snprintf(emit_buf, sizeof(emit_buf),
- "# %8.8lx %02x %02x %02x %02x %02x "
- "%02x %02x %02x %02x %02x %02x %02x %02x "
- "%02x %02x %02x %02x %02x %02x\n",
- (unsigned long) hexdump_from,
- ptr[0], ptr[1], ptr[2], ptr[3], ptr[4], ptr[5], ptr[6],
- ptr[7], ptr[8], ptr[9], ptr[10], ptr[11], ptr[12], ptr[13],
- ptr[14], ptr[15], ptr[16], ptr[17], ptr[18]);
- DBUG_ASSERT(static_cast<size_t>(bytes_written) < sizeof(emit_buf));
- my_b_write(file, (uchar*) emit_buf, bytes_written);
- ptr += LOG_EVENT_MINIMAL_HEADER_LEN;
- hexdump_from += LOG_EVENT_MINIMAL_HEADER_LEN;
- }
-
- /* Rest of event (without common header) */
- for (i= 0, c= char_string, h=hex_string;
- i < size;
- i++, ptr++)
- {
- my_snprintf(h, 4, "%02x ", *ptr);
- h += 3;
-
- *c++= my_isalnum(&my_charset_bin, *ptr) ? *ptr : '.';
-
- if (i % 16 == 15)
- {
- /*
- my_b_printf() does not support full printf() formats, so we
- have to do it this way.
+ my_off_t size= uint4korr(ptr + EVENT_LEN_OFFSET);
+ my_off_t hdr_len= get_header_len(print_event_info->common_header_len);
- TODO: Rewrite my_b_printf() to support full printf() syntax.
- */
- char emit_buf[256];
- size_t const bytes_written=
- my_snprintf(emit_buf, sizeof(emit_buf),
- "# %8.8lx %-48.48s |%16s|\n",
- (unsigned long) (hexdump_from + (i & 0xfffffff0)),
- hex_string, char_string);
- DBUG_ASSERT(static_cast<size_t>(bytes_written) < sizeof(emit_buf));
- my_b_write(file, (uchar*) emit_buf, bytes_written);
- hex_string[0]= 0;
- char_string[0]= 0;
- c= char_string;
- h= hex_string;
- }
- else if (i % 8 == 7) *h++ = ' ';
- }
- *c= '\0';
+ size-= hdr_len;
+
+ my_b_printf(file, "# Position\n");
+
+ /* Write the header, nicely formatted by field. */
+ hexdump_minimal_header_to_io_cache(file, hexdump_from, ptr);
+
+ ptr+= hdr_len;
+ hexdump_from+= hdr_len;
+
+ /* Print the rest of the data, mimicking "hexdump -C" output. */
+ hexdump_data_to_io_cache(file, hexdump_from, ptr, size);
- if (hex_string[0])
- {
- char emit_buf[256];
- size_t const bytes_written=
- my_snprintf(emit_buf, sizeof(emit_buf),
- "# %8.8lx %-48.48s |%s|\n",
- (unsigned long) (hexdump_from + (i & 0xfffffff0)),
- hex_string, char_string);
- DBUG_ASSERT(static_cast<size_t>(bytes_written) < sizeof(emit_buf));
- my_b_write(file, (uchar*) emit_buf, bytes_written);
- }
/*
- need a # to prefix the rest of printouts for example those of
- Rows_log_event::print_helper().
+ Prefix the next line so that the output from print_helper()
+ will appear as a comment.
*/
- my_b_write(file, reinterpret_cast<const uchar*>("# "), 2);
+ my_b_write(file, "# Event: ", 9);
}
DBUG_VOID_RETURN;
}
@@ -1829,11 +2043,11 @@ static void
my_b_write_quoted(IO_CACHE *file, const uchar *ptr, uint length)
{
const uchar *s;
- my_b_printf(file, "'");
+ my_b_write_byte(file, '\'');
for (s= ptr; length > 0 ; s++, length--)
{
if (*s > 0x1F)
- my_b_write(file, s, 1);
+ my_b_write_byte(file, *s);
else if (*s == '\'')
my_b_write(file, "\\'", 2);
else if (*s == '\\')
@@ -1845,7 +2059,7 @@ my_b_write_quoted(IO_CACHE *file, const uchar *ptr, uint length)
my_b_write(file, hex, len);
}
}
- my_b_printf(file, "'");
+ my_b_write_byte(file, '\'');
}
@@ -1860,13 +2074,13 @@ static void
my_b_write_bit(IO_CACHE *file, const uchar *ptr, uint nbits)
{
uint bitnum, nbits8= ((nbits + 7) / 8) * 8, skip_bits= nbits8 - nbits;
- my_b_printf(file, "b'");
+ my_b_write(file, "b'", 2);
for (bitnum= skip_bits ; bitnum < nbits8; bitnum++)
{
int is_set= (ptr[(bitnum) / 8] >> (7 - bitnum % 8)) & 0x01;
- my_b_write(file, (const uchar*) (is_set ? "1" : "0"), 1);
+ my_b_write_byte(file, (is_set ? '1' : '0'));
}
- my_b_printf(file, "'");
+ my_b_write_byte(file, '\'');
}
@@ -1961,7 +2175,7 @@ log_event_print_value(IO_CACHE *file, const uchar *ptr,
int32 si= sint4korr(ptr);
uint32 ui= uint4korr(ptr);
my_b_write_sint32_and_uint32(file, si, ui);
- my_snprintf(typestr, typestr_length, "INT");
+ strmake(typestr, "INT", typestr_length);
return 4;
}
@@ -1969,7 +2183,7 @@ log_event_print_value(IO_CACHE *file, const uchar *ptr,
{
my_b_write_sint32_and_uint32(file, (int) (signed char) *ptr,
(uint) (unsigned char) *ptr);
- my_snprintf(typestr, typestr_length, "TINYINT");
+ strmake(typestr, "TINYINT", typestr_length);
return 1;
}
@@ -1978,7 +2192,7 @@ log_event_print_value(IO_CACHE *file, const uchar *ptr,
int32 si= (int32) sint2korr(ptr);
uint32 ui= (uint32) uint2korr(ptr);
my_b_write_sint32_and_uint32(file, si, ui);
- my_snprintf(typestr, typestr_length, "SHORTINT");
+ strmake(typestr, "SHORTINT", typestr_length);
return 2;
}
@@ -1987,23 +2201,24 @@ log_event_print_value(IO_CACHE *file, const uchar *ptr,
int32 si= sint3korr(ptr);
uint32 ui= uint3korr(ptr);
my_b_write_sint32_and_uint32(file, si, ui);
- my_snprintf(typestr, typestr_length, "MEDIUMINT");
+ strmake(typestr, "MEDIUMINT", typestr_length);
return 3;
}
case MYSQL_TYPE_LONGLONG:
{
char tmp[64];
+ size_t length;
longlong si= sint8korr(ptr);
- longlong10_to_str(si, tmp, -10);
- my_b_printf(file, "%s", tmp);
+ length= (longlong10_to_str(si, tmp, -10) - tmp);
+ my_b_write(file, tmp, length);
if (si < 0)
{
ulonglong ui= uint8korr(ptr);
longlong10_to_str((longlong) ui, tmp, 10);
my_b_printf(file, " (%s)", tmp);
}
- my_snprintf(typestr, typestr_length, "LONGINT");
+ strmake(typestr, "LONGINT", typestr_length);
return 8;
}
@@ -2031,7 +2246,7 @@ log_event_print_value(IO_CACHE *file, const uchar *ptr,
char tmp[320];
sprintf(tmp, "%-20g", (double) fl);
my_b_printf(file, "%s", tmp); /* my_snprintf doesn't support %-20g */
- my_snprintf(typestr, typestr_length, "FLOAT");
+ strmake(typestr, "FLOAT", typestr_length);
return 4;
}
@@ -2040,8 +2255,8 @@ log_event_print_value(IO_CACHE *file, const uchar *ptr,
double dbl;
float8get(dbl, ptr);
char tmp[320];
- sprintf(tmp, "%-.20g", dbl); /* my_snprintf doesn't support %-20g */
- my_b_printf(file, "%s", tmp);
+ sprintf(tmp, "%-.20g", dbl); /* strmake doesn't support %-20g */
+ my_b_printf(file, tmp, "%s");
strcpy(typestr, "DOUBLE");
return 8;
}
@@ -2060,10 +2275,21 @@ log_event_print_value(IO_CACHE *file, const uchar *ptr,
{
uint32 i32= uint4korr(ptr);
my_b_printf(file, "%d", i32);
- my_snprintf(typestr, typestr_length, "TIMESTAMP");
+ strmake(typestr, "TIMESTAMP", typestr_length);
return 4;
}
+ case MYSQL_TYPE_TIMESTAMP2:
+ {
+ char buf[MAX_DATE_STRING_REP_LENGTH];
+ struct timeval tm;
+ my_timestamp_from_binary(&tm, ptr, meta);
+ int buflen= my_timeval_to_str(&tm, buf, meta);
+ my_b_write(file, buf, buflen);
+ my_snprintf(typestr, typestr_length, "TIMESTAMP(%d)", meta);
+ return my_timestamp_binary_length(meta);
+ }
+
case MYSQL_TYPE_DATETIME:
{
ulong d, t;
@@ -2074,19 +2300,45 @@ log_event_print_value(IO_CACHE *file, const uchar *ptr,
my_b_printf(file, "%04d-%02d-%02d %02d:%02d:%02d",
(int) (d / 10000), (int) (d % 10000) / 100, (int) (d % 100),
(int) (t / 10000), (int) (t % 10000) / 100, (int) t % 100);
- my_snprintf(typestr, typestr_length, "DATETIME");
+ strmake(typestr, "DATETIME", typestr_length);
return 8;
}
+ case MYSQL_TYPE_DATETIME2:
+ {
+ char buf[MAX_DATE_STRING_REP_LENGTH];
+ MYSQL_TIME ltime;
+ longlong packed= my_datetime_packed_from_binary(ptr, meta);
+ TIME_from_longlong_datetime_packed(&ltime, packed);
+ int buflen= my_datetime_to_str(&ltime, buf, meta);
+ my_b_write_quoted(file, (uchar *) buf, buflen);
+ my_snprintf(typestr, typestr_length, "DATETIME(%d)", meta);
+ return my_datetime_binary_length(meta);
+ }
+
case MYSQL_TYPE_TIME:
{
- uint32 i32= uint3korr(ptr);
- my_b_printf(file, "'%02d:%02d:%02d'",
- i32 / 10000, (i32 % 10000) / 100, i32 % 100);
- my_snprintf(typestr, typestr_length, "TIME");
+ int32 tmp= sint3korr(ptr);
+ int32 i32= tmp >= 0 ? tmp : - tmp;
+ const char *sign= tmp < 0 ? "-" : "";
+ my_b_printf(file, "'%s%02d:%02d:%02d'",
+ sign, i32 / 10000, (i32 % 10000) / 100, i32 % 100, i32);
+ strmake(typestr, "TIME", typestr_length);
return 3;
}
-
+
+ case MYSQL_TYPE_TIME2:
+ {
+ char buf[MAX_DATE_STRING_REP_LENGTH];
+ MYSQL_TIME ltime;
+ longlong packed= my_time_packed_from_binary(ptr, meta);
+ TIME_from_longlong_time_packed(&ltime, packed);
+ int buflen= my_time_to_str(&ltime, buf, meta);
+ my_b_write_quoted(file, (uchar *) buf, buflen);
+ my_snprintf(typestr, typestr_length, "TIME(%d)", meta);
+ return my_time_binary_length(meta);
+ }
+
case MYSQL_TYPE_NEWDATE:
{
uint32 tmp= uint3korr(ptr);
@@ -2110,7 +2362,7 @@ log_event_print_value(IO_CACHE *file, const uchar *ptr,
*pos--= (char) ('0'+part%10); part/=10;
*pos= (char) ('0'+part);
my_b_printf(file , "'%s'", buf);
- my_snprintf(typestr, typestr_length, "DATE");
+ strmake(typestr, "DATE", typestr_length);
return 3;
}
@@ -2118,8 +2370,9 @@ log_event_print_value(IO_CACHE *file, const uchar *ptr,
{
uint i32= uint3korr(ptr);
my_b_printf(file , "'%04d:%02d:%02d'",
- (i32 / (16L * 32L)), (i32 / 32L % 16L), (i32 % 32L));
- my_snprintf(typestr, typestr_length, "DATE");
+ (int)(i32 / (16L * 32L)), (int)(i32 / 32L % 16L),
+ (int)(i32 % 32L));
+ strmake(typestr, "DATE", typestr_length);
return 3;
}
@@ -2127,7 +2380,7 @@ log_event_print_value(IO_CACHE *file, const uchar *ptr,
{
uint32 i32= *ptr;
my_b_printf(file, "%04d", i32+ 1900);
- my_snprintf(typestr, typestr_length, "YEAR");
+ strmake(typestr, "YEAR", typestr_length);
return 1;
}
@@ -2135,13 +2388,13 @@ log_event_print_value(IO_CACHE *file, const uchar *ptr,
switch (meta & 0xFF) {
case 1:
my_b_printf(file, "%d", (int) *ptr);
- my_snprintf(typestr, typestr_length, "ENUM(1 byte)");
+ strmake(typestr, "ENUM(1 byte)", typestr_length);
return 1;
case 2:
{
int32 i32= uint2korr(ptr);
my_b_printf(file, "%d", i32);
- my_snprintf(typestr, typestr_length, "ENUM(2 bytes)");
+ strmake(typestr, "ENUM(2 bytes)", typestr_length);
return 2;
}
default:
@@ -2160,22 +2413,22 @@ log_event_print_value(IO_CACHE *file, const uchar *ptr,
case 1:
length= *ptr;
my_b_write_quoted(file, ptr + 1, length);
- my_snprintf(typestr, typestr_length, "TINYBLOB/TINYTEXT");
+ strmake(typestr, "TINYBLOB/TINYTEXT", typestr_length);
return length + 1;
case 2:
length= uint2korr(ptr);
my_b_write_quoted(file, ptr + 2, length);
- my_snprintf(typestr, typestr_length, "BLOB/TEXT");
+ strmake(typestr, "BLOB/TEXT", typestr_length);
return length + 2;
case 3:
length= uint3korr(ptr);
my_b_write_quoted(file, ptr + 3, length);
- my_snprintf(typestr, typestr_length, "MEDIUMBLOB/MEDIUMTEXT");
+ strmake(typestr, "MEDIUMBLOB/MEDIUMTEXT", typestr_length);
return length + 3;
case 4:
length= uint4korr(ptr);
my_b_write_quoted(file, ptr + 4, length);
- my_snprintf(typestr, typestr_length, "LONGBLOB/LONGTEXT");
+ strmake(typestr, "LONGBLOB/LONGTEXT", typestr_length);
return length + 4;
default:
my_b_printf(file, "!! Unknown BLOB packlen=%d", length);
@@ -2252,11 +2505,11 @@ Rows_log_event::print_verbose_one_row(IO_CACHE *file, table_def *td,
if (is_null)
{
- my_b_printf(file, "### @%d=NULL", i + 1);
+ my_b_printf(file, "### @%lu=NULL", (ulong)i + 1);
}
else
{
- my_b_printf(file, "### @%d=", i + 1);
+ my_b_printf(file, "### @%lu=", (ulong)i + 1);
size_t fsize= td->calc_field_size((uint)i, (uchar*) value);
if (value + fsize > m_rows_end)
{
@@ -2276,7 +2529,7 @@ Rows_log_event::print_verbose_one_row(IO_CACHE *file, table_def *td,
if (print_event_info->verbose > 1)
{
- my_b_printf(file, " /* ");
+ my_b_write(file, " /* ", 4);
if (typestr[0])
my_b_printf(file, "%s ", typestr);
@@ -2286,10 +2539,10 @@ Rows_log_event::print_verbose_one_row(IO_CACHE *file, table_def *td,
my_b_printf(file, "meta=%d nullable=%d is_null=%d ",
td->field_metadata(i),
td->maybe_null(i), is_null);
- my_b_printf(file, "*/");
+ my_b_write(file, "*/", 2);
}
- my_b_printf(file, "\n");
+ my_b_write_byte(file, '\n');
null_bit_index++;
}
@@ -2309,9 +2562,33 @@ void Rows_log_event::print_verbose(IO_CACHE *file,
Table_map_log_event *map;
table_def *td;
const char *sql_command, *sql_clause1, *sql_clause2;
- Log_event_type type_code= get_type_code();
+ Log_event_type general_type_code= get_general_type_code();
- switch (type_code) {
+ if (m_extra_row_data)
+ {
+ uint8 extra_data_len= m_extra_row_data[EXTRA_ROW_INFO_LEN_OFFSET];
+ uint8 extra_payload_len= extra_data_len - EXTRA_ROW_INFO_HDR_BYTES;
+ assert(extra_data_len >= EXTRA_ROW_INFO_HDR_BYTES);
+
+ my_b_printf(file, "### Extra row data format: %u, len: %u :",
+ m_extra_row_data[EXTRA_ROW_INFO_FORMAT_OFFSET],
+ extra_payload_len);
+ if (extra_payload_len)
+ {
+ /*
+ Buffer for hex view of string, including '0x' prefix,
+ 2 hex chars / byte and trailing 0
+ */
+ const int buff_len= 2 + (256 * 2) + 1;
+ char buff[buff_len];
+ str_to_hex(buff, (const char*) &m_extra_row_data[EXTRA_ROW_INFO_HDR_BYTES],
+ extra_payload_len);
+ my_b_printf(file, "%s", buff);
+ }
+ my_b_printf(file, "\n");
+ }
+
+ switch (general_type_code) {
case WRITE_ROWS_EVENT:
sql_command= "INSERT INTO";
sql_clause1= "### SET\n";
@@ -2335,10 +2612,19 @@ void Rows_log_event::print_verbose(IO_CACHE *file,
if (!(map= print_event_info->m_table_map.get_table(m_table_id)) ||
!(td= map->create_table_def()))
{
- my_b_printf(file, "### Row event for unknown table #%d", m_table_id);
+ my_b_printf(file, "### Row event for unknown table #%lu",
+ (ulong) m_table_id);
return;
}
+ /* If the write rows event contained no values for the AI */
+ if (((general_type_code == WRITE_ROWS_EVENT) && (m_rows_buf==m_rows_end)))
+ {
+ my_b_printf(file, "### INSERT INTO %`s.%`s VALUES ()\n",
+ map->get_db_name(), map->get_table_name());
+ goto end;
+ }
+
for (const uchar *value= m_rows_buf; value < m_rows_end; )
{
size_t length;
@@ -2396,7 +2682,7 @@ void Log_event::print_base64(IO_CACHE* file,
if (print_event_info->base64_output_mode != BASE64_OUTPUT_DECODE_ROWS)
{
if (my_b_tell(file) == 0)
- my_b_printf(file, "\nBINLOG '\n");
+ my_b_write_string(file, "\nBINLOG '\n");
my_b_printf(file, "%s\n", tmp_str);
@@ -2407,31 +2693,45 @@ void Log_event::print_base64(IO_CACHE* file,
if (print_event_info->verbose)
{
Rows_log_event *ev= NULL;
+ Log_event_type et= (Log_event_type) ptr[EVENT_TYPE_OFFSET];
+
if (checksum_alg != BINLOG_CHECKSUM_ALG_UNDEF &&
checksum_alg != BINLOG_CHECKSUM_ALG_OFF)
size-= BINLOG_CHECKSUM_LEN; // checksum is displayed through the header
- if (ptr[4] == TABLE_MAP_EVENT)
+ switch (et)
+ {
+ case TABLE_MAP_EVENT:
{
Table_map_log_event *map;
map= new Table_map_log_event((const char*) ptr, size,
glob_description_event);
print_event_info->m_table_map.set_table(map->get_table_id(), map);
+ break;
}
- else if (ptr[4] == WRITE_ROWS_EVENT)
+ case WRITE_ROWS_EVENT:
+ case WRITE_ROWS_EVENT_V1:
{
ev= new Write_rows_log_event((const char*) ptr, size,
glob_description_event);
+ break;
}
- else if (ptr[4] == DELETE_ROWS_EVENT)
+ case DELETE_ROWS_EVENT:
+ case DELETE_ROWS_EVENT_V1:
{
ev= new Delete_rows_log_event((const char*) ptr, size,
glob_description_event);
+ break;
}
- else if (ptr[4] == UPDATE_ROWS_EVENT)
+ case UPDATE_ROWS_EVENT:
+ case UPDATE_ROWS_EVENT_V1:
{
ev= new Update_rows_log_event((const char*) ptr, size,
glob_description_event);
+ break;
+ }
+ default:
+ break;
}
if (ev)
@@ -2474,11 +2774,11 @@ void Log_event::print_timestamp(IO_CACHE* file, time_t* ts)
#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
inline Log_event::enum_skip_reason
-Log_event::continue_group(Relay_log_info *rli)
+Log_event::continue_group(rpl_group_info *rgi)
{
- if (rli->slave_skip_counter == 1)
+ if (rgi->rli->slave_skip_counter == 1)
return Log_event::EVENT_SKIP_IGNORE;
- return Log_event::do_shall_skip(rli);
+ return Log_event::do_shall_skip(rgi);
}
#endif
@@ -2703,17 +3003,22 @@ bool Query_log_event::write(IO_CACHE* file)
user= thd->get_invoker_user();
host= thd->get_invoker_host();
}
- else if (thd->security_ctx->priv_user)
+ else
{
Security_context *ctx= thd->security_ctx;
- user.length= strlen(ctx->priv_user);
- user.str= ctx->priv_user;
- if (ctx->priv_host[0] != '\0')
+ if (thd->need_binlog_invoker() == THD::INVOKER_USER)
{
+ user.str= ctx->priv_user;
host.str= ctx->priv_host;
- host.length= strlen(ctx->priv_host);
+ host.length= strlen(host.str);
}
+ else
+ {
+ user.str= ctx->priv_role;
+ host= empty_lex_str;
+ }
+ user.length= strlen(user.str);
}
if (user.length > 0)
@@ -2800,6 +3105,7 @@ Query_log_event::Query_log_event()
query_arg - array of char representing the query
query_length - size of the `query_arg' array
using_trans - there is a modified transactional table
+ direct - Don't cache statement
suppress_use - suppress the generation of 'USE' statements
errcode - the error code of the query
@@ -2925,13 +3231,23 @@ Query_log_event::Query_log_event(THD* thd_arg, const char* query_arg,
break;
case SQLCOM_CREATE_TABLE:
- trx_cache= (lex->select_lex.item_list.elements &&
- thd->is_current_stmt_binlog_format_row());
- use_cache= ((lex->create_info.options & HA_LEX_CREATE_TMP_TABLE) &&
- thd->in_multi_stmt_transaction_mode()) || trx_cache;
+ /*
+ If we are using CREATE ... SELECT or if we are a slave
+ executing BEGIN...COMMIT (generated by CREATE...SELECT) we
+ have to use the transactional cache to ensure we don't
+ calculate any checksum for the CREATE part.
+ */
+ trx_cache= ((lex->select_lex.item_list.elements &&
+ thd->is_current_stmt_binlog_format_row()) ||
+ (thd->variables.option_bits & OPTION_GTID_BEGIN));
+ use_cache= ((lex->create_info.tmp_table() &&
+ thd->in_multi_stmt_transaction_mode()) || trx_cache);
break;
case SQLCOM_SET_OPTION:
- use_cache= trx_cache= (lex->autocommit ? FALSE : TRUE);
+ if (lex->autocommit)
+ use_cache= trx_cache= FALSE;
+ else
+ use_cache= TRUE;
break;
case SQLCOM_RELEASE_SAVEPOINT:
case SQLCOM_ROLLBACK_TO_SAVEPOINT:
@@ -2956,8 +3272,8 @@ Query_log_event::Query_log_event(THD* thd_arg, const char* query_arg,
else
cache_type= Log_event::EVENT_STMT_CACHE;
DBUG_ASSERT(cache_type != Log_event::EVENT_INVALID_CACHE);
- DBUG_PRINT("info",("Query_log_event has flags2: %lu sql_mode: %llu",
- (ulong) flags2, sql_mode));
+ DBUG_PRINT("info",("Query_log_event has flags2: %lu sql_mode: %llu cache_tye: %d",
+ (ulong) flags2, sql_mode, cache_type));
}
#endif /* MYSQL_CLIENT */
@@ -3121,7 +3437,7 @@ Query_log_event::Query_log_event(const char* buf, uint event_len,
be even bigger, but this will suffice to catch most corruption
errors that can lead to a crash.
*/
- if (status_vars_len > min(data_len, MAX_SIZE_LOG_EVENT_STATUS))
+ if (status_vars_len > MY_MIN(data_len, MAX_SIZE_LOG_EVENT_STATUS))
{
DBUG_PRINT("info", ("status_vars_len (%u) > data_len (%lu); query= 0",
status_vars_len, data_len));
@@ -3355,6 +3671,180 @@ Query_log_event::Query_log_event(const char* buf, uint event_len,
}
+/*
+ Replace a binlog event read into a packet with a dummy event. Either a
+ Query_log_event that has just a comment, or if that will not fit in the
+ space used for the event to be replaced, then a NULL user_var event.
+
+ This is used when sending binlog data to a slave which does not understand
+ this particular event and which is too old to support informational events
+ or holes in the event stream.
+
+ This allows to write such events into the binlog on the master and still be
+ able to replicate against old slaves without them breaking.
+
+ Clears the flag LOG_EVENT_THREAD_SPECIFIC_F and set LOG_EVENT_SUPPRESS_USE_F.
+ Overwrites the type with QUERY_EVENT (or USER_VAR_EVENT), and replaces the
+ body with a minimal query / NULL user var.
+
+ Returns zero on success, -1 if error due to too little space in original
+ event. A minimum of 25 bytes (19 bytes fixed header + 6 bytes in the body)
+ is needed in any event to be replaced with a dummy event.
+*/
+int
+Query_log_event::dummy_event(String *packet, ulong ev_offset,
+ uint8 checksum_alg)
+{
+ uchar *p= (uchar *)packet->ptr() + ev_offset;
+ size_t data_len= packet->length() - ev_offset;
+ uint16 flags;
+ static const size_t min_user_var_event_len=
+ LOG_EVENT_HEADER_LEN + UV_NAME_LEN_SIZE + 1 + UV_VAL_IS_NULL; // 25
+ static const size_t min_query_event_len=
+ LOG_EVENT_HEADER_LEN + QUERY_HEADER_LEN + 1 + 1; // 34
+
+ if (checksum_alg == BINLOG_CHECKSUM_ALG_CRC32)
+ data_len-= BINLOG_CHECKSUM_LEN;
+ else
+ DBUG_ASSERT(checksum_alg == BINLOG_CHECKSUM_ALG_UNDEF ||
+ checksum_alg == BINLOG_CHECKSUM_ALG_OFF);
+
+ if (data_len < min_user_var_event_len)
+ /* Cannot replace with dummy, event too short. */
+ return -1;
+
+ flags= uint2korr(p + FLAGS_OFFSET);
+ flags&= ~LOG_EVENT_THREAD_SPECIFIC_F;
+ flags|= LOG_EVENT_SUPPRESS_USE_F;
+ int2store(p + FLAGS_OFFSET, flags);
+
+ if (data_len < min_query_event_len)
+ {
+ /*
+ Have to use dummy user_var event for such a short packet.
+
+ This works, but the event will be considered part of an event group with
+ the following event. So for example @@global.sql_slave_skip_counter=1
+ will skip not only the dummy event, but also the immediately following
+ event.
+
+ We write a NULL user var with the name @`!dummyvar` (or as much
+ as that as will fit within the size of the original event - so
+ possibly just @`!`).
+ */
+ static const char var_name[]= "!dummyvar";
+ uint name_len= data_len - (min_user_var_event_len - 1);
+
+ p[EVENT_TYPE_OFFSET]= USER_VAR_EVENT;
+ int4store(p + LOG_EVENT_HEADER_LEN, name_len);
+ memcpy(p + LOG_EVENT_HEADER_LEN + UV_NAME_LEN_SIZE, var_name, name_len);
+ p[LOG_EVENT_HEADER_LEN + UV_NAME_LEN_SIZE + name_len]= 1; // indicates NULL
+ }
+ else
+ {
+ /*
+ Use a dummy query event, just a comment.
+ */
+ static const char message[]=
+ "# Dummy event replacing event type %u that slave cannot handle.";
+ char buf[sizeof(message)+1]; /* +1, as %u can expand to 3 digits. */
+ uchar old_type= p[EVENT_TYPE_OFFSET];
+ uchar *q= p + LOG_EVENT_HEADER_LEN;
+ size_t comment_len, len;
+
+ p[EVENT_TYPE_OFFSET]= QUERY_EVENT;
+ int4store(q + Q_THREAD_ID_OFFSET, 0);
+ int4store(q + Q_EXEC_TIME_OFFSET, 0);
+ q[Q_DB_LEN_OFFSET]= 0;
+ int2store(q + Q_ERR_CODE_OFFSET, 0);
+ int2store(q + Q_STATUS_VARS_LEN_OFFSET, 0);
+ q[Q_DATA_OFFSET]= 0; /* Zero terminator for empty db */
+ q+= Q_DATA_OFFSET + 1;
+ len= my_snprintf(buf, sizeof(buf), message, old_type);
+ comment_len= data_len - (min_query_event_len - 1);
+ if (comment_len <= len)
+ memcpy(q, buf, comment_len);
+ else
+ {
+ memcpy(q, buf, len);
+ memset(q+len, ' ', comment_len - len);
+ }
+ }
+
+ if (checksum_alg == BINLOG_CHECKSUM_ALG_CRC32)
+ {
+ ha_checksum crc= my_checksum(0L, p, data_len);
+ int4store(p + data_len, crc);
+ }
+ return 0;
+}
+
+/*
+ Replace an event (GTID event) with a BEGIN query event, to be compatible
+ with an old slave.
+*/
+int
+Query_log_event::begin_event(String *packet, ulong ev_offset,
+ uint8 checksum_alg)
+{
+ uchar *p= (uchar *)packet->ptr() + ev_offset;
+ uchar *q= p + LOG_EVENT_HEADER_LEN;
+ size_t data_len= packet->length() - ev_offset;
+ uint16 flags;
+
+ if (checksum_alg == BINLOG_CHECKSUM_ALG_CRC32)
+ data_len-= BINLOG_CHECKSUM_LEN;
+ else
+ DBUG_ASSERT(checksum_alg == BINLOG_CHECKSUM_ALG_UNDEF ||
+ checksum_alg == BINLOG_CHECKSUM_ALG_OFF);
+
+ /*
+ Currently we only need to replace GTID event.
+ The length of GTID differs depending on whether it contains commit id.
+ */
+ DBUG_ASSERT(data_len == LOG_EVENT_HEADER_LEN + GTID_HEADER_LEN ||
+ data_len == LOG_EVENT_HEADER_LEN + GTID_HEADER_LEN + 2);
+ if (data_len != LOG_EVENT_HEADER_LEN + GTID_HEADER_LEN &&
+ data_len != LOG_EVENT_HEADER_LEN + GTID_HEADER_LEN + 2)
+ return 1;
+
+ flags= uint2korr(p + FLAGS_OFFSET);
+ flags&= ~LOG_EVENT_THREAD_SPECIFIC_F;
+ flags|= LOG_EVENT_SUPPRESS_USE_F;
+ int2store(p + FLAGS_OFFSET, flags);
+
+ p[EVENT_TYPE_OFFSET]= QUERY_EVENT;
+ int4store(q + Q_THREAD_ID_OFFSET, 0);
+ int4store(q + Q_EXEC_TIME_OFFSET, 0);
+ q[Q_DB_LEN_OFFSET]= 0;
+ int2store(q + Q_ERR_CODE_OFFSET, 0);
+ if (data_len == LOG_EVENT_HEADER_LEN + GTID_HEADER_LEN)
+ {
+ int2store(q + Q_STATUS_VARS_LEN_OFFSET, 0);
+ q[Q_DATA_OFFSET]= 0; /* Zero terminator for empty db */
+ q+= Q_DATA_OFFSET + 1;
+ }
+ else
+ {
+ DBUG_ASSERT(data_len == LOG_EVENT_HEADER_LEN + GTID_HEADER_LEN + 2);
+ /* Put in an empty time_zone_str to take up the extra 2 bytes. */
+ int2store(q + Q_STATUS_VARS_LEN_OFFSET, 2);
+ q[Q_DATA_OFFSET]= Q_TIME_ZONE_CODE;
+ q[Q_DATA_OFFSET+1]= 0; /* Zero length for empty time_zone_str */
+ q[Q_DATA_OFFSET+2]= 0; /* Zero terminator for empty db */
+ q+= Q_DATA_OFFSET + 3;
+ }
+ memcpy(q, "BEGIN", 5);
+
+ if (checksum_alg == BINLOG_CHECKSUM_ALG_CRC32)
+ {
+ ha_checksum crc= my_checksum(0L, p, data_len);
+ int4store(p + data_len, crc);
+ }
+ return 0;
+}
+
+
#ifdef MYSQL_CLIENT
/**
Query_log_event::print().
@@ -3433,7 +3923,7 @@ void Query_log_event::print_query_header(IO_CACHE* file,
if (unlikely(tmp)) /* some bits have changed */
{
bool need_comma= 0;
- my_b_printf(file, "SET ");
+ my_b_write_string(file, "SET ");
print_set_option(file, tmp, OPTION_NO_FOREIGN_KEY_CHECKS, ~flags2,
"@@session.foreign_key_checks", &need_comma);
print_set_option(file, tmp, OPTION_AUTO_IS_NULL, flags2,
@@ -3556,9 +4046,9 @@ void Query_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
-int Query_log_event::do_apply_event(Relay_log_info const *rli)
+int Query_log_event::do_apply_event(rpl_group_info *rgi)
{
- return do_apply_event(rli, query, q_len);
+ return do_apply_event(rgi, query, q_len);
}
/**
@@ -3583,6 +4073,8 @@ bool test_if_equal_repl_errors(int expected_error, int actual_error)
case ER_AUTOINC_READ_FAILED:
return (actual_error == ER_AUTOINC_READ_FAILED ||
actual_error == HA_ERR_AUTOINC_ERANGE);
+ case ER_UNKNOWN_TABLE:
+ return actual_error == ER_IT_IS_A_VIEW;
default:
break;
}
@@ -3607,12 +4099,21 @@ bool test_if_equal_repl_errors(int expected_error, int actual_error)
mismatch. This mismatch could be implemented with a new ER_ code, and
to ignore it you would use --slave-skip-errors...
*/
-int Query_log_event::do_apply_event(Relay_log_info const *rli,
- const char *query_arg, uint32 q_len_arg)
+int Query_log_event::do_apply_event(rpl_group_info *rgi,
+ const char *query_arg, uint32 q_len_arg)
{
LEX_STRING new_db;
int expected_error,actual_error= 0;
HA_CREATE_INFO db_options;
+ uint64 sub_id= 0;
+ rpl_gtid gtid;
+ Relay_log_info const *rli= rgi->rli;
+#ifdef WITH_WSREP
+ Rpl_filter *rpl_filter= (rli->mi) ? rli->mi->rpl_filter: NULL;
+#else
+ Rpl_filter *rpl_filter= rli->mi->rpl_filter;
+#endif /* WITH_WSREP */
+ bool current_stmt_is_commit;
DBUG_ENTER("Query_log_event::do_apply_event");
/*
@@ -3636,21 +4137,12 @@ int Query_log_event::do_apply_event(Relay_log_info const *rli,
thd->variables.auto_increment_increment= auto_increment_increment;
thd->variables.auto_increment_offset= auto_increment_offset;
- /*
- InnoDB internally stores the master log position it has executed so far,
- i.e. the position just after the COMMIT event.
- When InnoDB will want to store, the positions in rli won't have
- been updated yet, so group_master_log_* will point to old BEGIN
- and event_master_log* will point to the beginning of current COMMIT.
- But log_pos of the COMMIT Query event is what we want, i.e. the pos of the
- END of the current log event (COMMIT). We save it in rli so that InnoDB can
- access it.
- */
- const_cast<Relay_log_info*>(rli)->future_group_master_log_pos= log_pos;
DBUG_PRINT("info", ("log_pos: %lu", (ulong) log_pos));
clear_all_errors(thd, const_cast<Relay_log_info*>(rli));
- if (strcmp("COMMIT", query) == 0 && rli->tables_to_lock)
+ current_stmt_is_commit= is_commit();
+
+ if (current_stmt_is_commit && rgi->tables_to_lock)
{
/*
Cleaning-up the last statement context:
@@ -3659,9 +4151,10 @@ int Query_log_event::do_apply_event(Relay_log_info const *rli,
*/
int error;
char llbuff[22];
- if ((error= rows_event_stmt_cleanup(const_cast<Relay_log_info*>(rli), thd)))
+ if ((error= rows_event_stmt_cleanup(rgi, thd)) &&
+ !is_parallel_retry_error(rgi, error))
{
- const_cast<Relay_log_info*>(rli)->report(ERROR_LEVEL, error,
+ rli->report(ERROR_LEVEL, error, rgi->gtid_info(),
"Error in cleaning up after an event preceding the commit; "
"the group log file/position: %s %s",
const_cast<Relay_log_info*>(rli)->group_master_log_name,
@@ -3674,12 +4167,11 @@ int Query_log_event::do_apply_event(Relay_log_info const *rli,
future-change-proof addon, e.g if COMMIT handling will start checking
invariants like IN_STMT flag must be off at committing the transaction.
*/
- const_cast<Relay_log_info*>(rli)->inc_event_relay_log_pos();
- const_cast<Relay_log_info*>(rli)->clear_flag(Relay_log_info::IN_STMT);
+ rgi->inc_event_relay_log_pos();
}
else
{
- const_cast<Relay_log_info*>(rli)->slave_close_thread_tables(thd);
+ rgi->slave_close_thread_tables(thd);
}
/*
@@ -3700,9 +4192,11 @@ int Query_log_event::do_apply_event(Relay_log_info const *rli,
thd->variables.pseudo_thread_id= thread_id; // for temp tables
DBUG_PRINT("query",("%s", thd->query()));
- if (ignored_error_code((expected_error= error_code)) ||
- !unexpected_error_code(expected_error))
+ if (!(expected_error= error_code) ||
+ ignored_error_code(expected_error) ||
+ !unexpected_error_code(expected_error))
{
+ thd->slave_expected_error= expected_error;
if (flags2_inited)
/*
all bits of thd->variables.option_bits which are 1 in OPTIONS_WRITTEN_TO_BIN_LOG
@@ -3715,11 +4209,11 @@ int Query_log_event::do_apply_event(Relay_log_info const *rli,
nothing to do.
*/
/*
- We do not replicate IGNORE_DIR_IN_CREATE. That is, if the master is a
- slave which runs with SQL_MODE=IGNORE_DIR_IN_CREATE, this should not
+ We do not replicate MODE_NO_DIR_IN_CREATE. That is, if the master is a
+ slave which runs with SQL_MODE=MODE_NO_DIR_IN_CREATE, this should not
force us to ignore the dir too. Imagine you are a ring of machines, and
one has a disk problem so that you temporarily need
- IGNORE_DIR_IN_CREATE on this machine; you don't want it to propagate
+ MODE_NO_DIR_IN_CREATE on this machine; you don't want it to propagate
elsewhere (you don't want all slaves to start ignoring the dirs).
*/
if (sql_mode_inited)
@@ -3728,7 +4222,8 @@ int Query_log_event::do_apply_event(Relay_log_info const *rli,
(sql_mode & ~(ulong) MODE_NO_DIR_IN_CREATE));
if (charset_inited)
{
- if (rli->cached_charset_compare(charset))
+ rpl_sql_thread_info *sql_info= thd->system_thread_info.rpl_sql_info;
+ if (sql_info->cached_charset_compare(charset))
{
/* Verify that we support the charsets found in the event. */
if (!(thd->variables.character_set_client=
@@ -3744,7 +4239,7 @@ int Query_log_event::do_apply_event(Relay_log_info const *rli,
stop with EE_UNKNOWN_CHARSET in compare_errors (unless set to
ignore this error).
*/
- set_slave_thread_default_charset(thd, rli);
+ set_slave_thread_default_charset(thd, rgi);
goto compare_errors;
}
thd->update_charset(); // for the charset change to take effect
@@ -3800,6 +4295,38 @@ int Query_log_event::do_apply_event(Relay_log_info const *rli,
else
thd->variables.collation_database= thd->db_charset;
+ /*
+ Record any GTID in the same transaction, so slave state is
+ transactionally consistent.
+ */
+ if (current_stmt_is_commit)
+ {
+ thd->variables.option_bits&= ~OPTION_GTID_BEGIN;
+ if (rgi->gtid_pending)
+ {
+ sub_id= rgi->gtid_sub_id;
+ rgi->gtid_pending= false;
+
+ gtid= rgi->current_gtid;
+ if (rpl_global_gtid_slave_state->record_gtid(thd, &gtid, sub_id,
+ true, false))
+ {
+ int errcode= thd->get_stmt_da()->sql_errno();
+ if (!is_parallel_retry_error(rgi, errcode))
+ rli->report(ERROR_LEVEL, ER_CANNOT_UPDATE_GTID_STATE,
+ rgi->gtid_info(),
+ "Error during COMMIT: failed to update GTID state in "
+ "%s.%s: %d: %s",
+ "mysql", rpl_gtid_slave_state_table_name.str,
+ errcode,
+ thd->get_stmt_da()->message());
+ sub_id= 0;
+ thd->is_slave_error= 1;
+ goto end;
+ }
+ }
+ }
+
thd->table_map_for_update= (table_map)table_map_for_update;
thd->set_invoker(&user, &host);
/*
@@ -3814,11 +4341,24 @@ int Query_log_event::do_apply_event(Relay_log_info const *rli,
concurrency_error_code(expected_error)))
{
thd->variables.option_bits|= OPTION_MASTER_SQL_ERROR;
+ thd->variables.option_bits&= ~OPTION_GTID_BEGIN;
}
/* Execute the query (note that we bypass dispatch_command()) */
Parser_state parser_state;
if (!parser_state.init(thd, thd->query(), thd->query_length()))
{
+ DBUG_ASSERT(thd->m_digest == NULL);
+ thd->m_digest= & thd->m_digest_state;
+ DBUG_ASSERT(thd->m_statement_psi == NULL);
+ thd->m_statement_psi= MYSQL_START_STATEMENT(&thd->m_statement_state,
+ stmt_info_rpl.m_key,
+ thd->db, thd->db_length,
+ thd->charset());
+ THD_STAGE_INFO(thd, stage_init);
+ MYSQL_SET_STATEMENT_TEXT(thd->m_statement_psi, thd->query(), thd->query_length());
+ if (thd->m_digest != NULL)
+ thd->m_digest->reset(thd->m_token_array, max_digest_length);
+
mysql_parse(thd, thd->query(), thd->query_length(), &parser_state);
/* Finalize server status flags after executing a statement. */
thd->update_server_status();
@@ -3852,7 +4392,7 @@ int Query_log_event::do_apply_event(Relay_log_info const *rli,
clear_all_errors(thd, const_cast<Relay_log_info*>(rli)); /* Can ignore query */
else
{
- rli->report(ERROR_LEVEL, expected_error,
+ rli->report(ERROR_LEVEL, expected_error, rgi->gtid_info(),
"\
Query partially completed on the master (error on master: %d) \
and was aborted. There is a chance that your master is inconsistent at this \
@@ -3865,7 +4405,8 @@ START SLAVE; . Query: '%s'", expected_error, thd->query());
}
/* If the query was not ignored, it is printed to the general log */
- if (!thd->is_error() || thd->stmt_da->sql_errno() != ER_SLAVE_IGNORED_TABLE)
+ if (!thd->is_error() ||
+ thd->get_stmt_da()->sql_errno() != ER_SLAVE_IGNORED_TABLE)
general_log_write(thd, COM_QUERY, thd->query(), thd->query_length());
else
{
@@ -3890,14 +4431,14 @@ compare_errors:
not exist errors", we silently clear the error if TEMPORARY was used.
*/
if (thd->lex->sql_command == SQLCOM_DROP_TABLE && thd->lex->drop_temporary &&
- thd->is_error() && thd->stmt_da->sql_errno() == ER_BAD_TABLE_ERROR &&
+ thd->is_error() && thd->get_stmt_da()->sql_errno() == ER_BAD_TABLE_ERROR &&
!expected_error)
- thd->stmt_da->reset_diagnostics_area();
+ thd->get_stmt_da()->reset_diagnostics_area();
/*
If we expected a non-zero error code, and we don't get the same error
code, and it should be ignored or is related to a concurrency issue.
*/
- actual_error= thd->is_error() ? thd->stmt_da->sql_errno() : 0;
+ actual_error= thd->is_error() ? thd->get_stmt_da()->sql_errno() : 0;
DBUG_PRINT("info",("expected_error: %d sql_errno: %d",
expected_error, actual_error));
@@ -3907,15 +4448,14 @@ compare_errors:
!ignored_error_code(actual_error) &&
!ignored_error_code(expected_error))
{
- rli->report(ERROR_LEVEL, 0,
- "\
-Query caused different errors on master and slave. \
-Error on master: message (format)='%s' error code=%d ; \
-Error on slave: actual message='%s', error code=%d. \
-Default database: '%s'. Query: '%s'",
+ rli->report(ERROR_LEVEL, 0, rgi->gtid_info(),
+ "Query caused different errors on master and slave. "
+ "Error on master: message (format)='%s' error code=%d ; "
+ "Error on slave: actual message='%s', error code=%d. "
+ "Default database: '%s'. Query: '%s'",
ER_SAFE(expected_error),
expected_error,
- actual_error ? thd->stmt_da->message() : "no error",
+ actual_error ? thd->get_stmt_da()->message() : "no error",
actual_error,
print_slave_db_safe(db), query_arg);
thd->is_slave_error= 1;
@@ -3930,18 +4470,21 @@ Default database: '%s'. Query: '%s'",
{
DBUG_PRINT("info",("error ignored"));
clear_all_errors(thd, const_cast<Relay_log_info*>(rli));
- thd->reset_killed();
+ if (actual_error == ER_QUERY_INTERRUPTED ||
+ actual_error == ER_CONNECTION_KILLED)
+ thd->reset_killed();
}
/*
Other cases: mostly we expected no error and get one.
*/
else if (thd->is_slave_error || thd->is_fatal_error)
{
- rli->report(ERROR_LEVEL, actual_error,
- "Error '%s' on query. Default database: '%s'. Query: '%s'",
- (actual_error ? thd->stmt_da->message() :
- "unexpected success or fatal error"),
- print_slave_db_safe(thd->db), query_arg);
+ if (!is_parallel_retry_error(rgi, actual_error))
+ rli->report(ERROR_LEVEL, actual_error, rgi->gtid_info(),
+ "Error '%s' on query. Default database: '%s'. Query: '%s'",
+ (actual_error ? thd->get_stmt_da()->message() :
+ "unexpected success or fatal error"),
+ print_slave_db_safe(thd->db), query_arg);
thd->is_slave_error= 1;
}
@@ -3976,8 +4519,7 @@ Default database: '%s'. Query: '%s'",
to shutdown trying to finish incomplete events group.
*/
DBUG_EXECUTE_IF("stop_slave_middle_group",
- if (strcmp("COMMIT", query) != 0 &&
- strcmp("BEGIN", query) != 0)
+ if (!current_stmt_is_commit && is_begin() == 0)
{
if (thd->transaction.all.modified_non_trans_table)
const_cast<Relay_log_info*>(rli)->abort_slave= 1;
@@ -3985,6 +4527,9 @@ Default database: '%s'. Query: '%s'",
}
end:
+ if (sub_id && !thd->is_slave_error)
+ rpl_global_gtid_slave_state->update_state_hash(sub_id, &gtid, rgi);
+
/*
Probably we have set thd->query, thd->db, thd->catalog to point to places
in the data_buf of this event. Now the event is going to be deleted
@@ -3999,6 +4544,12 @@ end:
thd->set_db(NULL, 0); /* will free the current database */
thd->reset_query();
DBUG_PRINT("info", ("end: query= 0"));
+
+ /* Mark the statement completed. */
+ MYSQL_END_STATEMENT(thd->m_statement_psi, thd->get_stmt_da());
+ thd->m_statement_psi= NULL;
+ thd->m_digest= NULL;
+
/*
As a disk space optimization, future masters will not log an event for
LAST_INSERT_ID() if that function returned 0 (and thus they will be able
@@ -4013,29 +4564,20 @@ end:
DBUG_RETURN(thd->is_slave_error);
}
-int Query_log_event::do_update_pos(Relay_log_info *rli)
+int Query_log_event::do_update_pos(rpl_group_info *rgi)
{
- /*
- Note that we will not increment group* positions if we are just
- after a SET ONE_SHOT, because SET ONE_SHOT should not be separated
- from its following updating query.
- */
- if (thd->one_shot_set)
- {
- rli->inc_event_relay_log_pos();
- return 0;
- }
- else
- return Log_event::do_update_pos(rli);
+ return Log_event::do_update_pos(rgi);
}
Log_event::enum_skip_reason
-Query_log_event::do_shall_skip(Relay_log_info *rli)
+Query_log_event::do_shall_skip(rpl_group_info *rgi)
{
+ Relay_log_info *rli= rgi->rli;
DBUG_ENTER("Query_log_event::do_shall_skip");
- DBUG_PRINT("debug", ("query: %s; q_len: %d", query, q_len));
+ DBUG_PRINT("debug", ("query: '%s' q_len: %d", query, q_len));
DBUG_ASSERT(query && q_len > 0);
+ DBUG_ASSERT(thd == rgi->thd);
/*
An event skipped due to @@skip_replication must not be counted towards the
@@ -4047,15 +4589,15 @@ Query_log_event::do_shall_skip(Relay_log_info *rli)
if (rli->slave_skip_counter > 0)
{
- if (strcmp("BEGIN", query) == 0)
+ if (is_begin())
{
- thd->variables.option_bits|= OPTION_BEGIN;
- DBUG_RETURN(Log_event::continue_group(rli));
+ thd->variables.option_bits|= OPTION_BEGIN | OPTION_GTID_BEGIN;
+ DBUG_RETURN(Log_event::continue_group(rgi));
}
- if (strcmp("COMMIT", query) == 0 || strcmp("ROLLBACK", query) == 0)
+ if (is_commit() || is_rollback())
{
- thd->variables.option_bits&= ~OPTION_BEGIN;
+ thd->variables.option_bits&= ~(OPTION_BEGIN | OPTION_GTID_BEGIN);
DBUG_RETURN(Log_event::EVENT_SKIP_COUNT);
}
}
@@ -4074,7 +4616,29 @@ Query_log_event::do_shall_skip(Relay_log_info *rli)
}
}
#endif
- DBUG_RETURN(Log_event::do_shall_skip(rli));
+ DBUG_RETURN(Log_event::do_shall_skip(rgi));
+}
+
+
+bool
+Query_log_event::peek_is_commit_rollback(const char *event_start,
+ size_t event_len, uint8 checksum_alg)
+{
+ if (checksum_alg == BINLOG_CHECKSUM_ALG_CRC32)
+ {
+ if (event_len > BINLOG_CHECKSUM_LEN)
+ event_len-= BINLOG_CHECKSUM_LEN;
+ else
+ event_len= 0;
+ }
+ else
+ DBUG_ASSERT(checksum_alg == BINLOG_CHECKSUM_ALG_UNDEF ||
+ checksum_alg == BINLOG_CHECKSUM_ALG_OFF);
+
+ if (event_len < LOG_EVENT_HEADER_LEN + QUERY_HEADER_LEN || event_len < 9)
+ return false;
+ return !memcmp(event_start + (event_len-7), "\0COMMIT", 7) ||
+ !memcmp(event_start + (event_len-9), "\0ROLLBACK", 9);
}
#endif
@@ -4171,13 +4735,12 @@ Start_log_event_v3::Start_log_event_v3(const char* buf, uint event_len,
*description_event)
:Log_event(buf, description_event), binlog_version(BINLOG_VERSION)
{
- if (event_len < (uint)description_event->common_header_len +
- ST_COMMON_HEADER_LEN_OFFSET)
+ if (event_len < LOG_EVENT_MINIMAL_HEADER_LEN + ST_COMMON_HEADER_LEN_OFFSET)
{
server_version[0]= 0;
return;
}
- buf+= description_event->common_header_len;
+ buf+= LOG_EVENT_MINIMAL_HEADER_LEN;
binlog_version= uint2korr(buf+ST_BINLOG_VER_OFFSET);
memcpy(server_version, buf+ST_SERVER_VER_OFFSET,
ST_SERVER_VER_LEN);
@@ -4228,10 +4791,12 @@ bool Start_log_event_v3::write(IO_CACHE* file)
other words, no deadlock problem.
*/
-int Start_log_event_v3::do_apply_event(Relay_log_info const *rli)
+int Start_log_event_v3::do_apply_event(rpl_group_info *rgi)
{
DBUG_ENTER("Start_log_event_v3::do_apply_event");
int error= 0;
+ Relay_log_info *rli= rgi->rli;
+
switch (binlog_version)
{
case 3:
@@ -4244,19 +4809,13 @@ int Start_log_event_v3::do_apply_event(Relay_log_info const *rli)
*/
if (created)
{
- error= close_temporary_tables(thd);
- cleanup_load_tmpdir();
- }
- else
- {
+ rli->close_temporary_tables();
+
/*
- Set all temporary tables thread references to the current thread
- as they may point to the "old" SQL slave thread in case of its
- restart.
+ The following is only false if we get here with a BINLOG statement
*/
- TABLE *table;
- for (table= thd->temporary_tables; table; table= table->next)
- table->in_use= thd;
+ if (rli->mi)
+ cleanup_load_tmpdir(&rli->mi->cmp_connection_name);
}
break;
@@ -4272,7 +4831,7 @@ int Start_log_event_v3::do_apply_event(Relay_log_info const *rli)
Can distinguish, based on the value of 'created': this event was
generated at master startup.
*/
- error= close_temporary_tables(thd);
+ rli->close_temporary_tables();
}
/*
Otherwise, can't distinguish a Start_log_event generated at
@@ -4367,10 +4926,10 @@ Format_description_log_event(uint8 binlog_ver, const char* server_ver)
post_header_len[PRE_GA_UPDATE_ROWS_EVENT-1] = 0;
post_header_len[PRE_GA_DELETE_ROWS_EVENT-1] = 0;
- post_header_len[TABLE_MAP_EVENT-1]= TABLE_MAP_HEADER_LEN;
- post_header_len[WRITE_ROWS_EVENT-1]= ROWS_HEADER_LEN;
- post_header_len[UPDATE_ROWS_EVENT-1]= ROWS_HEADER_LEN;
- post_header_len[DELETE_ROWS_EVENT-1]= ROWS_HEADER_LEN;
+ post_header_len[TABLE_MAP_EVENT-1]= TABLE_MAP_HEADER_LEN;
+ post_header_len[WRITE_ROWS_EVENT_V1-1]= ROWS_HEADER_LEN_V1;
+ post_header_len[UPDATE_ROWS_EVENT_V1-1]= ROWS_HEADER_LEN_V1;
+ post_header_len[DELETE_ROWS_EVENT_V1-1]= ROWS_HEADER_LEN_V1;
/*
We here have the possibility to simulate a master of before we changed
the table map id to be stored in 6 bytes: when it was stored in 4
@@ -4383,11 +4942,19 @@ Format_description_log_event(uint8 binlog_ver, const char* server_ver)
*/
DBUG_EXECUTE_IF("old_row_based_repl_4_byte_map_id_master",
post_header_len[TABLE_MAP_EVENT-1]=
- post_header_len[WRITE_ROWS_EVENT-1]=
- post_header_len[UPDATE_ROWS_EVENT-1]=
- post_header_len[DELETE_ROWS_EVENT-1]= 6;);
+ post_header_len[WRITE_ROWS_EVENT_V1-1]=
+ post_header_len[UPDATE_ROWS_EVENT_V1-1]=
+ post_header_len[DELETE_ROWS_EVENT_V1-1]= 6;);
post_header_len[INCIDENT_EVENT-1]= INCIDENT_HEADER_LEN;
post_header_len[HEARTBEAT_LOG_EVENT-1]= 0;
+ post_header_len[IGNORABLE_LOG_EVENT-1]= 0;
+ post_header_len[ROWS_QUERY_LOG_EVENT-1]= 0;
+ post_header_len[GTID_LOG_EVENT-1]= 0;
+ post_header_len[ANONYMOUS_GTID_LOG_EVENT-1]= 0;
+ post_header_len[PREVIOUS_GTIDS_LOG_EVENT-1]= 0;
+ post_header_len[WRITE_ROWS_EVENT-1]= ROWS_HEADER_LEN_V2;
+ post_header_len[UPDATE_ROWS_EVENT-1]= ROWS_HEADER_LEN_V2;
+ post_header_len[DELETE_ROWS_EVENT-1]= ROWS_HEADER_LEN_V2;
// Set header length of the reserved events to 0
memset(post_header_len + MYSQL_EVENTS_END - 1, 0,
@@ -4395,6 +4962,10 @@ Format_description_log_event(uint8 binlog_ver, const char* server_ver)
// Set header lengths of Maria events
post_header_len[ANNOTATE_ROWS_EVENT-1]= ANNOTATE_ROWS_HEADER_LEN;
+ post_header_len[BINLOG_CHECKPOINT_EVENT-1]=
+ BINLOG_CHECKPOINT_HEADER_LEN;
+ post_header_len[GTID_EVENT-1]= GTID_HEADER_LEN;
+ post_header_len[GTID_LIST_EVENT-1]= GTID_LIST_HEADER_LEN;
// Sanity-check that all post header lengths are initialized.
int i;
@@ -4507,109 +5078,6 @@ Format_description_log_event(const char* buf,
checksum_alg= (uint8) BINLOG_CHECKSUM_ALG_UNDEF;
}
- /*
- In some previous versions, the events were given other event type
- id numbers than in the present version. When replicating from such
- a version, we therefore set up an array that maps those id numbers
- to the id numbers of the present server.
-
- If post_header_len is null, it means malloc failed, and is_valid
- will fail, so there is no need to do anything.
-
- The trees in which events have wrong id's are:
-
- mysql-5.1-wl1012.old mysql-5.1-wl2325-5.0-drop6p13-alpha
- mysql-5.1-wl2325-5.0-drop6 mysql-5.1-wl2325-5.0
- mysql-5.1-wl2325-no-dd
-
- (this was found by grepping for two lines in sequence where the
- first matches "FORMAT_DESCRIPTION_EVENT," and the second matches
- "TABLE_MAP_EVENT," in log_event.h in all trees)
-
- In these trees, the following server_versions existed since
- TABLE_MAP_EVENT was introduced:
-
- 5.1.1-a_drop5p3 5.1.1-a_drop5p4 5.1.1-alpha
- 5.1.2-a_drop5p10 5.1.2-a_drop5p11 5.1.2-a_drop5p12
- 5.1.2-a_drop5p13 5.1.2-a_drop5p14 5.1.2-a_drop5p15
- 5.1.2-a_drop5p16 5.1.2-a_drop5p16b 5.1.2-a_drop5p16c
- 5.1.2-a_drop5p17 5.1.2-a_drop5p4 5.1.2-a_drop5p5
- 5.1.2-a_drop5p6 5.1.2-a_drop5p7 5.1.2-a_drop5p8
- 5.1.2-a_drop5p9 5.1.3-a_drop5p17 5.1.3-a_drop5p17b
- 5.1.3-a_drop5p17c 5.1.4-a_drop5p18 5.1.4-a_drop5p19
- 5.1.4-a_drop5p20 5.1.4-a_drop6p0 5.1.4-a_drop6p1
- 5.1.4-a_drop6p2 5.1.5-a_drop5p20 5.2.0-a_drop6p3
- 5.2.0-a_drop6p4 5.2.0-a_drop6p5 5.2.0-a_drop6p6
- 5.2.1-a_drop6p10 5.2.1-a_drop6p11 5.2.1-a_drop6p12
- 5.2.1-a_drop6p6 5.2.1-a_drop6p7 5.2.1-a_drop6p8
- 5.2.2-a_drop6p13 5.2.2-a_drop6p13-alpha 5.2.2-a_drop6p13b
- 5.2.2-a_drop6p13c
-
- (this was found by grepping for "mysql," in all historical
- versions of configure.in in the trees listed above).
-
- There are 5.1.1-alpha versions that use the new event id's, so we
- do not test that version string. So replication from 5.1.1-alpha
- with the other event id's to a new version does not work.
- Moreover, we can safely ignore the part after drop[56]. This
- allows us to simplify the big list above to the following regexes:
-
- 5\.1\.[1-5]-a_drop5.*
- 5\.1\.4-a_drop6.*
- 5\.2\.[0-2]-a_drop6.*
-
- This is what we test for in the 'if' below.
- */
- if (post_header_len &&
- server_version[0] == '5' && server_version[1] == '.' &&
- server_version[3] == '.' &&
- strncmp(server_version + 5, "-a_drop", 7) == 0 &&
- ((server_version[2] == '1' &&
- server_version[4] >= '1' && server_version[4] <= '5' &&
- server_version[12] == '5') ||
- (server_version[2] == '1' &&
- server_version[4] == '4' &&
- server_version[12] == '6') ||
- (server_version[2] == '2' &&
- server_version[4] >= '0' && server_version[4] <= '2' &&
- server_version[12] == '6')))
- {
- if (number_of_event_types != 22)
- {
- DBUG_PRINT("info", (" number_of_event_types=%d",
- number_of_event_types));
- /* this makes is_valid() return false. */
- my_free(post_header_len);
- post_header_len= NULL;
- DBUG_VOID_RETURN;
- }
- static const uint8 perm[23]=
- {
- UNKNOWN_EVENT, START_EVENT_V3, QUERY_EVENT, STOP_EVENT, ROTATE_EVENT,
- INTVAR_EVENT, LOAD_EVENT, SLAVE_EVENT, CREATE_FILE_EVENT,
- APPEND_BLOCK_EVENT, EXEC_LOAD_EVENT, DELETE_FILE_EVENT,
- NEW_LOAD_EVENT,
- RAND_EVENT, USER_VAR_EVENT,
- FORMAT_DESCRIPTION_EVENT,
- TABLE_MAP_EVENT,
- PRE_GA_WRITE_ROWS_EVENT,
- PRE_GA_UPDATE_ROWS_EVENT,
- PRE_GA_DELETE_ROWS_EVENT,
- XID_EVENT,
- BEGIN_LOAD_QUERY_EVENT,
- EXECUTE_LOAD_QUERY_EVENT,
- };
- event_type_permutation= perm;
- /*
- Since we use (permuted) event id's to index the post_header_len
- array, we need to permute the post_header_len array too.
- */
- uint8 post_header_len_temp[23];
- for (int i= 1; i < 23; i++)
- post_header_len_temp[perm[i] - 1]= post_header_len[i - 1];
- for (int i= 0; i < 22; i++)
- post_header_len[i] = post_header_len_temp[i];
- }
DBUG_VOID_RETURN;
}
@@ -4622,16 +5090,15 @@ bool Format_description_log_event::write(IO_CACHE* file)
We don't call Start_log_event_v3::write() because this would make 2
my_b_safe_write().
*/
- uchar buff[FORMAT_DESCRIPTION_HEADER_LEN + BINLOG_CHECKSUM_ALG_DESC_LEN];
- size_t rec_size= sizeof(buff);
+ uchar buff[START_V3_HEADER_LEN+1];
+ size_t rec_size= sizeof(buff) + BINLOG_CHECKSUM_ALG_DESC_LEN +
+ number_of_event_types;
int2store(buff + ST_BINLOG_VER_OFFSET,binlog_version);
memcpy((char*) buff + ST_SERVER_VER_OFFSET,server_version,ST_SERVER_VER_LEN);
if (!dont_set_created)
created= get_time();
int4store(buff + ST_CREATED_OFFSET,created);
- buff[ST_COMMON_HEADER_LEN_OFFSET]= LOG_EVENT_HEADER_LEN;
- memcpy((char*) buff+ST_COMMON_HEADER_LEN_OFFSET + 1, (uchar*) post_header_len,
- LOG_EVENT_TYPES);
+ buff[ST_COMMON_HEADER_LEN_OFFSET]= common_header_len;
/*
if checksum is requested
record the checksum-algorithm descriptor next to
@@ -4644,7 +5111,7 @@ bool Format_description_log_event::write(IO_CACHE* file)
#ifndef DBUG_OFF
data_written= 0; // to prepare for need_checksum assert
#endif
- buff[FORMAT_DESCRIPTION_HEADER_LEN]= need_checksum() ?
+ uchar checksum_byte= need_checksum() ?
checksum_alg : (uint8) BINLOG_CHECKSUM_ALG_OFF;
/*
FD of checksum-aware server is always checksum-equipped, (V) is in,
@@ -4664,7 +5131,10 @@ bool Format_description_log_event::write(IO_CACHE* file)
checksum_alg= BINLOG_CHECKSUM_ALG_CRC32; // Forcing (V) room to fill anyway
}
ret= (write_header(file, rec_size) ||
- wrapper_my_b_safe_write(file, buff, rec_size) ||
+ wrapper_my_b_safe_write(file, buff, sizeof(buff)) ||
+ wrapper_my_b_safe_write(file, (uchar*)post_header_len,
+ number_of_event_types) ||
+ wrapper_my_b_safe_write(file, &checksum_byte, sizeof(checksum_byte)) ||
write_footer(file));
if (no_checksum)
checksum_alg= BINLOG_CHECKSUM_ALG_OFF;
@@ -4673,9 +5143,10 @@ bool Format_description_log_event::write(IO_CACHE* file)
#endif
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
-int Format_description_log_event::do_apply_event(Relay_log_info const *rli)
+int Format_description_log_event::do_apply_event(rpl_group_info *rgi)
{
int ret= 0;
+ Relay_log_info const *rli= rgi->rli;
DBUG_ENTER("Format_description_log_event::do_apply_event");
/*
@@ -4692,12 +5163,12 @@ int Format_description_log_event::do_apply_event(Relay_log_info const *rli)
if (!is_artificial_event() && created && thd->transaction.all.ha_list)
{
/* This is not an error (XA is safe), just an information */
- rli->report(INFORMATION_LEVEL, 0,
+ rli->report(INFORMATION_LEVEL, 0, NULL,
"Rolling back unfinished transaction (no COMMIT "
"or ROLLBACK in relay log). A probable cause is that "
"the master died while writing the transaction to "
"its binary log, thus rolled back too.");
- const_cast<Relay_log_info*>(rli)->cleanup_context(thd, 1);
+ rgi->cleanup_context(thd, 1);
}
/*
@@ -4705,7 +5176,7 @@ int Format_description_log_event::do_apply_event(Relay_log_info const *rli)
perform, we don't call Start_log_event_v3::do_apply_event()
(this was just to update the log's description event).
*/
- if (server_id != (uint32) ::server_id)
+ if (server_id != (uint32) global_system_variables.server_id)
{
/*
If the event was not requested by the slave i.e. the master sent
@@ -4716,7 +5187,7 @@ int Format_description_log_event::do_apply_event(Relay_log_info const *rli)
0, then 96, then jump to first really asked event (which is
>96). So this is ok.
*/
- ret= Start_log_event_v3::do_apply_event(rli);
+ ret= Start_log_event_v3::do_apply_event(rgi);
}
if (!ret)
@@ -4729,9 +5200,9 @@ int Format_description_log_event::do_apply_event(Relay_log_info const *rli)
DBUG_RETURN(ret);
}
-int Format_description_log_event::do_update_pos(Relay_log_info *rli)
+int Format_description_log_event::do_update_pos(rpl_group_info *rgi)
{
- if (server_id == (uint32) ::server_id)
+ if (server_id == (uint32) global_system_variables.server_id)
{
/*
We only increase the relay log position if we are skipping
@@ -4746,17 +5217,17 @@ int Format_description_log_event::do_update_pos(Relay_log_info *rli)
Intvar_log_event instead of starting at a Table_map_log_event or
the Intvar_log_event respectively.
*/
- rli->inc_event_relay_log_pos();
+ rgi->inc_event_relay_log_pos();
return 0;
}
else
{
- return Log_event::do_update_pos(rli);
+ return Log_event::do_update_pos(rgi);
}
}
Log_event::enum_skip_reason
-Format_description_log_event::do_shall_skip(Relay_log_info *rli)
+Format_description_log_event::do_shall_skip(rpl_group_info *rgi)
{
return Log_event::EVENT_SKIP_NOT;
}
@@ -4855,9 +5326,9 @@ uint8 get_checksum_alg(const char* buf, ulong len)
DBUG_ENTER("get_checksum_alg");
DBUG_ASSERT(buf[EVENT_TYPE_OFFSET] == FORMAT_DESCRIPTION_EVENT);
- memcpy(version, buf +
- buf[LOG_EVENT_MINIMAL_HEADER_LEN + ST_COMMON_HEADER_LEN_OFFSET]
- + ST_SERVER_VER_OFFSET, ST_SERVER_VER_LEN);
+ memcpy(version,
+ buf + LOG_EVENT_MINIMAL_HEADER_LEN + ST_SERVER_VER_OFFSET,
+ ST_SERVER_VER_LEN);
version[ST_SERVER_VER_LEN - 1]= 0;
do_server_version_split(version, &version_split);
@@ -5277,33 +5748,33 @@ void Load_log_event::print(FILE* file_arg, PRINT_EVENT_INFO* print_event_info,
my_b_printf(&cache, "%sLOAD DATA ",
commented ? "# " : "");
if (check_fname_outside_temp_buf())
- my_b_printf(&cache, "LOCAL ");
+ my_b_write_string(&cache, "LOCAL ");
my_b_printf(&cache, "INFILE '%-*s' ", fname_len, fname);
if (sql_ex.opt_flags & REPLACE_FLAG)
- my_b_printf(&cache,"REPLACE ");
+ my_b_write_string(&cache, "REPLACE ");
else if (sql_ex.opt_flags & IGNORE_FLAG)
- my_b_printf(&cache,"IGNORE ");
+ my_b_write_string(&cache, "IGNORE ");
my_b_printf(&cache, "INTO TABLE `%s`", table_name);
- my_b_printf(&cache, " FIELDS TERMINATED BY ");
+ my_b_write_string(&cache, " FIELDS TERMINATED BY ");
pretty_print_str(&cache, sql_ex.field_term, sql_ex.field_term_len);
if (sql_ex.opt_flags & OPT_ENCLOSED_FLAG)
- my_b_printf(&cache," OPTIONALLY ");
- my_b_printf(&cache, " ENCLOSED BY ");
+ my_b_write_string(&cache, " OPTIONALLY ");
+ my_b_write_string(&cache, " ENCLOSED BY ");
pretty_print_str(&cache, sql_ex.enclosed, sql_ex.enclosed_len);
- my_b_printf(&cache, " ESCAPED BY ");
+ my_b_write_string(&cache, " ESCAPED BY ");
pretty_print_str(&cache, sql_ex.escaped, sql_ex.escaped_len);
- my_b_printf(&cache," LINES TERMINATED BY ");
+ my_b_write_string(&cache, " LINES TERMINATED BY ");
pretty_print_str(&cache, sql_ex.line_term, sql_ex.line_term_len);
if (sql_ex.line_start)
{
- my_b_printf(&cache," STARTING BY ");
+ my_b_write_string(&cache," STARTING BY ");
pretty_print_str(&cache, sql_ex.line_start, sql_ex.line_start_len);
}
if ((long) skip_lines > 0)
@@ -5313,16 +5784,16 @@ void Load_log_event::print(FILE* file_arg, PRINT_EVENT_INFO* print_event_info,
{
uint i;
const char* field = fields;
- my_b_printf(&cache, " (");
+ my_b_write_string(&cache, " (");
for (i = 0; i < num_fields; i++)
{
if (i)
- my_b_printf(&cache, ",");
+ my_b_write_byte(&cache, ',');
my_b_printf(&cache, "%`s", field);
field += field_lens[i] + 1;
}
- my_b_printf(&cache, ")");
+ my_b_write_byte(&cache, ')');
}
my_b_printf(&cache, "%s\n", print_event_info->delimiter);
@@ -5388,10 +5859,12 @@ void Load_log_event::set_fields(const char* affected_db,
1 Failure
*/
-int Load_log_event::do_apply_event(NET* net, Relay_log_info const *rli,
+int Load_log_event::do_apply_event(NET* net, rpl_group_info *rgi,
bool use_rli_only_for_errors)
{
LEX_STRING new_db;
+ Relay_log_info const *rli= rgi->rli;
+ Rpl_filter *rpl_filter= rli->mi->rpl_filter;
DBUG_ENTER("Load_log_event::do_apply_event");
new_db.length= db_len;
@@ -5403,7 +5876,7 @@ int Load_log_event::do_apply_event(NET* net, Relay_log_info const *rli,
clear_all_errors(thd, const_cast<Relay_log_info*>(rli));
/* see Query_log_event::do_apply_event() and BUG#13360 */
- DBUG_ASSERT(!rli->m_table_map.count());
+ DBUG_ASSERT(!rgi->m_table_map.count());
/*
Usually lex_start() is called by mysql_parse(), but we need it here
as the present method does not call mysql_parse().
@@ -5412,16 +5885,6 @@ int Load_log_event::do_apply_event(NET* net, Relay_log_info const *rli,
thd->lex->local_file= local_fname;
mysql_reset_thd_for_next_command(thd);
- if (!use_rli_only_for_errors)
- {
- /*
- Saved for InnoDB, see comment in
- Query_log_event::do_apply_event()
- */
- const_cast<Relay_log_info*>(rli)->future_group_master_log_pos= log_pos;
- DBUG_PRINT("info", ("log_pos: %lu", (ulong) log_pos));
- }
-
/*
We test replicate_*_db rules. Note that we have already prepared
the file to load, even if we are going to ignore and delete it
@@ -5449,7 +5912,7 @@ int Load_log_event::do_apply_event(NET* net, Relay_log_info const *rli,
{
thd->set_time(when, when_sec_part);
thd->set_query_id(next_query_id());
- thd->warning_info->opt_clear_warning_info(thd->query_id);
+ thd->get_stmt_da()->opt_clear_warning_info(thd->query_id);
TABLE_LIST tables;
tables.init_one_table(thd->strmake(thd->db, thd->db_length),
@@ -5560,7 +6023,8 @@ int Load_log_event::do_apply_event(NET* net, Relay_log_info const *rli,
update it inside mysql_load().
*/
List<Item> tmp_list;
- if (mysql_load(thd, &ex, &tables, field_list, tmp_list, tmp_list,
+ if (open_temporary_tables(thd, &tables) ||
+ mysql_load(thd, &ex, &tables, field_list, tmp_list, tmp_list,
handle_dup, ignore, net != 0))
thd->is_slave_error= 1;
if (thd->cuted_fields)
@@ -5595,9 +6059,10 @@ error:
thd->catalog= 0;
thd->set_db(NULL, 0); /* will free the current database */
thd->reset_query();
- thd->stmt_da->can_overwrite_status= TRUE;
+ thd->get_stmt_da()->set_overwrite_status(true);
thd->is_error() ? trans_rollback_stmt(thd) : trans_commit_stmt(thd);
- thd->stmt_da->can_overwrite_status= FALSE;
+ thd->variables.option_bits&= ~(OPTION_BEGIN | OPTION_GTID_BEGIN);
+ thd->get_stmt_da()->set_overwrite_status(false);
close_thread_tables(thd);
/*
- If transaction rollback was requested due to deadlock
@@ -5631,15 +6096,15 @@ error:
int sql_errno;
if (thd->is_error())
{
- err= thd->stmt_da->message();
- sql_errno= thd->stmt_da->sql_errno();
+ err= thd->get_stmt_da()->message();
+ sql_errno= thd->get_stmt_da()->sql_errno();
}
else
{
sql_errno=ER_UNKNOWN_ERROR;
err=ER(sql_errno);
}
- rli->report(ERROR_LEVEL, sql_errno,"\
+ rli->report(ERROR_LEVEL, sql_errno, rgi->gtid_info(), "\
Error '%s' running LOAD DATA INFILE on table '%s'. Default database: '%s'",
err, (char*)table_name, print_slave_db_safe(remember_db));
free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC));
@@ -5656,12 +6121,12 @@ Error '%s' running LOAD DATA INFILE on table '%s'. Default database: '%s'",
(char*)table_name,
print_slave_db_safe(remember_db));
- rli->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR,
+ rli->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR, rgi->gtid_info(),
ER(ER_SLAVE_FATAL_ERROR), buf);
DBUG_RETURN(1);
}
- DBUG_RETURN( use_rli_only_for_errors ? 0 : Log_event::do_apply_event(rli) );
+ DBUG_RETURN( use_rli_only_for_errors ? 0 : Log_event::do_apply_event(rgi) );
}
#endif
@@ -5702,7 +6167,7 @@ void Rotate_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
if (print_event_info->short_form)
return;
print_header(&cache, print_event_info, FALSE);
- my_b_printf(&cache, "\tRotate to ");
+ my_b_write_string(&cache, "\tRotate to ");
if (new_log_ident)
my_b_write(&cache, (uchar*) new_log_ident, (uint)ident_len);
my_b_printf(&cache, " pos: %s\n", llstr(pos, buf));
@@ -5746,16 +6211,14 @@ Rotate_log_event::Rotate_log_event(const char* buf, uint event_len,
{
DBUG_ENTER("Rotate_log_event::Rotate_log_event(char*,...)");
// The caller will ensure that event_len is what we have at EVENT_LEN_OFFSET
- uint8 header_size= description_event->common_header_len;
uint8 post_header_len= description_event->post_header_len[ROTATE_EVENT-1];
uint ident_offset;
- if (event_len < header_size)
+ if (event_len < LOG_EVENT_MINIMAL_HEADER_LEN)
DBUG_VOID_RETURN;
- buf += header_size;
- pos = post_header_len ? uint8korr(buf + R_POS_OFFSET) : 4;
- ident_len = (uint)(event_len -
- (header_size+post_header_len));
- ident_offset = post_header_len;
+ buf+= LOG_EVENT_MINIMAL_HEADER_LEN;
+ pos= post_header_len ? uint8korr(buf + R_POS_OFFSET) : 4;
+ ident_len= (uint)(event_len - (LOG_EVENT_MINIMAL_HEADER_LEN + post_header_len));
+ ident_offset= post_header_len;
set_if_smaller(ident_len,FN_REFLEN-1);
new_log_ident= my_strndup(buf + ident_offset, (uint) ident_len, MYF(MY_WME));
DBUG_PRINT("debug", ("new_log_ident: '%s'", new_log_ident));
@@ -5796,15 +6259,16 @@ bool Rotate_log_event::write(IO_CACHE* file)
@retval
0 ok
*/
-int Rotate_log_event::do_update_pos(Relay_log_info *rli)
+int Rotate_log_event::do_update_pos(rpl_group_info *rgi)
{
+ Relay_log_info *rli= rgi->rli;
DBUG_ENTER("Rotate_log_event::do_update_pos");
#ifndef DBUG_OFF
char buf[32];
#endif
DBUG_PRINT("info", ("server_id=%lu; ::server_id=%lu",
- (ulong) this->server_id, (ulong) ::server_id));
+ (ulong) this->server_id, (ulong) global_system_variables.server_id));
DBUG_PRINT("info", ("new_log_ident: %s", this->new_log_ident));
DBUG_PRINT("info", ("pos: %s", llstr(this->pos, buf)));
@@ -5823,10 +6287,16 @@ int Rotate_log_event::do_update_pos(Relay_log_info *rli)
correspond to the beginning of the transaction. Starting from
5.0.0, there also are some rotates from the slave itself, in the
relay log, which shall not change the group positions.
+
+ In parallel replication, rotate event is executed out-of-band with normal
+ events, so we cannot update group_master_log_name or _pos here, it will
+ be updated with the next normal event instead.
*/
- if ((server_id != ::server_id || rli->replicate_same_server_id) &&
+ if ((server_id != global_system_variables.server_id ||
+ rli->replicate_same_server_id) &&
!is_relay_log_event() &&
- !rli->is_in_group())
+ !rli->is_in_group() &&
+ !rgi->is_parallel_exec)
{
mysql_mutex_lock(&rli->data_lock);
DBUG_PRINT("info", ("old group_master_log_name: '%s' "
@@ -5835,29 +6305,30 @@ int Rotate_log_event::do_update_pos(Relay_log_info *rli)
(ulong) rli->group_master_log_pos));
memcpy(rli->group_master_log_name, new_log_ident, ident_len+1);
rli->notify_group_master_log_name_update();
- rli->inc_group_relay_log_pos(pos, TRUE /* skip_lock */);
+ rli->inc_group_relay_log_pos(pos, rgi, TRUE /* skip_lock */);
DBUG_PRINT("info", ("new group_master_log_name: '%s' "
"new group_master_log_pos: %lu",
rli->group_master_log_name,
(ulong) rli->group_master_log_pos));
mysql_mutex_unlock(&rli->data_lock);
+ rpl_global_gtid_slave_state->record_and_update_gtid(thd, rgi);
flush_relay_log_info(rli);
/*
- Reset thd->variables.option_bits and sql_mode etc, because this could be the signal of
- a master's downgrade from 5.0 to 4.0.
+ Reset thd->variables.option_bits and sql_mode etc, because this could
+ be the signal of a master's downgrade from 5.0 to 4.0.
However, no need to reset description_event_for_exec: indeed, if the next
master is 5.0 (even 5.0.1) we will soon get a Format_desc; if the next
master is 4.0 then the events are in the slave's format (conversion).
*/
set_slave_thread_options(thd);
- set_slave_thread_default_charset(thd, rli);
+ set_slave_thread_default_charset(thd, rgi);
thd->variables.sql_mode= global_system_variables.sql_mode;
thd->variables.auto_increment_increment=
thd->variables.auto_increment_offset= 1;
}
else
- rli->inc_event_relay_log_pos();
+ rgi->inc_event_relay_log_pos();
DBUG_RETURN(0);
@@ -5865,9 +6336,9 @@ int Rotate_log_event::do_update_pos(Relay_log_info *rli)
Log_event::enum_skip_reason
-Rotate_log_event::do_shall_skip(Relay_log_info *rli)
+Rotate_log_event::do_shall_skip(rpl_group_info *rgi)
{
- enum_skip_reason reason= Log_event::do_shall_skip(rli);
+ enum_skip_reason reason= Log_event::do_shall_skip(rgi);
switch (reason) {
case Log_event::EVENT_SKIP_NOT:
@@ -5885,6 +6356,696 @@ Rotate_log_event::do_shall_skip(Relay_log_info *rli)
/**************************************************************************
+ Binlog_checkpoint_log_event methods
+**************************************************************************/
+
+#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
+void Binlog_checkpoint_log_event::pack_info(THD *thd, Protocol *protocol)
+{
+ protocol->store(binlog_file_name, binlog_file_len, &my_charset_bin);
+}
+
+
+Log_event::enum_skip_reason
+Binlog_checkpoint_log_event::do_shall_skip(rpl_group_info *rgi)
+{
+ enum_skip_reason reason= Log_event::do_shall_skip(rgi);
+ if (reason == EVENT_SKIP_COUNT)
+ reason= EVENT_SKIP_NOT;
+ return reason;
+}
+#endif
+
+
+#ifdef MYSQL_CLIENT
+void Binlog_checkpoint_log_event::print(FILE *file,
+ PRINT_EVENT_INFO *print_event_info)
+{
+ Write_on_release_cache cache(&print_event_info->head_cache, file,
+ Write_on_release_cache::FLUSH_F);
+
+ if (print_event_info->short_form)
+ return;
+ print_header(&cache, print_event_info, FALSE);
+ my_b_write_string(&cache, "\tBinlog checkpoint ");
+ my_b_write(&cache, (uchar*)binlog_file_name, binlog_file_len);
+ my_b_write_byte(&cache, '\n');
+}
+#endif /* MYSQL_CLIENT */
+
+
+#ifdef MYSQL_SERVER
+Binlog_checkpoint_log_event::Binlog_checkpoint_log_event(
+ const char *binlog_file_name_arg,
+ uint binlog_file_len_arg)
+ :Log_event(),
+ binlog_file_name(my_strndup(binlog_file_name_arg, binlog_file_len_arg,
+ MYF(MY_WME))),
+ binlog_file_len(binlog_file_len_arg)
+{
+ cache_type= EVENT_NO_CACHE;
+}
+#endif /* MYSQL_SERVER */
+
+
+Binlog_checkpoint_log_event::Binlog_checkpoint_log_event(
+ const char *buf, uint event_len,
+ const Format_description_log_event *description_event)
+ :Log_event(buf, description_event), binlog_file_name(0)
+{
+ uint8 header_size= description_event->common_header_len;
+ uint8 post_header_len=
+ description_event->post_header_len[BINLOG_CHECKPOINT_EVENT-1];
+ if (event_len < header_size + post_header_len ||
+ post_header_len < BINLOG_CHECKPOINT_HEADER_LEN)
+ return;
+ buf+= header_size;
+ /* See uint4korr and int4store below */
+ compile_time_assert(BINLOG_CHECKPOINT_HEADER_LEN == 4);
+ binlog_file_len= uint4korr(buf);
+ if (event_len - (header_size + post_header_len) < binlog_file_len)
+ return;
+ binlog_file_name= my_strndup(buf + post_header_len, binlog_file_len,
+ MYF(MY_WME));
+ return;
+}
+
+
+#ifndef MYSQL_CLIENT
+bool Binlog_checkpoint_log_event::write(IO_CACHE *file)
+{
+ uchar buf[BINLOG_CHECKPOINT_HEADER_LEN];
+ int4store(buf, binlog_file_len);
+ return write_header(file, BINLOG_CHECKPOINT_HEADER_LEN + binlog_file_len) ||
+ wrapper_my_b_safe_write(file, buf, BINLOG_CHECKPOINT_HEADER_LEN) ||
+ wrapper_my_b_safe_write(file, (const uchar *)binlog_file_name,
+ binlog_file_len) ||
+ write_footer(file);
+}
+#endif /* MYSQL_CLIENT */
+
+
+/**************************************************************************
+ Global transaction ID stuff
+**************************************************************************/
+
+Gtid_log_event::Gtid_log_event(const char *buf, uint event_len,
+ const Format_description_log_event *description_event)
+ : Log_event(buf, description_event), seq_no(0), commit_id(0)
+{
+ uint8 header_size= description_event->common_header_len;
+ uint8 post_header_len= description_event->post_header_len[GTID_EVENT-1];
+ if (event_len < header_size + post_header_len ||
+ post_header_len < GTID_HEADER_LEN)
+ return;
+
+ buf+= header_size;
+ seq_no= uint8korr(buf);
+ buf+= 8;
+ domain_id= uint4korr(buf);
+ buf+= 4;
+ flags2= *buf;
+ if (flags2 & FL_GROUP_COMMIT_ID)
+ {
+ if (event_len < (uint)header_size + GTID_HEADER_LEN + 2)
+ {
+ seq_no= 0; // So is_valid() returns false
+ return;
+ }
+ ++buf;
+ commit_id= uint8korr(buf);
+ }
+}
+
+
+#ifdef MYSQL_SERVER
+
+Gtid_log_event::Gtid_log_event(THD *thd_arg, uint64 seq_no_arg,
+ uint32 domain_id_arg, bool standalone,
+ uint16 flags_arg, bool is_transactional,
+ uint64 commit_id_arg)
+ : Log_event(thd_arg, flags_arg, is_transactional),
+ seq_no(seq_no_arg), commit_id(commit_id_arg), domain_id(domain_id_arg),
+ flags2((standalone ? FL_STANDALONE : 0) | (commit_id_arg ? FL_GROUP_COMMIT_ID : 0))
+{
+ cache_type= Log_event::EVENT_NO_CACHE;
+}
+
+
+/*
+ Used to record GTID while sending binlog to slave, without having to
+ fully contruct every Gtid_log_event() needlessly.
+*/
+bool
+Gtid_log_event::peek(const char *event_start, size_t event_len,
+ uint8 checksum_alg,
+ uint32 *domain_id, uint32 *server_id, uint64 *seq_no,
+ uchar *flags2, const Format_description_log_event *fdev)
+{
+ const char *p;
+
+ if (checksum_alg == BINLOG_CHECKSUM_ALG_CRC32)
+ {
+ if (event_len > BINLOG_CHECKSUM_LEN)
+ event_len-= BINLOG_CHECKSUM_LEN;
+ else
+ event_len= 0;
+ }
+ else
+ DBUG_ASSERT(checksum_alg == BINLOG_CHECKSUM_ALG_UNDEF ||
+ checksum_alg == BINLOG_CHECKSUM_ALG_OFF);
+
+ if (event_len < (uint32)fdev->common_header_len + GTID_HEADER_LEN)
+ return true;
+ *server_id= uint4korr(event_start + SERVER_ID_OFFSET);
+ p= event_start + fdev->common_header_len;
+ *seq_no= uint8korr(p);
+ p+= 8;
+ *domain_id= uint4korr(p);
+ p+= 4;
+ *flags2= (uchar)*p;
+ return false;
+}
+
+
+bool
+Gtid_log_event::write(IO_CACHE *file)
+{
+ uchar buf[GTID_HEADER_LEN+2];
+ size_t write_len;
+
+ int8store(buf, seq_no);
+ int4store(buf+8, domain_id);
+ buf[12]= flags2;
+ if (flags2 & FL_GROUP_COMMIT_ID)
+ {
+ int8store(buf+13, commit_id);
+ write_len= GTID_HEADER_LEN + 2;
+ }
+ else
+ {
+ bzero(buf+13, GTID_HEADER_LEN-13);
+ write_len= GTID_HEADER_LEN;
+ }
+ return write_header(file, write_len) ||
+ wrapper_my_b_safe_write(file, buf, write_len) ||
+ write_footer(file);
+}
+
+
+/*
+ Replace a GTID event with either a BEGIN event, dummy event, or nothing, as
+ appropriate to work with old slave that does not know global transaction id.
+
+ The need_dummy_event argument is an IN/OUT argument. It is passed as TRUE
+ if slave has capability lower than MARIA_SLAVE_CAPABILITY_TOLERATE_HOLES.
+ It is returned TRUE if we return a BEGIN (or dummy) event to be sent to the
+ slave, FALSE if event should be skipped completely.
+*/
+int
+Gtid_log_event::make_compatible_event(String *packet, bool *need_dummy_event,
+ ulong ev_offset, uint8 checksum_alg)
+{
+ uchar flags2;
+ if (packet->length() - ev_offset < LOG_EVENT_HEADER_LEN + GTID_HEADER_LEN)
+ return 1;
+ flags2= (*packet)[ev_offset + LOG_EVENT_HEADER_LEN + 12];
+ if (flags2 & FL_STANDALONE)
+ {
+ if (*need_dummy_event)
+ return Query_log_event::dummy_event(packet, ev_offset, checksum_alg);
+ return 0;
+ }
+
+ *need_dummy_event= true;
+ return Query_log_event::begin_event(packet, ev_offset, checksum_alg);
+}
+
+
+#ifdef HAVE_REPLICATION
+void
+Gtid_log_event::pack_info(THD *thd, Protocol *protocol)
+{
+ char buf[6+5+10+1+10+1+20+1+4+20+1];
+ char *p;
+ p = strmov(buf, (flags2 & FL_STANDALONE ? "GTID " : "BEGIN GTID "));
+ p= longlong10_to_str(domain_id, p, 10);
+ *p++= '-';
+ p= longlong10_to_str(server_id, p, 10);
+ *p++= '-';
+ p= longlong10_to_str(seq_no, p, 10);
+ if (flags2 & FL_GROUP_COMMIT_ID)
+ {
+ p= strmov(p, " cid=");
+ p= longlong10_to_str(commit_id, p, 10);
+ }
+
+ protocol->store(buf, p-buf, &my_charset_bin);
+}
+
+static char gtid_begin_string[] = "BEGIN";
+
+int
+Gtid_log_event::do_apply_event(rpl_group_info *rgi)
+{
+ thd->variables.server_id= this->server_id;
+ thd->variables.gtid_domain_id= this->domain_id;
+ thd->variables.gtid_seq_no= this->seq_no;
+ mysql_reset_thd_for_next_command(thd);
+
+ if (opt_gtid_strict_mode && opt_bin_log && opt_log_slave_updates)
+ {
+ if (mysql_bin_log.check_strict_gtid_sequence(this->domain_id,
+ this->server_id, this->seq_no))
+ return 1;
+ }
+
+ DBUG_ASSERT((thd->variables.option_bits & OPTION_GTID_BEGIN) == 0);
+ if (flags2 & FL_STANDALONE)
+ return 0;
+
+ /* Execute this like a BEGIN query event. */
+ thd->variables.option_bits|= OPTION_GTID_BEGIN;
+ DBUG_PRINT("info", ("Set OPTION_GTID_BEGIN"));
+ thd->set_query_and_id(gtid_begin_string, sizeof(gtid_begin_string)-1,
+ &my_charset_bin, next_query_id());
+ thd->lex->sql_command= SQLCOM_BEGIN;
+ thd->is_slave_error= 0;
+ status_var_increment(thd->status_var.com_stat[thd->lex->sql_command]);
+ if (trans_begin(thd, 0))
+ {
+ DBUG_PRINT("error", ("trans_begin() failed"));
+ thd->is_slave_error= 1;
+ }
+ thd->update_stats();
+
+ if (likely(!thd->is_slave_error))
+ general_log_write(thd, COM_QUERY, thd->query(), thd->query_length());
+
+ thd->reset_query();
+ free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC));
+ return thd->is_slave_error;
+}
+
+
+int
+Gtid_log_event::do_update_pos(rpl_group_info *rgi)
+{
+ rgi->inc_event_relay_log_pos();
+ return 0;
+}
+
+
+Log_event::enum_skip_reason
+Gtid_log_event::do_shall_skip(rpl_group_info *rgi)
+{
+ Relay_log_info *rli= rgi->rli;
+ /*
+ An event skipped due to @@skip_replication must not be counted towards the
+ number of events to be skipped due to @@sql_slave_skip_counter.
+ */
+ if (flags & LOG_EVENT_SKIP_REPLICATION_F &&
+ opt_replicate_events_marked_for_skip != RPL_SKIP_REPLICATE)
+ return Log_event::EVENT_SKIP_IGNORE;
+
+ if (rli->slave_skip_counter > 0)
+ {
+ if (!(flags2 & FL_STANDALONE))
+ {
+ thd->variables.option_bits|= OPTION_BEGIN;
+ DBUG_ASSERT(rgi->rli->get_flag(Relay_log_info::IN_TRANSACTION));
+ }
+ return Log_event::continue_group(rgi);
+ }
+ return Log_event::do_shall_skip(rgi);
+}
+
+
+#endif /* HAVE_REPLICATION */
+
+#else /* !MYSQL_SERVER */
+
+void
+Gtid_log_event::print(FILE *file, PRINT_EVENT_INFO *print_event_info)
+{
+ Write_on_release_cache cache(&print_event_info->head_cache, file,
+ Write_on_release_cache::FLUSH_F);
+ char buf[21];
+ char buf2[21];
+
+ if (!print_event_info->short_form)
+ {
+ print_header(&cache, print_event_info, FALSE);
+ longlong10_to_str(seq_no, buf, 10);
+ if (flags2 & FL_GROUP_COMMIT_ID)
+ {
+ longlong10_to_str(commit_id, buf2, 10);
+ my_b_printf(&cache, "\tGTID %u-%u-%s cid=%s\n",
+ domain_id, server_id, buf, buf2);
+ }
+ else
+ my_b_printf(&cache, "\tGTID %u-%u-%s\n", domain_id, server_id, buf);
+
+ if (!print_event_info->domain_id_printed ||
+ print_event_info->domain_id != domain_id)
+ {
+ my_b_printf(&cache, "/*!100001 SET @@session.gtid_domain_id=%u*/%s\n",
+ domain_id, print_event_info->delimiter);
+ print_event_info->domain_id= domain_id;
+ print_event_info->domain_id_printed= true;
+ }
+
+ if (!print_event_info->server_id_printed ||
+ print_event_info->server_id != server_id)
+ {
+ my_b_printf(&cache, "/*!100001 SET @@session.server_id=%u*/%s\n",
+ server_id, print_event_info->delimiter);
+ print_event_info->server_id= server_id;
+ print_event_info->server_id_printed= true;
+ }
+
+ my_b_printf(&cache, "/*!100001 SET @@session.gtid_seq_no=%s*/%s\n",
+ buf, print_event_info->delimiter);
+ }
+ if (!(flags2 & FL_STANDALONE))
+ my_b_printf(&cache, "BEGIN\n%s\n", print_event_info->delimiter);
+}
+
+#endif /* MYSQL_SERVER */
+
+
+/* GTID list. */
+
+Gtid_list_log_event::Gtid_list_log_event(const char *buf, uint event_len,
+ const Format_description_log_event *description_event)
+ : Log_event(buf, description_event), count(0), list(0), sub_id_list(0)
+{
+ uint32 i;
+ uint32 val;
+ uint8 header_size= description_event->common_header_len;
+ uint8 post_header_len= description_event->post_header_len[GTID_LIST_EVENT-1];
+ if (event_len < header_size + post_header_len ||
+ post_header_len < GTID_LIST_HEADER_LEN)
+ return;
+
+ buf+= header_size;
+ val= uint4korr(buf);
+ count= val & ((1<<28)-1);
+ gl_flags= val & ((uint32)0xf << 28);
+ buf+= 4;
+ if (event_len - (header_size + post_header_len) < count*element_size ||
+ (!(list= (rpl_gtid *)my_malloc(count*sizeof(*list) + (count == 0),
+ MYF(MY_WME)))))
+ return;
+
+ for (i= 0; i < count; ++i)
+ {
+ list[i].domain_id= uint4korr(buf);
+ buf+= 4;
+ list[i].server_id= uint4korr(buf);
+ buf+= 4;
+ list[i].seq_no= uint8korr(buf);
+ buf+= 8;
+ }
+
+#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
+ if ((gl_flags & FLAG_IGN_GTIDS))
+ {
+ uint32 i;
+ if (!(sub_id_list= (uint64 *)my_malloc(count*sizeof(uint64), MYF(MY_WME))))
+ {
+ my_free(list);
+ list= NULL;
+ return;
+ }
+ for (i= 0; i < count; ++i)
+ {
+ if (!(sub_id_list[i]=
+ rpl_global_gtid_slave_state->next_sub_id(list[i].domain_id)))
+ {
+ my_free(list);
+ my_free(sub_id_list);
+ list= NULL;
+ sub_id_list= NULL;
+ return;
+ }
+ }
+ }
+#endif
+}
+
+
+#ifdef MYSQL_SERVER
+
+Gtid_list_log_event::Gtid_list_log_event(rpl_binlog_state *gtid_set,
+ uint32 gl_flags_)
+ : count(gtid_set->count()), gl_flags(gl_flags_), list(0), sub_id_list(0)
+{
+ cache_type= EVENT_NO_CACHE;
+ /* Failure to allocate memory will be caught by is_valid() returning false. */
+ if (count < (1<<28) &&
+ (list = (rpl_gtid *)my_malloc(count * sizeof(*list) + (count == 0),
+ MYF(MY_WME))))
+ gtid_set->get_gtid_list(list, count);
+}
+
+
+Gtid_list_log_event::Gtid_list_log_event(slave_connection_state *gtid_set,
+ uint32 gl_flags_)
+ : count(gtid_set->count()), gl_flags(gl_flags_), list(0), sub_id_list(0)
+{
+ cache_type= EVENT_NO_CACHE;
+ /* Failure to allocate memory will be caught by is_valid() returning false. */
+ if (count < (1<<28) &&
+ (list = (rpl_gtid *)my_malloc(count * sizeof(*list) + (count == 0),
+ MYF(MY_WME))))
+ {
+ gtid_set->get_gtid_list(list, count);
+#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
+ if (gl_flags & FLAG_IGN_GTIDS)
+ {
+ uint32 i;
+
+ if (!(sub_id_list= (uint64 *)my_malloc(count * sizeof(uint64),
+ MYF(MY_WME))))
+ {
+ my_free(list);
+ list= NULL;
+ return;
+ }
+ for (i= 0; i < count; ++i)
+ {
+ if (!(sub_id_list[i]=
+ rpl_global_gtid_slave_state->next_sub_id(list[i].domain_id)))
+ {
+ my_free(list);
+ my_free(sub_id_list);
+ list= NULL;
+ sub_id_list= NULL;
+ return;
+ }
+ }
+ }
+#endif
+ }
+}
+
+
+#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
+bool
+Gtid_list_log_event::to_packet(String *packet)
+{
+ uint32 i;
+ uchar *p;
+ uint32 needed_length;
+
+ DBUG_ASSERT(count < 1<<28);
+
+ needed_length= packet->length() + get_data_size();
+ if (packet->reserve(needed_length))
+ return true;
+ p= (uchar *)packet->ptr() + packet->length();;
+ packet->length(needed_length);
+ int4store(p, (count & ((1<<28)-1)) | gl_flags);
+ p += 4;
+ /* Initialise the padding for empty Gtid_list. */
+ if (count == 0)
+ int2store(p, 0);
+ for (i= 0; i < count; ++i)
+ {
+ int4store(p, list[i].domain_id);
+ int4store(p+4, list[i].server_id);
+ int8store(p+8, list[i].seq_no);
+ p += 16;
+ }
+
+ return false;
+}
+
+
+bool
+Gtid_list_log_event::write(IO_CACHE *file)
+{
+ char buf[128];
+ String packet(buf, sizeof(buf), system_charset_info);
+
+ packet.length(0);
+ if (to_packet(&packet))
+ return true;
+ return
+ write_header(file, get_data_size()) ||
+ wrapper_my_b_safe_write(file, (uchar *)packet.ptr(), packet.length()) ||
+ write_footer(file);
+}
+
+
+int
+Gtid_list_log_event::do_apply_event(rpl_group_info *rgi)
+{
+ Relay_log_info *rli= const_cast<Relay_log_info*>(rgi->rli);
+ int ret;
+ if (gl_flags & FLAG_IGN_GTIDS)
+ {
+ uint32 i;
+ for (i= 0; i < count; ++i)
+ {
+ if ((ret= rpl_global_gtid_slave_state->record_gtid(thd, &list[i],
+ sub_id_list[i],
+ false, false)))
+ return ret;
+ rpl_global_gtid_slave_state->update_state_hash(sub_id_list[i], &list[i],
+ NULL);
+ }
+ }
+ ret= Log_event::do_apply_event(rgi);
+ if (rli->until_condition == Relay_log_info::UNTIL_GTID &&
+ (gl_flags & FLAG_UNTIL_REACHED))
+ {
+ char str_buf[128];
+ String str(str_buf, sizeof(str_buf), system_charset_info);
+ rli->until_gtid_pos.to_string(&str);
+ sql_print_information("Slave SQL thread stops because it reached its"
+ " UNTIL master_gtid_pos %s", str.c_ptr_safe());
+ rli->abort_slave= true;
+ rli->stop_for_until= true;
+ }
+ return ret;
+}
+
+
+Log_event::enum_skip_reason
+Gtid_list_log_event::do_shall_skip(rpl_group_info *rgi)
+{
+ enum_skip_reason reason= Log_event::do_shall_skip(rgi);
+ if (reason == EVENT_SKIP_COUNT)
+ reason= EVENT_SKIP_NOT;
+ return reason;
+}
+
+
+void
+Gtid_list_log_event::pack_info(THD *thd, Protocol *protocol)
+{
+ char buf_mem[1024];
+ String buf(buf_mem, sizeof(buf_mem), system_charset_info);
+ uint32 i;
+ bool first;
+
+ buf.length(0);
+ buf.append(STRING_WITH_LEN("["));
+ first= true;
+ for (i= 0; i < count; ++i)
+ rpl_slave_state_tostring_helper(&buf, &list[i], &first);
+ buf.append(STRING_WITH_LEN("]"));
+
+ protocol->store(&buf);
+}
+#endif /* HAVE_REPLICATION */
+
+#else /* !MYSQL_SERVER */
+
+void
+Gtid_list_log_event::print(FILE *file, PRINT_EVENT_INFO *print_event_info)
+{
+ if (!print_event_info->short_form)
+ {
+ Write_on_release_cache cache(&print_event_info->head_cache, file,
+ Write_on_release_cache::FLUSH_F);
+ char buf[21];
+ uint32 i;
+
+ print_header(&cache, print_event_info, FALSE);
+ my_b_printf(&cache, "\tGtid list [");
+ for (i= 0; i < count; ++i)
+ {
+ longlong10_to_str(list[i].seq_no, buf, 10);
+ my_b_printf(&cache, "%u-%u-%s", list[i].domain_id,
+ list[i].server_id, buf);
+ if (i < count-1)
+ my_b_printf(&cache, ",\n# ");
+ }
+ my_b_printf(&cache, "]\n");
+ }
+}
+
+#endif /* MYSQL_SERVER */
+
+
+/*
+ Used to record gtid_list event while sending binlog to slave, without having to
+ fully contruct the event object.
+*/
+bool
+Gtid_list_log_event::peek(const char *event_start, uint32 event_len,
+ uint8 checksum_alg,
+ rpl_gtid **out_gtid_list, uint32 *out_list_len,
+ const Format_description_log_event *fdev)
+{
+ const char *p;
+ uint32 count_field, count;
+ rpl_gtid *gtid_list;
+
+ if (checksum_alg == BINLOG_CHECKSUM_ALG_CRC32)
+ {
+ if (event_len > BINLOG_CHECKSUM_LEN)
+ event_len-= BINLOG_CHECKSUM_LEN;
+ else
+ event_len= 0;
+ }
+ else
+ DBUG_ASSERT(checksum_alg == BINLOG_CHECKSUM_ALG_UNDEF ||
+ checksum_alg == BINLOG_CHECKSUM_ALG_OFF);
+
+ if (event_len < (uint32)fdev->common_header_len + GTID_LIST_HEADER_LEN)
+ return true;
+ p= event_start + fdev->common_header_len;
+ count_field= uint4korr(p);
+ p+= 4;
+ count= count_field & ((1<<28)-1);
+ if (event_len < (uint32)fdev->common_header_len + GTID_LIST_HEADER_LEN +
+ 16 * count)
+ return true;
+ if (!(gtid_list= (rpl_gtid *)my_malloc(sizeof(rpl_gtid)*count + (count == 0),
+ MYF(MY_WME))))
+ return true;
+ *out_gtid_list= gtid_list;
+ *out_list_len= count;
+ while (count--)
+ {
+ gtid_list->domain_id= uint4korr(p);
+ p+= 4;
+ gtid_list->server_id= uint4korr(p);
+ p+= 4;
+ gtid_list->seq_no= uint8korr(p);
+ p+= 8;
+ ++gtid_list;
+ }
+
+ return false;
+}
+
+
+/**************************************************************************
Intvar_log_event methods
**************************************************************************/
@@ -5967,7 +7128,7 @@ void Intvar_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
if (!print_event_info->short_form)
{
print_header(&cache, print_event_info, FALSE);
- my_b_printf(&cache, "\tIntvar\n");
+ my_b_write_string(&cache, "\tIntvar\n");
}
my_b_printf(&cache, "SET ");
@@ -5995,19 +7156,13 @@ void Intvar_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
Intvar_log_event::do_apply_event()
*/
-int Intvar_log_event::do_apply_event(Relay_log_info const *rli)
+int Intvar_log_event::do_apply_event(rpl_group_info *rgi)
{
DBUG_ENTER("Intvar_log_event::do_apply_event");
- /*
- We are now in a statement until the associated query log event has
- been processed.
- */
- const_cast<Relay_log_info*>(rli)->set_flag(Relay_log_info::IN_STMT);
-
- if (rli->deferred_events_collecting)
+ if (rgi->deferred_events_collecting)
{
DBUG_PRINT("info",("deferring event"));
- DBUG_RETURN(rli->deferred_events->add(this));
+ DBUG_RETURN(rgi->deferred_events->add(this));
}
switch (type) {
@@ -6022,15 +7177,15 @@ int Intvar_log_event::do_apply_event(Relay_log_info const *rli)
DBUG_RETURN(0);
}
-int Intvar_log_event::do_update_pos(Relay_log_info *rli)
+int Intvar_log_event::do_update_pos(rpl_group_info *rgi)
{
- rli->inc_event_relay_log_pos();
+ rgi->inc_event_relay_log_pos();
return 0;
}
Log_event::enum_skip_reason
-Intvar_log_event::do_shall_skip(Relay_log_info *rli)
+Intvar_log_event::do_shall_skip(rpl_group_info *rgi)
{
/*
It is a common error to set the slave skip counter to 1 instead of
@@ -6040,7 +7195,7 @@ Intvar_log_event::do_shall_skip(Relay_log_info *rli)
that we do not change the value of the slave skip counter since it
will be decreased by the following insert event.
*/
- return continue_group(rli);
+ return continue_group(rgi);
}
#endif
@@ -6098,7 +7253,7 @@ void Rand_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
if (!print_event_info->short_form)
{
print_header(&cache, print_event_info, FALSE);
- my_b_printf(&cache, "\tRand\n");
+ my_b_write_string(&cache, "\tRand\n");
}
my_b_printf(&cache, "SET @@RAND_SEED1=%s, @@RAND_SEED2=%s%s\n",
llstr(seed1, llbuff),llstr(seed2, llbuff2),
@@ -6108,31 +7263,25 @@ void Rand_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
-int Rand_log_event::do_apply_event(Relay_log_info const *rli)
+int Rand_log_event::do_apply_event(rpl_group_info *rgi)
{
- /*
- We are now in a statement until the associated query log event has
- been processed.
- */
- const_cast<Relay_log_info*>(rli)->set_flag(Relay_log_info::IN_STMT);
-
- if (rli->deferred_events_collecting)
- return rli->deferred_events->add(this);
+ if (rgi->deferred_events_collecting)
+ return rgi->deferred_events->add(this);
thd->rand.seed1= (ulong) seed1;
thd->rand.seed2= (ulong) seed2;
return 0;
}
-int Rand_log_event::do_update_pos(Relay_log_info *rli)
+int Rand_log_event::do_update_pos(rpl_group_info *rgi)
{
- rli->inc_event_relay_log_pos();
+ rgi->inc_event_relay_log_pos();
return 0;
}
Log_event::enum_skip_reason
-Rand_log_event::do_shall_skip(Relay_log_info *rli)
+Rand_log_event::do_shall_skip(rpl_group_info *rgi)
{
/*
It is a common error to set the slave skip counter to 1 instead of
@@ -6142,7 +7291,7 @@ Rand_log_event::do_shall_skip(Relay_log_info *rli)
that we do not change the value of the slave skip counter since it
will be decreased by the following insert event.
*/
- return continue_group(rli);
+ return continue_group(rgi);
}
/**
@@ -6156,14 +7305,14 @@ Rand_log_event::do_shall_skip(Relay_log_info *rli)
bool slave_execute_deferred_events(THD *thd)
{
bool res= false;
- Relay_log_info *rli= thd->rli_slave;
+ rpl_group_info *rgi= thd->rgi_slave;
- DBUG_ASSERT(rli && (!rli->deferred_events_collecting || rli->deferred_events));
+ DBUG_ASSERT(rgi && (!rgi->deferred_events_collecting || rgi->deferred_events));
- if (!rli->deferred_events_collecting || rli->deferred_events->is_empty())
+ if (!rgi->deferred_events_collecting || rgi->deferred_events->is_empty())
return res;
- res= rli->deferred_events->execute(rli);
+ res= rgi->deferred_events->execute(rgi);
return res;
}
@@ -6238,15 +7387,68 @@ void Xid_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
-int Xid_log_event::do_apply_event(Relay_log_info const *rli)
+int Xid_log_event::do_apply_event(rpl_group_info *rgi)
{
bool res;
+ int err;
+ rpl_gtid gtid;
+ uint64 sub_id= 0;
+ Relay_log_info const *rli= rgi->rli;
+
+ /*
+ XID_EVENT works like a COMMIT statement. And it also updates the
+ mysql.gtid_slave_pos table with the GTID of the current transaction.
+
+ Therefore, it acts much like a normal SQL statement, so we need to do
+ mysql_reset_thd_for_next_command() as if starting a new statement.
+ */
+ mysql_reset_thd_for_next_command(thd);
+ /*
+ Record any GTID in the same transaction, so slave state is transactionally
+ consistent.
+ */
+ if (rgi->gtid_pending)
+ {
+ sub_id= rgi->gtid_sub_id;
+ rgi->gtid_pending= false;
+
+ gtid= rgi->current_gtid;
+ err= rpl_global_gtid_slave_state->record_gtid(thd, &gtid, sub_id, true,
+ false);
+ if (err)
+ {
+ int ec= thd->get_stmt_da()->sql_errno();
+ /*
+ Do not report an error if this is really a kill due to a deadlock.
+ In this case, the transaction will be re-tried instead.
+ */
+ if (!is_parallel_retry_error(rgi, ec))
+ rli->report(ERROR_LEVEL, ER_CANNOT_UPDATE_GTID_STATE, rgi->gtid_info(),
+ "Error during XID COMMIT: failed to update GTID state in "
+ "%s.%s: %d: %s",
+ "mysql", rpl_gtid_slave_state_table_name.str, ec,
+ thd->get_stmt_da()->message());
+ thd->is_slave_error= 1;
+ return err;
+ }
+
+ DBUG_EXECUTE_IF("gtid_fail_after_record_gtid",
+ { my_error(ER_ERROR_DURING_COMMIT, MYF(0), HA_ERR_WRONG_COMMAND);
+ thd->is_slave_error= 1;
+ return 1;
+ });
+ }
+
/* For a slave Xid_log_event is COMMIT */
general_log_print(thd, COM_QUERY,
"COMMIT /* implicit, from Xid_log_event */");
+ thd->variables.option_bits&= ~OPTION_GTID_BEGIN;
res= trans_commit(thd); /* Automatically rolls back on error. */
thd->mdl_context.release_transactional_locks();
+ if (!res && sub_id)
+ rpl_global_gtid_slave_state->update_state_hash(sub_id, &gtid, rgi);
+
/*
Increment the global status commit count variable
*/
@@ -6256,11 +7458,13 @@ int Xid_log_event::do_apply_event(Relay_log_info const *rli)
}
Log_event::enum_skip_reason
-Xid_log_event::do_shall_skip(Relay_log_info *rli)
+Xid_log_event::do_shall_skip(rpl_group_info *rgi)
{
DBUG_ENTER("Xid_log_event::do_shall_skip");
- if (rli->slave_skip_counter > 0) {
- thd->variables.option_bits&= ~OPTION_BEGIN;
+ if (rgi->rli->slave_skip_counter > 0)
+ {
+ DBUG_ASSERT(!rgi->rli->get_flag(Relay_log_info::IN_TRANSACTION));
+ thd->variables.option_bits&= ~(OPTION_BEGIN | OPTION_GTID_BEGIN);
DBUG_RETURN(Log_event::EVENT_SKIP_COUNT);
}
#ifdef WITH_WSREP
@@ -6277,7 +7481,7 @@ Xid_log_event::do_shall_skip(Relay_log_info *rli)
}
}
#endif
- DBUG_RETURN(Log_event::do_shall_skip(rli));
+ DBUG_RETURN(Log_event::do_shall_skip(rgi));
}
#endif /* !MYSQL_CLIENT */
@@ -6411,9 +7615,9 @@ User_var_log_event(const char* buf, uint event_len,
#endif
{
bool error= false;
- const char* buf_start= buf;
+ const char* buf_start= buf, *buf_end= buf + event_len;
+
/* The Post-Header is empty. The Variable Data part begins immediately. */
- const char *start= buf;
buf+= description_event->common_header_len +
description_event->post_header_len[USER_VAR_EVENT-1];
name_len= uint4korr(buf);
@@ -6424,8 +7628,7 @@ User_var_log_event(const char* buf, uint event_len,
may have the bigger value possible, is_null= True and there is no
payload for val, or even that name_len is 0.
*/
- if (!valid_buffer_range<uint>(name_len, buf_start, name,
- event_len - UV_VAL_IS_NULL))
+ if (name + name_len + UV_VAL_IS_NULL > buf_end)
{
error= true;
goto err;
@@ -6443,9 +7646,10 @@ User_var_log_event(const char* buf, uint event_len,
}
else
{
- if (!valid_buffer_range<uint>(UV_VAL_IS_NULL + UV_VAL_TYPE_SIZE
- + UV_CHARSET_NUMBER_SIZE + UV_VAL_LEN_SIZE,
- buf_start, buf, event_len))
+ val= (char *) (buf + UV_VAL_IS_NULL + UV_VAL_TYPE_SIZE +
+ UV_CHARSET_NUMBER_SIZE + UV_VAL_LEN_SIZE);
+
+ if (val > buf_end)
{
error= true;
goto err;
@@ -6455,10 +7659,8 @@ User_var_log_event(const char* buf, uint event_len,
charset_number= uint4korr(buf + UV_VAL_IS_NULL + UV_VAL_TYPE_SIZE);
val_len= uint4korr(buf + UV_VAL_IS_NULL + UV_VAL_TYPE_SIZE +
UV_CHARSET_NUMBER_SIZE);
- val= (char *) (buf + UV_VAL_IS_NULL + UV_VAL_TYPE_SIZE +
- UV_CHARSET_NUMBER_SIZE + UV_VAL_LEN_SIZE);
- if (!valid_buffer_range<uint>(val_len, buf_start, val, event_len))
+ if (val + val_len > buf_end)
{
error= true;
goto err;
@@ -6475,7 +7677,7 @@ User_var_log_event(const char* buf, uint event_len,
Old events will not have this extra byte, thence,
we keep the flags set to UNDEF_F.
*/
- uint bytes_read= ((val + val_len) - start);
+ uint bytes_read= ((val + val_len) - buf_start);
#ifndef DBUG_OFF
bool old_pre_checksum_fd= description_event->is_version_before_checksum(
&description_event->server_version_split);
@@ -6511,7 +7713,7 @@ bool User_var_log_event::write(IO_CACHE* file)
char buf[UV_NAME_LEN_SIZE];
char buf1[UV_VAL_IS_NULL + UV_VAL_TYPE_SIZE +
UV_CHARSET_NUMBER_SIZE + UV_VAL_LEN_SIZE];
- uchar buf2[max(8, DECIMAL_MAX_FIELD_SIZE + 2)], *pos= buf2;
+ uchar buf2[MY_MAX(8, DECIMAL_MAX_FIELD_SIZE + 2)], *pos= buf2;
uint unsigned_len= 0;
uint buf1_length;
ulong event_length;
@@ -6585,10 +7787,10 @@ void User_var_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
if (!print_event_info->short_form)
{
print_header(&cache, print_event_info, FALSE);
- my_b_printf(&cache, "\tUser_var\n");
+ my_b_write_string(&cache, "\tUser_var\n");
}
- my_b_printf(&cache, "SET @");
+ my_b_write_string(&cache, "SET @");
my_b_write_backtick_quote(&cache, name, name_len);
if (is_null)
@@ -6686,17 +7888,17 @@ void User_var_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
*/
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
-int User_var_log_event::do_apply_event(Relay_log_info const *rli)
+int User_var_log_event::do_apply_event(rpl_group_info *rgi)
{
Item *it= 0;
CHARSET_INFO *charset;
DBUG_ENTER("User_var_log_event::do_apply_event");
query_id_t sav_query_id= 0; /* memorize orig id when deferred applying */
- if (rli->deferred_events_collecting)
+ if (rgi->deferred_events_collecting)
{
set_deferred(current_thd->query_id);
- DBUG_RETURN(rli->deferred_events->add(this));
+ DBUG_RETURN(rgi->deferred_events->add(this));
}
else if (is_deferred())
{
@@ -6712,12 +7914,6 @@ int User_var_log_event::do_apply_event(Relay_log_info const *rli)
double real_val;
longlong int_val;
- /*
- We are now in a statement until the associated query log event has
- been processed.
- */
- const_cast<Relay_log_info*>(rli)->set_flag(Relay_log_info::IN_STMT);
-
if (is_null)
{
it= new Item_null();
@@ -6782,14 +7978,14 @@ int User_var_log_event::do_apply_event(Relay_log_info const *rli)
DBUG_RETURN(0);
}
-int User_var_log_event::do_update_pos(Relay_log_info *rli)
+int User_var_log_event::do_update_pos(rpl_group_info *rgi)
{
- rli->inc_event_relay_log_pos();
+ rgi->inc_event_relay_log_pos();
return 0;
}
Log_event::enum_skip_reason
-User_var_log_event::do_shall_skip(Relay_log_info *rli)
+User_var_log_event::do_shall_skip(rpl_group_info *rgi)
{
/*
It is a common error to set the slave skip counter to 1 instead
@@ -6799,7 +7995,7 @@ User_var_log_event::do_shall_skip(Relay_log_info *rli)
that we do not change the value of the slave skip counter since it
will be decreased by the following insert event.
*/
- return continue_group(rli);
+ return continue_group(rgi);
}
#endif /* !MYSQL_CLIENT */
@@ -6958,7 +8154,7 @@ Slave_log_event::Slave_log_event(const char* buf,
#ifndef MYSQL_CLIENT
-int Slave_log_event::do_apply_event(Relay_log_info const *rli)
+int Slave_log_event::do_apply_event(rpl_group_info *rgi)
{
if (mysql_bin_log.is_open())
return mysql_bin_log.write(this);
@@ -6985,7 +8181,7 @@ void Stop_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
return;
print_header(&cache, print_event_info, FALSE);
- my_b_printf(&cache, "\tStop\n");
+ my_b_write_string(&cache, "\tStop\n");
}
#endif /* MYSQL_CLIENT */
@@ -7002,8 +8198,11 @@ void Stop_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
Start_log_event_v3::do_apply_event(), not here. Because if we come
here, the master was sane.
*/
-int Stop_log_event::do_update_pos(Relay_log_info *rli)
+
+int Stop_log_event::do_update_pos(rpl_group_info *rgi)
{
+ Relay_log_info *rli= rgi->rli;
+ DBUG_ENTER("Stop_log_event::do_update_pos");
/*
We do not want to update master_log pos because we get a rotate event
before stop, so by now group_master_log_name is set to the next log.
@@ -7011,14 +8210,15 @@ int Stop_log_event::do_update_pos(Relay_log_info *rli)
could give false triggers in MASTER_POS_WAIT() that we have reached
the target position when in fact we have not.
*/
- if (thd->variables.option_bits & OPTION_BEGIN)
- rli->inc_event_relay_log_pos();
- else
+ if (rli->get_flag(Relay_log_info::IN_TRANSACTION))
+ rgi->inc_event_relay_log_pos();
+ else if (!rgi->is_parallel_exec)
{
- rli->inc_group_relay_log_pos(0);
+ rpl_global_gtid_slave_state->record_and_update_gtid(thd, rgi);
+ rli->inc_group_relay_log_pos(0, rgi);
flush_relay_log_info(rli);
}
- return 0;
+ DBUG_RETURN(0);
}
#endif /* !MYSQL_CLIENT */
@@ -7185,7 +8385,7 @@ void Create_file_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info
That one is for "file_id: etc" below: in mysqlbinlog we want the #, in
SHOW BINLOG EVENTS we don't.
*/
- my_b_printf(&cache, "#");
+ my_b_write_byte(&cache, '#');
}
my_b_printf(&cache, " file_id: %d block_len: %d\n", file_id, block_len);
@@ -7232,18 +8432,19 @@ void Create_file_log_event::pack_info(THD *thd, Protocol *protocol)
*/
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
-int Create_file_log_event::do_apply_event(Relay_log_info const *rli)
+int Create_file_log_event::do_apply_event(rpl_group_info *rgi)
{
- char proc_info[17+FN_REFLEN+10], *fname_buf;
+ char fname_buf[FN_REFLEN];
char *ext;
int fd = -1;
IO_CACHE file;
int error = 1;
+ Relay_log_info const *rli= rgi->rli;
+ THD_STAGE_INFO(thd, stage_making_temp_file_create_before_load_data);
bzero((char*)&file, sizeof(file));
- fname_buf= strmov(proc_info, "Making temp file ");
- ext= slave_load_file_stem(fname_buf, file_id, server_id, ".info");
- thd_proc_info(thd, proc_info);
+ ext= slave_load_file_stem(fname_buf, file_id, server_id, ".info",
+ &rli->mi->connection_name);
/* old copy may exist already */
mysql_file_delete(key_file_log_event_info, fname_buf, MYF(0));
if ((fd= mysql_file_create(key_file_log_event_info,
@@ -7253,7 +8454,7 @@ int Create_file_log_event::do_apply_event(Relay_log_info const *rli)
init_io_cache(&file, fd, IO_SIZE, WRITE_CACHE, (my_off_t)0, 0,
MYF(MY_WME|MY_NABP)))
{
- rli->report(ERROR_LEVEL, my_errno,
+ rli->report(ERROR_LEVEL, my_errno, rgi->gtid_info(),
"Error in Create_file event: could not open file '%s'",
fname_buf);
goto err;
@@ -7265,7 +8466,7 @@ int Create_file_log_event::do_apply_event(Relay_log_info const *rli)
if (write_base(&file))
{
strmov(ext, ".info"); // to have it right in the error message
- rli->report(ERROR_LEVEL, my_errno,
+ rli->report(ERROR_LEVEL, my_errno, rgi->gtid_info(),
"Error in Create_file event: could not write to file '%s'",
fname_buf);
goto err;
@@ -7281,14 +8482,14 @@ int Create_file_log_event::do_apply_event(Relay_log_info const *rli)
O_WRONLY | O_BINARY | O_EXCL | O_NOFOLLOW,
MYF(MY_WME))) < 0)
{
- rli->report(ERROR_LEVEL, my_errno,
+ rli->report(ERROR_LEVEL, my_errno, rgi->gtid_info(),
"Error in Create_file event: could not open file '%s'",
fname_buf);
goto err;
}
if (mysql_file_write(fd, (uchar*) block, block_len, MYF(MY_WME+MY_NABP)))
{
- rli->report(ERROR_LEVEL, my_errno,
+ rli->report(ERROR_LEVEL, my_errno, rgi->gtid_info(),
"Error in Create_file event: write to '%s' failed",
fname_buf);
goto err;
@@ -7419,16 +8620,17 @@ int Append_block_log_event::get_create_or_append() const
Append_block_log_event::do_apply_event()
*/
-int Append_block_log_event::do_apply_event(Relay_log_info const *rli)
+int Append_block_log_event::do_apply_event(rpl_group_info *rgi)
{
- char proc_info[17+FN_REFLEN+10], *fname= proc_info+17;
+ char fname[FN_REFLEN];
int fd;
int error = 1;
+ Relay_log_info const *rli= rgi->rli;
DBUG_ENTER("Append_block_log_event::do_apply_event");
- fname= strmov(proc_info, "Making temp file ");
- slave_load_file_stem(fname, file_id, server_id, ".data");
- thd_proc_info(thd, proc_info);
+ THD_STAGE_INFO(thd, stage_making_temp_file_append_before_load_data);
+ slave_load_file_stem(fname, file_id, server_id, ".data",
+ &rli->mi->cmp_connection_name);
if (get_create_or_append())
{
/*
@@ -7444,7 +8646,7 @@ int Append_block_log_event::do_apply_event(Relay_log_info const *rli)
O_WRONLY | O_BINARY | O_EXCL | O_NOFOLLOW,
MYF(MY_WME))) < 0)
{
- rli->report(ERROR_LEVEL, my_errno,
+ rli->report(ERROR_LEVEL, my_errno, rgi->gtid_info(),
"Error in %s event: could not create file '%s'",
get_type_str(), fname);
goto err;
@@ -7455,7 +8657,7 @@ int Append_block_log_event::do_apply_event(Relay_log_info const *rli)
O_WRONLY | O_APPEND | O_BINARY | O_NOFOLLOW,
MYF(MY_WME))) < 0)
{
- rli->report(ERROR_LEVEL, my_errno,
+ rli->report(ERROR_LEVEL, my_errno, rgi->gtid_info(),
"Error in %s event: could not open file '%s'",
get_type_str(), fname);
goto err;
@@ -7468,7 +8670,7 @@ int Append_block_log_event::do_apply_event(Relay_log_info const *rli)
if (mysql_file_write(fd, (uchar*) block, block_len, MYF(MY_WME+MY_NABP)))
{
- rli->report(ERROR_LEVEL, my_errno,
+ rli->report(ERROR_LEVEL, my_errno, rgi->gtid_info(),
"Error in %s event: write to '%s' failed",
get_type_str(), fname);
goto err;
@@ -7575,10 +8777,12 @@ void Delete_file_log_event::pack_info(THD *thd, Protocol *protocol)
*/
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
-int Delete_file_log_event::do_apply_event(Relay_log_info const *rli)
+int Delete_file_log_event::do_apply_event(rpl_group_info *rgi)
{
char fname[FN_REFLEN+10];
- char *ext= slave_load_file_stem(fname, file_id, server_id, ".data");
+ Relay_log_info const *rli= rgi->rli;
+ char *ext= slave_load_file_stem(fname, file_id, server_id, ".data",
+ &rli->mi->cmp_connection_name);
mysql_file_delete(key_file_log_event_data, fname, MYF(MY_WME));
strmov(ext, ".info");
mysql_file_delete(key_file_log_event_info, fname, MYF(MY_WME));
@@ -7673,7 +8877,7 @@ void Execute_load_log_event::pack_info(THD *thd, Protocol *protocol)
Execute_load_log_event::do_apply_event()
*/
-int Execute_load_log_event::do_apply_event(Relay_log_info const *rli)
+int Execute_load_log_event::do_apply_event(rpl_group_info *rgi)
{
char fname[FN_REFLEN+10];
char *ext;
@@ -7681,15 +8885,17 @@ int Execute_load_log_event::do_apply_event(Relay_log_info const *rli)
int error= 1;
IO_CACHE file;
Load_log_event *lev= 0;
+ Relay_log_info const *rli= rgi->rli;
- ext= slave_load_file_stem(fname, file_id, server_id, ".info");
+ ext= slave_load_file_stem(fname, file_id, server_id, ".info",
+ &rli->mi->cmp_connection_name);
if ((fd= mysql_file_open(key_file_log_event_info,
fname, O_RDONLY | O_BINARY | O_NOFOLLOW,
MYF(MY_WME))) < 0 ||
init_io_cache(&file, fd, IO_SIZE, READ_CACHE, (my_off_t)0, 0,
MYF(MY_WME|MY_NABP)))
{
- rli->report(ERROR_LEVEL, my_errno,
+ rli->report(ERROR_LEVEL, my_errno, rgi->gtid_info(),
"Error in Exec_load event: could not open file '%s'",
fname);
goto err;
@@ -7701,7 +8907,7 @@ int Execute_load_log_event::do_apply_event(Relay_log_info const *rli)
opt_slave_sql_verify_checksum)) ||
lev->get_type_code() != NEW_LOAD_EVENT)
{
- rli->report(ERROR_LEVEL, 0, "Error in Exec_load event: "
+ rli->report(ERROR_LEVEL, 0, rgi->gtid_info(), "Error in Exec_load event: "
"file '%s' appears corrupted", fname);
goto err;
}
@@ -7714,8 +8920,7 @@ int Execute_load_log_event::do_apply_event(Relay_log_info const *rli)
calls mysql_load()).
*/
- const_cast<Relay_log_info*>(rli)->future_group_master_log_pos= log_pos;
- if (lev->do_apply_event(0,rli,1))
+ if (lev->do_apply_event(0,rgi,1))
{
/*
We want to indicate the name of the file that could not be loaded
@@ -7728,7 +8933,7 @@ int Execute_load_log_event::do_apply_event(Relay_log_info const *rli)
char *tmp= my_strdup(rli->last_error().message, MYF(MY_WME));
if (tmp)
{
- rli->report(ERROR_LEVEL, rli->last_error().number,
+ rli->report(ERROR_LEVEL, rli->last_error().number, rgi->gtid_info(),
"%s. Failed executing load from '%s'", tmp, fname);
my_free(tmp);
}
@@ -7796,13 +9001,13 @@ int Begin_load_query_log_event::get_create_or_append() const
#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
Log_event::enum_skip_reason
-Begin_load_query_log_event::do_shall_skip(Relay_log_info *rli)
+Begin_load_query_log_event::do_shall_skip(rpl_group_info *rgi)
{
/*
If the slave skip counter is 1, then we should not start executing
on the next event.
*/
- return continue_group(rli);
+ return continue_group(rgi);
}
#endif
@@ -7900,12 +9105,12 @@ void Execute_load_query_log_event::print(FILE* file,
if (local_fname)
{
my_b_write(&cache, (uchar*) query, fn_pos_start);
- my_b_printf(&cache, " LOCAL INFILE ");
+ my_b_write_string(&cache, " LOCAL INFILE ");
pretty_print_str(&cache, local_fname, strlen(local_fname));
if (dup_handling == LOAD_DUP_REPLACE)
- my_b_printf(&cache, " REPLACE");
- my_b_printf(&cache, " INTO");
+ my_b_write_string(&cache, " REPLACE");
+ my_b_write_string(&cache, " INTO");
my_b_write(&cache, (uchar*) query + fn_pos_end, q_len-fn_pos_end);
my_b_printf(&cache, "\n%s\n", print_event_info->delimiter);
}
@@ -7944,13 +9149,14 @@ void Execute_load_query_log_event::pack_info(THD *thd, Protocol *protocol)
int
-Execute_load_query_log_event::do_apply_event(Relay_log_info const *rli)
+Execute_load_query_log_event::do_apply_event(rpl_group_info *rgi)
{
char *p;
char *buf;
char *fname;
char *fname_end;
int error;
+ Relay_log_info const *rli= rgi->rli;
buf= (char*) my_malloc(q_len + 1 - (fn_pos_end - fn_pos_start) +
(FN_REFLEN + 10) + 10 + 8 + 5, MYF(MY_WME));
@@ -7960,7 +9166,7 @@ Execute_load_query_log_event::do_apply_event(Relay_log_info const *rli)
/* Replace filename and LOCAL keyword in query before executing it */
if (buf == NULL)
{
- rli->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR,
+ rli->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR, rgi->gtid_info(),
ER(ER_SLAVE_FATAL_ERROR), "Not enough memory");
return 1;
}
@@ -7969,7 +9175,8 @@ Execute_load_query_log_event::do_apply_event(Relay_log_info const *rli)
memcpy(p, query, fn_pos_start);
p+= fn_pos_start;
fname= (p= strmake(p, STRING_WITH_LEN(" INFILE \'")));
- p= slave_load_file_stem(p, file_id, server_id, ".data");
+ p= slave_load_file_stem(p, file_id, server_id, ".data",
+ &rli->mi->cmp_connection_name);
fname_end= p= strend(p); // Safer than p=p+5
*(p++)='\'';
switch (dup_handling) {
@@ -7986,7 +9193,7 @@ Execute_load_query_log_event::do_apply_event(Relay_log_info const *rli)
p= strmake(p, STRING_WITH_LEN(" INTO "));
p= strmake(p, query+fn_pos_end, q_len-fn_pos_end);
- error= Query_log_event::do_apply_event(rli, buf, p-buf);
+ error= Query_log_event::do_apply_event(rgi, buf, p-buf);
/* Forging file name for deletion in same buffer */
*fname_end= 0;
@@ -8099,16 +9306,19 @@ const char *sql_ex_info::init(const char *buf, const char *buf_end,
#ifndef MYSQL_CLIENT
Rows_log_event::Rows_log_event(THD *thd_arg, TABLE *tbl_arg, ulong tid,
- MY_BITMAP const *cols, bool is_transactional)
+ MY_BITMAP const *cols, bool is_transactional,
+ Log_event_type event_type)
: Log_event(thd_arg, 0, is_transactional),
m_row_count(0),
m_table(tbl_arg),
m_table_id(tid),
m_width(tbl_arg ? tbl_arg->s->fields : 1),
- m_rows_buf(0), m_rows_cur(0), m_rows_end(0), m_flags(0)
+ m_rows_buf(0), m_rows_cur(0), m_rows_end(0), m_flags(0),
+ m_type(event_type), m_extra_row_data(0)
#ifdef HAVE_REPLICATION
, m_curr_row(NULL), m_curr_row_end(NULL),
- m_key(NULL), m_key_info(NULL), m_key_nr(0)
+ m_key(NULL), m_key_info(NULL), m_key_nr(0),
+ master_had_triggers(0)
#endif
{
/*
@@ -8124,8 +9334,8 @@ Rows_log_event::Rows_log_event(THD *thd_arg, TABLE *tbl_arg, ulong tid,
set_flags(NO_FOREIGN_KEY_CHECKS_F);
if (thd_arg->variables.option_bits & OPTION_RELAXED_UNIQUE_CHECKS)
set_flags(RELAXED_UNIQUE_CHECKS_F);
- /* if bitmap_init fails, caught in is_valid() */
- if (likely(!bitmap_init(&m_cols,
+ /* if my_bitmap_init fails, caught in is_valid() */
+ if (likely(!my_bitmap_init(&m_cols,
m_width <= sizeof(m_bitbuf)*8 ? m_bitbuf : NULL,
m_width,
false)))
@@ -8139,14 +9349,13 @@ Rows_log_event::Rows_log_event(THD *thd_arg, TABLE *tbl_arg, ulong tid,
}
else
{
- // Needed because bitmap_init() does not set it to null on failure
+ // Needed because my_bitmap_init() does not set it to null on failure
m_cols.bitmap= 0;
}
}
#endif
Rows_log_event::Rows_log_event(const char *buf, uint event_len,
- Log_event_type event_type,
const Format_description_log_event
*description_event)
: Log_event(buf, description_event),
@@ -8154,14 +9363,19 @@ Rows_log_event::Rows_log_event(const char *buf, uint event_len,
#ifndef MYSQL_CLIENT
m_table(NULL),
#endif
- m_table_id(0), m_rows_buf(0), m_rows_cur(0), m_rows_end(0)
+ m_table_id(0), m_rows_buf(0), m_rows_cur(0), m_rows_end(0),
+ m_extra_row_data(0)
#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
, m_curr_row(NULL), m_curr_row_end(NULL),
- m_key(NULL), m_key_info(NULL), m_key_nr(0)
+ m_key(NULL), m_key_info(NULL), m_key_nr(0),
+ master_had_triggers(0)
#endif
{
DBUG_ENTER("Rows_log_event::Rows_log_event(const char*,...)");
uint8 const common_header_len= description_event->common_header_len;
+ Log_event_type event_type= (Log_event_type) buf[EVENT_TYPE_OFFSET];
+ m_type= event_type;
+
uint8 const post_header_len= description_event->post_header_len[event_type-1];
DBUG_PRINT("enter",("event_len: %u common_header_len: %d "
@@ -8184,16 +9398,61 @@ Rows_log_event::Rows_log_event(const char *buf, uint event_len,
}
m_flags= uint2korr(post_start);
+ post_start+= 2;
+
+ uint16 var_header_len= 0;
+ if (post_header_len == ROWS_HEADER_LEN_V2)
+ {
+ /*
+ Have variable length header, check length,
+ which includes length bytes
+ */
+ var_header_len= uint2korr(post_start);
+ assert(var_header_len >= 2);
+ var_header_len-= 2;
+
+ /* Iterate over var-len header, extracting 'chunks' */
+ const char* start= post_start + 2;
+ const char* end= start + var_header_len;
+ for (const char* pos= start; pos < end;)
+ {
+ switch(*pos++)
+ {
+ case RW_V_EXTRAINFO_TAG:
+ {
+ /* Have an 'extra info' section, read it in */
+ assert((end - pos) >= EXTRA_ROW_INFO_HDR_BYTES);
+ uint8 infoLen= pos[EXTRA_ROW_INFO_LEN_OFFSET];
+ assert((end - pos) >= infoLen);
+ /* Just store/use the first tag of this type, skip others */
+ if (likely(!m_extra_row_data))
+ {
+ m_extra_row_data= (uchar*) my_malloc(infoLen,
+ MYF(MY_WME));
+ if (likely(m_extra_row_data != NULL))
+ {
+ memcpy(m_extra_row_data, pos, infoLen);
+ }
+ }
+ pos+= infoLen;
+ break;
+ }
+ default:
+ /* Unknown code, we will not understand anything further here */
+ pos= end; /* Break loop */
+ }
+ }
+ }
uchar const *const var_start=
- (const uchar *)buf + common_header_len + post_header_len;
+ (const uchar *)buf + common_header_len + post_header_len + var_header_len;
uchar const *const ptr_width= var_start;
uchar *ptr_after_width= (uchar*) ptr_width;
DBUG_PRINT("debug", ("Reading from %p", ptr_after_width));
m_width = net_field_length(&ptr_after_width);
DBUG_PRINT("debug", ("m_width=%lu", m_width));
- /* if bitmap_init fails, catched in is_valid() */
- if (likely(!bitmap_init(&m_cols,
+ /* if my_bitmap_init fails, catched in is_valid() */
+ if (likely(!my_bitmap_init(&m_cols,
m_width <= sizeof(m_bitbuf)*8 ? m_bitbuf : NULL,
m_width,
false)))
@@ -8206,19 +9465,20 @@ Rows_log_event::Rows_log_event(const char *buf, uint event_len,
}
else
{
- // Needed because bitmap_init() does not set it to null on failure
+ // Needed because my_bitmap_init() does not set it to null on failure
m_cols.bitmap= NULL;
DBUG_VOID_RETURN;
}
m_cols_ai.bitmap= m_cols.bitmap; /* See explanation in is_valid() */
- if (event_type == UPDATE_ROWS_EVENT)
+ if ((event_type == UPDATE_ROWS_EVENT) ||
+ (event_type == UPDATE_ROWS_EVENT_V1))
{
DBUG_PRINT("debug", ("Reading from %p", ptr_after_width));
- /* if bitmap_init fails, caught in is_valid() */
- if (likely(!bitmap_init(&m_cols_ai,
+ /* if my_bitmap_init fails, caught in is_valid() */
+ if (likely(!my_bitmap_init(&m_cols_ai,
m_width <= sizeof(m_bitbuf_ai)*8 ? m_bitbuf_ai : NULL,
m_width,
false)))
@@ -8232,7 +9492,7 @@ Rows_log_event::Rows_log_event(const char *buf, uint event_len,
}
else
{
- // Needed because bitmap_init() does not set it to null on failure
+ // Needed because my_bitmap_init() does not set it to null on failure
m_cols_ai.bitmap= 0;
DBUG_VOID_RETURN;
}
@@ -8263,27 +9523,41 @@ Rows_log_event::Rows_log_event(const char *buf, uint event_len,
Rows_log_event::~Rows_log_event()
{
if (m_cols.bitmap == m_bitbuf) // no my_malloc happened
- m_cols.bitmap= 0; // so no my_free in bitmap_free
- bitmap_free(&m_cols); // To pair with bitmap_init().
+ m_cols.bitmap= 0; // so no my_free in my_bitmap_free
+ my_bitmap_free(&m_cols); // To pair with my_bitmap_init().
my_free(m_rows_buf);
+ my_free(m_extra_row_data);
}
int Rows_log_event::get_data_size()
{
- int const type_code= get_type_code();
+ int const general_type_code= get_general_type_code();
- uchar buf[sizeof(m_width) + 1];
+ uchar buf[MAX_INT_WIDTH];
uchar *end= net_store_length(buf, m_width);
DBUG_EXECUTE_IF("old_row_based_repl_4_byte_map_id_master",
return 6 + no_bytes_in_map(&m_cols) + (end - buf) +
- (type_code == UPDATE_ROWS_EVENT ? no_bytes_in_map(&m_cols_ai) : 0) +
+ (general_type_code == UPDATE_ROWS_EVENT ? no_bytes_in_map(&m_cols_ai) : 0) +
(m_rows_cur - m_rows_buf););
- int data_size= ROWS_HEADER_LEN;
+
+ int data_size= 0;
+ bool is_v2_event= get_type_code() > DELETE_ROWS_EVENT_V1;
+ if (is_v2_event)
+ {
+ data_size= ROWS_HEADER_LEN_V2 +
+ (m_extra_row_data ?
+ RW_V_TAG_LEN + m_extra_row_data[EXTRA_ROW_INFO_LEN_OFFSET]:
+ 0);
+ }
+ else
+ {
+ data_size= ROWS_HEADER_LEN_V1;
+ }
data_size+= no_bytes_in_map(&m_cols);
data_size+= (uint) (end - buf);
- if (type_code == UPDATE_ROWS_EVENT)
+ if (general_type_code == UPDATE_ROWS_EVENT)
data_size+= no_bytes_in_map(&m_cols_ai);
data_size+= (uint) (m_rows_cur - m_rows_buf);
@@ -8307,7 +9581,7 @@ int Rows_log_event::do_add_row_data(uchar *row_data, size_t length)
trigger false warnings.
*/
#ifndef HAVE_valgrind
- DBUG_DUMP("row_data", row_data, min(length, 32));
+ DBUG_DUMP("row_data", row_data, MY_MIN(length, 32));
#endif
DBUG_ASSERT(m_rows_buf <= m_rows_cur);
@@ -8372,9 +9646,30 @@ int Rows_log_event::do_add_row_data(uchar *row_data, size_t length)
}
#endif
-#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
-int Rows_log_event::do_apply_event(Relay_log_info const *rli)
+#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
+
+/**
+ Restores empty table list as it was before trigger processing.
+
+ @note We have a lot of ASSERTS that check the lists when we close tables.
+ There was the same problem with MERGE MYISAM tables and so here we try to
+ go the same way.
+*/
+static void restore_empty_query_table_list(LEX *lex)
+{
+#ifdef RBR_TRIGGERS
+ if (lex->first_not_own_table())
+ (*lex->first_not_own_table()->prev_global)= NULL;
+ lex->query_tables= NULL;
+ lex->query_tables_last= &lex->query_tables;
+#endif //RBR_TRIGGERS
+}
+
+
+int Rows_log_event::do_apply_event(rpl_group_info *rgi)
{
+ Relay_log_info const *rli= rgi->rli;
+ TABLE* table;
DBUG_ENTER("Rows_log_event::do_apply_event(Relay_log_info*)");
int error= 0;
/*
@@ -8391,7 +9686,7 @@ int Rows_log_event::do_apply_event(Relay_log_info const *rli)
*/
DBUG_ASSERT(get_flags(STMT_END_F));
- const_cast<Relay_log_info*>(rli)->slave_close_thread_tables(thd);
+ rgi->slave_close_thread_tables(thd);
thd->clear_error();
DBUG_RETURN(0);
}
@@ -8401,7 +9696,7 @@ int Rows_log_event::do_apply_event(Relay_log_info const *rli)
do_apply_event(). We still check here to prevent future coding
errors.
*/
- DBUG_ASSERT(rli->sql_thd == thd);
+ DBUG_ASSERT(rgi->thd == thd);
/*
If there is no locks taken, this is the first binrow event seen
@@ -8420,6 +9715,7 @@ int Rows_log_event::do_apply_event(Relay_log_info const *rli)
call might reset the value of current_stmt_binlog_format, so
we need to do any changes to that value after this function.
*/
+ delete_explain_query(thd->lex);
lex_start(thd);
mysql_reset_thd_for_next_command(thd);
/*
@@ -8452,22 +9748,48 @@ int Rows_log_event::do_apply_event(Relay_log_info const *rli)
thd->variables.option_bits&= ~OPTION_RELAXED_UNIQUE_CHECKS;
/* A small test to verify that objects have consistent types */
DBUG_ASSERT(sizeof(thd->variables.option_bits) == sizeof(OPTION_RELAXED_UNIQUE_CHECKS));
- if (open_and_lock_tables(thd, rli->tables_to_lock, FALSE, 0))
+
+ if (slave_run_triggers_for_rbr)
+ {
+ LEX *lex= thd->lex;
+ uint8 new_trg_event_map= get_trg_event_map();
+
+ /*
+ Trigger's procedures work with global table list. So we have to add
+ rgi->tables_to_lock content there to get trigger's in the list.
+
+ Then restore_empty_query_table_list() restore the list as it was
+ */
+ DBUG_ASSERT(lex->query_tables == NULL);
+ if ((lex->query_tables= rgi->tables_to_lock))
+ rgi->tables_to_lock->prev_global= &lex->query_tables;
+
+ for (TABLE_LIST *tables= rgi->tables_to_lock; tables;
+ tables= tables->next_global)
+ {
+ tables->trg_event_map= new_trg_event_map;
+ lex->query_tables_last= &tables->next_global;
+ }
+ }
+ if (open_and_lock_tables(thd, rgi->tables_to_lock, FALSE, 0))
{
- uint actual_error= thd->stmt_da->sql_errno();
+ uint actual_error= thd->get_stmt_da()->sql_errno();
+
#ifdef WITH_WSREP
if (WSREP(thd))
{
WSREP_WARN("BF applier failed to open_and_lock_tables: %u, fatal: %d "
"wsrep = (exec_mode: %d conflict_state: %d seqno: %lld)",
- thd->stmt_da->sql_errno(),
+ thd->get_stmt_da()->sql_errno(),
thd->is_fatal_error,
thd->wsrep_exec_mode,
thd->wsrep_conflict_state,
(long long)wsrep_thd_trx_seqno(thd));
- }
+ }
#endif
- if (thd->is_slave_error || thd->is_fatal_error)
+
+ if ((thd->is_slave_error || thd->is_fatal_error) &&
+ !is_parallel_retry_error(rgi, actual_error))
{
/*
Error reporting borrowed from Query_log_event with many excessive
@@ -8475,14 +9797,15 @@ int Rows_log_event::do_apply_event(Relay_log_info const *rli)
We should not honour --slave-skip-errors at this point as we are
having severe errors which should not be skiped.
*/
- rli->report(ERROR_LEVEL, actual_error,
+ rli->report(ERROR_LEVEL, actual_error, rgi->gtid_info(),
"Error executing row event: '%s'",
- (actual_error ? thd->stmt_da->message() :
+ (actual_error ? thd->get_stmt_da()->message() :
"unexpected success or fatal error"));
thd->is_slave_error= 1;
}
- const_cast<Relay_log_info*>(rli)->slave_close_thread_tables(thd);
- DBUG_RETURN(actual_error);
+ /* remove trigger's tables */
+ error= actual_error;
+ goto err;
}
/*
@@ -8495,7 +9818,7 @@ int Rows_log_event::do_apply_event(Relay_log_info const *rli)
{
DBUG_PRINT("debug", ("Checking compability of tables to lock - tables_to_lock: %p",
- rli->tables_to_lock));
+ rgi->tables_to_lock));
/**
When using RBR and MyISAM MERGE tables the base tables that make
@@ -8509,14 +9832,13 @@ int Rows_log_event::do_apply_event(Relay_log_info const *rli)
NOTE: The base tables are added here are removed when
close_thread_tables is called.
*/
- RPL_TABLE_LIST *ptr= rli->tables_to_lock;
- for (uint i= 0 ; ptr && (i < rli->tables_to_lock_count);
+ RPL_TABLE_LIST *ptr= rgi->tables_to_lock;
+ for (uint i= 0 ; ptr && (i < rgi->tables_to_lock_count);
ptr= static_cast<RPL_TABLE_LIST*>(ptr->next_global), i++)
{
DBUG_ASSERT(ptr->m_tabledef_valid);
TABLE *conv_table;
- if (!ptr->m_tabledef.compatible_with(thd, const_cast<Relay_log_info*>(rli),
- ptr->table, &conv_table))
+ if (!ptr->m_tabledef.compatible_with(thd, rgi, ptr->table, &conv_table))
{
DBUG_PRINT("debug", ("Table: %s.%s is not compatible with master",
ptr->table->s->db.str,
@@ -8526,8 +9848,9 @@ int Rows_log_event::do_apply_event(Relay_log_info const *rli)
having severe errors which should not be skiped.
*/
thd->is_slave_error= 1;
- const_cast<Relay_log_info*>(rli)->slave_close_thread_tables(thd);
- DBUG_RETURN(ERR_BAD_TABLE_DEF);
+ /* remove trigger's tables */
+ error= ERR_BAD_TABLE_DEF;
+ goto err;
}
DBUG_PRINT("debug", ("Table: %s.%s is compatible with master"
" - conv_table: %p",
@@ -8551,9 +9874,22 @@ int Rows_log_event::do_apply_event(Relay_log_info const *rli)
Rows_log_event, we can invalidate the query cache for the
associated table.
*/
- TABLE_LIST *ptr= rli->tables_to_lock;
- for (uint i=0 ; ptr && (i < rli->tables_to_lock_count); ptr= ptr->next_global, i++)
- const_cast<Relay_log_info*>(rli)->m_table_map.set_table(ptr->table_id, ptr->table);
+ TABLE_LIST *ptr= rgi->tables_to_lock;
+ for (uint i=0 ; ptr && (i < rgi->tables_to_lock_count); ptr= ptr->next_global, i++)
+ {
+ rgi->m_table_map.set_table(ptr->table_id, ptr->table);
+ /*
+ Following is passing flag about triggers on the server. The problem was
+ to pass it between table map event and row event. I do it via extended
+ TABLE_LIST (RPL_TABLE_LIST) but row event uses only TABLE so I need to
+ find somehow the corresponding TABLE_LIST.
+ */
+ if (m_table_id == ptr->table_id)
+ {
+ ptr->table->master_had_triggers=
+ ((RPL_TABLE_LIST*)ptr)->master_had_triggers;
+ }
+ }
#ifdef HAVE_QUERY_CACHE
#ifdef WITH_WSREP
@@ -8564,21 +9900,22 @@ int Rows_log_event::do_apply_event(Relay_log_info const *rli)
if (! (WSREP(thd) && (thd->wsrep_exec_mode == REPL_RECV)))
{
#endif /* WITH_WSREP */
- query_cache.invalidate_locked_for_write(thd, rli->tables_to_lock);
+ query_cache.invalidate_locked_for_write(thd, rgi->tables_to_lock);
#ifdef WITH_WSREP
}
#endif /* WITH_WSREP */
#endif
}
- TABLE*
- table=
- m_table= const_cast<Relay_log_info*>(rli)->m_table_map.get_table(m_table_id);
-
- DBUG_PRINT("debug", ("m_table: 0x%lx, m_table_id: %lu", (ulong) m_table, m_table_id));
+ table= m_table= rgi->m_table_map.get_table(m_table_id);
+ DBUG_PRINT("debug", ("m_table: 0x%lx, m_table_id: %lu%s",
+ (ulong) m_table, m_table_id,
+ table && master_had_triggers ?
+ " (master had triggers)" : ""));
if (table)
{
+ master_had_triggers= table->master_had_triggers;
bool transactional_table= table->file->has_transactions();
/*
table == NULL means that this table should not be replicated
@@ -8596,18 +9933,7 @@ int Rows_log_event::do_apply_event(Relay_log_info const *rli)
*/
thd->set_time(when, when_sec_part);
- /*
- Now we are in a statement and will stay in a statement until we
- see a STMT_END_F.
-
- We set this flag here, before actually applying any rows, in
- case the SQL thread is stopped and we need to detect that we're
- inside a statement and halting abruptly might cause problems
- when restarting.
- */
- const_cast<Relay_log_info*>(rli)->set_flag(Relay_log_info::IN_STMT);
-
- if ( m_width == table->s->fields && bitmap_is_set_all(&m_cols))
+ if (m_width == table->s->fields && bitmap_is_set_all(&m_cols))
set_flags(COMPLETE_ROWS_F);
/*
@@ -8650,8 +9976,9 @@ int Rows_log_event::do_apply_event(Relay_log_info const *rli)
set the initial time of this ROWS statement if it was not done
before in some other ROWS event.
*/
- const_cast<Relay_log_info*>(rli)->set_row_stmt_start_timestamp();
+ rgi->set_row_stmt_start_timestamp();
+ THD_STAGE_INFO(thd, stage_executing);
while (error == 0 && m_curr_row < m_rows_end)
{
/* in_use can have been set to NULL in close_tables_for_reopen */
@@ -8659,7 +9986,7 @@ int Rows_log_event::do_apply_event(Relay_log_info const *rli)
if (!table->in_use)
table->in_use= thd;
- error= do_exec_row(rli);
+ error= do_exec_row(rgi);
if (error)
DBUG_PRINT("info", ("error: %s", HA_ERR(error)));
@@ -8678,7 +10005,7 @@ int Rows_log_event::do_apply_event(Relay_log_info const *rli)
if (idempotent_error || ignored_error)
{
if (global_system_variables.log_warnings)
- slave_rows_error_report(WARNING_LEVEL, error, rli, thd, table,
+ slave_rows_error_report(WARNING_LEVEL, error, rgi, thd, table,
get_type_str(),
RPL_LOG_NAME, (ulong) log_pos);
clear_all_errors(thd, const_cast<Relay_log_info*>(rli));
@@ -8699,7 +10026,7 @@ int Rows_log_event::do_apply_event(Relay_log_info const *rli)
(ulong) m_curr_row, (ulong) m_curr_row_end, (ulong) m_rows_end));
if (!m_curr_row_end && !error)
- error= unpack_current_row(rli);
+ error= unpack_current_row(rgi);
// at this moment m_curr_row_end should be set
DBUG_ASSERT(error || m_curr_row_end != NULL);
@@ -8734,7 +10061,7 @@ int Rows_log_event::do_apply_event(Relay_log_info const *rli)
{
if (global_system_variables.log_warnings)
- slave_rows_error_report(WARNING_LEVEL, error, rli, thd, table,
+ slave_rows_error_report(WARNING_LEVEL, error, rgi, thd, table,
get_type_str(),
RPL_LOG_NAME, (ulong) log_pos);
clear_all_errors(thd, const_cast<Relay_log_info*>(rli));
@@ -8745,7 +10072,7 @@ int Rows_log_event::do_apply_event(Relay_log_info const *rli)
if (error)
{
- slave_rows_error_report(ERROR_LEVEL, error, rli, thd, table,
+ slave_rows_error_report(ERROR_LEVEL, error, rgi, thd, table,
get_type_str(),
RPL_LOG_NAME, (ulong) log_pos);
/*
@@ -8757,37 +10084,48 @@ int Rows_log_event::do_apply_event(Relay_log_info const *rli)
*/
thd->reset_current_stmt_binlog_format_row();
thd->is_slave_error= 1;
- DBUG_RETURN(error);
+ /* remove trigger's tables */
+ goto err;
}
+ /* remove trigger's tables */
+ if (slave_run_triggers_for_rbr)
+ restore_empty_query_table_list(thd->lex);
+
#if defined(WITH_WSREP) && defined(HAVE_QUERY_CACHE)
- if (get_flags(STMT_END_F) &&
- WSREP(thd) && thd->wsrep_exec_mode == REPL_RECV)
- {
- query_cache.invalidate_locked_for_write(thd, rli->tables_to_lock);
- }
-#endif /* WITH_WSREP */
- if (get_flags(STMT_END_F) && (error= rows_event_stmt_cleanup(rli, thd)))
+ if (WSREP(thd) && thd->wsrep_exec_mode == REPL_RECV)
+ {
+ query_cache.invalidate_locked_for_write(thd, rgi->tables_to_lock);
+ }
+#endif /* WITH_WSREP && HAVE_QUERY_CACHE */
+
+ if (get_flags(STMT_END_F) && (error= rows_event_stmt_cleanup(rgi, thd)))
slave_rows_error_report(ERROR_LEVEL,
thd->is_error() ? 0 : error,
- rli, thd, table,
+ rgi, thd, table,
get_type_str(),
RPL_LOG_NAME, (ulong) log_pos);
DBUG_RETURN(error);
+
+err:
+ if (slave_run_triggers_for_rbr)
+ restore_empty_query_table_list(thd->lex);
+ rgi->slave_close_thread_tables(thd);
+ DBUG_RETURN(error);
}
Log_event::enum_skip_reason
-Rows_log_event::do_shall_skip(Relay_log_info *rli)
+Rows_log_event::do_shall_skip(rpl_group_info *rgi)
{
/*
If the slave skip counter is 1 and this event does not end a
statement, then we should not start executing on the next event.
Otherwise, we defer the decision to the normal skipping logic.
*/
- if (rli->slave_skip_counter == 1 && !get_flags(STMT_END_F))
+ if (rgi->rli->slave_skip_counter == 1 && !get_flags(STMT_END_F))
return Log_event::EVENT_SKIP_IGNORE;
else
- return Log_event::do_shall_skip(rli);
+ return Log_event::do_shall_skip(rgi);
}
/**
@@ -8801,9 +10139,11 @@ Rows_log_event::do_shall_skip(Relay_log_info *rli)
@retval non-zero Error at the commit.
*/
-static int rows_event_stmt_cleanup(Relay_log_info const *rli, THD * thd)
+static int rows_event_stmt_cleanup(rpl_group_info *rgi, THD * thd)
{
int error;
+ DBUG_ENTER("rows_event_stmt_cleanup");
+
{
/*
This is the end of a statement or transaction, so close (and
@@ -8858,9 +10198,16 @@ static int rows_event_stmt_cleanup(Relay_log_info const *rli, THD * thd)
*/
thd->reset_current_stmt_binlog_format_row();
- const_cast<Relay_log_info*>(rli)->cleanup_context(thd, 0);
+ /*
+ Reset modified_non_trans_table that we have set in
+ rows_log_event::do_apply_event()
+ */
+ if (!thd->in_multi_stmt_transaction_mode())
+ thd->transaction.all.modified_non_trans_table= 0;
+
+ rgi->cleanup_context(thd, 0);
}
- return error;
+ DBUG_RETURN(error);
}
/**
@@ -8874,8 +10221,9 @@ static int rows_event_stmt_cleanup(Relay_log_info const *rli, THD * thd)
@retval non-zero Error in the statement commit
*/
int
-Rows_log_event::do_update_pos(Relay_log_info *rli)
+Rows_log_event::do_update_pos(rpl_group_info *rgi)
{
+ Relay_log_info *rli= rgi->rli;
DBUG_ENTER("Rows_log_event::do_update_pos");
int error= 0;
@@ -8889,7 +10237,7 @@ Rows_log_event::do_update_pos(Relay_log_info *rli)
Step the group log position if we are not in a transaction,
otherwise increase the event log position.
*/
- rli->stmt_done(log_pos, when);
+ rli->stmt_done(log_pos, thd, rgi);
/*
Clear any errors in thd->net.last_err*. It is not known if this is
needed or not. It is believed that any errors that may exist in
@@ -8900,18 +10248,18 @@ Rows_log_event::do_update_pos(Relay_log_info *rli)
}
else
{
- rli->inc_event_relay_log_pos();
+ rgi->inc_event_relay_log_pos();
}
DBUG_RETURN(error);
}
-#endif /* !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) */
+#endif //defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
#ifndef MYSQL_CLIENT
bool Rows_log_event::write_data_header(IO_CACHE *file)
{
- uchar buf[ROWS_HEADER_LEN]; // No need to init the buffer
+ uchar buf[ROWS_HEADER_LEN_V2]; // No need to init the buffer
DBUG_ASSERT(m_table_id != ~0UL);
DBUG_EXECUTE_IF("old_row_based_repl_4_byte_map_id_master",
{
@@ -8930,7 +10278,7 @@ bool Rows_log_event::write_data_body(IO_CACHE*file)
Note that this should be the number of *bits*, not the number of
bytes.
*/
- uchar sbuf[sizeof(m_width) + 1];
+ uchar sbuf[MAX_INT_WIDTH];
my_ptrdiff_t const data_size= m_rows_cur - m_rows_buf;
bool res= false;
uchar *const sbuf_end= net_store_length(sbuf, (size_t) m_width);
@@ -8945,7 +10293,7 @@ bool Rows_log_event::write_data_body(IO_CACHE*file)
/*
TODO[refactor write]: Remove the "down cast" here (and elsewhere).
*/
- if (get_type_code() == UPDATE_ROWS_EVENT)
+ if (get_general_type_code() == UPDATE_ROWS_EVENT)
{
DBUG_DUMP("m_cols_ai", (uchar*) m_cols_ai.bitmap,
no_bytes_in_map(&m_cols_ai));
@@ -9112,7 +10460,7 @@ void Annotate_rows_log_event::print(FILE *file, PRINT_EVENT_INFO *pinfo)
#endif
#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
-int Annotate_rows_log_event::do_apply_event(Relay_log_info const *rli)
+int Annotate_rows_log_event::do_apply_event(rpl_group_info *rgi)
{
m_save_thd_query_txt= thd->query();
m_save_thd_query_len= thd->query_length();
@@ -9122,18 +10470,18 @@ int Annotate_rows_log_event::do_apply_event(Relay_log_info const *rli)
#endif
#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
-int Annotate_rows_log_event::do_update_pos(Relay_log_info *rli)
+int Annotate_rows_log_event::do_update_pos(rpl_group_info *rgi)
{
- rli->inc_event_relay_log_pos();
+ rgi->inc_event_relay_log_pos();
return 0;
}
#endif
#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
Log_event::enum_skip_reason
-Annotate_rows_log_event::do_shall_skip(Relay_log_info *rli)
+Annotate_rows_log_event::do_shall_skip(rpl_group_info *rgi)
{
- return continue_group(rli);
+ return continue_group(rgi);
}
#endif
@@ -9235,8 +10583,9 @@ Table_map_log_event::Table_map_log_event(THD *thd, TABLE *tbl, ulong tid,
m_null_bits(0),
m_meta_memory(NULL)
{
- uchar cbuf[sizeof(m_colcnt) + 1];
+ uchar cbuf[MAX_INT_WIDTH];
uchar *cbuf_end;
+ DBUG_ENTER("Table_map_log_event::Table_map_log_event(TABLE)");
DBUG_ASSERT(m_table_id != ~0UL);
/*
In TABLE_SHARE, "db" and "table_name" are 0-terminated (see this comment in
@@ -9257,12 +10606,17 @@ Table_map_log_event::Table_map_log_event(THD *thd, TABLE *tbl, ulong tid,
DBUG_ASSERT(static_cast<size_t>(cbuf_end - cbuf) <= sizeof(cbuf));
m_data_size+= (cbuf_end - cbuf) + m_colcnt; // COLCNT and column types
+#ifdef RBR_TRIGGERS
+ if (tbl->triggers)
+ m_flags|= TM_BIT_HAS_TRIGGERS_F;
+#endif //RBR_TRIGGERS
+
/* If malloc fails, caught in is_valid() */
if ((m_memory= (uchar*) my_malloc(m_colcnt, MYF(MY_WME))))
{
m_coltype= reinterpret_cast<uchar*>(m_memory);
for (unsigned int i= 0 ; i < m_table->s->fields ; ++i)
- m_coltype[i]= m_table->field[i]->type();
+ m_coltype[i]= m_table->field[i]->binlog_type();
}
/*
@@ -9301,6 +10655,7 @@ Table_map_log_event::Table_map_log_event(THD *thd, TABLE *tbl, ulong tid,
if (m_table->field[i]->maybe_null())
m_null_bits[(i / 8)]+= 1 << (i % 8);
+ DBUG_VOID_RETURN;
}
#endif /* !defined(MYSQL_CLIENT) */
@@ -9448,7 +10803,7 @@ int Table_map_log_event::rewrite_db(const char* new_db, size_t new_len,
DBUG_ENTER("Table_map_log_event::rewrite_db");
DBUG_ASSERT(temp_buf);
- uint header_len= min(desc->common_header_len,
+ uint header_len= MY_MIN(desc->common_header_len,
LOG_EVENT_MINIMAL_HEADER_LEN) + TABLE_MAP_HEADER_LEN;
int len_diff;
@@ -9595,23 +10950,25 @@ enum enum_tbl_map_status
rli->tables_to_lock.
*/
static enum_tbl_map_status
-check_table_map(Relay_log_info const *rli, RPL_TABLE_LIST *table_list)
+check_table_map(rpl_group_info *rgi, RPL_TABLE_LIST *table_list)
{
DBUG_ENTER("check_table_map");
enum_tbl_map_status res= OK_TO_PROCESS;
+ Relay_log_info *rli= rgi->rli;
+
#ifdef WITH_WSREP
- if ((rli->sql_thd->slave_thread /* filtering is for slave only */ ||
- (WSREP(rli->sql_thd) && rli->sql_thd->wsrep_applier)) &&
+ if ((rgi->thd->slave_thread /* filtering is for slave only */ ||
+ (WSREP(rgi->thd) && rgi->thd->wsrep_applier)) &&
#else
- if (rli->sql_thd->slave_thread /* filtering is for slave only */ &&
+ if (rgi->thd->slave_thread /* filtering is for slave only */ &&
#endif /* WITH_WSREP */
- (!rpl_filter->db_ok(table_list->db) ||
- (rpl_filter->is_on() && !rpl_filter->tables_ok("", table_list))))
+ (!rli->mi->rpl_filter->db_ok(table_list->db) ||
+ (rli->mi->rpl_filter->is_on() && !rli->mi->rpl_filter->tables_ok("", table_list))))
res= FILTERED_OUT;
else
{
- RPL_TABLE_LIST *ptr= static_cast<RPL_TABLE_LIST*>(rli->tables_to_lock);
- for(uint i=0 ; ptr && (i< rli->tables_to_lock_count);
+ RPL_TABLE_LIST *ptr= static_cast<RPL_TABLE_LIST*>(rgi->tables_to_lock);
+ for(uint i=0 ; ptr && (i< rgi->tables_to_lock_count);
ptr= static_cast<RPL_TABLE_LIST*>(ptr->next_local), i++)
{
if (ptr->table_id == table_list->table_id)
@@ -9634,14 +10991,15 @@ check_table_map(Relay_log_info const *rli, RPL_TABLE_LIST *table_list)
DBUG_RETURN(res);
}
-int Table_map_log_event::do_apply_event(Relay_log_info const *rli)
+int Table_map_log_event::do_apply_event(rpl_group_info *rgi)
{
RPL_TABLE_LIST *table_list;
char *db_mem, *tname_mem;
size_t dummy_len;
void *memory;
+ Rpl_filter *filter;
+ Relay_log_info const *rli= rgi->rli;
DBUG_ENTER("Table_map_log_event::do_apply_event(Relay_log_info*)");
- DBUG_ASSERT(rli->sql_thd == thd);
/* Step the query id to mark what columns that are actually used. */
thd->set_query_id(next_query_id());
@@ -9653,7 +11011,9 @@ int Table_map_log_event::do_apply_event(Relay_log_info const *rli)
NullS)))
DBUG_RETURN(HA_ERR_OUT_OF_MEM);
- strmov(db_mem, rpl_filter->get_rewrite_db(m_dbnam, &dummy_len));
+ /* call from mysql_client_binlog_statement() will not set rli->mi */
+ filter= rgi->thd->slave_thread ? rli->mi->rpl_filter : global_rpl_filter;
+ strmov(db_mem, filter->get_rewrite_db(m_dbnam, &dummy_len));
strmov(tname_mem, m_tblnam);
table_list->init_one_table(db_mem, strlen(db_mem),
@@ -9663,8 +11023,16 @@ int Table_map_log_event::do_apply_event(Relay_log_info const *rli)
table_list->table_id= DBUG_EVALUATE_IF("inject_tblmap_same_id_maps_diff_table", 0, m_table_id);
table_list->updating= 1;
table_list->required_type= FRMTYPE_TABLE;
- DBUG_PRINT("debug", ("table: %s is mapped to %u", table_list->table_name, table_list->table_id));
- enum_tbl_map_status tblmap_status= check_table_map(rli, table_list);
+
+ DBUG_PRINT("debug", ("table: %s is mapped to %u", table_list->table_name,
+ table_list->table_id));
+#ifdef RBR_TRIGGERS
+ table_list->master_had_triggers= ((m_flags & TM_BIT_HAS_TRIGGERS_F) ? 1 : 0);
+ DBUG_PRINT("debug", ("table->master_had_triggers=%d",
+ (int)table_list->master_had_triggers));
+#endif //RBR_TRIGGERS
+
+ enum_tbl_map_status tblmap_status= check_table_map(rgi, table_list);
if (tblmap_status == OK_TO_PROCESS)
{
DBUG_ASSERT(thd->lex->query_tables != table_list);
@@ -9690,9 +11058,9 @@ int Table_map_log_event::do_apply_event(Relay_log_info const *rli)
We record in the slave's information that the table should be
locked by linking the table into the list of tables to lock.
*/
- table_list->next_global= table_list->next_local= rli->tables_to_lock;
- const_cast<Relay_log_info*>(rli)->tables_to_lock= table_list;
- const_cast<Relay_log_info*>(rli)->tables_to_lock_count++;
+ table_list->next_global= table_list->next_local= rgi->tables_to_lock;
+ rgi->tables_to_lock= table_list;
+ rgi->tables_to_lock_count++;
/* 'memory' is freed in clear_tables_to_lock */
}
else // FILTERED_OUT, SAME_ID_MAPPING_*
@@ -9722,7 +11090,7 @@ int Table_map_log_event::do_apply_event(Relay_log_info const *rli)
table_list->table_id);
if (thd->slave_thread)
- rli->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR,
+ rli->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR, rgi->gtid_info(),
ER(ER_SLAVE_FATAL_ERROR), buf);
else
/*
@@ -9740,18 +11108,18 @@ int Table_map_log_event::do_apply_event(Relay_log_info const *rli)
}
Log_event::enum_skip_reason
-Table_map_log_event::do_shall_skip(Relay_log_info *rli)
+Table_map_log_event::do_shall_skip(rpl_group_info *rgi)
{
/*
If the slave skip counter is 1, then we should not start executing
on the next event.
*/
- return continue_group(rli);
+ return continue_group(rgi);
}
-int Table_map_log_event::do_update_pos(Relay_log_info *rli)
+int Table_map_log_event::do_update_pos(rpl_group_info *rgi)
{
- rli->inc_event_relay_log_pos();
+ rgi->inc_event_relay_log_pos();
return 0;
}
@@ -9778,20 +11146,20 @@ bool Table_map_log_event::write_data_body(IO_CACHE *file)
DBUG_ASSERT(m_dbnam != NULL);
DBUG_ASSERT(m_tblnam != NULL);
/* We use only one byte per length for storage in event: */
- DBUG_ASSERT(m_dblen <= min(NAME_LEN, 255));
- DBUG_ASSERT(m_tbllen <= min(NAME_LEN, 255));
+ DBUG_ASSERT(m_dblen <= MY_MIN(NAME_LEN, 255));
+ DBUG_ASSERT(m_tbllen <= MY_MIN(NAME_LEN, 255));
uchar const dbuf[]= { (uchar) m_dblen };
uchar const tbuf[]= { (uchar) m_tbllen };
- uchar cbuf[sizeof(m_colcnt) + 1];
+ uchar cbuf[MAX_INT_WIDTH];
uchar *const cbuf_end= net_store_length(cbuf, (size_t) m_colcnt);
DBUG_ASSERT(static_cast<size_t>(cbuf_end - cbuf) <= sizeof(cbuf));
/*
Store the size of the field metadata.
*/
- uchar mbuf[sizeof(m_field_metadata_size)];
+ uchar mbuf[MAX_INT_WIDTH];
uchar *const mbuf_end= net_store_length(mbuf, m_field_metadata_size);
return (wrapper_my_b_safe_write(file, dbuf, sizeof(dbuf)) ||
@@ -9835,8 +11203,10 @@ void Table_map_log_event::print(FILE *, PRINT_EVENT_INFO *print_event_info)
{
print_header(&print_event_info->head_cache, print_event_info, TRUE);
my_b_printf(&print_event_info->head_cache,
- "\tTable_map: %`s.%`s mapped to number %lu\n",
- m_dbnam, m_tblnam, m_table_id);
+ "\tTable_map: %`s.%`s mapped to number %lu%s\n",
+ m_dbnam, m_tblnam, m_table_id,
+ ((m_flags & TM_BIT_HAS_TRIGGERS_F) ?
+ " (has triggers)" : ""));
print_base64(&print_event_info->body_cache, print_event_info, TRUE);
}
}
@@ -9854,7 +11224,7 @@ Write_rows_log_event::Write_rows_log_event(THD *thd_arg, TABLE *tbl_arg,
ulong tid_arg,
MY_BITMAP const *cols,
bool is_transactional)
- : Rows_log_event(thd_arg, tbl_arg, tid_arg, cols, is_transactional)
+ : Rows_log_event(thd_arg, tbl_arg, tid_arg, cols, is_transactional, WRITE_ROWS_EVENT_V1)
{
}
#endif
@@ -9866,7 +11236,7 @@ Write_rows_log_event::Write_rows_log_event(THD *thd_arg, TABLE *tbl_arg,
Write_rows_log_event::Write_rows_log_event(const char *buf, uint event_len,
const Format_description_log_event
*description_event)
-: Rows_log_event(buf, event_len, WRITE_ROWS_EVENT, description_event)
+: Rows_log_event(buf, event_len, description_event)
{
}
#endif
@@ -9912,6 +11282,8 @@ Write_rows_log_event::do_before_row_operations(const Slave_reporting_capability
/*
NDB specific: update from ndb master wrapped as Write_rows
so that the event should be applied to replace slave's row
+
+ Also following is needed in case if we have AFTER DELETE triggers.
*/
m_table->file->extra(HA_EXTRA_WRITE_CAN_REPLACE);
/*
@@ -9926,24 +11298,9 @@ Write_rows_log_event::do_before_row_operations(const Slave_reporting_capability
from the start.
*/
}
+ if (slave_run_triggers_for_rbr && !master_had_triggers && m_table->triggers )
+ m_table->prepare_triggers_for_insert_stmt_or_event();
- /*
- We need TIMESTAMP_NO_AUTO_SET otherwise ha_write_row() will not use fill
- any TIMESTAMP column with data from the row but instead will use
- the event's current time.
- As we replicate from TIMESTAMP to TIMESTAMP and slave has no extra
- columns, we know that all TIMESTAMP columns on slave will receive explicit
- data from the row, so TIMESTAMP_NO_AUTO_SET is ok.
- When we allow a table without TIMESTAMP to be replicated to a table having
- more columns including a TIMESTAMP column, or when we allow a TIMESTAMP
- column to be replicated into a BIGINT column and the slave's table has a
- TIMESTAMP column, then the slave's TIMESTAMP column will take its value
- from set_time() which we called earlier (consistent with SBR). And then in
- some cases we won't want TIMESTAMP_NO_AUTO_SET (will require some code to
- analyze if explicit data is provided for slave's TIMESTAMP columns).
- */
- m_table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
-
/* Honor next number column if present */
m_table->next_number_field= m_table->found_next_number_field;
/*
@@ -10020,6 +11377,29 @@ Write_rows_log_event::do_after_row_operations(const Slave_reporting_capability *
#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+bool Rows_log_event::process_triggers(trg_event_type event,
+ trg_action_time_type time_type,
+ bool old_row_is_record1)
+{
+#ifdef RBR_TRIGGERS
+ bool result;
+ DBUG_ENTER("Rows_log_event::process_triggers");
+ if (slave_run_triggers_for_rbr == SLAVE_RUN_TRIGGERS_FOR_RBR_YES)
+ {
+ tmp_disable_binlog(thd); /* Do not replicate the low-level changes. */
+ result= m_table->triggers->process_triggers(thd, event,
+ time_type, old_row_is_record1);
+ reenable_binlog(thd);
+ }
+ else
+ result= m_table->triggers->process_triggers(thd, event,
+ time_type, old_row_is_record1);
+
+ DBUG_RETURN(result);
+#else
+ return TRUE;
+#endif //RBR_TRIGGERS
+}
/*
Check if there are more UNIQUE keys after the given key.
*/
@@ -10093,7 +11473,7 @@ is_duplicate_key_error(int errcode)
*/
int
-Rows_log_event::write_row(const Relay_log_info *const rli,
+Rows_log_event::write_row(rpl_group_info *rgi,
const bool overwrite)
{
DBUG_ENTER("write_row");
@@ -10102,22 +11482,28 @@ Rows_log_event::write_row(const Relay_log_info *const rli,
TABLE *table= m_table; // pointer to event's table
int error;
int UNINIT_VAR(keynum);
+ const bool invoke_triggers=
+ slave_run_triggers_for_rbr && !master_had_triggers && table->triggers;
auto_afree_ptr<char> key(NULL);
prepare_record(table, m_width,
table->file->ht->db_type != DB_TYPE_NDBCLUSTER);
/* unpack row into table->record[0] */
- if ((error= unpack_current_row(rli)))
+ if ((error= unpack_current_row(rgi)))
DBUG_RETURN(error);
- if (m_curr_row == m_rows_buf)
+ if (m_curr_row == m_rows_buf && !invoke_triggers)
{
- /* this is the first row to be inserted, we estimate the rows with
+ /*
+ This table has no triggers so we can do bulk insert.
+
+ This is the first row to be inserted, we estimate the rows with
the size of the first row and use that value to initialize
- storage engine for bulk insertion */
+ storage engine for bulk insertion.
+ */
ulong estimated_rows= (m_rows_end - m_curr_row) / (m_curr_row_end - m_curr_row);
- m_table->file->ha_start_bulk_insert(estimated_rows);
+ table->file->ha_start_bulk_insert(estimated_rows);
}
/*
@@ -10133,6 +11519,12 @@ Rows_log_event::write_row(const Relay_log_info *const rli,
DBUG_PRINT_BITSET("debug", "read_set = %s", table->read_set);
#endif
+ if (invoke_triggers &&
+ process_triggers(TRG_EVENT_INSERT, TRG_ACTION_BEFORE, TRUE))
+ {
+ DBUG_RETURN(HA_ERR_GENERIC); // in case if error is not set yet
+ }
+
/*
Try to write record. If a corresponding record already exists in the table,
we try to change it using ha_update_row() if possible. Otherwise we delete
@@ -10231,7 +11623,7 @@ Rows_log_event::write_row(const Relay_log_info *const rli,
if (!get_flags(COMPLETE_ROWS_F))
{
restore_record(table,record[1]);
- error= unpack_current_row(rli);
+ error= unpack_current_row(rgi);
}
#ifndef DBUG_OFF
@@ -10259,45 +11651,68 @@ Rows_log_event::write_row(const Relay_log_info *const rli,
!table->file->referenced_by_foreign_key())
{
DBUG_PRINT("info",("Updating row using ha_update_row()"));
- error=table->file->ha_update_row(table->record[1],
- table->record[0]);
- switch (error) {
-
- case HA_ERR_RECORD_IS_THE_SAME:
- DBUG_PRINT("info",("ignoring HA_ERR_RECORD_IS_THE_SAME error from"
- " ha_update_row()"));
- error= 0;
-
- case 0:
- break;
-
- default:
- DBUG_PRINT("info",("ha_update_row() returns error %d",error));
- table->file->print_error(error, MYF(0));
+ if (invoke_triggers &&
+ process_triggers(TRG_EVENT_UPDATE, TRG_ACTION_BEFORE, FALSE))
+ error= HA_ERR_GENERIC; // in case if error is not set yet
+ else
+ {
+ error= table->file->ha_update_row(table->record[1],
+ table->record[0]);
+ switch (error) {
+
+ case HA_ERR_RECORD_IS_THE_SAME:
+ DBUG_PRINT("info",("ignoring HA_ERR_RECORD_IS_THE_SAME error from"
+ " ha_update_row()"));
+ error= 0;
+
+ case 0:
+ break;
+
+ default:
+ DBUG_PRINT("info",("ha_update_row() returns error %d",error));
+ table->file->print_error(error, MYF(0));
+ }
+ if (invoke_triggers && !error &&
+ (process_triggers(TRG_EVENT_UPDATE, TRG_ACTION_AFTER, TRUE) ||
+ process_triggers(TRG_EVENT_INSERT, TRG_ACTION_AFTER, TRUE)))
+ error= HA_ERR_GENERIC; // in case if error is not set yet
}
-
+
DBUG_RETURN(error);
}
else
{
DBUG_PRINT("info",("Deleting offending row and trying to write new one again"));
- if ((error= table->file->ha_delete_row(table->record[1])))
+ if (invoke_triggers &&
+ process_triggers(TRG_EVENT_DELETE, TRG_ACTION_BEFORE, TRUE))
+ error= HA_ERR_GENERIC; // in case if error is not set yet
+ else
{
- DBUG_PRINT("info",("ha_delete_row() returns error %d",error));
- table->file->print_error(error, MYF(0));
- DBUG_RETURN(error);
+ if ((error= table->file->ha_delete_row(table->record[1])))
+ {
+ DBUG_PRINT("info",("ha_delete_row() returns error %d",error));
+ table->file->print_error(error, MYF(0));
+ DBUG_RETURN(error);
+ }
+ if (invoke_triggers &&
+ process_triggers(TRG_EVENT_DELETE, TRG_ACTION_AFTER, TRUE))
+ DBUG_RETURN(HA_ERR_GENERIC); // in case if error is not set yet
}
/* Will retry ha_write_row() with the offending row removed. */
}
}
+ if (invoke_triggers &&
+ process_triggers(TRG_EVENT_INSERT, TRG_ACTION_AFTER, TRUE))
+ error= HA_ERR_GENERIC; // in case if error is not set yet
+
DBUG_RETURN(error);
}
#endif
int
-Write_rows_log_event::do_exec_row(const Relay_log_info *const rli)
+Write_rows_log_event::do_exec_row(rpl_group_info *rgi)
{
DBUG_ASSERT(m_table != NULL);
#ifdef WITH_WSREP
@@ -10312,7 +11727,7 @@ Write_rows_log_event::do_exec_row(const Relay_log_info *const rli)
thd_proc_info(thd,"Write_rows_log_event::write_row()") : NULL;
#endif /* WSREP_PROC_INFO */
#endif /* WITH_WSREP */
- int error= write_row(rli, slave_exec_mode == SLAVE_EXEC_MODE_IDEMPOTENT);
+ int error= write_row(rgi, slave_exec_mode == SLAVE_EXEC_MODE_IDEMPOTENT);
#ifdef WITH_WSREP
if (WSREP(thd)) thd_proc_info(thd, tmp);
@@ -10337,6 +11752,16 @@ void Write_rows_log_event::print(FILE *file, PRINT_EVENT_INFO* print_event_info)
}
#endif
+
+#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
+uint8 Write_rows_log_event::get_trg_event_map()
+{
+ return (static_cast<uint8> (1 << static_cast<int>(TRG_EVENT_INSERT)) |
+ static_cast<uint8> (1 << static_cast<int>(TRG_EVENT_UPDATE)) |
+ static_cast<uint8> (1 << static_cast<int>(TRG_EVENT_DELETE)));
+}
+#endif
+
/**************************************************************************
Delete_rows_log_event member functions
**************************************************************************/
@@ -10512,7 +11937,7 @@ int Rows_log_event::find_key()
We can only use a non-unique key if it allows range scans (ie. skip
FULLTEXT indexes and such).
*/
- last_part= key->key_parts - 1;
+ last_part= key->user_defined_key_parts - 1;
DBUG_PRINT("info", ("Index %s rec_per_key[%u]= %lu",
key->name, last_part, key->rec_per_key[last_part]));
if (!(m_table->file->index_flags(i, last_part, 1) & HA_READ_NEXT))
@@ -10560,13 +11985,13 @@ static inline
void issue_long_find_row_warning(Log_event_type type,
const char *table_name,
bool is_index_scan,
- const Relay_log_info *rli)
+ rpl_group_info *rgi)
{
if ((global_system_variables.log_warnings > 1 &&
- !const_cast<Relay_log_info*>(rli)->is_long_find_row_note_printed()))
+ !rgi->is_long_find_row_note_printed()))
{
time_t now= my_time(0);
- time_t stmt_ts= const_cast<Relay_log_info*>(rli)->get_row_stmt_start_timestamp();
+ time_t stmt_ts= rgi->get_row_stmt_start_timestamp();
DBUG_EXECUTE_IF("inject_long_find_row_note",
stmt_ts-=(LONG_FIND_ROW_THRESHOLD*2););
@@ -10575,7 +12000,7 @@ void issue_long_find_row_warning(Log_event_type type,
if (delta > LONG_FIND_ROW_THRESHOLD)
{
- const_cast<Relay_log_info*>(rli)->set_long_find_row_note_printed();
+ rgi->set_long_find_row_note_printed();
const char* evt_type= type == DELETE_ROWS_EVENT ? " DELETE" : "n UPDATE";
const char* scan_type= is_index_scan ? "scanning an index" : "scanning the table";
@@ -10621,7 +12046,7 @@ void issue_long_find_row_warning(Log_event_type type,
for any following update/delete command.
*/
-int Rows_log_event::find_row(const Relay_log_info *rli)
+int Rows_log_event::find_row(rpl_group_info *rgi)
{
DBUG_ENTER("Rows_log_event::find_row");
@@ -10639,7 +12064,7 @@ int Rows_log_event::find_row(const Relay_log_info *rli)
*/
prepare_record(table, m_width, FALSE);
- error= unpack_current_row(rli);
+ error= unpack_current_row(rgi);
#ifndef DBUG_OFF
DBUG_PRINT("info",("looking for the following record"));
@@ -10668,8 +12093,14 @@ int Rows_log_event::find_row(const Relay_log_info *rli)
table->s->reclength) == 0);
*/
+ int error;
DBUG_PRINT("info",("locating record using primary key (position)"));
- int error= table->file->ha_rnd_pos_by_record(table->record[0]);
+
+ if (!table->file->inited &&
+ (error= table->file->ha_rnd_init_with_error(0)))
+ DBUG_RETURN(error);
+
+ error= table->file->ha_rnd_pos_by_record(table->record[0]);
if (error)
{
DBUG_PRINT("info",("rnd_pos returns error %d",error));
@@ -10784,7 +12215,7 @@ int Rows_log_event::find_row(const Relay_log_info *rli)
field in the BI image that is null and part of UNNI.
*/
bool null_found= FALSE;
- for (uint i=0; i < keyinfo->key_parts && !null_found; i++)
+ for (uint i=0; i < keyinfo->user_defined_key_parts && !null_found; i++)
{
uint fieldnr= keyinfo->key_part[i].fieldnr - 1;
Field **f= table->field+fieldnr;
@@ -10903,8 +12334,8 @@ int Rows_log_event::find_row(const Relay_log_info *rli)
end:
if (is_table_scan || is_index_scan)
- issue_long_find_row_warning(get_type_code(), m_table->alias.c_ptr(),
- is_index_scan, rli);
+ issue_long_find_row_warning(get_general_type_code(), m_table->alias.c_ptr(),
+ is_index_scan, rgi);
table->default_column_bitmaps();
DBUG_RETURN(error);
}
@@ -10919,7 +12350,7 @@ end:
Delete_rows_log_event::Delete_rows_log_event(THD *thd_arg, TABLE *tbl_arg,
ulong tid, MY_BITMAP const *cols,
bool is_transactional)
- : Rows_log_event(thd_arg, tbl_arg, tid, cols, is_transactional)
+ : Rows_log_event(thd_arg, tbl_arg, tid, cols, is_transactional, DELETE_ROWS_EVENT_V1)
{
}
#endif /* #if !defined(MYSQL_CLIENT) */
@@ -10931,7 +12362,7 @@ Delete_rows_log_event::Delete_rows_log_event(THD *thd_arg, TABLE *tbl_arg,
Delete_rows_log_event::Delete_rows_log_event(const char *buf, uint event_len,
const Format_description_log_event
*description_event)
- : Rows_log_event(buf, event_len, DELETE_ROWS_EVENT, description_event)
+ : Rows_log_event(buf, event_len, description_event)
{
}
#endif
@@ -10955,6 +12386,10 @@ Delete_rows_log_event::do_before_row_operations(const Slave_reporting_capability
*/
return 0;
}
+#ifdef RBR_TRIGGERS
+ if (slave_run_triggers_for_rbr && !master_had_triggers)
+ m_table->prepare_triggers_for_delete_stmt_or_event();
+#endif //RBR_TRIGGERS
return find_key();
}
@@ -10972,9 +12407,11 @@ Delete_rows_log_event::do_after_row_operations(const Slave_reporting_capability
return error;
}
-int Delete_rows_log_event::do_exec_row(const Relay_log_info *const rli)
+int Delete_rows_log_event::do_exec_row(rpl_group_info *rgi)
{
int error;
+ const bool invoke_triggers=
+ slave_run_triggers_for_rbr && !master_had_triggers && m_table->triggers;
DBUG_ASSERT(m_table != NULL);
#ifdef WITH_WSREP
@@ -10989,7 +12426,7 @@ int Delete_rows_log_event::do_exec_row(const Relay_log_info *const rli)
thd_proc_info(thd,"Delete_rows_log_event::find_row()") : NULL;
#endif /* WSREP_PROC_INFO */
#endif /* WITH_WSREP */
- if (!(error= find_row(rli)))
+ if (!(error= find_row(rgi)))
{
/*
Delete the record found, located in record[0]
@@ -11004,7 +12441,14 @@ int Delete_rows_log_event::do_exec_row(const Relay_log_info *const rli)
if (WSREP(thd)) thd_proc_info(thd,"Delete_rows_log_event::ha_delete_row()");
#endif /* WSREP_PROC_INFO */
#endif /* WITH_WSREP */
- error= m_table->file->ha_delete_row(m_table->record[0]);
+ if (invoke_triggers &&
+ process_triggers(TRG_EVENT_DELETE, TRG_ACTION_BEFORE, FALSE))
+ error= HA_ERR_GENERIC; // in case if error is not set yet
+ if (!error)
+ error= m_table->file->ha_delete_row(m_table->record[0]);
+ if (invoke_triggers && !error &&
+ process_triggers(TRG_EVENT_DELETE, TRG_ACTION_AFTER, FALSE))
+ error= HA_ERR_GENERIC; // in case if error is not set yet
m_table->file->ha_index_or_rnd_end();
}
#ifdef WITH_WSREP
@@ -11024,6 +12468,13 @@ void Delete_rows_log_event::print(FILE *file,
#endif
+#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
+uint8 Delete_rows_log_event::get_trg_event_map()
+{
+ return static_cast<uint8> (1 << static_cast<int>(TRG_EVENT_DELETE));
+}
+#endif
+
/**************************************************************************
Update_rows_log_event member functions
**************************************************************************/
@@ -11037,7 +12488,7 @@ Update_rows_log_event::Update_rows_log_event(THD *thd_arg, TABLE *tbl_arg,
MY_BITMAP const *cols_bi,
MY_BITMAP const *cols_ai,
bool is_transactional)
-: Rows_log_event(thd_arg, tbl_arg, tid, cols_bi, is_transactional)
+: Rows_log_event(thd_arg, tbl_arg, tid, cols_bi, is_transactional, UPDATE_ROWS_EVENT_V1)
{
init(cols_ai);
}
@@ -11046,15 +12497,15 @@ Update_rows_log_event::Update_rows_log_event(THD *thd_arg, TABLE *tbl_arg,
ulong tid,
MY_BITMAP const *cols,
bool is_transactional)
-: Rows_log_event(thd_arg, tbl_arg, tid, cols, is_transactional)
+: Rows_log_event(thd_arg, tbl_arg, tid, cols, is_transactional, UPDATE_ROWS_EVENT_V1)
{
init(cols);
}
void Update_rows_log_event::init(MY_BITMAP const *cols)
{
- /* if bitmap_init fails, caught in is_valid() */
- if (likely(!bitmap_init(&m_cols_ai,
+ /* if my_bitmap_init fails, caught in is_valid() */
+ if (likely(!my_bitmap_init(&m_cols_ai,
m_width <= sizeof(m_bitbuf_ai)*8 ? m_bitbuf_ai : NULL,
m_width,
false)))
@@ -11073,8 +12524,8 @@ void Update_rows_log_event::init(MY_BITMAP const *cols)
Update_rows_log_event::~Update_rows_log_event()
{
if (m_cols_ai.bitmap == m_bitbuf_ai) // no my_malloc happened
- m_cols_ai.bitmap= 0; // so no my_free in bitmap_free
- bitmap_free(&m_cols_ai); // To pair with bitmap_init().
+ m_cols_ai.bitmap= 0; // so no my_free in my_bitmap_free
+ my_bitmap_free(&m_cols_ai); // To pair with my_bitmap_init().
}
@@ -11086,7 +12537,7 @@ Update_rows_log_event::Update_rows_log_event(const char *buf, uint event_len,
const
Format_description_log_event
*description_event)
- : Rows_log_event(buf, event_len, UPDATE_ROWS_EVENT, description_event)
+ : Rows_log_event(buf, event_len, description_event)
{
}
#endif
@@ -11106,7 +12557,8 @@ Update_rows_log_event::do_before_row_operations(const Slave_reporting_capability
if ((err= find_key()))
return err;
- m_table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
+ if (slave_run_triggers_for_rbr && !master_had_triggers)
+ m_table->prepare_triggers_for_update_stmt_or_event();
return 0;
}
@@ -11125,8 +12577,10 @@ Update_rows_log_event::do_after_row_operations(const Slave_reporting_capability
}
int
-Update_rows_log_event::do_exec_row(const Relay_log_info *const rli)
+Update_rows_log_event::do_exec_row(rpl_group_info *rgi)
{
+ const bool invoke_triggers=
+ slave_run_triggers_for_rbr && !master_had_triggers && m_table->triggers;
DBUG_ASSERT(m_table != NULL);
#ifdef WITH_WSREP
@@ -11141,7 +12595,7 @@ Update_rows_log_event::do_exec_row(const Relay_log_info *const rli)
thd_proc_info(thd,"Update_rows_log_event::find_row()") : NULL;
#endif /* WSREP_PROC_INFO */
#endif /* WITH_WSREP */
- int error= find_row(rli);
+ int error= find_row(rgi);
if (error)
{
/*
@@ -11149,7 +12603,7 @@ Update_rows_log_event::do_exec_row(const Relay_log_info *const rli)
able to skip to the next pair of updates
*/
m_curr_row= m_curr_row_end;
- unpack_current_row(rli);
+ unpack_current_row(rgi);
return error;
}
@@ -11179,7 +12633,7 @@ Update_rows_log_event::do_exec_row(const Relay_log_info *const rli)
#endif /* WSREP_PROC_INFO */
#endif /* WITH_WSREP */
/* this also updates m_curr_row_end */
- if ((error= unpack_current_row(rli)))
+ if ((error= unpack_current_row(rgi)))
goto err;
/*
@@ -11206,10 +12660,22 @@ Update_rows_log_event::do_exec_row(const Relay_log_info *const rli)
if (WSREP(thd)) thd_proc_info(thd,"Update_rows_log_event::ha_update_row()");
#endif /* WSREP_PROC_INFO */
#endif /* WITH_WSREP */
+
+ if (invoke_triggers &&
+ process_triggers(TRG_EVENT_UPDATE, TRG_ACTION_BEFORE, TRUE))
+ {
+ error= HA_ERR_GENERIC; // in case if error is not set yet
+ goto err;
+ }
+
error= m_table->file->ha_update_row(m_table->record[1], m_table->record[0]);
if (error == HA_ERR_RECORD_IS_THE_SAME)
error= 0;
+ if (invoke_triggers && !error &&
+ process_triggers(TRG_EVENT_UPDATE, TRG_ACTION_AFTER, TRUE))
+ error= HA_ERR_GENERIC; // in case if error is not set yet
+
#ifdef WITH_WSREP
if (WSREP(thd)) thd_proc_info(thd, tmp);
#endif /* WITH_WSREP */
@@ -11228,6 +12694,12 @@ void Update_rows_log_event::print(FILE *file,
}
#endif
+#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
+uint8 Update_rows_log_event::get_trg_event_map()
+{
+ return static_cast<uint8> (1 << static_cast<int>(TRG_EVENT_UPDATE));
+}
+#endif
Incident_log_event::Incident_log_event(const char *buf, uint event_len,
const Format_description_log_event *descr_event)
@@ -11242,6 +12714,8 @@ Incident_log_event::Incident_log_event(const char *buf, uint event_len,
DBUG_PRINT("info",("event_len: %u; common_header_len: %d; post_header_len: %d",
event_len, common_header_len, post_header_len));
+ m_message.str= NULL;
+ m_message.length= 0;
int incident_number= uint2korr(buf + common_header_len);
if (incident_number >= INCIDENT_COUNT ||
incident_number <= INCIDENT_NONE)
@@ -11258,7 +12732,13 @@ Incident_log_event::Incident_log_event(const char *buf, uint event_len,
uint8 len= 0; // Assignment to keep compiler happy
const char *str= NULL; // Assignment to keep compiler happy
read_str(&ptr, str_end, &str, &len);
- m_message.str= const_cast<char*>(str);
+ if (!(m_message.str= (char*) my_malloc(len+1, MYF(MY_WME))))
+ {
+ /* Mark this event invalid */
+ m_incident= INCIDENT_NONE;
+ DBUG_VOID_RETURN;
+ }
+ strmake(m_message.str, str, len);
m_message.length= len;
DBUG_PRINT("info", ("m_incident: %d", m_incident));
DBUG_VOID_RETURN;
@@ -11267,6 +12747,8 @@ Incident_log_event::Incident_log_event(const char *buf, uint event_len,
Incident_log_event::~Incident_log_event()
{
+ if (m_message.str)
+ my_free(m_message.str);
}
@@ -11297,6 +12779,47 @@ void Incident_log_event::pack_info(THD *thd, Protocol *protocol)
protocol->store(buf, bytes, &my_charset_bin);
}
#endif
+#if WITH_WSREP && !defined(MYSQL_CLIENT)
+/*
+ read the first event from (*buf). The size of the (*buf) is (*buf_len).
+ At the end (*buf) is shitfed to point to the following event or NULL and
+ (*buf_len) will be changed to account just being read bytes of the 1st event.
+*/
+#define WSREP_MAX_ALLOWED_PACKET 1024*1024*1024 // current protocol max
+
+Log_event* wsrep_read_log_event(
+ char **arg_buf, size_t *arg_buf_len,
+ const Format_description_log_event *description_event)
+{
+ DBUG_ENTER("wsrep_read_log_event");
+ char *head= (*arg_buf);
+
+ uint data_len = uint4korr(head + EVENT_LEN_OFFSET);
+ char *buf= (*arg_buf);
+ const char *error= 0;
+ Log_event *res= 0;
+
+ if (data_len > WSREP_MAX_ALLOWED_PACKET)
+ {
+ error = "Event too big";
+ goto err;
+ }
+
+ res= Log_event::read_log_event(buf, data_len, &error, description_event, false);
+
+err:
+ if (!res)
+ {
+ DBUG_ASSERT(error != 0);
+ sql_print_error("Error in Log_event::read_log_event(): "
+ "'%s', data_len: %d, event_type: %d",
+ error,data_len,head[EVENT_TYPE_OFFSET]);
+ }
+ (*arg_buf)+= data_len;
+ (*arg_buf_len)-= data_len;
+ DBUG_RETURN(res);
+}
+#endif
#ifdef MYSQL_CLIENT
@@ -11315,10 +12838,18 @@ Incident_log_event::print(FILE *file,
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
int
-Incident_log_event::do_apply_event(Relay_log_info const *rli)
+Incident_log_event::do_apply_event(rpl_group_info *rgi)
{
+ Relay_log_info const *rli= rgi->rli;
DBUG_ENTER("Incident_log_event::do_apply_event");
- rli->report(ERROR_LEVEL, ER_SLAVE_INCIDENT,
+
+ if (ignored_error_code(ER_SLAVE_INCIDENT))
+ {
+ DBUG_PRINT("info", ("Ignoring Incident"));
+ DBUG_RETURN(0);
+ }
+
+ rli->report(ERROR_LEVEL, ER_SLAVE_INCIDENT, NULL,
ER(ER_SLAVE_INCIDENT),
description(),
m_message.length > 0 ? m_message.str : "<none>");
@@ -11356,6 +12887,52 @@ Incident_log_event::write_data_body(IO_CACHE *file)
}
+Ignorable_log_event::Ignorable_log_event(const char *buf,
+ const Format_description_log_event
+ *descr_event,
+ const char *event_name)
+ :Log_event(buf, descr_event), number((int) (uchar) buf[EVENT_TYPE_OFFSET]),
+ description(event_name)
+{
+ DBUG_ENTER("Ignorable_log_event::Ignorable_log_event");
+ DBUG_VOID_RETURN;
+}
+
+Ignorable_log_event::~Ignorable_log_event()
+{
+}
+
+#ifndef MYSQL_CLIENT
+/* Pack info for its unrecognized ignorable event */
+void Ignorable_log_event::pack_info(THD *thd, Protocol *protocol)
+{
+ char buf[256];
+ size_t bytes;
+ bytes= my_snprintf(buf, sizeof(buf), "# Ignorable event type %d (%s)",
+ number, description);
+ protocol->store(buf, bytes, &my_charset_bin);
+}
+#endif
+
+#ifdef MYSQL_CLIENT
+/* Print for its unrecognized ignorable event */
+void
+Ignorable_log_event::print(FILE *file,
+ PRINT_EVENT_INFO *print_event_info)
+{
+ if (print_event_info->short_form)
+ return;
+
+ print_header(&print_event_info->head_cache, print_event_info, FALSE);
+ my_b_printf(&print_event_info->head_cache, "\tIgnorable\n");
+ my_b_printf(&print_event_info->head_cache,
+ "# Ignorable event type %d (%s)\n", number, description);
+ copy_event_cache_to_file_and_reinit(&print_event_info->head_cache,
+ file);
+}
+#endif
+
+
#ifdef MYSQL_CLIENT
/**
The default values for these variables should be values that are
@@ -11367,7 +12944,9 @@ st_print_event_info::st_print_event_info()
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), skip_replication(0),
+ thread_id(0), thread_id_printed(false), server_id(0),
+ server_id_printed(false), domain_id(0), domain_id_printed(false),
+ skip_replication(0),
base64_output_mode(BASE64_OUTPUT_UNSPEC), printed_fd_event(FALSE)
{
/*
@@ -11386,7 +12965,6 @@ st_print_event_info::st_print_event_info()
}
#endif
-
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
Heartbeat_log_event::Heartbeat_log_event(const char* buf, uint event_len,
const Format_description_log_event* description_event)
@@ -11406,6 +12984,9 @@ Heartbeat_log_event::Heartbeat_log_event(const char* buf, uint event_len,
There is a dummy replacement for this in the embedded library that returns
FALSE; this is used by XtraDB to allow it to access replication stuff while
still being able to use the same plugin in both stand-alone and embedded.
+
+ In this function it's ok to use active_mi, as this is only called for
+ the main replication server.
*/
bool rpl_get_position_info(const char **log_file_name, ulonglong *log_pos,
const char **group_relay_log_name,
@@ -11415,12 +12996,43 @@ bool rpl_get_position_info(const char **log_file_name, ulonglong *log_pos,
return FALSE;
#else
const Relay_log_info *rli= &(active_mi->rli);
- *log_file_name= rli->group_master_log_name;
- *log_pos= rli->group_master_log_pos +
- (rli->future_event_relay_log_pos - rli->group_relay_log_pos);
- *group_relay_log_name= rli->group_relay_log_name;
- *relay_log_pos= rli->future_event_relay_log_pos;
+ if (opt_slave_parallel_threads == 0)
+ {
+ *log_file_name= rli->group_master_log_name;
+ *log_pos= rli->group_master_log_pos +
+ (rli->future_event_relay_log_pos - rli->group_relay_log_pos);
+ *group_relay_log_name= rli->group_relay_log_name;
+ *relay_log_pos= rli->future_event_relay_log_pos;
+ }
+ else
+ {
+ *log_file_name= "";
+ *log_pos= 0;
+ *group_relay_log_name= "";
+ *relay_log_pos= 0;
+ }
return TRUE;
#endif
}
+
+/**
+ Check if we should write event to the relay log
+
+ This is used to skip events that is only supported by MySQL
+
+ Return:
+ 0 ok
+ 1 Don't write event
+*/
+
+bool event_that_should_be_ignored(const char *buf)
+{
+ uint event_type= (uchar)buf[EVENT_TYPE_OFFSET];
+ if (event_type == GTID_LOG_EVENT ||
+ event_type == ANONYMOUS_GTID_LOG_EVENT ||
+ event_type == PREVIOUS_GTIDS_LOG_EVENT ||
+ (uint2korr(buf + FLAGS_OFFSET) & LOG_EVENT_IGNORABLE_F))
+ return 1;
+ return 0;
+}
#endif
diff --git a/sql/log_event.h b/sql/log_event.h
index 4ae01323b4b..8661e6e49e5 100644
--- a/sql/log_event.h
+++ b/sql/log_event.h
@@ -1,4 +1,5 @@
/* Copyright (c) 2000, 2014, Oracle and/or its affiliates.
+ Copyright (c) 2009, 2014, Monty Program Ab.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -48,6 +49,8 @@
#include "sql_class.h" /* THD */
#endif
+#include "rpl_gtid.h"
+
/* Forward declarations */
class String;
@@ -251,13 +254,18 @@ struct sql_ex_info
#define FORMAT_DESCRIPTION_HEADER_LEN (START_V3_HEADER_LEN+1+LOG_EVENT_TYPES)
#define XID_HEADER_LEN 0
#define BEGIN_LOAD_QUERY_HEADER_LEN APPEND_BLOCK_HEADER_LEN
-#define ROWS_HEADER_LEN 8
+#define ROWS_HEADER_LEN_V1 8
#define TABLE_MAP_HEADER_LEN 8
#define EXECUTE_LOAD_QUERY_EXTRA_HEADER_LEN (4 + 4 + 4 + 1)
#define EXECUTE_LOAD_QUERY_HEADER_LEN (QUERY_HEADER_LEN + EXECUTE_LOAD_QUERY_EXTRA_HEADER_LEN)
#define INCIDENT_HEADER_LEN 2
#define HEARTBEAT_HEADER_LEN 0
+#define IGNORABLE_HEADER_LEN 0
+#define ROWS_HEADER_LEN_V2 10
#define ANNOTATE_ROWS_HEADER_LEN 0
+#define BINLOG_CHECKPOINT_HEADER_LEN 4
+#define GTID_HEADER_LEN 19
+#define GTID_LIST_HEADER_LEN 4
/*
Max number of possible extra bytes in a replication event compared to a
@@ -288,7 +296,7 @@ struct sql_ex_info
to the slave. It is used to increase the thd(max_allowed) for both the
DUMP thread on the master and the SQL/IO thread on the slave.
*/
-#define MAX_MAX_ALLOWED_PACKET 1024*1024*1024
+#define MAX_MAX_ALLOWED_PACKET (1024*1024*1024)
/*
Event header offsets;
@@ -409,6 +417,9 @@ struct sql_ex_info
/* RW = "RoWs" */
#define RW_MAPID_OFFSET 0
#define RW_FLAGS_OFFSET 6
+#define RW_VHLEN_OFFSET 8
+#define RW_V_TAG_LEN 1
+#define RW_V_EXTRAINFO_TAG 0
/* ELQ = "Execute Load Query" */
#define ELQ_FILE_ID_OFFSET QUERY_HEADER_LEN
@@ -510,6 +521,17 @@ struct sql_ex_info
#define LOG_EVENT_RELAY_LOG_F 0x40
/**
+ @def LOG_EVENT_IGNORABLE_F
+
+ For an event, 'e', carrying a type code, that a slave,
+ 's', does not recognize, 's' will check 'e' for
+ LOG_EVENT_IGNORABLE_F, and if the flag is set, then 'e'
+ is ignored. Otherwise, 's' acknowledges that it has
+ found an unknown event in the relay log.
+*/
+#define LOG_EVENT_IGNORABLE_F 0x80
+
+/**
@def LOG_EVENT_SKIP_REPLICATION_F
Flag set by application creating the event (with @@skip_replication); the
@@ -549,7 +571,7 @@ struct sql_ex_info
/* Shouldn't be defined before */
#define EXPECTED_OPTIONS \
- ((ULL(1) << 14) | (ULL(1) << 26) | (ULL(1) << 27) | (ULL(1) << 19))
+ ((1ULL << 14) | (1ULL << 26) | (1ULL << 27) | (1ULL << 19))
#if OPTIONS_WRITTEN_TO_BIN_LOG != EXPECTED_OPTIONS
#error OPTIONS_WRITTEN_TO_BIN_LOG must NOT change their values!
@@ -572,6 +594,40 @@ enum enum_binlog_checksum_alg {
#define BINLOG_CHECKSUM_LEN CHECKSUM_CRC32_SIGNATURE_LEN
#define BINLOG_CHECKSUM_ALG_DESC_LEN 1 /* 1 byte checksum alg descriptor */
+/*
+ These are capability numbers for MariaDB slave servers.
+
+ Newer MariaDB slaves set this to inform the master about their capabilities.
+ This allows the master to decide which events it can send to the slave
+ without breaking replication on old slaves that maybe do not understand
+ all events from newer masters.
+
+ As new releases are backwards compatible, a given capability implies also
+ all capabilities with smaller number.
+
+ Older MariaDB slaves and other MySQL slave servers do not set this, so they
+ are recorded with capability 0.
+*/
+
+/* MySQL or old MariaDB slave with no announced capability. */
+#define MARIA_SLAVE_CAPABILITY_UNKNOWN 0
+/* MariaDB >= 5.3, which understands ANNOTATE_ROWS_EVENT. */
+#define MARIA_SLAVE_CAPABILITY_ANNOTATE 1
+/*
+ MariaDB >= 5.5. This version has the capability to tolerate events omitted
+ from the binlog stream without breaking replication (MySQL slaves fail
+ because they mis-compute the offsets into the master's binlog).
+*/
+#define MARIA_SLAVE_CAPABILITY_TOLERATE_HOLES 2
+/* MariaDB >= 10.0, which knows about binlog_checkpoint_log_event. */
+#define MARIA_SLAVE_CAPABILITY_BINLOG_CHECKPOINT 3
+/* MariaDB >= 10.0.1, which knows about global transaction id events. */
+#define MARIA_SLAVE_CAPABILITY_GTID 4
+
+/* Our capability. */
+#define MARIA_SLAVE_CAPABILITY_MINE MARIA_SLAVE_CAPABILITY_GTID
+
+
/**
@enum Log_event_type
@@ -619,11 +675,12 @@ enum Log_event_type
PRE_GA_DELETE_ROWS_EVENT = 22,
/*
- These event numbers are used from 5.1.16 and forward
+ These event numbers are used from 5.1.16 until mysql-5.6.6,
+ and in MariaDB
*/
- WRITE_ROWS_EVENT = 23,
- UPDATE_ROWS_EVENT = 24,
- DELETE_ROWS_EVENT = 25,
+ WRITE_ROWS_EVENT_V1 = 23,
+ UPDATE_ROWS_EVENT_V1 = 24,
+ DELETE_ROWS_EVENT_V1 = 25,
/*
Something out of the ordinary happened on the master
@@ -637,6 +694,27 @@ enum Log_event_type
HEARTBEAT_LOG_EVENT= 27,
/*
+ In some situations, it is necessary to send over ignorable
+ data to the slave: data that a slave can handle in case there
+ is code for handling it, but which can be ignored if it is not
+ recognized.
+
+ These mysql-5.6 events are not recognized (and ignored) by MariaDB
+ */
+ IGNORABLE_LOG_EVENT= 28,
+ ROWS_QUERY_LOG_EVENT= 29,
+
+ /* Version 2 of the Row events, generated only by mysql-5.6.6+ */
+ WRITE_ROWS_EVENT = 30,
+ UPDATE_ROWS_EVENT = 31,
+ DELETE_ROWS_EVENT = 32,
+
+ /* MySQL 5.6 GTID events, ignored by MariaDB */
+ GTID_LOG_EVENT= 33,
+ ANONYMOUS_GTID_LOG_EVENT= 34,
+ PREVIOUS_GTIDS_LOG_EVENT= 35,
+
+ /*
Add new events here - right above this comment!
Existing events (except ENUM_END_EVENT) should never change their numbers
*/
@@ -647,6 +725,26 @@ enum Log_event_type
MARIA_EVENTS_BEGIN= 160,
/* New Maria event numbers start from here */
ANNOTATE_ROWS_EVENT= 160,
+ /*
+ Binlog checkpoint event. Used for XA crash recovery on the master, not used
+ in replication.
+ A binlog checkpoint event specifies a binlog file such that XA crash
+ recovery can start from that file - and it is guaranteed to find all XIDs
+ that are prepared in storage engines but not yet committed.
+ */
+ BINLOG_CHECKPOINT_EVENT= 161,
+ /*
+ Gtid event. For global transaction ID, used to start a new event group,
+ instead of the old BEGIN query event, and also to mark stand-alone
+ events.
+ */
+ GTID_EVENT= 162,
+ /*
+ Gtid list event. Logged at the start of every binlog, to record the
+ current replication state. This consists of the last GTID seen for
+ each replication domain.
+ */
+ GTID_LIST_EVENT= 163,
/* Add new MariaDB events here - right above this comment! */
@@ -719,6 +817,11 @@ typedef struct st_print_event_info
uint charset_database_number;
uint thread_id;
bool thread_id_printed;
+ uint32 server_id;
+ bool server_id_printed;
+ uint32 domain_id;
+ bool domain_id_printed;
+
/*
Track when @@skip_replication changes so we need to output a SET
statement for it.
@@ -1187,6 +1290,7 @@ public:
#endif
virtual Log_event_type get_type_code() = 0;
virtual bool is_valid() const = 0;
+ virtual my_off_t get_header_len(my_off_t len) { return len; }
void set_artificial_event() { flags |= LOG_EVENT_ARTIFICIAL_F; }
void set_relay_log_event() { flags |= LOG_EVENT_RELAY_LOG_F; }
bool is_artificial_event() const { return flags & LOG_EVENT_ARTIFICIAL_F; }
@@ -1251,9 +1355,9 @@ public:
@see do_apply_event
*/
- int apply_event(Relay_log_info const *rli)
+ int apply_event(rpl_group_info *rgi)
{
- return do_apply_event(rli);
+ return do_apply_event(rgi);
}
@@ -1265,9 +1369,9 @@ public:
@see do_update_pos
*/
- int update_pos(Relay_log_info *rli)
+ int update_pos(rpl_group_info *rgi)
{
- return do_update_pos(rli);
+ return do_update_pos(rgi);
}
/**
@@ -1276,11 +1380,66 @@ public:
@see do_shall_skip
*/
- enum_skip_reason shall_skip(Relay_log_info *rli)
+ enum_skip_reason shall_skip(rpl_group_info *rgi)
+ {
+ return do_shall_skip(rgi);
+ }
+
+
+ /*
+ Check if an event is non-final part of a stand-alone event group,
+ such as Intvar_log_event (such events should be processed as part
+ of the following event group, not individually).
+ See also is_part_of_group()
+ */
+ static bool is_part_of_group(enum Log_event_type ev_type)
{
- return do_shall_skip(rli);
+ switch (ev_type)
+ {
+ case GTID_EVENT:
+ case INTVAR_EVENT:
+ case RAND_EVENT:
+ case USER_VAR_EVENT:
+ case TABLE_MAP_EVENT:
+ case ANNOTATE_ROWS_EVENT:
+ return true;
+ case DELETE_ROWS_EVENT:
+ case UPDATE_ROWS_EVENT:
+ case WRITE_ROWS_EVENT:
+ /*
+ ToDo: also check for non-final Rows_log_event (though such events
+ are usually in a BEGIN-COMMIT group).
+ */
+ default:
+ return false;
+ }
}
+ /*
+ Same as above, but works on the object. In addition this is true for all
+ rows event except the last one.
+ */
+ virtual bool is_part_of_group() { return 0; }
+ static bool is_group_event(enum Log_event_type ev_type)
+ {
+ switch (ev_type)
+ {
+ case START_EVENT_V3:
+ case STOP_EVENT:
+ case ROTATE_EVENT:
+ case SLAVE_EVENT:
+ case FORMAT_DESCRIPTION_EVENT:
+ case INCIDENT_EVENT:
+ case HEARTBEAT_LOG_EVENT:
+ case BINLOG_CHECKPOINT_EVENT:
+ case GTID_LIST_EVENT:
+ return false;
+
+ default:
+ return true;
+ }
+ }
+
protected:
/**
@@ -1293,14 +1452,14 @@ protected:
A typical usage is:
@code
- enum_skip_reason do_shall_skip(Relay_log_info *rli) {
- return continue_group(rli);
+ enum_skip_reason do_shall_skip(rpl_group_info *rgi) {
+ return continue_group(rgi);
}
@endcode
@return Skip reason
*/
- enum_skip_reason continue_group(Relay_log_info *rli);
+ enum_skip_reason continue_group(rpl_group_info *rgi);
/**
Primitive to apply an event to the database.
@@ -1317,7 +1476,7 @@ protected:
@retval 0 Event applied successfully
@retval errno Error code if event application failed
*/
- virtual int do_apply_event(Relay_log_info const *rli)
+ virtual int do_apply_event(rpl_group_info *rgi)
{
return 0; /* Default implementation does nothing */
}
@@ -1346,7 +1505,7 @@ protected:
1). Observe that handler errors are returned by the
do_apply_event() function, and not by this one.
*/
- virtual int do_update_pos(Relay_log_info *rli);
+ virtual int do_update_pos(rpl_group_info *rgi);
/**
@@ -1378,7 +1537,7 @@ protected:
The event shall be skipped because the slave skip counter was
non-zero. The caller shall decrease the counter by one.
*/
- virtual enum_skip_reason do_shall_skip(Relay_log_info *rli);
+ virtual enum_skip_reason do_shall_skip(rpl_group_info *rgi);
#endif
};
@@ -1853,6 +2012,8 @@ public:
my_free(data_buf);
}
Log_event_type get_type_code() { return QUERY_EVENT; }
+ static int dummy_event(String *packet, ulong ev_offset, uint8 checksum_alg);
+ static int begin_event(String *packet, ulong ev_offset, uint8 checksum_alg);
#ifdef MYSQL_SERVER
bool write(IO_CACHE* file);
virtual bool write_post_header_for_derived(IO_CACHE* file) { return FALSE; }
@@ -1860,7 +2021,7 @@ public:
bool is_valid() const { return query != 0; }
/*
- Returns number of bytes additionaly written to post header by derived
+ Returns number of bytes additionally written to post header by derived
events (so far it is only Execute_load_query event).
*/
virtual ulong get_post_header_size_for_derived() { return 0; }
@@ -1868,13 +2029,15 @@ public:
public: /* !!! Public in this patch to allow old usage */
#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
- virtual enum_skip_reason do_shall_skip(Relay_log_info *rli);
- virtual int do_apply_event(Relay_log_info const *rli);
- virtual int do_update_pos(Relay_log_info *rli);
+ virtual enum_skip_reason do_shall_skip(rpl_group_info *rgi);
+ virtual int do_apply_event(rpl_group_info *rgi);
+ virtual int do_update_pos(rpl_group_info *rgi);
- int do_apply_event(Relay_log_info const *rli,
+ int do_apply_event(rpl_group_info *rgi,
const char *query_arg,
uint32 q_len_arg);
+ static bool peek_is_commit_rollback(const char *event_start,
+ size_t event_len, uint8 checksum_alg);
#endif /* HAVE_REPLICATION */
/*
If true, the event always be applied by slave SQL thread or be printed by
@@ -1898,6 +2061,9 @@ public: /* !!! Public in this patch to allow old usage */
!strncasecmp(query, "SAVEPOINT", 9) ||
!strncasecmp(query, "ROLLBACK", 8);
}
+ bool is_begin() { return !strcmp(query, "BEGIN"); }
+ bool is_commit() { return !strcmp(query, "COMMIT"); }
+ bool is_rollback() { return !strcmp(query, "ROLLBACK"); }
};
@@ -1984,7 +2150,7 @@ public:
private:
#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
- virtual int do_apply_event(Relay_log_info const* rli);
+ virtual int do_apply_event(rpl_group_info *rgi);
#endif
};
@@ -2297,12 +2463,12 @@ public:
public: /* !!! Public in this patch to allow old usage */
#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
- virtual int do_apply_event(Relay_log_info const* rli)
+ virtual int do_apply_event(rpl_group_info *rgi)
{
- return do_apply_event(thd->slave_net,rli,0);
+ return do_apply_event(thd->slave_net,rgi,0);
}
- int do_apply_event(NET *net, Relay_log_info const *rli,
+ int do_apply_event(NET *net, rpl_group_info *rgi,
bool use_rli_only_for_errors);
#endif
};
@@ -2370,6 +2536,8 @@ public:
const Format_description_log_event* description_event);
~Start_log_event_v3() {}
Log_event_type get_type_code() { return START_EVENT_V3;}
+ my_off_t get_header_len(my_off_t l __attribute__((unused)))
+ { return LOG_EVENT_MINIMAL_HEADER_LEN; }
#ifdef MYSQL_SERVER
bool write(IO_CACHE* file);
#endif
@@ -2381,14 +2549,14 @@ public:
protected:
#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
- virtual int do_apply_event(Relay_log_info const *rli);
- virtual enum_skip_reason do_shall_skip(Relay_log_info*)
+ virtual int do_apply_event(rpl_group_info *rgi);
+ virtual enum_skip_reason do_shall_skip(rpl_group_info*)
{
/*
Events from ourself should be skipped, but they should not
decrease the slave skip counter.
*/
- if (this->server_id == ::server_id)
+ if (this->server_id == global_system_variables.server_id)
return Log_event::EVENT_SKIP_IGNORE;
else
return Log_event::EVENT_SKIP_NOT;
@@ -2477,9 +2645,9 @@ public:
static bool is_version_before_checksum(const master_version_split *version_split);
protected:
#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
- virtual int do_apply_event(Relay_log_info const *rli);
- virtual int do_update_pos(Relay_log_info *rli);
- virtual enum_skip_reason do_shall_skip(Relay_log_info *rli);
+ virtual int do_apply_event(rpl_group_info *rgi);
+ virtual int do_update_pos(rpl_group_info *rgi);
+ virtual enum_skip_reason do_shall_skip(rpl_group_info *rgi);
#endif
};
@@ -2553,12 +2721,13 @@ Intvar_log_event(THD* thd_arg,uchar type_arg, ulonglong val_arg,
bool write(IO_CACHE* file);
#endif
bool is_valid() const { return 1; }
+ bool is_part_of_group() { return 1; }
private:
#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
- virtual int do_apply_event(Relay_log_info const *rli);
- virtual int do_update_pos(Relay_log_info *rli);
- virtual enum_skip_reason do_shall_skip(Relay_log_info *rli);
+ virtual int do_apply_event(rpl_group_info *rgi);
+ virtual int do_update_pos(rpl_group_info *rgi);
+ virtual enum_skip_reason do_shall_skip(rpl_group_info *rgi);
#endif
};
@@ -2632,12 +2801,13 @@ class Rand_log_event: public Log_event
bool write(IO_CACHE* file);
#endif
bool is_valid() const { return 1; }
+ bool is_part_of_group() { return 1; }
private:
#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
- virtual int do_apply_event(Relay_log_info const *rli);
- virtual int do_update_pos(Relay_log_info *rli);
- virtual enum_skip_reason do_shall_skip(Relay_log_info *rli);
+ virtual int do_apply_event(rpl_group_info *rgi);
+ virtual int do_update_pos(rpl_group_info *rgi);
+ virtual enum_skip_reason do_shall_skip(rpl_group_info *rgi);
#endif
};
@@ -2684,8 +2854,8 @@ class Xid_log_event: public Log_event
private:
#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
- virtual int do_apply_event(Relay_log_info const *rli);
- enum_skip_reason do_shall_skip(Relay_log_info *rli);
+ virtual int do_apply_event(rpl_group_info *rgi);
+ enum_skip_reason do_shall_skip(rpl_group_info *rgi);
#endif
};
@@ -2753,12 +2923,13 @@ public:
void set_deferred(query_id_t qid) { deferred= true; query_id= qid; }
#endif
bool is_valid() const { return name != 0; }
+ bool is_part_of_group() { return 1; }
private:
#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
- virtual int do_apply_event(Relay_log_info const *rli);
- virtual int do_update_pos(Relay_log_info *rli);
- virtual enum_skip_reason do_shall_skip(Relay_log_info *rli);
+ virtual int do_apply_event(rpl_group_info *rgi);
+ virtual int do_update_pos(rpl_group_info *rgi);
+ virtual enum_skip_reason do_shall_skip(rpl_group_info *rgi);
#endif
};
@@ -2791,14 +2962,14 @@ public:
private:
#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
- virtual int do_update_pos(Relay_log_info *rli);
- virtual enum_skip_reason do_shall_skip(Relay_log_info *rli)
+ virtual int do_update_pos(rpl_group_info *rgi);
+ virtual enum_skip_reason do_shall_skip(rpl_group_info *rgi)
{
/*
Events from ourself should be skipped, but they should not
decrease the slave skip counter.
*/
- if (this->server_id == ::server_id)
+ if (this->server_id == global_system_variables.server_id)
return Log_event::EVENT_SKIP_IGNORE;
else
return Log_event::EVENT_SKIP_NOT;
@@ -2885,6 +3056,8 @@ public:
my_free((void*) new_log_ident);
}
Log_event_type get_type_code() { return ROTATE_EVENT;}
+ my_off_t get_header_len(my_off_t l __attribute__((unused)))
+ { return LOG_EVENT_MINIMAL_HEADER_LEN; }
int get_data_size() { return ident_len + ROTATE_HEADER_LEN;}
bool is_valid() const { return new_log_ident != 0; }
#ifdef MYSQL_SERVER
@@ -2893,9 +3066,262 @@ public:
private:
#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
- virtual int do_update_pos(Relay_log_info *rli);
- virtual enum_skip_reason do_shall_skip(Relay_log_info *rli);
+ virtual int do_update_pos(rpl_group_info *rgi);
+ virtual enum_skip_reason do_shall_skip(rpl_group_info *rgi);
+#endif
+};
+
+
+class Binlog_checkpoint_log_event: public Log_event
+{
+public:
+ char *binlog_file_name;
+ uint binlog_file_len;
+
+#ifdef MYSQL_SERVER
+ Binlog_checkpoint_log_event(const char *binlog_file_name_arg,
+ uint binlog_file_len_arg);
+#ifdef HAVE_REPLICATION
+ void pack_info(THD *thd, Protocol *protocol);
+#endif
+#else
+ void print(FILE *file, PRINT_EVENT_INFO *print_event_info);
+#endif
+ Binlog_checkpoint_log_event(const char *buf, uint event_len,
+ const Format_description_log_event *description_event);
+ ~Binlog_checkpoint_log_event() { my_free(binlog_file_name); }
+ Log_event_type get_type_code() { return BINLOG_CHECKPOINT_EVENT;}
+ int get_data_size() { return binlog_file_len + BINLOG_CHECKPOINT_HEADER_LEN;}
+ bool is_valid() const { return binlog_file_name != 0; }
+#ifdef MYSQL_SERVER
+ bool write(IO_CACHE* file);
+ enum_skip_reason do_shall_skip(rpl_group_info *rgi);
+#endif
+};
+
+
+/**
+ @class Gtid_log_event
+
+ This event is logged as part of every event group to give the global
+ transaction id (GTID) of that group.
+
+ It replaces the BEGIN query event used in earlier versions to begin most
+ event groups, but is also used for events that used to be stand-alone.
+
+ @section Gtid_log_event_binary_format Binary Format
+
+ The binary format for Gtid_log_event has 6 extra reserved bytes to make the
+ length a total of 19 byte (+ 19 bytes of header in common with all events).
+ This is just the minimal size for a BEGIN query event, which makes it easy
+ to replace this event with such BEGIN event to remain compatible with old
+ slave servers.
+
+ <table>
+ <caption>Post-Header</caption>
+
+ <tr>
+ <th>Name</th>
+ <th>Format</th>
+ <th>Description</th>
+ </tr>
+
+ <tr>
+ <td>seq_no</td>
+ <td>8 byte unsigned integer</td>
+ <td>increasing id within one server_id. Starts at 1, holes in the sequence
+ may occur</td>
+ </tr>
+
+ <tr>
+ <td>domain_id</td>
+ <td>4 byte unsigned integer</td>
+ <td>Replication domain id, identifying independent replication streams></td>
+ </tr>
+
+ <tr>
+ <td>flags</td>
+ <td>1 byte bitfield</td>
+ <td>Bit 0 set indicates stand-alone event (no terminating COMMIT)</td>
+ <td>Bit 1 set indicates group commit, and that commit id exists</td>
+ </tr>
+
+ <tr>
+ <td>Reserved (no group commit) / commit id (group commit) (see flags bit 1)</td>
+ <td>6 bytes / 8 bytes</td>
+ <td>Reserved bytes, set to 0. Maybe be used for future expansion (no
+ group commit). OR commit id, same for all GTIDs in the same group
+ commit (see flags bit 1).</td>
+ </tr>
+ </table>
+
+ The Body of Gtid_log_event is empty. The total event size is 19 bytes +
+ the normal 19 bytes common-header.
+*/
+
+class Gtid_log_event: public Log_event
+{
+public:
+ uint64 seq_no;
+ uint64 commit_id;
+ uint32 domain_id;
+ uchar flags2;
+
+ /* Flags2. */
+
+ /* FL_STANDALONE is set when there is no terminating COMMIT event. */
+ static const uchar FL_STANDALONE= 1;
+ /*
+ FL_GROUP_COMMIT_ID is set when event group is part of a group commit on the
+ master. Groups with same commit_id are part of the same group commit.
+ */
+ static const uchar FL_GROUP_COMMIT_ID= 2;
+
+#ifdef MYSQL_SERVER
+ Gtid_log_event(THD *thd_arg, uint64 seq_no, uint32 domain_id, bool standalone,
+ uint16 flags, bool is_transactional, uint64 commit_id);
+#ifdef HAVE_REPLICATION
+ void pack_info(THD *thd, Protocol *protocol);
+ virtual int do_apply_event(rpl_group_info *rgi);
+ virtual int do_update_pos(rpl_group_info *rgi);
+ virtual enum_skip_reason do_shall_skip(rpl_group_info *rgi);
+#endif
+#else
+ void print(FILE *file, PRINT_EVENT_INFO *print_event_info);
+#endif
+ Gtid_log_event(const char *buf, uint event_len,
+ const Format_description_log_event *description_event);
+ ~Gtid_log_event() { }
+ Log_event_type get_type_code() { return GTID_EVENT; }
+ int get_data_size()
+ {
+ return GTID_HEADER_LEN + ((flags2 & FL_GROUP_COMMIT_ID) ? 2 : 0);
+ }
+ bool is_valid() const { return seq_no != 0; }
+#ifdef MYSQL_SERVER
+ bool write(IO_CACHE *file);
+ static int make_compatible_event(String *packet, bool *need_dummy_event,
+ ulong ev_offset, uint8 checksum_alg);
+ static bool peek(const char *event_start, size_t event_len,
+ uint8 checksum_alg,
+ uint32 *domain_id, uint32 *server_id, uint64 *seq_no,
+ uchar *flags2, const Format_description_log_event *fdev);
+#endif
+};
+
+
+/**
+ @class Gtid_list_log_event
+
+ This event is logged at the start of every binlog file to record the
+ current replication state: the last global transaction id (GTID) applied
+ on the server within each replication domain.
+
+ It consists of a list of GTIDs, one for each replication domain ever seen
+ on the server.
+
+ @section Gtid_list_log_event_binary_format Binary Format
+
+ <table>
+ <caption>Post-Header</caption>
+
+ <tr>
+ <th>Name</th>
+ <th>Format</th>
+ <th>Description</th>
+ </tr>
+
+ <tr>
+ <td>count</td>
+ <td>4 byte unsigned integer</td>
+ <td>The lower 28 bits are the number of GTIDs. The upper 4 bits are
+ flags bits.</td>
+ </tr>
+ </table>
+
+ <table>
+ <caption>Body</caption>
+
+ <tr>
+ <th>Name</th>
+ <th>Format</th>
+ <th>Description</th>
+ </tr>
+
+ <tr>
+ <td>domain_id</td>
+ <td>4 byte unsigned integer</td>
+ <td>Replication domain id of one GTID</td>
+ </tr>
+
+ <tr>
+ <td>server_id</td>
+ <td>4 byte unsigned integer</td>
+ <td>Server id of one GTID</td>
+ </tr>
+
+ <tr>
+ <td>seq_no</td>
+ <td>8 byte unsigned integer</td>
+ <td>sequence number of one GTID</td>
+ </tr>
+ </table>
+
+ The three elements in the body repeat COUNT times to form the GTID list.
+
+ At the time of writing, only one flag bit is in use.
+
+ Bit 28 of `count' is used for flag FLAG_UNTIL_REACHED, which is sent in a
+ Gtid_list event from the master to the slave to indicate that the START
+ SLAVE UNTIL master_gtid_pos=xxx condition has been reached. (This flag is
+ only sent in "fake" events generated on the fly, it is not written into
+ the binlog).
+*/
+
+class Gtid_list_log_event: public Log_event
+{
+public:
+ uint32 count;
+ uint32 gl_flags;
+ struct rpl_gtid *list;
+ uint64 *sub_id_list;
+
+ static const uint element_size= 4+4+8;
+ static const uint32 FLAG_UNTIL_REACHED= (1<<28);
+ static const uint32 FLAG_IGN_GTIDS= (1<<29);
+
+#ifdef MYSQL_SERVER
+ Gtid_list_log_event(rpl_binlog_state *gtid_set, uint32 gl_flags);
+ Gtid_list_log_event(slave_connection_state *gtid_set, uint32 gl_flags);
+#ifdef HAVE_REPLICATION
+ void pack_info(THD *thd, Protocol *protocol);
+#endif
+#else
+ void print(FILE *file, PRINT_EVENT_INFO *print_event_info);
+#endif
+ Gtid_list_log_event(const char *buf, uint event_len,
+ const Format_description_log_event *description_event);
+ ~Gtid_list_log_event() { my_free(list); my_free(sub_id_list); }
+ Log_event_type get_type_code() { return GTID_LIST_EVENT; }
+ int get_data_size() {
+ /*
+ Replacing with dummy event, needed for older slaves, requires a minimum
+ of 6 bytes in the body.
+ */
+ return (count==0 ?
+ GTID_LIST_HEADER_LEN+2 : GTID_LIST_HEADER_LEN+count*element_size);
+ }
+ bool is_valid() const { return list != NULL; }
+#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
+ bool to_packet(String *packet);
+ bool write(IO_CACHE *file);
+ virtual int do_apply_event(rpl_group_info *rgi);
+ enum_skip_reason do_shall_skip(rpl_group_info *rgi);
#endif
+ static bool peek(const char *event_start, uint32 event_len,
+ uint8 checksum_alg,
+ rpl_gtid **out_gtid_list, uint32 *out_list_len,
+ const Format_description_log_event *fdev);
};
@@ -2970,7 +3396,7 @@ public:
private:
#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
- virtual int do_apply_event(Relay_log_info const *rli);
+ virtual int do_apply_event(rpl_group_info *rgi);
#endif
};
@@ -3025,7 +3451,7 @@ public:
private:
#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
- virtual int do_apply_event(Relay_log_info const *rli);
+ virtual int do_apply_event(rpl_group_info *rgi);
#endif
};
@@ -3066,7 +3492,7 @@ public:
private:
#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
- virtual int do_apply_event(Relay_log_info const *rli);
+ virtual int do_apply_event(rpl_group_info *rgi);
#endif
};
@@ -3106,7 +3532,7 @@ public:
private:
#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
- virtual int do_apply_event(Relay_log_info const *rli);
+ virtual int do_apply_event(rpl_group_info *rgi);
#endif
};
@@ -3139,7 +3565,7 @@ public:
Log_event_type get_type_code() { return BEGIN_LOAD_QUERY_EVENT; }
private:
#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
- virtual enum_skip_reason do_shall_skip(Relay_log_info *rli);
+ virtual enum_skip_reason do_shall_skip(rpl_group_info *rgi);
#endif
};
@@ -3205,7 +3631,7 @@ public:
private:
#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
- virtual int do_apply_event(Relay_log_info const *rli);
+ virtual int do_apply_event(rpl_group_info *rgi);
#endif
};
@@ -3261,6 +3687,7 @@ public:
virtual int get_data_size();
virtual Log_event_type get_type_code();
virtual bool is_valid() const;
+ virtual bool is_part_of_group() { return 1; }
#ifndef MYSQL_CLIENT
virtual bool write_data_header(IO_CACHE*);
@@ -3277,9 +3704,9 @@ public:
#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
private:
- virtual int do_apply_event(Relay_log_info const*);
- virtual int do_update_pos(Relay_log_info*);
- virtual enum_skip_reason do_shall_skip(Relay_log_info*);
+ virtual int do_apply_event(rpl_group_info *rgi);
+ virtual int do_update_pos(rpl_group_info *rgi);
+ virtual enum_skip_reason do_shall_skip(rpl_group_info*);
#endif
private:
@@ -3642,7 +4069,9 @@ public:
enum
{
TM_NO_FLAGS = 0U,
- TM_BIT_LEN_EXACT_F = (1U << 0)
+ TM_BIT_LEN_EXACT_F = (1U << 0),
+ // MariaDB flags (we starts from the other end)
+ TM_BIT_HAS_TRIGGERS_F= (1U << 14)
};
flag_set get_flags(flag_set flag) const { return m_flags & flag; }
@@ -3672,6 +4101,7 @@ public:
virtual Log_event_type get_type_code() { return TABLE_MAP_EVENT; }
virtual bool is_valid() const { return m_memory != NULL; /* we check malloc */ }
+ virtual bool is_part_of_group() { return 1; }
virtual int get_data_size() { return (uint) m_data_size; }
#ifdef MYSQL_SERVER
@@ -3692,9 +4122,9 @@ public:
private:
#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
- virtual int do_apply_event(Relay_log_info const *rli);
- virtual int do_update_pos(Relay_log_info *rli);
- virtual enum_skip_reason do_shall_skip(Relay_log_info *rli);
+ virtual int do_apply_event(rpl_group_info *rgi);
+ virtual int do_update_pos(rpl_group_info *rgi);
+ virtual enum_skip_reason do_shall_skip(rpl_group_info *rgi);
#endif
#ifdef MYSQL_SERVER
@@ -3793,6 +4223,9 @@ public:
void clear_flags(flag_set flags_arg) { m_flags &= ~flags_arg; }
flag_set get_flags(flag_set flags_arg) const { return m_flags & flags_arg; }
+ Log_event_type get_type_code() { return m_type; } /* Specific type (_V1 etc) */
+ virtual Log_event_type get_general_type_code() = 0; /* General rows op type, no version */
+
#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
virtual void pack_info(THD *thd, Protocol *protocol);
#endif
@@ -3837,9 +4270,16 @@ public:
{
return m_rows_buf && m_cols.bitmap;
}
+ bool is_part_of_group() { return get_flags(STMT_END_F) != 0; }
uint m_row_count; /* The number of rows added to the event */
+ const uchar* get_extra_row_data() const { return m_extra_row_data; }
+
+#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
+ virtual uint8 get_trg_event_map()= 0;
+#endif
+
protected:
/*
The constructors are protected since you're supposed to inherit
@@ -3847,10 +4287,10 @@ protected:
*/
#ifdef MYSQL_SERVER
Rows_log_event(THD*, TABLE*, ulong table_id,
- MY_BITMAP const *cols, bool is_transactional);
+ MY_BITMAP const *cols, bool is_transactional,
+ Log_event_type event_type);
#endif
Rows_log_event(const char *row_data, uint event_len,
- Log_event_type event_type,
const Format_description_log_event *description_event);
#ifdef MYSQL_CLIENT
@@ -3888,6 +4328,12 @@ protected:
flag_set m_flags; /* Flags for row-level events */
+ Log_event_type m_type; /* Actual event type */
+
+ uchar *m_extra_row_data; /* Pointer to extra row data if any */
+ /* If non null, first byte is length */
+
+
/* helper functions */
#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
@@ -3896,20 +4342,24 @@ protected:
uchar *m_key; /* Buffer to keep key value during searches */
KEY *m_key_info; /* Pointer to KEY info for m_key_nr */
uint m_key_nr; /* Key number */
+ bool master_had_triggers; /* set after tables opening */
int find_key(); // Find a best key to use in find_row()
- int find_row(const Relay_log_info *const);
- int write_row(const Relay_log_info *const, const bool);
+ int find_row(rpl_group_info *);
+ int write_row(rpl_group_info *, const bool);
// Unpack the current row into m_table->record[0]
- int unpack_current_row(const Relay_log_info *const rli)
+ int unpack_current_row(rpl_group_info *rgi)
{
DBUG_ASSERT(m_table);
ASSERT_OR_RETURN_ERROR(m_curr_row < m_rows_end, HA_ERR_CORRUPT_EVENT);
- return ::unpack_row(rli, m_table, m_width, m_curr_row, &m_cols,
+ return ::unpack_row(rgi, m_table, m_width, m_curr_row, &m_cols,
&m_curr_row_end, &m_master_reclength, m_rows_end);
}
+ bool process_triggers(trg_event_type event,
+ trg_action_time_type time_type,
+ bool old_row_is_record1);
/**
Helper function to check whether there is an auto increment
@@ -3929,9 +4379,9 @@ protected:
private:
#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
- virtual int do_apply_event(Relay_log_info const *rli);
- virtual int do_update_pos(Relay_log_info *rli);
- virtual enum_skip_reason do_shall_skip(Relay_log_info *rli);
+ virtual int do_apply_event(rpl_group_info *rgi);
+ virtual int do_update_pos(rpl_group_info *rgi);
+ virtual enum_skip_reason do_shall_skip(rpl_group_info *rgi);
/*
Primitive to prepare for a sequence of row executions.
@@ -3982,7 +4432,7 @@ private:
0 if execution succeeded, 1 if execution failed.
*/
- virtual int do_exec_row(const Relay_log_info *const rli) = 0;
+ virtual int do_exec_row(rpl_group_info *rli) = 0;
#endif /* defined(MYSQL_SERVER) && defined(HAVE_REPLICATION) */
friend class Old_rows_log_event;
@@ -4028,8 +4478,12 @@ public:
}
#endif
+#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
+ uint8 get_trg_event_map();
+#endif
+
private:
- virtual Log_event_type get_type_code() { return (Log_event_type)TYPE_CODE; }
+ virtual Log_event_type get_general_type_code() { return (Log_event_type)TYPE_CODE; }
#ifdef MYSQL_CLIENT
void print(FILE *file, PRINT_EVENT_INFO *print_event_info);
@@ -4038,7 +4492,7 @@ private:
#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
virtual int do_before_row_operations(const Slave_reporting_capability *const);
virtual int do_after_row_operations(const Slave_reporting_capability *const,int);
- virtual int do_exec_row(const Relay_log_info *const);
+ virtual int do_exec_row(rpl_group_info *);
#endif
};
@@ -4102,8 +4556,12 @@ public:
return Rows_log_event::is_valid() && m_cols_ai.bitmap;
}
+#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
+ uint8 get_trg_event_map();
+#endif
+
protected:
- virtual Log_event_type get_type_code() { return (Log_event_type)TYPE_CODE; }
+ virtual Log_event_type get_general_type_code() { return (Log_event_type)TYPE_CODE; }
#ifdef MYSQL_CLIENT
void print(FILE *file, PRINT_EVENT_INFO *print_event_info);
@@ -4112,7 +4570,7 @@ protected:
#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
virtual int do_before_row_operations(const Slave_reporting_capability *const);
virtual int do_after_row_operations(const Slave_reporting_capability *const,int);
- virtual int do_exec_row(const Relay_log_info *const);
+ virtual int do_exec_row(rpl_group_info *);
#endif /* defined(MYSQL_SERVER) && defined(HAVE_REPLICATION) */
};
@@ -4166,9 +4624,13 @@ public:
cols, fields, before_record);
}
#endif
-
+
+#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
+ uint8 get_trg_event_map();
+#endif
+
protected:
- virtual Log_event_type get_type_code() { return (Log_event_type)TYPE_CODE; }
+ virtual Log_event_type get_general_type_code() { return (Log_event_type)TYPE_CODE; }
#ifdef MYSQL_CLIENT
void print(FILE *file, PRINT_EVENT_INFO *print_event_info);
@@ -4177,7 +4639,7 @@ protected:
#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
virtual int do_before_row_operations(const Slave_reporting_capability *const);
virtual int do_after_row_operations(const Slave_reporting_capability *const,int);
- virtual int do_exec_row(const Relay_log_info *const);
+ virtual int do_exec_row(rpl_group_info *);
#endif
};
@@ -4241,7 +4703,16 @@ public:
{
DBUG_ENTER("Incident_log_event::Incident_log_event");
DBUG_PRINT("enter", ("m_incident: %d", m_incident));
- m_message= msg;
+ m_message.str= NULL;
+ m_message.length= 0;
+ if (!(m_message.str= (char*) my_malloc(msg.length+1, MYF(MY_WME))))
+ {
+ /* Mark this event invalid */
+ m_incident= INCIDENT_NONE;
+ DBUG_VOID_RETURN;
+ }
+ strmake(m_message.str, msg.str, msg.length);
+ m_message.length= msg.length;
set_direct_logging();
/* Replicate the incident irregardless of @@skip_replication. */
flags&= ~LOG_EVENT_SKIP_REPLICATION_F;
@@ -4263,7 +4734,7 @@ public:
#endif
#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
- virtual int do_apply_event(Relay_log_info const *rli);
+ virtual int do_apply_event(rpl_group_info *rgi);
#endif
virtual bool write_data_header(IO_CACHE *file);
@@ -4286,6 +4757,60 @@ private:
LEX_STRING m_message;
};
+/**
+ @class Ignorable_log_event
+
+ Base class for ignorable log events. Events deriving from
+ this class can be safely ignored by slaves that cannot
+ recognize them. Newer slaves, will be able to read and
+ handle them. This has been designed to be an open-ended
+ architecture, so adding new derived events shall not harm
+ the old slaves that support ignorable log event mechanism
+ (they will just ignore unrecognized ignorable events).
+
+ @note The only thing that makes an event ignorable is that it has
+ the LOG_EVENT_IGNORABLE_F flag set. It is not strictly necessary
+ that ignorable event types derive from Ignorable_log_event; they may
+ just as well derive from Log_event and pass LOG_EVENT_IGNORABLE_F as
+ argument to the Log_event constructor.
+**/
+
+class Ignorable_log_event : public Log_event {
+public:
+ int number;
+ const char *description;
+
+#ifndef MYSQL_CLIENT
+ Ignorable_log_event(THD *thd_arg)
+ :Log_event(thd_arg, LOG_EVENT_IGNORABLE_F, FALSE),
+ number(0), description("internal")
+ {
+ DBUG_ENTER("Ignorable_log_event::Ignorable_log_event");
+ DBUG_VOID_RETURN;
+ }
+#endif
+
+ Ignorable_log_event(const char *buf,
+ const Format_description_log_event *descr_event,
+ const char *event_name);
+ virtual ~Ignorable_log_event();
+
+#ifndef MYSQL_CLIENT
+ void pack_info(THD *, Protocol*);
+#endif
+
+#ifdef MYSQL_CLIENT
+ virtual void print(FILE *file, PRINT_EVENT_INFO *print_event_info);
+#endif
+
+ virtual Log_event_type get_type_code() { return IGNORABLE_LOG_EVENT; }
+
+ virtual bool is_valid() const { return 1; }
+
+ virtual int get_data_size() { return IGNORABLE_HEADER_LEN; }
+};
+
+
static inline bool copy_event_cache_to_file_and_reinit(IO_CACHE *cache,
FILE *file)
{
@@ -4338,27 +4863,15 @@ private:
bool slave_execute_deferred_events(THD *thd);
#endif
-int append_query_string(THD *thd, CHARSET_INFO *csinfo,
- String const *from, String *to);
-
bool rpl_get_position_info(const char **log_file_name, ulonglong *log_pos,
const char **group_relay_log_name,
ulonglong *relay_log_pos);
bool event_checksum_test(uchar *buf, ulong event_len, uint8 alg);
+bool event_that_should_be_ignored(const char *buf);
uint8 get_checksum_alg(const char* buf, ulong len);
extern TYPELIB binlog_checksum_typelib;
-#ifndef MYSQL_CLIENT
-/**
- The function is called by slave applier in case there are
- active table filtering rules to force gathering events associated
- with Query-log-event into an array to execute
- them once the fate of the Query is determined for execution.
-*/
-bool slave_execute_deferred_events(THD *thd);
-#endif
-
/**
@} (end of group Replication)
*/
diff --git a/sql/log_event_old.cc b/sql/log_event_old.cc
index 70b3ec12356..e6c05aeb849 100644
--- a/sql/log_event_old.cc
+++ b/sql/log_event_old.cc
@@ -13,11 +13,11 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+#include <my_global.h>
#include "sql_priv.h"
#ifndef MYSQL_CLIENT
#include "unireg.h"
#endif
-#include "my_global.h" // REQUIRED by log_event.h > m_string.h > my_bitmap.h
#include "log_event.h"
#ifndef MYSQL_CLIENT
#include "sql_cache.h" // QUERY_CACHE_FLAGS_SIZE
@@ -36,12 +36,13 @@
// Old implementation of do_apply_event()
int
-Old_rows_log_event::do_apply_event(Old_rows_log_event *ev, const Relay_log_info *rli)
+Old_rows_log_event::do_apply_event(Old_rows_log_event *ev, rpl_group_info *rgi)
{
DBUG_ENTER("Old_rows_log_event::do_apply_event(st_relay_log_info*)");
int error= 0;
THD *ev_thd= ev->thd;
uchar const *row_start= ev->m_rows_buf;
+ const Relay_log_info *rli= rgi->rli;
/*
If m_table_id == ~0UL, then we have a dummy event that does not
@@ -57,7 +58,7 @@ Old_rows_log_event::do_apply_event(Old_rows_log_event *ev, const Relay_log_info
*/
DBUG_ASSERT(ev->get_flags(Old_rows_log_event::STMT_END_F));
- const_cast<Relay_log_info*>(rli)->slave_close_thread_tables(ev_thd);
+ rgi->slave_close_thread_tables(ev_thd);
ev_thd->clear_error();
DBUG_RETURN(0);
}
@@ -67,7 +68,7 @@ Old_rows_log_event::do_apply_event(Old_rows_log_event *ev, const Relay_log_info
do_apply_event(). We still check here to prevent future coding
errors.
*/
- DBUG_ASSERT(rli->sql_thd == ev_thd);
+ DBUG_ASSERT(rgi->thd == ev_thd);
/*
If there is no locks taken, this is the first binrow event seen
@@ -86,6 +87,7 @@ Old_rows_log_event::do_apply_event(Old_rows_log_event *ev, const Relay_log_info
call might reset the value of current_stmt_binlog_format, so
we need to do any changes to that value after this function.
*/
+ delete_explain_query(thd->lex);
lex_start(ev_thd);
mysql_reset_thd_for_next_command(ev_thd);
@@ -97,22 +99,22 @@ Old_rows_log_event::do_apply_event(Old_rows_log_event *ev, const Relay_log_info
*/
ev_thd->lex->set_stmt_row_injection();
- if (open_and_lock_tables(ev_thd, rli->tables_to_lock, FALSE, 0))
+ if (open_and_lock_tables(ev_thd, rgi->tables_to_lock, FALSE, 0))
{
- uint actual_error= ev_thd->stmt_da->sql_errno();
+ uint actual_error= ev_thd->get_stmt_da()->sql_errno();
if (ev_thd->is_slave_error || ev_thd->is_fatal_error)
{
/*
Error reporting borrowed from Query_log_event with many excessive
simplifications (we don't honour --slave-skip-errors)
*/
- rli->report(ERROR_LEVEL, actual_error,
+ rli->report(ERROR_LEVEL, actual_error, NULL,
"Error '%s' on opening tables",
- (actual_error ? ev_thd->stmt_da->message() :
+ (actual_error ? ev_thd->get_stmt_da()->message() :
"unexpected success or fatal error"));
ev_thd->is_slave_error= 1;
}
- const_cast<Relay_log_info*>(rli)->slave_close_thread_tables(thd);
+ rgi->slave_close_thread_tables(thd);
DBUG_RETURN(actual_error);
}
@@ -125,17 +127,16 @@ Old_rows_log_event::do_apply_event(Old_rows_log_event *ev, const Relay_log_info
*/
{
- RPL_TABLE_LIST *ptr= rli->tables_to_lock;
- for (uint i= 0 ; ptr&& (i< rli->tables_to_lock_count);
+ RPL_TABLE_LIST *ptr= rgi->tables_to_lock;
+ for (uint i= 0 ; ptr&& (i< rgi->tables_to_lock_count);
ptr= static_cast<RPL_TABLE_LIST*>(ptr->next_global), i++)
{
DBUG_ASSERT(ptr->m_tabledef_valid);
TABLE *conv_table;
- if (!ptr->m_tabledef.compatible_with(thd, const_cast<Relay_log_info*>(rli),
- ptr->table, &conv_table))
+ if (!ptr->m_tabledef.compatible_with(thd, rgi, ptr->table, &conv_table))
{
ev_thd->is_slave_error= 1;
- const_cast<Relay_log_info*>(rli)->slave_close_thread_tables(ev_thd);
+ rgi->slave_close_thread_tables(ev_thd);
DBUG_RETURN(Old_rows_log_event::ERR_BAD_TABLE_DEF);
}
DBUG_PRINT("debug", ("Table: %s.%s is compatible with master"
@@ -160,15 +161,15 @@ Old_rows_log_event::do_apply_event(Old_rows_log_event *ev, const Relay_log_info
Old_rows_log_event, we can invalidate the query cache for the
associated table.
*/
- TABLE_LIST *ptr= rli->tables_to_lock;
- for (uint i=0; ptr && (i < rli->tables_to_lock_count); ptr= ptr->next_global, i++)
- const_cast<Relay_log_info*>(rli)->m_table_map.set_table(ptr->table_id, ptr->table);
+ TABLE_LIST *ptr= rgi->tables_to_lock;
+ for (uint i=0; ptr && (i < rgi->tables_to_lock_count); ptr= ptr->next_global, i++)
+ rgi->m_table_map.set_table(ptr->table_id, ptr->table);
#ifdef HAVE_QUERY_CACHE
- query_cache.invalidate_locked_for_write(thd, rli->tables_to_lock);
+ query_cache.invalidate_locked_for_write(thd, rgi->tables_to_lock);
#endif
}
- TABLE* table= const_cast<Relay_log_info*>(rli)->m_table_map.get_table(ev->m_table_id);
+ TABLE* table= rgi->m_table_map.get_table(ev->m_table_id);
if (table)
{
@@ -204,22 +205,11 @@ Old_rows_log_event::do_apply_event(Old_rows_log_event *ev, const Relay_log_info
/* A small test to verify that objects have consistent types */
DBUG_ASSERT(sizeof(ev_thd->variables.option_bits) == sizeof(OPTION_RELAXED_UNIQUE_CHECKS));
- /*
- Now we are in a statement and will stay in a statement until we
- see a STMT_END_F.
-
- We set this flag here, before actually applying any rows, in
- case the SQL thread is stopped and we need to detect that we're
- inside a statement and halting abruptly might cause problems
- when restarting.
- */
- const_cast<Relay_log_info*>(rli)->set_flag(Relay_log_info::IN_STMT);
-
error= do_before_row_operations(table);
while (error == 0 && row_start < ev->m_rows_end)
{
uchar const *row_end= NULL;
- if ((error= do_prepare_row(ev_thd, rli, table, row_start, &row_end)))
+ if ((error= do_prepare_row(ev_thd, rgi, table, row_start, &row_end)))
break; // We should perform the after-row operation even in
// the case of error
@@ -243,10 +233,10 @@ Old_rows_log_event::do_apply_event(Old_rows_log_event *ev, const Relay_log_info
break;
default:
- rli->report(ERROR_LEVEL, ev_thd->stmt_da->sql_errno(),
+ rli->report(ERROR_LEVEL, ev_thd->get_stmt_da()->sql_errno(), NULL,
"Error in %s event: row application failed. %s",
ev->get_type_str(),
- ev_thd->is_error() ? ev_thd->stmt_da->message() : "");
+ ev_thd->is_error() ? ev_thd->get_stmt_da()->message() : "");
thd->is_slave_error= 1;
break;
}
@@ -260,12 +250,12 @@ Old_rows_log_event::do_apply_event(Old_rows_log_event *ev, const Relay_log_info
if (error)
{ /* error has occured during the transaction */
- rli->report(ERROR_LEVEL, ev_thd->stmt_da->sql_errno(),
+ rli->report(ERROR_LEVEL, ev_thd->get_stmt_da()->sql_errno(), NULL,
"Error in %s event: error during transaction execution "
"on table %s.%s. %s",
ev->get_type_str(), table->s->db.str,
table->s->table_name.str,
- ev_thd->is_error() ? ev_thd->stmt_da->message() : "");
+ ev_thd->is_error() ? ev_thd->get_stmt_da()->message() : "");
/*
If one day we honour --skip-slave-errors in row-based replication, and
@@ -279,7 +269,7 @@ Old_rows_log_event::do_apply_event(Old_rows_log_event *ev, const Relay_log_info
rollback at the caller along with sbr.
*/
ev_thd->reset_current_stmt_binlog_format_row();
- const_cast<Relay_log_info*>(rli)->cleanup_context(ev_thd, error);
+ rgi->cleanup_context(ev_thd, error);
ev_thd->is_slave_error= 1;
DBUG_RETURN(error);
}
@@ -927,22 +917,6 @@ int Write_rows_log_event_old::do_before_row_operations(TABLE *table)
from the start.
*/
table->file->ha_start_bulk_insert(0);
- /*
- We need TIMESTAMP_NO_AUTO_SET otherwise ha_write_row() will not use fill
- any TIMESTAMP column with data from the row but instead will use
- the event's current time.
- As we replicate from TIMESTAMP to TIMESTAMP and slave has no extra
- columns, we know that all TIMESTAMP columns on slave will receive explicit
- data from the row, so TIMESTAMP_NO_AUTO_SET is ok.
- When we allow a table without TIMESTAMP to be replicated to a table having
- more columns including a TIMESTAMP column, or when we allow a TIMESTAMP
- column to be replicated into a BIGINT column and the slave's table has a
- TIMESTAMP column, then the slave's TIMESTAMP column will take its value
- from set_time() which we called earlier (consistent with SBR). And then in
- some cases we won't want TIMESTAMP_NO_AUTO_SET (will require some code to
- analyze if explicit data is provided for slave's TIMESTAMP columns).
- */
- table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
return error;
}
@@ -968,7 +942,7 @@ int Write_rows_log_event_old::do_after_row_operations(TABLE *table, int error)
int
Write_rows_log_event_old::do_prepare_row(THD *thd_arg,
- Relay_log_info const *rli,
+ rpl_group_info *rgi,
TABLE *table,
uchar const *row_start,
uchar const **row_end)
@@ -977,7 +951,7 @@ Write_rows_log_event_old::do_prepare_row(THD *thd_arg,
DBUG_ASSERT(row_start && row_end);
int error;
- error= unpack_row_old(const_cast<Relay_log_info*>(rli),
+ error= unpack_row_old(rgi,
table, m_width, table->record[0],
row_start, m_rows_end,
&m_cols, row_end, &m_master_reclength,
@@ -1052,7 +1026,7 @@ int Delete_rows_log_event_old::do_after_row_operations(TABLE *table, int error)
int
Delete_rows_log_event_old::do_prepare_row(THD *thd_arg,
- Relay_log_info const *rli,
+ rpl_group_info *rgi,
TABLE *table,
uchar const *row_start,
uchar const **row_end)
@@ -1065,7 +1039,7 @@ Delete_rows_log_event_old::do_prepare_row(THD *thd_arg,
*/
DBUG_ASSERT(table->s->fields >= m_width);
- error= unpack_row_old(const_cast<Relay_log_info*>(rli),
+ error= unpack_row_old(rgi,
table, m_width, table->record[0],
row_start, m_rows_end,
&m_cols, row_end, &m_master_reclength,
@@ -1131,8 +1105,6 @@ int Update_rows_log_event_old::do_before_row_operations(TABLE *table)
if (!m_memory)
return HA_ERR_OUT_OF_MEM;
- table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
-
return error;
}
@@ -1151,7 +1123,7 @@ int Update_rows_log_event_old::do_after_row_operations(TABLE *table, int error)
int Update_rows_log_event_old::do_prepare_row(THD *thd_arg,
- Relay_log_info const *rli,
+ rpl_group_info *rgi,
TABLE *table,
uchar const *row_start,
uchar const **row_end)
@@ -1165,14 +1137,14 @@ int Update_rows_log_event_old::do_prepare_row(THD *thd_arg,
DBUG_ASSERT(table->s->fields >= m_width);
/* record[0] is the before image for the update */
- error= unpack_row_old(const_cast<Relay_log_info*>(rli),
+ error= unpack_row_old(rgi,
table, m_width, table->record[0],
row_start, m_rows_end,
&m_cols, row_end, &m_master_reclength,
table->read_set, PRE_GA_UPDATE_ROWS_EVENT);
row_start = *row_end;
/* m_after_image is the after image for the update */
- error= unpack_row_old(const_cast<Relay_log_info*>(rli),
+ error= unpack_row_old(rgi,
table, m_width, m_after_image,
row_start, m_rows_end,
&m_cols, row_end, &m_master_reclength,
@@ -1271,8 +1243,8 @@ Old_rows_log_event::Old_rows_log_event(THD *thd_arg, TABLE *tbl_arg, ulong tid,
set_flags(NO_FOREIGN_KEY_CHECKS_F);
if (thd_arg->variables.option_bits & OPTION_RELAXED_UNIQUE_CHECKS)
set_flags(RELAXED_UNIQUE_CHECKS_F);
- /* if bitmap_init fails, caught in is_valid() */
- if (likely(!bitmap_init(&m_cols,
+ /* if my_bitmap_init fails, caught in is_valid() */
+ if (likely(!my_bitmap_init(&m_cols,
m_width <= sizeof(m_bitbuf)*8 ? m_bitbuf : NULL,
m_width,
false)))
@@ -1286,7 +1258,7 @@ Old_rows_log_event::Old_rows_log_event(THD *thd_arg, TABLE *tbl_arg, ulong tid,
}
else
{
- // Needed because bitmap_init() does not set it to null on failure
+ // Needed because my_bitmap_init() does not set it to null on failure
m_cols.bitmap= 0;
}
}
@@ -1340,8 +1312,8 @@ Old_rows_log_event::Old_rows_log_event(const char *buf, uint event_len,
DBUG_PRINT("debug", ("Reading from %p", ptr_after_width));
m_width = net_field_length(&ptr_after_width);
DBUG_PRINT("debug", ("m_width=%lu", m_width));
- /* if bitmap_init fails, catched in is_valid() */
- if (likely(!bitmap_init(&m_cols,
+ /* if my_bitmap_init fails, catched in is_valid() */
+ if (likely(!my_bitmap_init(&m_cols,
m_width <= sizeof(m_bitbuf)*8 ? m_bitbuf : NULL,
m_width,
false)))
@@ -1354,7 +1326,7 @@ Old_rows_log_event::Old_rows_log_event(const char *buf, uint event_len,
}
else
{
- // Needed because bitmap_init() does not set it to null on failure
+ // Needed because my_bitmap_init() does not set it to null on failure
m_cols.bitmap= NULL;
DBUG_VOID_RETURN;
}
@@ -1385,15 +1357,15 @@ Old_rows_log_event::Old_rows_log_event(const char *buf, uint event_len,
Old_rows_log_event::~Old_rows_log_event()
{
if (m_cols.bitmap == m_bitbuf) // no my_malloc happened
- m_cols.bitmap= 0; // so no my_free in bitmap_free
- bitmap_free(&m_cols); // To pair with bitmap_init().
+ m_cols.bitmap= 0; // so no my_free in my_bitmap_free
+ my_bitmap_free(&m_cols); // To pair with my_bitmap_init().
my_free(m_rows_buf);
}
int Old_rows_log_event::get_data_size()
{
- uchar buf[sizeof(m_width)+1];
+ uchar buf[MAX_INT_WIDTH];
uchar *end= net_store_length(buf, (m_width + 7) / 8);
DBUG_EXECUTE_IF("old_row_based_repl_4_byte_map_id_master",
@@ -1424,7 +1396,7 @@ int Old_rows_log_event::do_add_row_data(uchar *row_data, size_t length)
trigger false warnings.
*/
#ifndef HAVE_valgrind
- DBUG_DUMP("row_data", row_data, min(length, 32));
+ DBUG_DUMP("row_data", row_data, MY_MIN(length, 32));
#endif
DBUG_ASSERT(m_rows_buf <= m_rows_cur);
@@ -1468,10 +1440,11 @@ int Old_rows_log_event::do_add_row_data(uchar *row_data, size_t length)
#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
-int Old_rows_log_event::do_apply_event(Relay_log_info const *rli)
+int Old_rows_log_event::do_apply_event(rpl_group_info *rgi)
{
DBUG_ENTER("Old_rows_log_event::do_apply_event(Relay_log_info*)");
int error= 0;
+ Relay_log_info const *rli= rgi->rli;
/*
If m_table_id == ~0UL, then we have a dummy event that does not
@@ -1487,7 +1460,7 @@ int Old_rows_log_event::do_apply_event(Relay_log_info const *rli)
*/
DBUG_ASSERT(get_flags(STMT_END_F));
- const_cast<Relay_log_info*>(rli)->slave_close_thread_tables(thd);
+ rgi->slave_close_thread_tables(thd);
thd->clear_error();
DBUG_RETURN(0);
}
@@ -1497,7 +1470,7 @@ int Old_rows_log_event::do_apply_event(Relay_log_info const *rli)
do_apply_event(). We still check here to prevent future coding
errors.
*/
- DBUG_ASSERT(rli->sql_thd == thd);
+ DBUG_ASSERT(rgi->thd == thd);
/*
If there is no locks taken, this is the first binrow event seen
@@ -1515,8 +1488,8 @@ int Old_rows_log_event::do_apply_event(Relay_log_info const *rli)
*/
lex_start(thd);
- if ((error= lock_tables(thd, rli->tables_to_lock,
- rli->tables_to_lock_count, 0)))
+ if ((error= lock_tables(thd, rgi->tables_to_lock,
+ rgi->tables_to_lock_count, 0)))
{
if (thd->is_slave_error || thd->is_fatal_error)
{
@@ -1525,7 +1498,7 @@ int Old_rows_log_event::do_apply_event(Relay_log_info const *rli)
simplifications (we don't honour --slave-skip-errors)
*/
uint actual_error= thd->net.last_errno;
- rli->report(ERROR_LEVEL, actual_error,
+ rli->report(ERROR_LEVEL, actual_error, NULL,
"Error '%s' in %s event: when locking tables",
(actual_error ? thd->net.last_error :
"unexpected success or fatal error"),
@@ -1534,11 +1507,11 @@ int Old_rows_log_event::do_apply_event(Relay_log_info const *rli)
}
else
{
- rli->report(ERROR_LEVEL, error,
+ rli->report(ERROR_LEVEL, error, NULL,
"Error in %s event: when locking tables",
get_type_str());
}
- const_cast<Relay_log_info*>(rli)->slave_close_thread_tables(thd);
+ rgi->slave_close_thread_tables(thd);
DBUG_RETURN(error);
}
@@ -1551,16 +1524,15 @@ int Old_rows_log_event::do_apply_event(Relay_log_info const *rli)
*/
{
- RPL_TABLE_LIST *ptr= rli->tables_to_lock;
- for (uint i= 0 ; ptr&& (i< rli->tables_to_lock_count);
+ RPL_TABLE_LIST *ptr= rgi->tables_to_lock;
+ for (uint i= 0 ; ptr&& (i< rgi->tables_to_lock_count);
ptr= static_cast<RPL_TABLE_LIST*>(ptr->next_global), i++)
{
TABLE *conv_table;
- if (ptr->m_tabledef.compatible_with(thd, const_cast<Relay_log_info*>(rli),
- ptr->table, &conv_table))
+ if (ptr->m_tabledef.compatible_with(thd, rgi, ptr->table, &conv_table))
{
thd->is_slave_error= 1;
- const_cast<Relay_log_info*>(rli)->slave_close_thread_tables(thd);
+ rgi->slave_close_thread_tables(thd);
DBUG_RETURN(ERR_BAD_TABLE_DEF);
}
ptr->m_conv_table= conv_table;
@@ -1582,18 +1554,18 @@ int Old_rows_log_event::do_apply_event(Relay_log_info const *rli)
Old_rows_log_event, we can invalidate the query cache for the
associated table.
*/
- for (TABLE_LIST *ptr= rli->tables_to_lock ; ptr ; ptr= ptr->next_global)
+ for (TABLE_LIST *ptr= rgi->tables_to_lock ; ptr ; ptr= ptr->next_global)
{
- const_cast<Relay_log_info*>(rli)->m_table_map.set_table(ptr->table_id, ptr->table);
+ rgi->m_table_map.set_table(ptr->table_id, ptr->table);
}
#ifdef HAVE_QUERY_CACHE
- query_cache.invalidate_locked_for_write(thd, rli->tables_to_lock);
+ query_cache.invalidate_locked_for_write(thd, rgi->tables_to_lock);
#endif
}
TABLE*
table=
- m_table= const_cast<Relay_log_info*>(rli)->m_table_map.get_table(m_table_id);
+ m_table= rgi->m_table_map.get_table(m_table_id);
if (table)
{
@@ -1629,17 +1601,6 @@ int Old_rows_log_event::do_apply_event(Relay_log_info const *rli)
/* A small test to verify that objects have consistent types */
DBUG_ASSERT(sizeof(thd->variables.option_bits) == sizeof(OPTION_RELAXED_UNIQUE_CHECKS));
- /*
- Now we are in a statement and will stay in a statement until we
- see a STMT_END_F.
-
- We set this flag here, before actually applying any rows, in
- case the SQL thread is stopped and we need to detect that we're
- inside a statement and halting abruptly might cause problems
- when restarting.
- */
- const_cast<Relay_log_info*>(rli)->set_flag(Relay_log_info::IN_STMT);
-
if ( m_width == table->s->fields && bitmap_is_set_all(&m_cols))
set_flags(COMPLETE_ROWS_F);
@@ -1673,7 +1634,7 @@ int Old_rows_log_event::do_apply_event(Relay_log_info const *rli)
if (!table->in_use)
table->in_use= thd;
- error= do_exec_row(rli);
+ error= do_exec_row(rgi);
DBUG_PRINT("info", ("error: %d", error));
DBUG_ASSERT(error != HA_ERR_RECORD_DELETED);
@@ -1692,7 +1653,7 @@ int Old_rows_log_event::do_apply_event(Relay_log_info const *rli)
break;
default:
- rli->report(ERROR_LEVEL, thd->net.last_errno,
+ rli->report(ERROR_LEVEL, thd->net.last_errno, NULL,
"Error in %s event: row application failed. %s",
get_type_str(),
thd->net.last_error ? thd->net.last_error : "");
@@ -1712,7 +1673,7 @@ int Old_rows_log_event::do_apply_event(Relay_log_info const *rli)
(ulong) m_curr_row, (ulong) m_curr_row_end, (ulong) m_rows_end));
if (!m_curr_row_end && !error)
- unpack_current_row(rli);
+ unpack_current_row(rgi);
// at this moment m_curr_row_end should be set
DBUG_ASSERT(error || m_curr_row_end != NULL);
@@ -1730,7 +1691,7 @@ 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.last_errno,
+ rli->report(ERROR_LEVEL, thd->net.last_errno, NULL,
"Error in %s event: error during transaction execution "
"on table %s.%s. %s",
get_type_str(), table->s->db.str,
@@ -1749,7 +1710,7 @@ int Old_rows_log_event::do_apply_event(Relay_log_info const *rli)
rollback at the caller along with sbr.
*/
thd->reset_current_stmt_binlog_format_row();
- const_cast<Relay_log_info*>(rli)->cleanup_context(thd, error);
+ rgi->cleanup_context(thd, error);
thd->is_slave_error= 1;
DBUG_RETURN(error);
}
@@ -1778,7 +1739,7 @@ int Old_rows_log_event::do_apply_event(Relay_log_info const *rli)
problem. When WL#2975 is implemented, just remove the member
Relay_log_info::last_event_start_time and all its occurrences.
*/
- const_cast<Relay_log_info*>(rli)->last_event_start_time= my_time(0);
+ rgi->last_event_start_time= my_time(0);
}
if (get_flags(STMT_END_F))
@@ -1813,7 +1774,7 @@ int Old_rows_log_event::do_apply_event(Relay_log_info const *rli)
*/
DBUG_ASSERT(! thd->transaction_rollback_request);
if ((error= (binlog_error ? trans_rollback_stmt(thd) : trans_commit_stmt(thd))))
- rli->report(ERROR_LEVEL, error,
+ rli->report(ERROR_LEVEL, error, NULL,
"Error in %s event: commit of row events failed, "
"table `%s`.`%s`",
get_type_str(), m_table->s->db.str,
@@ -1831,7 +1792,7 @@ int Old_rows_log_event::do_apply_event(Relay_log_info const *rli)
*/
thd->reset_current_stmt_binlog_format_row();
- const_cast<Relay_log_info*>(rli)->cleanup_context(thd, 0);
+ rgi->cleanup_context(thd, 0);
}
DBUG_RETURN(error);
@@ -1839,22 +1800,23 @@ int Old_rows_log_event::do_apply_event(Relay_log_info const *rli)
Log_event::enum_skip_reason
-Old_rows_log_event::do_shall_skip(Relay_log_info *rli)
+Old_rows_log_event::do_shall_skip(rpl_group_info *rgi)
{
/*
If the slave skip counter is 1 and this event does not end a
statement, then we should not start executing on the next event.
Otherwise, we defer the decision to the normal skipping logic.
*/
- if (rli->slave_skip_counter == 1 && !get_flags(STMT_END_F))
+ if (rgi->rli->slave_skip_counter == 1 && !get_flags(STMT_END_F))
return Log_event::EVENT_SKIP_IGNORE;
else
- return Log_event::do_shall_skip(rli);
+ return Log_event::do_shall_skip(rgi);
}
int
-Old_rows_log_event::do_update_pos(Relay_log_info *rli)
+Old_rows_log_event::do_update_pos(rpl_group_info *rgi)
{
+ Relay_log_info *rli= rgi->rli;
DBUG_ENTER("Old_rows_log_event::do_update_pos");
int error= 0;
@@ -1868,7 +1830,7 @@ Old_rows_log_event::do_update_pos(Relay_log_info *rli)
Step the group log position if we are not in a transaction,
otherwise increase the event log position.
*/
- rli->stmt_done(log_pos, when);
+ rli->stmt_done(log_pos, thd, rgi);
/*
Clear any errors in thd->net.last_err*. It is not known if this is
needed or not. It is believed that any errors that may exist in
@@ -1879,7 +1841,7 @@ Old_rows_log_event::do_update_pos(Relay_log_info *rli)
}
else
{
- rli->inc_event_relay_log_pos();
+ rgi->inc_event_relay_log_pos();
}
DBUG_RETURN(error);
@@ -1915,7 +1877,7 @@ bool Old_rows_log_event::write_data_body(IO_CACHE*file)
Note that this should be the number of *bits*, not the number of
bytes.
*/
- uchar sbuf[sizeof(m_width)];
+ uchar sbuf[MAX_INT_WIDTH];
my_ptrdiff_t const data_size= m_rows_cur - m_rows_buf;
// This method should not be reached.
@@ -2016,8 +1978,7 @@ void Old_rows_log_event::print_helper(FILE *file,
*/
int
-Old_rows_log_event::write_row(const Relay_log_info *const rli,
- const bool overwrite)
+Old_rows_log_event::write_row(rpl_group_info *rgi, const bool overwrite)
{
DBUG_ENTER("write_row");
DBUG_ASSERT(m_table != NULL && thd != NULL);
@@ -2034,7 +1995,7 @@ Old_rows_log_event::write_row(const Relay_log_info *const rli,
DBUG_RETURN(error);
/* unpack row into table->record[0] */
- error= unpack_current_row(rli); // TODO: how to handle errors?
+ error= unpack_current_row(rgi); // TODO: how to handle errors?
#ifndef DBUG_OFF
DBUG_DUMP("record[0]", table->record[0], table->s->reclength);
@@ -2141,7 +2102,7 @@ Old_rows_log_event::write_row(const Relay_log_info *const rli,
if (!get_flags(COMPLETE_ROWS_F))
{
restore_record(table,record[1]);
- error= unpack_current_row(rli);
+ error= unpack_current_row(rgi);
}
#ifndef DBUG_OFF
@@ -2236,7 +2197,7 @@ Old_rows_log_event::write_row(const Relay_log_info *const rli,
for any following update/delete command.
*/
-int Old_rows_log_event::find_row(const Relay_log_info *rli)
+int Old_rows_log_event::find_row(rpl_group_info *rgi)
{
DBUG_ENTER("find_row");
@@ -2249,7 +2210,7 @@ int Old_rows_log_event::find_row(const Relay_log_info *rli)
// TODO: shall we check and report errors here?
prepare_record(table, m_width, FALSE /* don't check errors */);
- error= unpack_current_row(rli);
+ error= unpack_current_row(rgi);
#ifndef DBUG_OFF
DBUG_PRINT("info",("looking for the following record"));
@@ -2387,7 +2348,7 @@ int Old_rows_log_event::find_row(const Relay_log_info *rli)
field in the BI image that is null and part of UNNI.
*/
bool null_found= FALSE;
- for (uint i=0; i < keyinfo->key_parts && !null_found; i++)
+ for (uint i=0; i < keyinfo->user_defined_key_parts && !null_found; i++)
{
uint fieldnr= keyinfo->key_part[i].fieldnr - 1;
Field **f= table->field+fieldnr;
@@ -2595,22 +2556,6 @@ Write_rows_log_event_old::do_before_row_operations(const Slave_reporting_capabil
from the start.
*/
m_table->file->ha_start_bulk_insert(0);
- /*
- We need TIMESTAMP_NO_AUTO_SET otherwise ha_write_row() will not use fill
- any TIMESTAMP column with data from the row but instead will use
- the event's current time.
- As we replicate from TIMESTAMP to TIMESTAMP and slave has no extra
- columns, we know that all TIMESTAMP columns on slave will receive explicit
- data from the row, so TIMESTAMP_NO_AUTO_SET is ok.
- When we allow a table without TIMESTAMP to be replicated to a table having
- more columns including a TIMESTAMP column, or when we allow a TIMESTAMP
- column to be replicated into a BIGINT column and the slave's table has a
- TIMESTAMP column, then the slave's TIMESTAMP column will take its value
- from set_time() which we called earlier (consistent with SBR). And then in
- some cases we won't want TIMESTAMP_NO_AUTO_SET (will require some code to
- analyze if explicit data is provided for slave's TIMESTAMP columns).
- */
- m_table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
return error;
}
@@ -2637,10 +2582,10 @@ Write_rows_log_event_old::do_after_row_operations(const Slave_reporting_capabili
int
-Write_rows_log_event_old::do_exec_row(const Relay_log_info *const rli)
+Write_rows_log_event_old::do_exec_row(rpl_group_info *rgi)
{
DBUG_ASSERT(m_table != NULL);
- int error= write_row(rli, TRUE /* overwrite */);
+ int error= write_row(rgi, TRUE /* overwrite */);
if (error && !thd->net.last_errno)
thd->net.last_errno= error;
@@ -2739,12 +2684,12 @@ Delete_rows_log_event_old::do_after_row_operations(const Slave_reporting_capabil
}
-int Delete_rows_log_event_old::do_exec_row(const Relay_log_info *const rli)
+int Delete_rows_log_event_old::do_exec_row(rpl_group_info *rgi)
{
int error;
DBUG_ASSERT(m_table != NULL);
- if (!(error= find_row(rli)))
+ if (!(error= find_row(rgi)))
{
/*
Delete the record found, located in record[0]
@@ -2820,8 +2765,6 @@ Update_rows_log_event_old::do_before_row_operations(const Slave_reporting_capabi
return HA_ERR_OUT_OF_MEM;
}
- m_table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
-
return 0;
}
@@ -2840,11 +2783,11 @@ Update_rows_log_event_old::do_after_row_operations(const Slave_reporting_capabil
int
-Update_rows_log_event_old::do_exec_row(const Relay_log_info *const rli)
+Update_rows_log_event_old::do_exec_row(rpl_group_info *rgi)
{
DBUG_ASSERT(m_table != NULL);
- int error= find_row(rli);
+ int error= find_row(rgi);
if (error)
{
/*
@@ -2852,7 +2795,7 @@ Update_rows_log_event_old::do_exec_row(const Relay_log_info *const rli)
able to skip to the next pair of updates
*/
m_curr_row= m_curr_row_end;
- unpack_current_row(rli);
+ unpack_current_row(rgi);
return error;
}
@@ -2870,7 +2813,7 @@ Update_rows_log_event_old::do_exec_row(const Relay_log_info *const rli)
store_record(m_table,record[1]);
m_curr_row= m_curr_row_end;
- error= unpack_current_row(rli); // this also updates m_curr_row_end
+ error= unpack_current_row(rgi); // this also updates m_curr_row_end
/*
Now we have the right row to update. The old row (the one we're
diff --git a/sql/log_event_old.h b/sql/log_event_old.h
index d9d9f25737b..7408e121f96 100644
--- a/sql/log_event_old.h
+++ b/sql/log_event_old.h
@@ -41,6 +41,9 @@
but we keep them this way for now. /Sven
*/
+/* These classes are based on the v1 RowsHeaderLen */
+#undef ROWS_HEADER_LEN
+#define ROWS_HEADER_LEN ROWS_HEADER_LEN_V1
/**
@class Old_rows_log_event
@@ -145,6 +148,7 @@ public:
{
return m_rows_buf && m_cols.bitmap;
}
+ bool is_part_of_group() { return 1; }
uint m_row_count; /* The number of rows added to the event */
@@ -195,15 +199,15 @@ protected:
const uchar *m_curr_row_end; /* One-after the end of the current row */
uchar *m_key; /* Buffer to keep key value during searches */
- int find_row(const Relay_log_info *const);
- int write_row(const Relay_log_info *const, const bool);
+ int find_row(rpl_group_info *);
+ int write_row(rpl_group_info *, const bool);
// Unpack the current row into m_table->record[0]
- int unpack_current_row(const Relay_log_info *const rli)
+ int unpack_current_row(rpl_group_info *rgi)
{
DBUG_ASSERT(m_table);
ASSERT_OR_RETURN_ERROR(m_curr_row < m_rows_end, HA_ERR_CORRUPT_EVENT);
- return ::unpack_row(rli, m_table, m_width, m_curr_row, &m_cols,
+ return ::unpack_row(rgi, m_table, m_width, m_curr_row, &m_cols,
&m_curr_row_end, &m_master_reclength, m_rows_end);
}
#endif
@@ -211,9 +215,9 @@ protected:
private:
#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
- virtual int do_apply_event(Relay_log_info const *rli);
- virtual int do_update_pos(Relay_log_info *rli);
- virtual enum_skip_reason do_shall_skip(Relay_log_info *rli);
+ virtual int do_apply_event(rpl_group_info *rgi);
+ virtual int do_update_pos(rpl_group_info *rgi);
+ virtual enum_skip_reason do_shall_skip(rpl_group_info *rgi);
/*
Primitive to prepare for a sequence of row executions.
@@ -264,7 +268,7 @@ private:
0 if execution succeeded, 1 if execution failed.
*/
- virtual int do_exec_row(const Relay_log_info *const rli) = 0;
+ virtual int do_exec_row(rpl_group_info *rgi) = 0;
#endif /* !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) */
/********** END OF CUT & PASTE FROM Rows_log_event **********/
@@ -272,7 +276,7 @@ private:
#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
- int do_apply_event(Old_rows_log_event*,const Relay_log_info*);
+ int do_apply_event(Old_rows_log_event*, rpl_group_info *rgi);
/*
Primitive to prepare for a sequence of row executions.
@@ -321,7 +325,7 @@ private:
RETURN VALUE
Error code, if something went wrong, 0 otherwise.
*/
- virtual int do_prepare_row(THD*, Relay_log_info const*, TABLE*,
+ virtual int do_prepare_row(THD*, rpl_group_info*, TABLE*,
uchar const *row_start,
uchar const **row_end) = 0;
@@ -384,7 +388,7 @@ private:
#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
virtual int do_before_row_operations(const Slave_reporting_capability *const);
virtual int do_after_row_operations(const Slave_reporting_capability *const,int);
- virtual int do_exec_row(const Relay_log_info *const);
+ virtual int do_exec_row(rpl_group_info *);
#endif
/********** END OF CUT & PASTE FROM Write_rows_log_event **********/
@@ -400,13 +404,13 @@ private:
#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
// use old definition of do_apply_event()
- virtual int do_apply_event(const Relay_log_info *rli)
- { return Old_rows_log_event::do_apply_event(this,rli); }
+ virtual int do_apply_event(rpl_group_info *rgi)
+ { return Old_rows_log_event::do_apply_event(this, rgi); }
// primitives for old version of do_apply_event()
virtual int do_before_row_operations(TABLE *table);
virtual int do_after_row_operations(TABLE *table, int error);
- virtual int do_prepare_row(THD*, Relay_log_info const*, TABLE*,
+ virtual int do_prepare_row(THD*, rpl_group_info*, TABLE*,
uchar const *row_start, uchar const **row_end);
virtual int do_exec_row(TABLE *table);
@@ -460,7 +464,7 @@ protected:
#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
virtual int do_before_row_operations(const Slave_reporting_capability *const);
virtual int do_after_row_operations(const Slave_reporting_capability *const,int);
- virtual int do_exec_row(const Relay_log_info *const);
+ virtual int do_exec_row(rpl_group_info *);
#endif /* !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) */
/********** END OF CUT & PASTE FROM Update_rows_log_event **********/
@@ -478,13 +482,13 @@ private:
#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
// use old definition of do_apply_event()
- virtual int do_apply_event(const Relay_log_info *rli)
- { return Old_rows_log_event::do_apply_event(this,rli); }
+ virtual int do_apply_event(rpl_group_info *rgi)
+ { return Old_rows_log_event::do_apply_event(this, rgi); }
// primitives for old version of do_apply_event()
virtual int do_before_row_operations(TABLE *table);
virtual int do_after_row_operations(TABLE *table, int error);
- virtual int do_prepare_row(THD*, Relay_log_info const*, TABLE*,
+ virtual int do_prepare_row(THD*, rpl_group_info*, TABLE*,
uchar const *row_start, uchar const **row_end);
virtual int do_exec_row(TABLE *table);
#endif /* !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) */
@@ -535,7 +539,7 @@ protected:
#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
virtual int do_before_row_operations(const Slave_reporting_capability *const);
virtual int do_after_row_operations(const Slave_reporting_capability *const,int);
- virtual int do_exec_row(const Relay_log_info *const);
+ virtual int do_exec_row(rpl_group_info *);
#endif
/********** END CUT & PASTE FROM Delete_rows_log_event **********/
@@ -553,13 +557,13 @@ private:
#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
// use old definition of do_apply_event()
- virtual int do_apply_event(const Relay_log_info *rli)
- { return Old_rows_log_event::do_apply_event(this,rli); }
+ virtual int do_apply_event(rpl_group_info *rgi)
+ { return Old_rows_log_event::do_apply_event(this, rgi); }
// primitives for old version of do_apply_event()
virtual int do_before_row_operations(TABLE *table);
virtual int do_after_row_operations(TABLE *table, int error);
- virtual int do_prepare_row(THD*, Relay_log_info const*, TABLE*,
+ virtual int do_prepare_row(THD*, rpl_group_info*, TABLE*,
uchar const *row_start, uchar const **row_end);
virtual int do_exec_row(TABLE *table);
#endif
diff --git a/sql/log_slow.h b/sql/log_slow.h
index 92a2d1bf4f6..3ae2060cc27 100644
--- a/sql/log_slow.h
+++ b/sql/log_slow.h
@@ -16,20 +16,23 @@
/* Defining what to log to slow log */
#define LOG_SLOW_VERBOSITY_INIT 0
-#define LOG_SLOW_VERBOSITY_INNODB 1 << 0
-#define LOG_SLOW_VERBOSITY_QUERY_PLAN 1 << 1
+#define LOG_SLOW_VERBOSITY_INNODB (1 << 0)
+#define LOG_SLOW_VERBOSITY_QUERY_PLAN (1 << 1)
+#define LOG_SLOW_VERBOSITY_EXPLAIN (1 << 2)
#define QPLAN_INIT QPLAN_QC_NO
-#define QPLAN_ADMIN 1 << 0
-#define QPLAN_FILESORT 1 << 1
-#define QPLAN_FILESORT_DISK 1 << 2
-#define QPLAN_FULL_JOIN 1 << 3
-#define QPLAN_FULL_SCAN 1 << 4
-#define QPLAN_QC 1 << 5
-#define QPLAN_QC_NO 1 << 6
-#define QPLAN_TMP_DISK 1 << 7
-#define QPLAN_TMP_TABLE 1 << 8
+#define QPLAN_ADMIN (1 << 0)
+#define QPLAN_FILESORT (1 << 1)
+#define QPLAN_FILESORT_DISK (1 << 2)
+#define QPLAN_FULL_JOIN (1 << 3)
+#define QPLAN_FULL_SCAN (1 << 4)
+#define QPLAN_QC (1 << 5)
+#define QPLAN_QC_NO (1 << 6)
+#define QPLAN_TMP_DISK (1 << 7)
+#define QPLAN_TMP_TABLE (1 << 8)
+#define QPLAN_FILESORT_PRIORITY_QUEUE (1 << 9)
+
/* ... */
-#define QPLAN_MAX ((ulong) 1) << 31 /* reserved as placeholder */
+#define QPLAN_MAX (((ulong) 1) << 31) /* reserved as placeholder */
diff --git a/sql/mdl.cc b/sql/mdl.cc
index 5183a9806c9..daa3d919c15 100644
--- a/sql/mdl.cc
+++ b/sql/mdl.cc
@@ -1,4 +1,4 @@
-/* Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved.
+/* Copyright (c) 2007, 2012, Oracle and/or its affiliates.
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
@@ -10,17 +10,21 @@
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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+ along with this program; if not, write to the Free Software Foundation,
+ 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */
#include "sql_class.h"
#include "debug_sync.h"
+#include "sql_array.h"
#include <hash.h>
#include <mysqld_error.h>
#include <mysql/plugin.h>
#include <mysql/service_thd_wait.h>
+#include <mysql/psi/mysql_stage.h>
+
#ifdef WITH_WSREP
+#include "debug_sync.h"
#include "wsrep_mysqld.h"
#include "wsrep_thd.h"
extern "C" my_thread_id wsrep_thd_thread_id(THD *thd);
@@ -30,7 +34,8 @@ void sql_print_information(const char *format, ...)
extern bool
wsrep_grant_mdl_exception(MDL_context *requestor_ctx,
- MDL_ticket *ticket);
+ MDL_ticket *ticket,
+ const MDL_key *key);
#endif /* WITH_WSREP */
#ifdef HAVE_PSI_INTERFACE
static PSI_mutex_key key_MDL_map_mutex;
@@ -38,7 +43,7 @@ static PSI_mutex_key key_MDL_wait_LOCK_wait_status;
static PSI_mutex_info all_mdl_mutexes[]=
{
- { &key_MDL_map_mutex, "MDL_map::mutex", PSI_FLAG_GLOBAL},
+ { &key_MDL_map_mutex, "MDL_map::mutex", 0},
{ &key_MDL_wait_LOCK_wait_status, "MDL_wait::LOCK_wait_status", 0}
};
@@ -64,20 +69,18 @@ static PSI_cond_info all_mdl_conds[]=
*/
static void init_mdl_psi_keys(void)
{
- const char *category= "sql";
int count;
- if (PSI_server == NULL)
- return;
-
count= array_elements(all_mdl_mutexes);
- PSI_server->register_mutex(category, all_mdl_mutexes, count);
+ mysql_mutex_register("sql", all_mdl_mutexes, count);
count= array_elements(all_mdl_rwlocks);
- PSI_server->register_rwlock(category, all_mdl_rwlocks, count);
+ mysql_rwlock_register("sql", all_mdl_rwlocks, count);
count= array_elements(all_mdl_conds);
- PSI_server->register_cond(category, all_mdl_conds, count);
+ mysql_cond_register("sql", all_mdl_conds, count);
+
+ MDL_key::init_psi_keys();
}
#endif /* HAVE_PSI_INTERFACE */
@@ -87,18 +90,36 @@ static void init_mdl_psi_keys(void)
belonging to certain namespace.
*/
-const char *MDL_key::m_namespace_to_wait_state_name[NAMESPACE_END]=
+PSI_stage_info MDL_key::m_namespace_to_wait_state_name[NAMESPACE_END]=
{
- "Waiting for global read lock",
- "Waiting for schema metadata lock",
- "Waiting for table metadata lock",
- "Waiting for stored function metadata lock",
- "Waiting for stored procedure metadata lock",
- "Waiting for trigger metadata lock",
- "Waiting for event metadata lock",
- "Waiting for commit lock"
+ {0, "Waiting for global read lock", 0},
+ {0, "Waiting for schema metadata lock", 0},
+ {0, "Waiting for table metadata lock", 0},
+ {0, "Waiting for stored function metadata lock", 0},
+ {0, "Waiting for stored procedure metadata lock", 0},
+ {0, "Waiting for trigger metadata lock", 0},
+ {0, "Waiting for event metadata lock", 0},
+ {0, "Waiting for commit lock", 0},
+ {0, "User lock", 0} /* Be compatible with old status. */
};
+#ifdef HAVE_PSI_INTERFACE
+void MDL_key::init_psi_keys()
+{
+ int i;
+ int count;
+ PSI_stage_info *info __attribute__((unused));
+
+ count= array_elements(MDL_key::m_namespace_to_wait_state_name);
+ for (i= 0; i<count; i++)
+ {
+ /* mysql_stage_register wants an array of pointers, registering 1 by 1. */
+ info= & MDL_key::m_namespace_to_wait_state_name[i];
+ mysql_stage_register("sql", &info, 1);
+ }
+}
+#endif
+
static bool mdl_initialized= 0;
@@ -107,22 +128,22 @@ class MDL_object_lock_cache_adapter;
/**
- A collection of all MDL locks. A singleton,
- there is only one instance of the map in the server.
+ A partition in a collection of all MDL locks.
+ MDL_map is partitioned for scalability reasons.
Maps MDL_key to MDL_lock instances.
*/
-class MDL_map
+class MDL_map_partition
{
public:
- void init();
- void destroy();
- MDL_lock *find_or_insert(const MDL_key *key);
- void remove(MDL_lock *lock);
+ MDL_map_partition();
+ ~MDL_map_partition();
+ inline MDL_lock *find_or_insert(const MDL_key *mdl_key);
+ unsigned long get_lock_owner(const MDL_key *key);
+ inline void remove(MDL_lock *lock);
private:
bool move_from_hash_to_lock_mutex(MDL_lock *lock);
-private:
- /** All acquired locks in the server. */
+ /** A partition of all acquired locks in the server. */
HASH m_locks;
/* Protects access to m_locks hash. */
mysql_mutex_t m_mutex;
@@ -145,10 +166,37 @@ private:
I_P_List_counter>
Lock_cache;
Lock_cache m_unused_locks_cache;
+ friend int mdl_iterate(int (*)(MDL_ticket *, void *), void *);
+};
+
+
+/**
+ Start-up parameter for the number of partitions of the MDL_lock hash.
+*/
+ulong mdl_locks_hash_partitions;
+
+/**
+ A collection of all MDL locks. A singleton,
+ there is only one instance of the map in the server.
+ Contains instances of MDL_map_partition
+*/
+
+class MDL_map
+{
+public:
+ void init();
+ void destroy();
+ MDL_lock *find_or_insert(const MDL_key *key);
+ unsigned long get_lock_owner(const MDL_key *key);
+ void remove(MDL_lock *lock);
+private:
+ /** Array of partitions where the locks are actually stored. */
+ Dynamic_array<MDL_map_partition *> m_partitions;
/** Pre-allocated MDL_lock object for GLOBAL namespace. */
MDL_lock *m_global_lock;
/** Pre-allocated MDL_lock object for COMMIT namespace. */
MDL_lock *m_commit_lock;
+ friend int mdl_iterate(int (*)(MDL_ticket *, void *), void *);
};
@@ -311,7 +359,7 @@ Deadlock_detection_visitor::opt_change_victim_to(MDL_context *new_victim)
class MDL_lock
{
public:
- typedef uchar bitmap_t;
+ typedef unsigned short bitmap_t;
class Ticket_list
{
@@ -392,7 +440,10 @@ public:
bool can_grant_lock(enum_mdl_type type, MDL_context *requstor_ctx,
bool ignore_lock_priority) const;
- inline static MDL_lock *create(const MDL_key *key);
+ inline static MDL_lock *create(const MDL_key *key,
+ MDL_map_partition *map_part);
+
+ inline unsigned long get_lock_owner() const;
void reschedule_waiters();
@@ -419,13 +470,14 @@ public:
public:
- MDL_lock(const MDL_key *key_arg)
+ MDL_lock(const MDL_key *key_arg, MDL_map_partition *map_part)
: key(key_arg),
m_hog_lock_count(0),
m_ref_usage(0),
m_ref_release(0),
m_is_destroyed(FALSE),
- m_version(0)
+ m_version(0),
+ m_map_part(map_part)
{
mysql_prlock_init(key_MDL_lock_rwlock, &m_rwlock);
}
@@ -438,18 +490,18 @@ public:
public:
/**
These three members are used to make it possible to separate
- the mdl_locks.m_mutex mutex and MDL_lock::m_rwlock in
+ the MDL_map_partition::m_mutex mutex and MDL_lock::m_rwlock in
MDL_map::find_or_insert() for increased scalability.
The 'm_is_destroyed' member is only set by destroyers that
- have both the mdl_locks.m_mutex and MDL_lock::m_rwlock, thus
+ have both the MDL_map_partition::m_mutex and MDL_lock::m_rwlock, thus
holding any of the mutexes is sufficient to read it.
The 'm_ref_usage; is incremented under protection by
- mdl_locks.m_mutex, but when 'm_is_destroyed' is set to TRUE, this
+ MDL_map_partition::m_mutex, but when 'm_is_destroyed' is set to TRUE, this
member is moved to be protected by the MDL_lock::m_rwlock.
This means that the MDL_map::find_or_insert() which only
holds the MDL_lock::m_rwlock can compare it to 'm_ref_release'
- without acquiring mdl_locks.m_mutex again and if equal it can also
- destroy the lock object safely.
+ without acquiring MDL_map_partition::m_mutex again and if equal
+ it can also destroy the lock object safely.
The 'm_ref_release' is incremented under protection by
MDL_lock::m_rwlock.
Note since we are only interested in equality of these two
@@ -463,19 +515,23 @@ public:
/**
We use the same idea and an additional version counter to support
caching of unused MDL_lock object for further re-use.
- This counter is incremented while holding both MDL_map::m_mutex and
- MDL_lock::m_rwlock locks each time when a MDL_lock is moved from
- the hash to the unused objects list (or destroyed).
+ This counter is incremented while holding both MDL_map_partition::m_mutex
+ and MDL_lock::m_rwlock locks each time when a MDL_lock is moved from
+ the partitioned hash to the paritioned unused objects list (or destroyed).
A thread, which has found a MDL_lock object for the key in the hash
- and then released the MDL_map::m_mutex before acquiring the
+ and then released the MDL_map_partition::m_mutex before acquiring the
MDL_lock::m_rwlock, can determine that this object was moved to the
unused objects list (or destroyed) while it held no locks by comparing
- the version value which it read while holding the MDL_map::m_mutex
+ the version value which it read while holding the MDL_map_partition::m_mutex
with the value read after acquiring the MDL_lock::m_rwlock.
Note that since it takes several years to overflow this counter such
theoretically possible overflows should not have any practical effects.
*/
ulonglong m_version;
+ /**
+ Partition of MDL_map where the lock is stored.
+ */
+ MDL_map_partition *m_map_part;
};
@@ -488,8 +544,8 @@ public:
class MDL_scoped_lock : public MDL_lock
{
public:
- MDL_scoped_lock(const MDL_key *key_arg)
- : MDL_lock(key_arg)
+ MDL_scoped_lock(const MDL_key *key_arg, MDL_map_partition *map_part)
+ : MDL_lock(key_arg, map_part)
{ }
virtual const bitmap_t *incompatible_granted_types_bitmap() const
@@ -529,8 +585,8 @@ private:
class MDL_object_lock : public MDL_lock
{
public:
- MDL_object_lock(const MDL_key *key_arg)
- : MDL_lock(key_arg)
+ MDL_object_lock(const MDL_key *key_arg, MDL_map_partition *map_part)
+ : MDL_lock(key_arg, map_part)
{ }
/**
@@ -563,7 +619,7 @@ public:
}
virtual bool needs_notification(const MDL_ticket *ticket) const
{
- return ticket->is_upgradable_or_exclusive();
+ return (ticket->get_type() >= MDL_SHARED_NO_WRITE);
}
virtual void notify_conflicting_locks(MDL_context *ctx);
@@ -660,33 +716,141 @@ void mdl_destroy()
}
-/** Initialize the global hash containing all MDL locks. */
+static inline int mdl_iterate_lock(MDL_lock *lock,
+ int (*callback)(MDL_ticket *ticket, void *arg),
+ void *arg)
+{
+ MDL_lock::Ticket_iterator ticket_it(lock->m_granted);
+ MDL_ticket *ticket;
+ int res= 0;
+ mysql_prlock_rdlock(&lock->m_rwlock);
+ while ((ticket= ticket_it++) && !(res= callback(ticket, arg))) /* no-op */;
+ mysql_prlock_unlock(&lock->m_rwlock);
+ return res;
+}
+
+
+int mdl_iterate(int (*callback)(MDL_ticket *ticket, void *arg), void *arg)
+{
+ DYNAMIC_ARRAY locks;
+ uint i, j;
+ int res;
+ DBUG_ENTER("mdl_iterate");
+
+ if ((res= mdl_iterate_lock(mdl_locks.m_global_lock, callback, arg)) ||
+ (res= mdl_iterate_lock(mdl_locks.m_commit_lock, callback, arg)))
+ DBUG_RETURN(res);
+
+ my_init_dynamic_array(&locks, sizeof(MDL_lock*), 512, 1, MYF(0));
+
+ for (i= 0; i < mdl_locks.m_partitions.elements(); i++)
+ {
+ MDL_map_partition *part= mdl_locks.m_partitions.at(i);
+ /* Collect all locks first */
+ mysql_mutex_lock(&part->m_mutex);
+ if (allocate_dynamic(&locks, part->m_locks.records))
+ {
+ res= 1;
+ mysql_mutex_unlock(&part->m_mutex);
+ break;
+ }
+ reset_dynamic(&locks);
+ for (j= 0; j < part->m_locks.records; j++)
+ {
+ MDL_lock *lock= (MDL_lock*) my_hash_element(&part->m_locks, j);
+ lock->m_ref_usage++;
+ insert_dynamic(&locks, &lock);
+ }
+ mysql_mutex_unlock(&part->m_mutex);
+
+ /* Now show them */
+ for (j= 0; j < locks.elements; j++)
+ {
+ MDL_lock *lock= (MDL_lock*) *dynamic_element(&locks, j, MDL_lock**);
+ res= mdl_iterate_lock(lock, callback, arg);
+
+ mysql_prlock_wrlock(&lock->m_rwlock);
+ uint ref_usage= lock->m_ref_usage;
+ uint ref_release= ++lock->m_ref_release;
+ bool is_destroyed= lock->m_is_destroyed;
+ mysql_prlock_unlock(&lock->m_rwlock);
+
+ if (unlikely(is_destroyed && ref_usage == ref_release))
+ MDL_lock::destroy(lock);
+
+ if (res)
+ break;
+ }
+ }
+ delete_dynamic(&locks);
+ DBUG_RETURN(res);
+}
+
+
+/** Initialize the container for all MDL locks. */
void MDL_map::init()
{
MDL_key global_lock_key(MDL_key::GLOBAL, "", "");
MDL_key commit_lock_key(MDL_key::COMMIT, "", "");
- mysql_mutex_init(key_MDL_map_mutex, &m_mutex, NULL);
- my_hash_init(&m_locks, &my_charset_bin, 16 /* FIXME */, 0, 0,
- mdl_locks_key, 0, 0);
- m_global_lock= MDL_lock::create(&global_lock_key);
- m_commit_lock= MDL_lock::create(&commit_lock_key);
+ m_global_lock= MDL_lock::create(&global_lock_key, NULL);
+ m_commit_lock= MDL_lock::create(&commit_lock_key, NULL);
+
+ for (uint i= 0; i < mdl_locks_hash_partitions; i++)
+ {
+ MDL_map_partition *part= new (std::nothrow) MDL_map_partition();
+ m_partitions.append(part);
+ }
}
+my_hash_value_type mdl_hash_function(const CHARSET_INFO *cs,
+ const uchar *key, size_t length)
+{
+ MDL_key *mdl_key= (MDL_key*) (key - offsetof(MDL_key, m_ptr));
+ return mdl_key->hash_value();
+}
+
+
+/** Initialize the partition in the container with all MDL locks. */
+
+MDL_map_partition::MDL_map_partition()
+{
+ mysql_mutex_init(key_MDL_map_mutex, &m_mutex, NULL);
+ my_hash_init2(&m_locks, 0, &my_charset_bin, 16 /* FIXME */, 0, 0,
+ mdl_locks_key, mdl_hash_function, 0, 0);
+};
+
+
/**
- Destroy the global hash containing all MDL locks.
+ Destroy the container for all MDL locks.
@pre It must be empty.
*/
void MDL_map::destroy()
{
+ MDL_lock::destroy(m_global_lock);
+ MDL_lock::destroy(m_commit_lock);
+
+ while (m_partitions.elements() > 0)
+ {
+ MDL_map_partition *part= m_partitions.pop();
+ delete part;
+ }
+}
+
+
+/**
+ Destroy the partition in container for all MDL locks.
+ @pre It must be empty.
+*/
+
+MDL_map_partition::~MDL_map_partition()
+{
DBUG_ASSERT(!m_locks.records);
mysql_mutex_destroy(&m_mutex);
my_hash_free(&m_locks);
- MDL_lock::destroy(m_global_lock);
- MDL_lock::destroy(m_commit_lock);
MDL_object_lock *lock;
while ((lock= m_unused_locks_cache.pop_front()))
@@ -706,13 +870,12 @@ void MDL_map::destroy()
MDL_lock* MDL_map::find_or_insert(const MDL_key *mdl_key)
{
MDL_lock *lock;
- my_hash_value_type hash_value;
if (mdl_key->mdl_namespace() == MDL_key::GLOBAL ||
mdl_key->mdl_namespace() == MDL_key::COMMIT)
{
/*
- Avoid locking m_mutex when lock for GLOBAL or COMMIT namespace is
+ Avoid locking any m_mutex when lock for GLOBAL or COMMIT namespace is
requested. Return pointer to pre-allocated MDL_lock instance instead.
Such an optimization allows to save one mutex lock/unlock for any
statement changing data.
@@ -730,13 +893,30 @@ MDL_lock* MDL_map::find_or_insert(const MDL_key *mdl_key)
return lock;
}
+ uint part_id= mdl_key->hash_value() % mdl_locks_hash_partitions;
+ MDL_map_partition *part= m_partitions.at(part_id);
+
+ return part->find_or_insert(mdl_key);
+}
- hash_value= my_calc_hash(&m_locks, mdl_key->ptr(), mdl_key->length());
+
+/**
+ Find MDL_lock object corresponding to the key and hash value in
+ MDL_map partition, create it if it does not exist.
+
+ @retval non-NULL - Success. MDL_lock instance for the key with
+ locked MDL_lock::m_rwlock.
+ @retval NULL - Failure (OOM).
+*/
+
+MDL_lock* MDL_map_partition::find_or_insert(const MDL_key *mdl_key)
+{
+ MDL_lock *lock;
retry:
mysql_mutex_lock(&m_mutex);
if (!(lock= (MDL_lock*) my_hash_search_using_hash_value(&m_locks,
- hash_value,
+ mdl_key->hash_value(),
mdl_key->ptr(),
mdl_key->length())))
{
@@ -764,7 +944,7 @@ retry:
}
else
{
- lock= MDL_lock::create(mdl_key);
+ lock= MDL_lock::create(mdl_key, this);
}
if (!lock || my_hash_insert(&m_locks, (uchar*)lock))
@@ -795,7 +975,7 @@ retry:
/**
- Release mdl_locks.m_mutex mutex and lock MDL_lock::m_rwlock for lock
+ Release MDL_map_partition::m_mutex mutex and lock MDL_lock::m_rwlock for lock
object from the hash. Handle situation when object was released
while we held no locks.
@@ -804,7 +984,7 @@ retry:
should re-try looking up MDL_lock object in the hash.
*/
-bool MDL_map::move_from_hash_to_lock_mutex(MDL_lock *lock)
+bool MDL_map_partition::move_from_hash_to_lock_mutex(MDL_lock *lock)
{
ulonglong version;
@@ -813,8 +993,8 @@ bool MDL_map::move_from_hash_to_lock_mutex(MDL_lock *lock)
/*
We increment m_ref_usage which is a reference counter protected by
- mdl_locks.m_mutex under the condition it is present in the hash and
- m_is_destroyed is FALSE.
+ MDL_map_partition::m_mutex under the condition it is present in the hash
+ and m_is_destroyed is FALSE.
*/
lock->m_ref_usage++;
/* Read value of the version counter under protection of m_mutex lock. */
@@ -868,6 +1048,55 @@ bool MDL_map::move_from_hash_to_lock_mutex(MDL_lock *lock)
/**
+ * Return thread id of the owner of the lock, if it is owned.
+ */
+
+unsigned long
+MDL_map::get_lock_owner(const MDL_key *mdl_key)
+{
+ MDL_lock *lock;
+ unsigned long res= 0;
+
+ if (mdl_key->mdl_namespace() == MDL_key::GLOBAL ||
+ mdl_key->mdl_namespace() == MDL_key::COMMIT)
+ {
+ lock= (mdl_key->mdl_namespace() == MDL_key::GLOBAL) ? m_global_lock :
+ m_commit_lock;
+ mysql_prlock_rdlock(&lock->m_rwlock);
+ res= lock->get_lock_owner();
+ mysql_prlock_unlock(&lock->m_rwlock);
+ }
+ else
+ {
+ uint part_id= mdl_key->hash_value() % mdl_locks_hash_partitions;
+ MDL_map_partition *part= m_partitions.at(part_id);
+ res= part->get_lock_owner(mdl_key);
+ }
+ return res;
+}
+
+
+
+unsigned long
+MDL_map_partition::get_lock_owner(const MDL_key *mdl_key)
+{
+ MDL_lock *lock;
+ unsigned long res= 0;
+
+ mysql_mutex_lock(&m_mutex);
+ lock= (MDL_lock*) my_hash_search_using_hash_value(&m_locks,
+ mdl_key->hash_value(),
+ mdl_key->ptr(),
+ mdl_key->length());
+ if (lock)
+ res= lock->get_lock_owner();
+ mysql_mutex_unlock(&m_mutex);
+
+ return res;
+}
+
+
+/**
Destroy MDL_lock object or delegate this responsibility to
whatever thread that holds the last outstanding reference to
it.
@@ -886,28 +1115,41 @@ void MDL_map::remove(MDL_lock *lock)
return;
}
+ lock->m_map_part->remove(lock);
+}
+
+
+/**
+ Destroy MDL_lock object belonging to specific MDL_map
+ partition or delegate this responsibility to whatever
+ thread that holds the last outstanding reference to it.
+*/
+
+void MDL_map_partition::remove(MDL_lock *lock)
+{
mysql_mutex_lock(&m_mutex);
my_hash_delete(&m_locks, (uchar*) lock);
/*
To let threads holding references to the MDL_lock object know that it was
moved to the list of unused objects or destroyed, we increment the version
- counter under protection of both MDL_map::m_mutex and MDL_lock::m_rwlock
- locks. This allows us to read the version value while having either one
- of those locks.
+ counter under protection of both MDL_map_partition::m_mutex and
+ MDL_lock::m_rwlock locks. This allows us to read the version value while
+ having either one of those locks.
*/
lock->m_version++;
if ((lock->key.mdl_namespace() != MDL_key::SCHEMA) &&
- (m_unused_locks_cache.elements() < mdl_locks_cache_size))
+ (m_unused_locks_cache.elements() <
+ mdl_locks_cache_size/mdl_locks_hash_partitions))
{
/*
This is an object of MDL_object_lock type and the cache of unused
objects has not reached its maximum size yet. So instead of destroying
object we move it to the list of unused objects to allow its later
re-use with possibly different key. Any threads holding references to
- this object (owning MDL_map::m_mutex or MDL_lock::m_rwlock) will notice
- this thanks to the fact that we have changed the MDL_lock::m_version
- counter.
+ this object (owning MDL_map_partition::m_mutex or MDL_lock::m_rwlock)
+ will notice this thanks to the fact that we have changed the
+ MDL_lock::m_version counter.
*/
DBUG_ASSERT(lock->key.mdl_namespace() != MDL_key::GLOBAL &&
lock->key.mdl_namespace() != MDL_key::COMMIT);
@@ -924,8 +1166,8 @@ void MDL_map::remove(MDL_lock *lock)
has the responsibility to release it.
Setting of m_is_destroyed to TRUE while holding _both_
- mdl_locks.m_mutex and MDL_lock::m_rwlock mutexes transfers the
- protection of m_ref_usage from mdl_locks.m_mutex to
+ MDL_map_partition::m_mutex and MDL_lock::m_rwlock mutexes transfers
+ the protection of m_ref_usage from MDL_map_partition::m_mutex to
MDL_lock::m_rwlock while removal of the object from the hash
(and cache of unused objects) makes it read-only. Therefore
whoever acquires MDL_lock::m_rwlock next will see the most up
@@ -955,7 +1197,8 @@ void MDL_map::remove(MDL_lock *lock)
*/
MDL_context::MDL_context()
- : m_thd(NULL),
+ :
+ m_owner(NULL),
m_needs_thr_lock_abort(FALSE),
m_waiting_for(NULL)
{
@@ -977,9 +1220,9 @@ MDL_context::MDL_context()
void MDL_context::destroy()
{
- DBUG_ASSERT(m_tickets[MDL_STATEMENT].is_empty() &&
- m_tickets[MDL_TRANSACTION].is_empty() &&
- m_tickets[MDL_EXPLICIT].is_empty());
+ DBUG_ASSERT(m_tickets[MDL_STATEMENT].is_empty());
+ DBUG_ASSERT(m_tickets[MDL_TRANSACTION].is_empty());
+ DBUG_ASSERT(m_tickets[MDL_EXPLICIT].is_empty());
mysql_prlock_destroy(&m_LOCK_waiting_for);
}
@@ -1044,16 +1287,17 @@ void MDL_request::init(const MDL_key *key_arg,
@note Also chooses an MDL_lock descendant appropriate for object namespace.
*/
-inline MDL_lock *MDL_lock::create(const MDL_key *mdl_key)
+inline MDL_lock *MDL_lock::create(const MDL_key *mdl_key,
+ MDL_map_partition *map_part)
{
switch (mdl_key->mdl_namespace())
{
case MDL_key::GLOBAL:
case MDL_key::SCHEMA:
case MDL_key::COMMIT:
- return new MDL_scoped_lock(mdl_key);
+ return new (std::nothrow) MDL_scoped_lock(mdl_key, map_part);
default:
- return new MDL_object_lock(mdl_key);
+ return new (std::nothrow) MDL_object_lock(mdl_key, map_part);
}
}
@@ -1078,7 +1322,8 @@ MDL_ticket *MDL_ticket::create(MDL_context *ctx_arg, enum_mdl_type type_arg
#endif
)
{
- return new MDL_ticket(ctx_arg, type_arg
+ return new (std::nothrow)
+ MDL_ticket(ctx_arg, type_arg
#ifndef DBUG_OFF
, duration_arg
#endif
@@ -1102,7 +1347,7 @@ void MDL_ticket::destroy(MDL_ticket *ticket)
uint MDL_ticket::get_deadlock_weight() const
{
return (m_lock->key.mdl_namespace() == MDL_key::GLOBAL ||
- m_type >= MDL_SHARED_NO_WRITE ?
+ m_type >= MDL_SHARED_UPGRADABLE ?
DEADLOCK_WEIGHT_DDL : DEADLOCK_WEIGHT_DML);
}
@@ -1171,6 +1416,7 @@ void MDL_wait::reset_status()
/**
Wait for the status to be assigned to this wait slot.
+ @param owner MDL context owner.
@param abs_timeout Absolute time after which waiting should stop.
@param set_status_on_timeout TRUE - If in case of timeout waiting
context should close the wait slot by
@@ -1182,34 +1428,43 @@ void MDL_wait::reset_status()
*/
MDL_wait::enum_wait_status
-MDL_wait::timed_wait(THD *thd, struct timespec *abs_timeout,
- bool set_status_on_timeout, const char *wait_state_name)
+MDL_wait::timed_wait(MDL_context_owner *owner, struct timespec *abs_timeout,
+ bool set_status_on_timeout,
+ const PSI_stage_info *wait_state_name)
{
- const char *old_msg;
+ PSI_stage_info old_stage;
enum_wait_status result;
int wait_result= 0;
DBUG_ENTER("MDL_wait::timed_wait");
mysql_mutex_lock(&m_LOCK_wait_status);
- old_msg= thd_enter_cond(thd, &m_COND_wait_status, &m_LOCK_wait_status,
- wait_state_name);
-
- thd_wait_begin(thd, THD_WAIT_META_DATA_LOCK);
- while (!m_wait_status && !thd->killed &&
+ owner->ENTER_COND(&m_COND_wait_status, &m_LOCK_wait_status,
+ wait_state_name, & old_stage);
+ thd_wait_begin(NULL, THD_WAIT_META_DATA_LOCK);
+ while (!m_wait_status && !owner->is_killed() &&
wait_result != ETIMEDOUT && wait_result != ETIME)
{
#ifdef WITH_WSREP
- if (wsrep_thd_is_BF(thd, true))
+ // Allow tests to block the applier thread using the DBUG facilities
+ DBUG_EXECUTE_IF("sync.wsrep_before_mdl_wait",
+ {
+ const char act[]=
+ "now "
+ "wait_for signal.wsrep_before_mdl_wait";
+ DBUG_ASSERT(!debug_sync_set_action((owner->get_thd()),
+ STRING_WITH_LEN(act)));
+ };);
+ if (wsrep_thd_is_BF(owner->get_thd(), false))
{
wait_result= mysql_cond_wait(&m_COND_wait_status, &m_LOCK_wait_status);
}
else
-#endif
+#endif /* WITH_WSREP */
wait_result= mysql_cond_timedwait(&m_COND_wait_status, &m_LOCK_wait_status,
abs_timeout);
}
- thd_wait_end(thd);
+ thd_wait_end(NULL);
if (m_wait_status == EMPTY)
{
@@ -1225,14 +1480,14 @@ MDL_wait::timed_wait(THD *thd, struct timespec *abs_timeout,
false, which means that the caller intends to restart the
wait.
*/
- if (thd->killed)
+ if (owner->is_killed())
m_wait_status= KILLED;
else if (set_status_on_timeout)
m_wait_status= TIMEOUT;
}
result= m_wait_status;
- thd_exit_cond(thd, old_msg);
+ owner->EXIT_COND(& old_stage);
DBUG_RETURN(result);
}
@@ -1274,7 +1529,7 @@ void MDL_lock::Ticket_list::add_ticket(MDL_ticket *ticket)
DBUG_ASSERT(ticket->get_lock());
#ifdef WITH_WSREP
if ((this == &(ticket->get_lock()->m_waiting)) &&
- wsrep_thd_is_BF((void *)(ticket->get_ctx()->get_thd()), false))
+ wsrep_thd_is_BF((void *)(ticket->get_ctx()->wsrep_get_thd()), false))
{
Ticket_iterator itw(ticket->get_lock()->m_waiting);
Ticket_iterator itg(ticket->get_lock()->m_granted);
@@ -1285,11 +1540,11 @@ void MDL_lock::Ticket_list::add_ticket(MDL_ticket *ticket)
while ((waiting= itw++) && !added)
{
- if (!wsrep_thd_is_BF((void *)(waiting->get_ctx()->get_thd()), true))
+ if (!wsrep_thd_is_BF((void *)(waiting->get_ctx()->wsrep_get_thd()), true))
{
- WSREP_DEBUG("MDL add_ticket inserted before: %lu %s",
- wsrep_thd_thread_id(waiting->get_ctx()->get_thd()),
- wsrep_thd_query(waiting->get_ctx()->get_thd()));
+ WSREP_DEBUG("MDL add_ticket inserted before: %lu %s",
+ wsrep_thd_thread_id(waiting->get_ctx()->wsrep_get_thd()),
+ wsrep_thd_query(waiting->get_ctx()->wsrep_get_thd()));
/* Insert the ticket before the first non-BF waiting thd. */
m_list.insert_after(prev, ticket);
added= true;
@@ -1305,7 +1560,8 @@ void MDL_lock::Ticket_list::add_ticket(MDL_ticket *ticket)
if (granted->get_ctx() != ticket->get_ctx() &&
granted->is_incompatible_when_granted(ticket->get_type()))
{
- if (!wsrep_grant_mdl_exception(ticket->get_ctx(), granted))
+ if (!wsrep_grant_mdl_exception(ticket->get_ctx(), granted,
+ &ticket->get_lock()->key))
{
WSREP_DEBUG("MDL victim killed at add_ticket");
}
@@ -1487,22 +1743,12 @@ void MDL_lock::reschedule_waiters()
lock. Arrays of bitmaps which elements specify which granted/waiting locks
are incompatible with type of lock being requested.
- Here is how types of individual locks are translated to type of scoped lock:
-
- ----------------+-------------+
- Type of request | Correspond. |
- for indiv. lock | scoped lock |
- ----------------+-------------+
- S, SH, SR, SW | IS |
- SNW, SNRW, X | IX |
- SNW, SNRW -> X | IX (*) |
-
The first array specifies if particular type of request can be satisfied
if there is granted scoped lock of certain type.
| Type of active |
Request | scoped lock |
- type | IS(**) IX S X |
+ type | IS(*) IX S X |
---------+------------------+
IS | + + + + |
IX | + + - - |
@@ -1515,7 +1761,7 @@ void MDL_lock::reschedule_waiters()
| Pending |
Request | scoped lock |
- type | IS(**) IX S X |
+ type | IS(*) IX S X |
---------+-----------------+
IS | + + + + |
IX | + + - - |
@@ -1525,24 +1771,33 @@ void MDL_lock::reschedule_waiters()
Here: "+" -- means that request can be satisfied
"-" -- means that request can't be satisfied and should wait
- (*) Since for upgradable locks we always take intention exclusive scoped
- lock at the same time when obtaining the shared lock, there is no
- need to obtain such lock during the upgrade itself.
- (**) Since intention shared scoped locks are compatible with all other
- type of locks we don't even have any accounting for them.
+ (*) Since intention shared scoped locks are compatible with all other
+ type of locks we don't even have any accounting for them.
+
+ Note that relation between scoped locks and objects locks requested
+ by statement is not straightforward and is therefore fully defined
+ by SQL-layer.
+ For example, in order to support global read lock implementation
+ SQL-layer acquires IX lock in GLOBAL namespace for each statement
+ that can modify metadata or data (i.e. for each statement that
+ needs SW, SU, SNW, SNRW or X object locks). OTOH, to ensure that
+ DROP DATABASE works correctly with concurrent DDL, IX metadata locks
+ in SCHEMA namespace are acquired for DDL statements which can update
+ metadata in the schema (i.e. which acquire SU, SNW, SNRW and X locks
+ on schema objects) and aren't acquired for DML.
*/
const MDL_lock::bitmap_t MDL_scoped_lock::m_granted_incompatible[MDL_TYPE_END] =
{
MDL_BIT(MDL_EXCLUSIVE) | MDL_BIT(MDL_SHARED),
- MDL_BIT(MDL_EXCLUSIVE) | MDL_BIT(MDL_INTENTION_EXCLUSIVE), 0, 0, 0, 0, 0,
+ MDL_BIT(MDL_EXCLUSIVE) | MDL_BIT(MDL_INTENTION_EXCLUSIVE), 0, 0, 0, 0, 0, 0,
MDL_BIT(MDL_EXCLUSIVE) | MDL_BIT(MDL_SHARED) | MDL_BIT(MDL_INTENTION_EXCLUSIVE)
};
const MDL_lock::bitmap_t MDL_scoped_lock::m_waiting_incompatible[MDL_TYPE_END] =
{
MDL_BIT(MDL_EXCLUSIVE) | MDL_BIT(MDL_SHARED),
- MDL_BIT(MDL_EXCLUSIVE), 0, 0, 0, 0, 0, 0
+ MDL_BIT(MDL_EXCLUSIVE), 0, 0, 0, 0, 0, 0, 0
};
@@ -1554,35 +1809,39 @@ const MDL_lock::bitmap_t MDL_scoped_lock::m_waiting_incompatible[MDL_TYPE_END] =
The first array specifies if particular type of request can be satisfied
if there is granted lock of certain type.
- Request | Granted requests for lock |
- type | S SH SR SW SNW SNRW X |
- ----------+------------------------------+
- S | + + + + + + - |
- SH | + + + + + + - |
- SR | + + + + + - - |
- SW | + + + + - - - |
- SNW | + + + - - - - |
- SNRW | + + - - - - - |
- X | - - - - - - - |
- SNW -> X | - - - 0 0 0 0 |
- SNRW -> X | - - 0 0 0 0 0 |
+ Request | Granted requests for lock |
+ type | S SH SR SW SU SNW SNRW X |
+ ----------+----------------------------------+
+ S | + + + + + + + - |
+ SH | + + + + + + + - |
+ SR | + + + + + + - - |
+ SW | + + + + + - - - |
+ SU | + + + + - - - - |
+ SNW | + + + - - - - - |
+ SNRW | + + - - - - - - |
+ X | - - - - - - - - |
+ SU -> X | - - - - 0 0 0 0 |
+ SNW -> X | - - - 0 0 0 0 0 |
+ SNRW -> X | - - 0 0 0 0 0 0 |
The second array specifies if particular type of request can be satisfied
if there is waiting request for the same lock of certain type. In other
words it specifies what is the priority of different lock types.
- Request | Pending requests for lock |
- type | S SH SR SW SNW SNRW X |
- ----------+-----------------------------+
- S | + + + + + + - |
- SH | + + + + + + + |
- SR | + + + + + - - |
- SW | + + + + - - - |
- SNW | + + + + + + - |
- SNRW | + + + + + + - |
- X | + + + + + + + |
- SNW -> X | + + + + + + + |
- SNRW -> X | + + + + + + + |
+ Request | Pending requests for lock |
+ type | S SH SR SW SU SNW SNRW X |
+ ----------+---------------------------------+
+ S | + + + + + + + - |
+ SH | + + + + + + + + |
+ SR | + + + + + + - - |
+ SW | + + + + + - - - |
+ SU | + + + + + + + - |
+ SNW | + + + + + + + - |
+ SNRW | + + + + + + + - |
+ X | + + + + + + + + |
+ SU -> X | + + + + + + + + |
+ SNW -> X | + + + + + + + + |
+ SNRW -> X | + + + + + + + + |
Here: "+" -- means that request can be satisfied
"-" -- means that request can't be satisfied and should wait
@@ -1591,6 +1850,9 @@ const MDL_lock::bitmap_t MDL_scoped_lock::m_waiting_incompatible[MDL_TYPE_END] =
@note In cases then current context already has "stronger" type
of lock on the object it will be automatically granted
thanks to usage of the MDL_context::find_ticket() method.
+
+ @note IX locks are excluded since they are not used for per-object
+ metadata locks.
*/
const MDL_lock::bitmap_t
@@ -1603,14 +1865,17 @@ MDL_object_lock::m_granted_incompatible[MDL_TYPE_END] =
MDL_BIT(MDL_EXCLUSIVE) | MDL_BIT(MDL_SHARED_NO_READ_WRITE) |
MDL_BIT(MDL_SHARED_NO_WRITE),
MDL_BIT(MDL_EXCLUSIVE) | MDL_BIT(MDL_SHARED_NO_READ_WRITE) |
- MDL_BIT(MDL_SHARED_NO_WRITE) | MDL_BIT(MDL_SHARED_WRITE),
+ MDL_BIT(MDL_SHARED_NO_WRITE) | MDL_BIT(MDL_SHARED_UPGRADABLE),
MDL_BIT(MDL_EXCLUSIVE) | MDL_BIT(MDL_SHARED_NO_READ_WRITE) |
- MDL_BIT(MDL_SHARED_NO_WRITE) | MDL_BIT(MDL_SHARED_WRITE) |
- MDL_BIT(MDL_SHARED_READ),
+ MDL_BIT(MDL_SHARED_NO_WRITE) | MDL_BIT(MDL_SHARED_UPGRADABLE) |
+ MDL_BIT(MDL_SHARED_WRITE),
MDL_BIT(MDL_EXCLUSIVE) | MDL_BIT(MDL_SHARED_NO_READ_WRITE) |
- MDL_BIT(MDL_SHARED_NO_WRITE) | MDL_BIT(MDL_SHARED_WRITE) |
- MDL_BIT(MDL_SHARED_READ) | MDL_BIT(MDL_SHARED_HIGH_PRIO) |
- MDL_BIT(MDL_SHARED)
+ MDL_BIT(MDL_SHARED_NO_WRITE) | MDL_BIT(MDL_SHARED_UPGRADABLE) |
+ MDL_BIT(MDL_SHARED_WRITE) | MDL_BIT(MDL_SHARED_READ),
+ MDL_BIT(MDL_EXCLUSIVE) | MDL_BIT(MDL_SHARED_NO_READ_WRITE) |
+ MDL_BIT(MDL_SHARED_NO_WRITE) | MDL_BIT(MDL_SHARED_UPGRADABLE) |
+ MDL_BIT(MDL_SHARED_WRITE) | MDL_BIT(MDL_SHARED_READ) |
+ MDL_BIT(MDL_SHARED_HIGH_PRIO) | MDL_BIT(MDL_SHARED)
};
@@ -1625,6 +1890,7 @@ MDL_object_lock::m_waiting_incompatible[MDL_TYPE_END] =
MDL_BIT(MDL_SHARED_NO_WRITE),
MDL_BIT(MDL_EXCLUSIVE),
MDL_BIT(MDL_EXCLUSIVE),
+ MDL_BIT(MDL_EXCLUSIVE),
0
};
@@ -1679,15 +1945,15 @@ MDL_lock::can_grant_lock(enum_mdl_type type_arg,
ticket->is_incompatible_when_granted(type_arg))
#ifdef WITH_WSREP
{
- if (wsrep_thd_is_BF((void *)(requestor_ctx->get_thd()), false) &&
+ if (wsrep_thd_is_BF((void *)(requestor_ctx->wsrep_get_thd()),false) &&
key.mdl_namespace() == MDL_key::GLOBAL)
{
WSREP_DEBUG("global lock granted for BF: %lu %s",
- wsrep_thd_thread_id(requestor_ctx->get_thd()),
- wsrep_thd_query(requestor_ctx->get_thd()));
+ wsrep_thd_thread_id(requestor_ctx->wsrep_get_thd()),
+ wsrep_thd_query(requestor_ctx->wsrep_get_thd()));
can_grant = true;
}
- else if (!wsrep_grant_mdl_exception(requestor_ctx, ticket))
+ else if (!wsrep_grant_mdl_exception(requestor_ctx, ticket, &key))
{
wsrep_can_grant= FALSE;
if (wsrep_log_conflicts)
@@ -1720,12 +1986,12 @@ MDL_lock::can_grant_lock(enum_mdl_type type_arg,
#ifdef WITH_WSREP
else
{
- if (wsrep_thd_is_BF((void *)(requestor_ctx->get_thd()), false) &&
+ if (wsrep_thd_is_BF((void *)(requestor_ctx->wsrep_get_thd()), false) &&
key.mdl_namespace() == MDL_key::GLOBAL)
{
WSREP_DEBUG("global lock granted for BF (waiting queue): %lu %s",
- wsrep_thd_thread_id(requestor_ctx->get_thd()),
- wsrep_thd_query(requestor_ctx->get_thd()));
+ wsrep_thd_thread_id(requestor_ctx->wsrep_get_thd()),
+ wsrep_thd_query(requestor_ctx->wsrep_get_thd()));
can_grant = true;
}
}
@@ -1734,6 +2000,23 @@ MDL_lock::can_grant_lock(enum_mdl_type type_arg,
}
+/**
+ Return thread id of the thread to which the first ticket was
+ granted.
+*/
+
+inline unsigned long
+MDL_lock::get_lock_owner() const
+{
+ Ticket_iterator it(m_granted);
+ MDL_ticket *ticket;
+
+ if ((ticket= it++))
+ return ticket->get_ctx()->get_thread_id();
+ return 0;
+}
+
+
/** Remove a ticket from waiting or pending queue and wakeup up waiters. */
void MDL_lock::remove_ticket(Ticket_list MDL_lock::*list, MDL_ticket *ticket)
@@ -1777,8 +2060,6 @@ bool MDL_lock::has_pending_conflicting_lock(enum_mdl_type type)
{
bool result;
- mysql_mutex_assert_not_owner(&LOCK_open);
-
mysql_prlock_rdlock(&m_rwlock);
result= (m_waiting.bitmap() & incompatible_granted_types_bitmap()[type]);
mysql_prlock_unlock(&m_rwlock);
@@ -1860,6 +2141,8 @@ MDL_context::find_ticket(MDL_request *mdl_request,
if (mdl_request->key.is_equal(&ticket->m_lock->key) &&
ticket->has_stronger_or_equal_type(mdl_request->type))
{
+ DBUG_PRINT("info", ("Adding mdl lock %d to %d",
+ mdl_request->type, ticket->m_type));
*result_duration= duration;
return ticket;
}
@@ -1951,7 +2234,6 @@ MDL_context::try_acquire_lock_impl(MDL_request *mdl_request,
/* Don't take chances in production. */
mdl_request->ticket= NULL;
- mysql_mutex_assert_not_owner(&LOCK_open);
/*
Check whether the context already holds a shared lock on the object,
@@ -2041,7 +2323,6 @@ MDL_context::clone_ticket(MDL_request *mdl_request)
{
MDL_ticket *ticket;
- mysql_mutex_assert_not_owner(&LOCK_open);
/*
By submitting mdl_request->type to MDL_ticket::create()
we effectively downgrade the cloned lock to the level of
@@ -2086,7 +2367,7 @@ void MDL_object_lock::notify_conflicting_locks(MDL_context *ctx)
{
/* Only try to abort locks on which we back off. */
if (conflicting_ticket->get_ctx() != ctx &&
- conflicting_ticket->get_type() < MDL_SHARED_NO_WRITE)
+ conflicting_ticket->get_type() < MDL_SHARED_UPGRADABLE)
{
MDL_context *conflicting_ctx= conflicting_ticket->get_ctx();
@@ -2096,9 +2377,9 @@ void MDL_object_lock::notify_conflicting_locks(MDL_context *ctx)
lock or some other non-MDL resource we might need to wake it up
by calling code outside of MDL.
*/
- mysql_notify_thread_having_shared_lock(ctx->get_thd(),
- conflicting_ctx->get_thd(),
- conflicting_ctx->get_needs_thr_lock_abort());
+ ctx->get_owner()->
+ notify_shared_lock(conflicting_ctx->get_owner(),
+ conflicting_ctx->get_needs_thr_lock_abort());
}
}
}
@@ -2128,9 +2409,9 @@ void MDL_scoped_lock::notify_conflicting_locks(MDL_context *ctx)
insert delayed. We need to kill such threads in order to get
global shared lock. We do this my calling code outside of MDL.
*/
- mysql_notify_thread_having_shared_lock(ctx->get_thd(),
- conflicting_ctx->get_thd(),
- conflicting_ctx->get_needs_thr_lock_abort());
+ ctx->get_owner()->
+ notify_shared_lock(conflicting_ctx->get_owner(),
+ conflicting_ctx->get_needs_thr_lock_abort());
}
}
}
@@ -2156,6 +2437,7 @@ MDL_context::acquire_lock(MDL_request *mdl_request, ulong lock_wait_timeout)
struct timespec abs_timeout;
MDL_wait::enum_wait_status wait_status;
DBUG_ENTER("MDL_context::acquire_lock");
+ DBUG_PRINT("enter", ("lock_type: %d", mdl_request->type));
/* Do some work outside the critical section. */
set_timespec(abs_timeout, lock_wait_timeout);
@@ -2170,6 +2452,7 @@ MDL_context::acquire_lock(MDL_request *mdl_request, ulong lock_wait_timeout)
MDL_lock, MDL_context and MDL_request were updated
accordingly, so we can simply return success.
*/
+ DBUG_PRINT("info", ("Got lock without waiting"));
DBUG_RETURN(FALSE);
}
@@ -2203,36 +2486,42 @@ MDL_context::acquire_lock(MDL_request *mdl_request, ulong lock_wait_timeout)
will_wait_for(ticket);
/* There is a shared or exclusive lock on the object. */
- DEBUG_SYNC(m_thd, "mdl_acquire_lock_wait");
+ DEBUG_SYNC(get_thd(), "mdl_acquire_lock_wait");
find_deadlock();
- if (lock->needs_notification(ticket))
+ struct timespec abs_shortwait;
+ set_timespec(abs_shortwait, 1);
+ wait_status= MDL_wait::EMPTY;
+
+ while (cmp_timespec(abs_shortwait, abs_timeout) <= 0)
{
- struct timespec abs_shortwait;
- set_timespec(abs_shortwait, 1);
- wait_status= MDL_wait::EMPTY;
+ /* abs_timeout is far away. Wait a short while and notify locks. */
+ wait_status= m_wait.timed_wait(m_owner, &abs_shortwait, FALSE,
+ mdl_request->key.get_wait_state_name());
- while (cmp_timespec(abs_shortwait, abs_timeout) <= 0)
+ if (wait_status != MDL_wait::EMPTY)
+ break;
+ /* Check if the client is gone while we were waiting. */
+ if (! thd_is_connected(m_owner->get_thd()))
{
- /* abs_timeout is far away. Wait a short while and notify locks. */
- wait_status= m_wait.timed_wait(m_thd, &abs_shortwait, FALSE,
- mdl_request->key.get_wait_state_name());
-
- if (wait_status != MDL_wait::EMPTY)
- break;
+ /*
+ * The client is disconnected. Don't wait forever:
+ * assume it's the same as a wait timeout, this
+ * ensures all error handling is correct.
+ */
+ wait_status= MDL_wait::TIMEOUT;
+ break;
+ }
- mysql_prlock_wrlock(&lock->m_rwlock);
+ mysql_prlock_wrlock(&lock->m_rwlock);
+ if (lock->needs_notification(ticket))
lock->notify_conflicting_locks(this);
- mysql_prlock_unlock(&lock->m_rwlock);
- set_timespec(abs_shortwait, 1);
- }
- if (wait_status == MDL_wait::EMPTY)
- wait_status= m_wait.timed_wait(m_thd, &abs_timeout, TRUE,
- mdl_request->key.get_wait_state_name());
+ mysql_prlock_unlock(&lock->m_rwlock);
+ set_timespec(abs_shortwait, 1);
}
- else
- wait_status= m_wait.timed_wait(m_thd, &abs_timeout, TRUE,
+ if (wait_status == MDL_wait::EMPTY)
+ wait_status= m_wait.timed_wait(m_owner, &abs_timeout, TRUE,
mdl_request->key.get_wait_state_name());
done_waiting_for();
@@ -2250,6 +2539,7 @@ MDL_context::acquire_lock(MDL_request *mdl_request, ulong lock_wait_timeout)
my_error(ER_LOCK_WAIT_TIMEOUT, MYF(0));
break;
case MDL_wait::KILLED:
+ get_thd()->send_kill_message();
break;
default:
DBUG_ASSERT(0);
@@ -2353,11 +2643,12 @@ err:
/**
- Upgrade a shared metadata lock to exclusive.
+ Upgrade a shared metadata lock.
- Used in ALTER TABLE, when a copy of the table with the
- new definition has been constructed.
+ Used in ALTER TABLE.
+ @param mdl_ticket Lock to upgrade.
+ @param new_type Lock type to upgrade to.
@param lock_wait_timeout Seconds to wait before timeout.
@note In case of failure to upgrade lock (e.g. because upgrader
@@ -2365,7 +2656,7 @@ err:
shared mode).
@note There can be only one upgrader for a lock or we will have deadlock.
- This invariant is ensured by the fact that upgradeable locks SNW
+ This invariant is ensured by the fact that upgradeable locks SU, SNW
and SNRW are not compatible with each other and themselves.
@retval FALSE Success
@@ -2373,28 +2664,31 @@ err:
*/
bool
-MDL_context::upgrade_shared_lock_to_exclusive(MDL_ticket *mdl_ticket,
- ulong lock_wait_timeout)
+MDL_context::upgrade_shared_lock(MDL_ticket *mdl_ticket,
+ enum_mdl_type new_type,
+ ulong lock_wait_timeout)
{
MDL_request mdl_xlock_request;
MDL_savepoint mdl_svp= mdl_savepoint();
bool is_new_ticket;
-
- DBUG_ENTER("MDL_ticket::upgrade_shared_lock_to_exclusive");
- DEBUG_SYNC(get_thd(), "mdl_upgrade_shared_lock_to_exclusive");
+ DBUG_ENTER("MDL_context::upgrade_shared_lock");
+ DBUG_PRINT("enter",("new_type: %d lock_wait_timeout: %lu", new_type,
+ lock_wait_timeout));
+ DEBUG_SYNC(get_thd(), "mdl_upgrade_lock");
/*
Do nothing if already upgraded. Used when we FLUSH TABLE under
LOCK TABLES and a table is listed twice in LOCK TABLES list.
*/
- if (mdl_ticket->m_type == MDL_EXCLUSIVE)
+ if (mdl_ticket->has_stronger_or_equal_type(new_type))
DBUG_RETURN(FALSE);
- /* Only allow upgrades from MDL_SHARED_NO_WRITE/NO_READ_WRITE */
- DBUG_ASSERT(mdl_ticket->m_type == MDL_SHARED_NO_WRITE ||
+ /* Only allow upgrades from SHARED_UPGRADABLE/NO_WRITE/NO_READ_WRITE */
+ DBUG_ASSERT(mdl_ticket->m_type == MDL_SHARED_UPGRADABLE ||
+ mdl_ticket->m_type == MDL_SHARED_NO_WRITE ||
mdl_ticket->m_type == MDL_SHARED_NO_READ_WRITE);
- mdl_xlock_request.init(&mdl_ticket->m_lock->key, MDL_EXCLUSIVE,
+ mdl_xlock_request.init(&mdl_ticket->m_lock->key, new_type,
MDL_TRANSACTION);
if (acquire_lock(&mdl_xlock_request, lock_wait_timeout))
@@ -2412,7 +2706,7 @@ MDL_context::upgrade_shared_lock_to_exclusive(MDL_ticket *mdl_ticket,
ticket from the granted queue and then include it back.
*/
mdl_ticket->m_lock->m_granted.remove_ticket(mdl_ticket);
- mdl_ticket->m_type= MDL_EXCLUSIVE;
+ mdl_ticket->m_type= new_type;
mdl_ticket->m_lock->m_granted.add_ticket(mdl_ticket);
mysql_prlock_unlock(&mdl_ticket->m_lock->m_rwlock);
@@ -2686,11 +2980,10 @@ void MDL_context::release_lock(enum_mdl_duration duration, MDL_ticket *ticket)
{
MDL_lock *lock= ticket->m_lock;
DBUG_ENTER("MDL_context::release_lock");
- DBUG_PRINT("enter", ("db=%s name=%s", lock->key.db_name(),
- lock->key.name()));
+ DBUG_PRINT("enter", ("db: '%s' name: '%s'",
+ lock->key.db_name(), lock->key.name()));
DBUG_ASSERT(this == ticket->get_ctx());
- mysql_mutex_assert_not_owner(&LOCK_open);
lock->remove_ticket(&MDL_lock::m_granted, ticket);
@@ -2725,7 +3018,7 @@ void MDL_context::release_lock(MDL_ticket *ticket)
the corresponding lists, i.e. stored in reverse temporal order.
This allows to employ this function to:
- back off in case of a lock conflict.
- - release all locks in the end of a statment or transaction
+ - release all locks in the end of a statement or transaction
- rollback to a savepoint.
*/
@@ -2781,22 +3074,27 @@ void MDL_context::release_all_locks_for_name(MDL_ticket *name)
/**
- Downgrade an exclusive lock to shared metadata lock.
+ Downgrade an EXCLUSIVE or SHARED_NO_WRITE lock to shared metadata lock.
@param type Type of lock to which exclusive lock should be downgraded.
*/
-void MDL_ticket::downgrade_exclusive_lock(enum_mdl_type type)
+void MDL_ticket::downgrade_lock(enum_mdl_type type)
{
- mysql_mutex_assert_not_owner(&LOCK_open);
-
/*
Do nothing if already downgraded. Used when we FLUSH TABLE under
LOCK TABLES and a table is listed twice in LOCK TABLES list.
+ Note that this code might even try to "downgrade" a weak lock
+ (e.g. SW) to a stronger one (e.g SNRW). So we can't even assert
+ here that target lock is weaker than existing lock.
*/
- if (m_type != MDL_EXCLUSIVE)
+ if (m_type == type || !has_stronger_or_equal_type(type))
return;
+ /* Only allow downgrade from EXCLUSIVE and SHARED_NO_WRITE. */
+ DBUG_ASSERT(m_type == MDL_EXCLUSIVE ||
+ m_type == MDL_SHARED_NO_WRITE);
+
mysql_prlock_wrlock(&m_lock->m_rwlock);
/*
To update state of MDL_lock object correctly we need to temporarily
@@ -2842,6 +3140,22 @@ MDL_context::is_lock_owner(MDL_key::enum_mdl_namespace mdl_namespace,
/**
+ Return thread id of the owner of the lock or 0 if
+ there is no owner.
+ @note: Lock type is not considered at all, the function
+ simply checks that there is some lock for the given key.
+
+ @return thread id of the owner of the lock or 0
+*/
+
+unsigned long
+MDL_context::get_lock_owner(MDL_key *key)
+{
+ return mdl_locks.get_lock_owner(key);
+}
+
+
+/**
Check if we have any pending locks which conflict with existing shared lock.
@pre The ticket must match an acquired lock.
@@ -2854,6 +3168,11 @@ bool MDL_ticket::has_pending_conflicting_lock() const
return m_lock->has_pending_conflicting_lock(m_type);
}
+/** Return a key identifying this lock. */
+MDL_key *MDL_ticket::get_key() const
+{
+ return &m_lock->key;
+}
/**
Releases metadata locks that were acquired after a specific savepoint.
@@ -3038,8 +3357,10 @@ void MDL_context::set_transaction_duration_for_all_locks()
#ifdef WITH_WSREP
void MDL_ticket::wsrep_report(bool debug)
{
- if (debug)
- {
+ if (debug)
+ {
+ const PSI_stage_info *psi_stage = m_lock->key.get_wait_state_name();
+
WSREP_DEBUG("MDL ticket: type: %s space: %s db: %s name: %s (%s)",
(get_type() == MDL_INTENTION_EXCLUSIVE) ? "intention exclusive" :
((get_type() == MDL_SHARED) ? "shared" :
@@ -3060,8 +3381,22 @@ void MDL_ticket::wsrep_report(bool debug)
((m_lock->key.mdl_namespace() == MDL_key::COMMIT) ? "COMMIT" :
(char *)"UNKNOWN"))))))),
m_lock->key.db_name(),
- m_lock->key.name(),
- m_lock->key.get_wait_state_name());
+ m_lock->key.name(),
+ psi_stage->m_name);
}
}
+bool MDL_context::wsrep_has_explicit_locks()
+{
+ MDL_ticket *ticket = NULL;
+
+ Ticket_iterator it(m_tickets[MDL_EXPLICIT]);
+
+ while ((ticket = it++))
+ {
+ return true;
+ }
+
+ return false;
+}
+
#endif /* WITH_WSREP */
diff --git a/sql/mdl.h b/sql/mdl.h
index 1363a07e7ab..7fb303d4720 100644
--- a/sql/mdl.h
+++ b/sql/mdl.h
@@ -1,6 +1,6 @@
#ifndef MDL_H
#define MDL_H
-/* Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved.
+/* Copyright (c) 2009, 2012, Oracle and/or its affiliates. All rights reserved.
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
@@ -12,8 +12,8 @@
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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+ along with this program; if not, write to the Free Software Foundation,
+ 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */
#if defined(__IBMC__) || defined(__IBMCPP__)
/* Further down, "next_in_lock" and "next_in_context" have the same type,
@@ -26,15 +26,97 @@
#include "sql_plist.h"
#include <my_sys.h>
-#include <my_pthread.h>
#include <m_string.h>
#include <mysql_com.h>
+#include <hash.h>
+
+#include <algorithm>
class THD;
class MDL_context;
class MDL_lock;
class MDL_ticket;
+bool ok_for_lower_case_names(const char *name);
+
+/**
+ @def ENTER_COND(C, M, S, O)
+ Start a wait on a condition.
+ @param C the condition to wait on
+ @param M the associated mutex
+ @param S the new stage to enter
+ @param O the previous stage
+ @sa EXIT_COND().
+*/
+#define ENTER_COND(C, M, S, O) enter_cond(C, M, S, O, __func__, __FILE__, __LINE__)
+
+/**
+ @def EXIT_COND(S)
+ End a wait on a condition
+ @param S the new stage to enter
+*/
+#define EXIT_COND(S) exit_cond(S, __func__, __FILE__, __LINE__)
+
+/**
+ An interface to separate the MDL module from the THD, and the rest of the
+ server code.
+ */
+
+class MDL_context_owner
+{
+public:
+ virtual ~MDL_context_owner() {}
+
+ /**
+ Enter a condition wait.
+ For @c enter_cond() / @c exit_cond() to work the mutex must be held before
+ @c enter_cond(); this mutex is then released by @c exit_cond().
+ Usage must be: lock mutex; enter_cond(); your code; exit_cond().
+ @param cond the condition to wait on
+ @param mutex the associated mutex
+ @param [in] stage the stage to enter, or NULL
+ @param [out] old_stage the previous stage, or NULL
+ @param src_function function name of the caller
+ @param src_file file name of the caller
+ @param src_line line number of the caller
+ @sa ENTER_COND(), THD::enter_cond()
+ @sa EXIT_COND(), THD::exit_cond()
+ */
+ virtual void enter_cond(mysql_cond_t *cond, mysql_mutex_t *mutex,
+ const PSI_stage_info *stage, PSI_stage_info *old_stage,
+ const char *src_function, const char *src_file,
+ int src_line) = 0;
+
+ /**
+ @def EXIT_COND(S)
+ End a wait on a condition
+ @param [in] stage the new stage to enter
+ @param src_function function name of the caller
+ @param src_file file name of the caller
+ @param src_line line number of the caller
+ @sa ENTER_COND(), THD::enter_cond()
+ @sa EXIT_COND(), THD::exit_cond()
+ */
+ virtual void exit_cond(const PSI_stage_info *stage,
+ const char *src_function, const char *src_file,
+ int src_line) = 0;
+ /**
+ Has the owner thread been killed?
+ */
+ virtual int is_killed() = 0;
+
+ /**
+ This one is only used for DEBUG_SYNC.
+ (Do not use it to peek/poke into other parts of THD.)
+ */
+ virtual THD* get_thd() = 0;
+
+ /**
+ @see THD::notify_shared_lock()
+ */
+ virtual bool notify_shared_lock(MDL_context_owner *in_use,
+ bool needs_thr_lock_abort) = 0;
+};
/**
Type of metadata lock request.
@@ -114,6 +196,15 @@ enum enum_mdl_type {
*/
MDL_SHARED_WRITE,
/*
+ An upgradable shared metadata lock for cases when there is an intention
+ to modify (and not just read) data in the table.
+ Can be upgraded to MDL_SHARED_NO_WRITE and MDL_EXCLUSIVE.
+ A connection holding SU lock can read table metadata and modify or read
+ table data (after acquiring appropriate table and row-level locks).
+ To be used for the first phase of ALTER TABLE.
+ */
+ MDL_SHARED_UPGRADABLE,
+ /*
An upgradable shared metadata lock which blocks all attempts to update
table data, allowing reads.
A connection holding this kind of lock can read table metadata and read
@@ -189,6 +280,10 @@ enum enum_mdl_duration {
class MDL_key
{
public:
+#ifdef HAVE_PSI_INTERFACE
+ static void init_psi_keys();
+#endif
+
/**
Object namespaces.
Sic: when adding a new member to this enum make sure to
@@ -212,6 +307,7 @@ public:
TRIGGER,
EVENT,
COMMIT,
+ USER_LOCK, /* user level locks. */
/* This should be the last ! */
NAMESPACE_END };
@@ -247,15 +343,22 @@ public:
are not longer than NAME_LEN. Still we play safe and try to avoid
buffer overruns.
*/
- m_db_name_length= (uint16) (strmake(m_ptr + 1, db, NAME_LEN) - m_ptr - 1);
- m_length= (uint16) (strmake(m_ptr + m_db_name_length + 2, name, NAME_LEN) -
- m_ptr + 1);
+ DBUG_ASSERT(strlen(db) <= NAME_LEN);
+ DBUG_ASSERT(strlen(name) <= NAME_LEN);
+ m_db_name_length= static_cast<uint16>(strmake(m_ptr + 1, db, NAME_LEN) -
+ m_ptr - 1);
+ m_length= static_cast<uint16>(strmake(m_ptr + m_db_name_length + 2, name,
+ NAME_LEN) - m_ptr + 1);
+ m_hash_value= my_hash_sort(&my_charset_bin, (uchar*) m_ptr + 1,
+ m_length - 1);
+ DBUG_ASSERT(mdl_namespace == USER_LOCK || ok_for_lower_case_names(db));
}
void mdl_key_init(const MDL_key *rhs)
{
memcpy(m_ptr, rhs->m_ptr, rhs->m_length);
m_length= rhs->m_length;
m_db_name_length= rhs->m_db_name_length;
+ m_hash_value= rhs->m_hash_value;
}
bool is_equal(const MDL_key *rhs) const
{
@@ -272,6 +375,7 @@ public:
character set is utf-8, we can safely assume that no
character starts with a zero byte.
*/
+ using std::min;
return memcmp(m_ptr, rhs->m_ptr, min(m_length, rhs->m_length));
}
@@ -290,19 +394,30 @@ public:
Get thread state name to be used in case when we have to
wait on resource identified by key.
*/
- const char * get_wait_state_name() const
+ const PSI_stage_info * get_wait_state_name() const
{
- return m_namespace_to_wait_state_name[(int)mdl_namespace()];
+ return & m_namespace_to_wait_state_name[(int)mdl_namespace()];
+ }
+ my_hash_value_type hash_value() const
+ {
+ return m_hash_value + mdl_namespace();
+ }
+ my_hash_value_type tc_hash_value() const
+ {
+ return m_hash_value;
}
private:
uint16 m_length;
uint16 m_db_name_length;
+ my_hash_value_type m_hash_value;
char m_ptr[MAX_MDLKEY_LENGTH];
- static const char * m_namespace_to_wait_state_name[NAMESPACE_END];
+ static PSI_stage_info m_namespace_to_wait_state_name[NAMESPACE_END];
private:
MDL_key(const MDL_key &); /* not implemented */
MDL_key &operator=(const MDL_key &); /* not implemented */
+ friend my_hash_value_type mdl_hash_function(const CHARSET_INFO *,
+ const uchar *, size_t);
};
@@ -342,6 +457,7 @@ public:
MDL_key key;
public:
+
static void *operator new(size_t size, MEM_ROOT *mem_root) throw ()
{ return alloc_root(mem_root, size); }
static void operator delete(void *ptr, MEM_ROOT *mem_root) {}
@@ -406,17 +522,7 @@ public:
virtual bool inspect_edge(MDL_context *dest) = 0;
virtual ~MDL_wait_for_graph_visitor();
- MDL_wait_for_graph_visitor() :m_lock_open_count(0) {}
-public:
- /**
- XXX, hack: During deadlock search, we may need to
- inspect TABLE_SHAREs and acquire LOCK_open. Since
- LOCK_open is not a recursive mutex, count here how many
- times we "took" it (but only take and release once).
- Not using a native recursive mutex or rwlock in 5.5 for
- LOCK_open since it has significant performance impacts.
- */
- uint m_lock_open_count;
+ MDL_wait_for_graph_visitor() {}
};
/**
@@ -489,13 +595,15 @@ public:
MDL_context *get_ctx() const { return m_ctx; }
bool is_upgradable_or_exclusive() const
{
- return m_type == MDL_SHARED_NO_WRITE ||
+ return m_type == MDL_SHARED_UPGRADABLE ||
+ m_type == MDL_SHARED_NO_WRITE ||
m_type == MDL_SHARED_NO_READ_WRITE ||
m_type == MDL_EXCLUSIVE;
}
enum_mdl_type get_type() const { return m_type; }
MDL_lock *get_lock() const { return m_lock; }
- void downgrade_exclusive_lock(enum_mdl_type type);
+ MDL_key *get_key() const;
+ void downgrade_lock(enum_mdl_type type);
bool has_stronger_or_equal_type(enum_mdl_type type) const;
@@ -601,8 +709,10 @@ public:
bool set_status(enum_wait_status result_arg);
enum_wait_status get_status();
void reset_status();
- enum_wait_status timed_wait(THD *thd, struct timespec *abs_timeout,
- bool signal_timeout, const char *wait_state_name);
+ enum_wait_status timed_wait(MDL_context_owner *owner,
+ struct timespec *abs_timeout,
+ bool signal_timeout,
+ const PSI_stage_info *wait_state_name);
private:
/**
Condvar which is used for waiting until this context's pending
@@ -645,8 +755,9 @@ public:
bool try_acquire_lock(MDL_request *mdl_request);
bool acquire_lock(MDL_request *mdl_request, ulong lock_wait_timeout);
bool acquire_locks(MDL_request_list *requests, ulong lock_wait_timeout);
- bool upgrade_shared_lock_to_exclusive(MDL_ticket *mdl_ticket,
- ulong lock_wait_timeout);
+ bool upgrade_shared_lock(MDL_ticket *mdl_ticket,
+ enum_mdl_type new_type,
+ ulong lock_wait_timeout);
bool clone_ticket(MDL_request *mdl_request);
@@ -656,6 +767,7 @@ public:
bool is_lock_owner(MDL_key::enum_mdl_namespace mdl_namespace,
const char *db, const char *name,
enum_mdl_type mdl_type);
+ unsigned long get_lock_owner(MDL_key *mdl_key);
bool has_lock(const MDL_savepoint &mdl_savepoint, MDL_ticket *mdl_ticket);
@@ -690,7 +802,7 @@ public:
#endif
void rollback_to_savepoint(const MDL_savepoint &mdl_savepoint);
- inline THD *get_thd() const { return m_thd; }
+ MDL_context_owner *get_owner() { return m_owner; }
/** @pre Only valid if we started waiting for lock. */
inline uint get_deadlock_weight() const
@@ -703,7 +815,7 @@ public:
already has received some signal or closed
signal slot.
*/
- void init(THD *thd_arg) { m_thd= thd_arg; }
+ void init(MDL_context_owner *arg) { m_owner= arg; }
void set_needs_thr_lock_abort(bool needs_thr_lock_abort)
{
@@ -734,9 +846,9 @@ private:
Lists of MDL tickets:
---------------------
The entire set of locks acquired by a connection can be separated
- in three subsets according to their: locks released at the end of
- statement, at the end of transaction and locks are released
- explicitly.
+ in three subsets according to their duration: locks released at
+ the end of statement, at the end of transaction and locks are
+ released explicitly.
Statement and transactional locks are locks with automatic scope.
They are accumulated in the course of a transaction, and released
@@ -745,11 +857,12 @@ private:
locks). They must not be (and never are) released manually,
i.e. with release_lock() call.
- Locks with explicit duration are taken for locks that span
+ Tickets with explicit duration are taken for locks that span
multiple transactions or savepoints.
These are: HANDLER SQL locks (HANDLER SQL is
transaction-agnostic), LOCK TABLES locks (you can COMMIT/etc
- under LOCK TABLES, and the locked tables stay locked), and
+ under LOCK TABLES, and the locked tables stay locked), user level
+ locks (GET_LOCK()/RELEASE_LOCK() functions) and
locks implementing "global read lock".
Statement/transactional locks are always prepended to the
@@ -758,20 +871,19 @@ private:
a savepoint, we start popping and releasing tickets from the
front until we reach the last ticket acquired after the savepoint.
- Locks with explicit duration stored are not stored in any
+ Locks with explicit duration are not stored in any
particular order, and among each other can be split into
- three sets:
+ four sets:
- [LOCK TABLES locks] [HANDLER locks] [GLOBAL READ LOCK locks]
+ [LOCK TABLES locks] [USER locks] [HANDLER locks] [GLOBAL READ LOCK locks]
The following is known about these sets:
- * GLOBAL READ LOCK locks are always stored after LOCK TABLES
- locks and after HANDLER locks. This is because one can't say
- SET GLOBAL read_only=1 or FLUSH TABLES WITH READ LOCK
- if one has locked tables. One can, however, LOCK TABLES
- after having entered the read only mode. Note, that
- subsequent LOCK TABLES statement will unlock the previous
+ * GLOBAL READ LOCK locks are always stored last.
+ This is because one can't say SET GLOBAL read_only=1 or
+ FLUSH TABLES WITH READ LOCK if one has locked tables. One can,
+ however, LOCK TABLES after having entered the read only mode.
+ Note, that subsequent LOCK TABLES statement will unlock the previous
set of tables, but not the GRL!
There are no HANDLER locks after GRL locks because
SET GLOBAL read_only performs a FLUSH TABLES WITH
@@ -783,7 +895,7 @@ private:
involved schemas and global intention exclusive lock.
*/
Ticket_list m_tickets[MDL_DURATION_END];
- THD *m_thd;
+ MDL_context_owner *m_owner;
/**
TRUE - if for this context we will break protocol and try to
acquire table-level locks while having only S lock on
@@ -812,6 +924,7 @@ private:
*/
MDL_wait_for_subgraph *m_waiting_for;
private:
+ THD *get_thd() const { return m_owner->get_thd(); }
MDL_ticket *find_ticket(MDL_request *mdl_req,
enum_mdl_duration *duration);
void release_locks_stored_before(enum_mdl_duration duration, MDL_ticket *sentinel);
@@ -820,8 +933,14 @@ private:
MDL_ticket **out_ticket);
public:
+#ifdef WITH_WSREP
+ THD *wsrep_get_thd() const { return get_thd(); }
+ bool wsrep_has_explicit_locks();
+#endif /* WITH_WSREP */
void find_deadlock();
+ ulong get_thread_id() const { return thd_get_thread_id(get_thd()); }
+
bool visit_subgraph(MDL_wait_for_graph_visitor *dvisitor);
/** Inform the deadlock detector there is an edge in the wait-for graph. */
@@ -850,26 +969,26 @@ public:
private:
MDL_context(const MDL_context &rhs); /* not implemented */
MDL_context &operator=(MDL_context &rhs); /* not implemented */
+
+ /* metadata_lock_info plugin */
+ friend int i_s_metadata_lock_info_fill_row(MDL_ticket*, void*);
};
void mdl_init();
void mdl_destroy();
+extern "C" unsigned long thd_get_thread_id(const MYSQL_THD thd);
-/*
- Functions in the server's kernel used by metadata locking subsystem.
-*/
-
-extern bool mysql_notify_thread_having_shared_lock(THD *thd, THD *in_use,
- bool needs_thr_lock_abort);
-extern "C" const char* thd_enter_cond(MYSQL_THD thd, mysql_cond_t *cond,
- mysql_mutex_t *mutex, const char *msg);
-extern "C" void thd_exit_cond(MYSQL_THD thd, const char *old_msg);
+/**
+ Check if a connection in question is no longer connected.
-#ifndef DBUG_OFF
-extern mysql_mutex_t LOCK_open;
-#endif
+ @details
+ Replication apply thread is always connected. Otherwise,
+ does a poll on the associated socket to check if the client
+ is gone.
+*/
+extern "C" int thd_is_connected(MYSQL_THD thd);
/*
@@ -880,9 +999,20 @@ extern ulong mdl_locks_cache_size;
static const ulong MDL_LOCKS_CACHE_SIZE_DEFAULT = 1024;
/*
+ Start-up parameter for the number of partitions of the hash
+ containing all the MDL_lock objects and a constant for
+ its default value.
+*/
+extern ulong mdl_locks_hash_partitions;
+static const ulong MDL_LOCKS_HASH_PARTITIONS_DEFAULT = 8;
+
+/*
Metadata locking subsystem tries not to grant more than
max_write_lock_count high-prio, strong locks successively,
to avoid starving out weak, low-prio locks.
*/
extern "C" ulong max_write_lock_count;
+
+extern MYSQL_PLUGIN_IMPORT
+int mdl_iterate(int (*callback)(MDL_ticket *ticket, void *arg), void *arg);
#endif
diff --git a/sql/mf_iocache.cc b/sql/mf_iocache.cc
index d8848c1ee35..6535f16445b 100644
--- a/sql/mf_iocache.cc
+++ b/sql/mf_iocache.cc
@@ -32,6 +32,7 @@
flush_io_cache().
*/
+#include <my_global.h>
#include "sql_priv.h"
#include "sql_class.h" // THD
#ifdef HAVE_REPLICATION
@@ -57,7 +58,7 @@ int _my_b_net_read(register IO_CACHE *info, uchar *Buffer,
if (!info->end_of_file)
DBUG_RETURN(1); /* because my_b_get (no _) takes 1 byte at a time */
- read_length=my_net_read(net);
+ read_length= my_net_read_packet(net, 0);
if (read_length == packet_error)
{
info->error= -1;
diff --git a/sql/multi_range_read.cc b/sql/multi_range_read.cc
index 2479bc3258c..3f55ff3684d 100644
--- a/sql/multi_range_read.cc
+++ b/sql/multi_range_read.cc
@@ -56,7 +56,7 @@
ha_rows
handler::multi_range_read_info_const(uint keyno, RANGE_SEQ_IF *seq,
void *seq_init_param, uint n_ranges_arg,
- uint *bufsz, uint *flags, COST_VECT *cost)
+ uint *bufsz, uint *flags, Cost_estimate *cost)
{
KEY_MULTI_RANGE range;
range_seq_t seq_it;
@@ -106,7 +106,7 @@ handler::multi_range_read_info_const(uint keyno, RANGE_SEQ_IF *seq,
{
/* The following calculation is the same as in multi_range_read_info(): */
*flags |= HA_MRR_USE_DEFAULT_IMPL;
- cost->zero();
+ cost->reset();
cost->avg_io_cost= 1; /* assume random seeks */
if ((*flags & HA_MRR_INDEX_ONLY) && total_rows > 2)
cost->io_count= keyread_time(keyno, n_ranges, (uint)total_rows);
@@ -154,7 +154,7 @@ handler::multi_range_read_info_const(uint keyno, RANGE_SEQ_IF *seq,
ha_rows handler::multi_range_read_info(uint keyno, uint n_ranges, uint n_rows,
uint key_parts, uint *bufsz,
- uint *flags, COST_VECT *cost)
+ uint *flags, Cost_estimate *cost)
{
/*
Currently we expect this function to be called only in preparation of scan
@@ -165,7 +165,7 @@ ha_rows handler::multi_range_read_info(uint keyno, uint n_ranges, uint n_rows,
*bufsz= 0; /* Default implementation doesn't need a buffer */
*flags |= HA_MRR_USE_DEFAULT_IMPL;
- cost->zero();
+ cost->reset();
cost->avg_io_cost= 1; /* assume random seeks */
/* Produce the same cost as non-MRR code does */
@@ -225,7 +225,7 @@ handler::multi_range_read_init(RANGE_SEQ_IF *seq_funcs, void *seq_init_param,
DBUG_ENTER("handler::multi_range_read_init");
mrr_iter= seq_funcs->init(seq_init_param, n_ranges, mode);
mrr_funcs= *seq_funcs;
- mrr_is_output_sorted= test(mode & HA_MRR_SORTED);
+ mrr_is_output_sorted= MY_TEST(mode & HA_MRR_SORTED);
mrr_have_range= FALSE;
DBUG_RETURN(0);
}
@@ -292,7 +292,7 @@ scan_it_again:
&mrr_cur_range.start_key : 0,
mrr_cur_range.end_key.keypart_map ?
&mrr_cur_range.end_key : 0,
- test(mrr_cur_range.range_flag & EQ_RANGE),
+ MY_TEST(mrr_cur_range.range_flag & EQ_RANGE),
mrr_is_output_sorted);
if (result != HA_ERR_END_OF_FILE)
break;
@@ -556,12 +556,12 @@ int Mrr_ordered_index_reader::init(handler *h_arg, RANGE_SEQ_IF *seq_funcs,
keypar= *key_par_arg;
KEY *key_info= &file->get_table()->key_info[file->active_index];
- keypar.index_ranges_unique= test(key_info->flags & HA_NOSAME &&
- key_info->key_parts ==
- my_count_bits(keypar.key_tuple_map));
+ keypar.index_ranges_unique= MY_TEST(key_info->flags & HA_NOSAME &&
+ key_info->user_defined_key_parts ==
+ my_count_bits(keypar.key_tuple_map));
mrr_iter= seq_funcs->init(seq_init_param, n_ranges, mode);
- is_mrr_assoc= !test(mode & HA_MRR_NO_ASSOCIATION);
+ is_mrr_assoc= !MY_TEST(mode & HA_MRR_NO_ASSOCIATION);
mrr_funcs= *seq_funcs;
source_exhausted= FALSE;
read_was_interrupted= false;
@@ -584,7 +584,7 @@ int Mrr_ordered_rndpos_reader::init(handler *h_arg,
file= h_arg;
index_reader= index_reader_arg;
rowid_buffer= buf;
- is_mrr_assoc= !test(mode & HA_MRR_NO_ASSOCIATION);
+ is_mrr_assoc= !MY_TEST(mode & HA_MRR_NO_ASSOCIATION);
index_reader_exhausted= FALSE;
index_reader_needs_refill= TRUE;
return 0;
@@ -834,7 +834,7 @@ int DsMrr_impl::dsmrr_init(handler *h_arg, RANGE_SEQ_IF *seq_funcs,
has not been called, so set the owner handler here as well.
*/
primary_file= h_arg;
- is_mrr_assoc= !test(mode & HA_MRR_NO_ASSOCIATION);
+ is_mrr_assoc= !MY_TEST(mode & HA_MRR_NO_ASSOCIATION);
strategy_exhausted= FALSE;
@@ -884,7 +884,7 @@ int DsMrr_impl::dsmrr_init(handler *h_arg, RANGE_SEQ_IF *seq_funcs,
if (do_sort_keys)
{
/* Pre-calculate some parameters of key sorting */
- keypar.use_key_pointers= test(mode & HA_MRR_MATERIALIZED_KEYS);
+ keypar.use_key_pointers= MY_TEST(mode & HA_MRR_MATERIALIZED_KEYS);
seq_funcs->get_key_info(seq_init_param, &keypar.key_tuple_length,
&keypar.key_tuple_map);
keypar.key_size_in_keybuf= keypar.use_key_pointers?
@@ -1013,7 +1013,7 @@ use_default_impl:
so small that it can accomodate one rowid and one index tuple)
*/
if ((res= primary_file->ha_rnd_end()) ||
- (res= primary_file->ha_index_init(keyno, test(mode & HA_MRR_SORTED))))
+ (res= primary_file->ha_index_init(keyno, MY_TEST(mode & HA_MRR_SORTED))))
{
DBUG_RETURN(res);
}
@@ -1217,9 +1217,9 @@ bool DsMrr_impl::setup_buffer_sharing(uint key_size_in_keybuf,
statistics?
*/
uint parts= my_count_bits(key_tuple_map);
- ulong rpc;
+ ha_rows rpc;
ulonglong rowids_size= rowid_buf_elem_size;
- if ((rpc= key_info->rec_per_key[parts - 1]))
+ if ((rpc= (ha_rows) key_info->actual_rec_per_key(parts - 1)))
rowids_size= rowid_buf_elem_size * rpc;
double fraction_for_rowids=
@@ -1358,8 +1358,14 @@ int Key_value_records_iterator::get_next(range_id_t *range_info)
}
handler *h= owner->file;
+ uchar *lookup_key;
+ if (owner->keypar.use_key_pointers)
+ memcpy(&lookup_key, identical_key_it.read_ptr1, sizeof(void*));
+ else
+ lookup_key= identical_key_it.read_ptr1;
+
if ((res= h->ha_index_next_same(h->get_table()->record[0],
- identical_key_it.read_ptr1,
+ lookup_key,
owner->keypar.key_tuple_length)))
{
/* It's either HA_ERR_END_OF_FILE or some other error */
@@ -1420,7 +1426,7 @@ int DsMrr_impl::dsmrr_next(range_id_t *range_info)
*/
ha_rows DsMrr_impl::dsmrr_info(uint keyno, uint n_ranges, uint rows,
uint key_parts,
- uint *bufsz, uint *flags, COST_VECT *cost)
+ uint *bufsz, uint *flags, Cost_estimate *cost)
{
ha_rows res __attribute__((unused));
uint def_flags= *flags;
@@ -1455,7 +1461,7 @@ ha_rows DsMrr_impl::dsmrr_info(uint keyno, uint n_ranges, uint rows,
ha_rows DsMrr_impl::dsmrr_info_const(uint keyno, RANGE_SEQ_IF *seq,
void *seq_init_param, uint n_ranges,
- uint *bufsz, uint *flags, COST_VECT *cost)
+ uint *bufsz, uint *flags, Cost_estimate *cost)
{
ha_rows rows;
uint def_flags= *flags;
@@ -1514,7 +1520,7 @@ ha_rows DsMrr_impl::dsmrr_info_const(uint keyno, RANGE_SEQ_IF *seq,
bool key_uses_partial_cols(TABLE_SHARE *share, uint keyno)
{
KEY_PART_INFO *kp= share->key_info[keyno].key_part;
- KEY_PART_INFO *kp_end= kp + share->key_info[keyno].key_parts;
+ KEY_PART_INFO *kp_end= kp + share->key_info[keyno].user_defined_key_parts;
for (; kp != kp_end; kp++)
{
if (!kp->field->part_of_key.is_set(keyno))
@@ -1538,10 +1544,10 @@ bool key_uses_partial_cols(TABLE_SHARE *share, uint keyno)
bool DsMrr_impl::check_cpk_scan(THD *thd, TABLE_SHARE *share, uint keyno,
uint mrr_flags)
{
- return test((mrr_flags & HA_MRR_SINGLE_POINT) &&
- keyno == share->primary_key &&
- primary_file->primary_key_is_clustered() &&
- optimizer_flag(thd, OPTIMIZER_SWITCH_MRR_SORT_KEYS));
+ return MY_TEST((mrr_flags & HA_MRR_SINGLE_POINT) &&
+ keyno == share->primary_key &&
+ primary_file->primary_key_is_clustered() &&
+ optimizer_flag(thd, OPTIMIZER_SWITCH_MRR_SORT_KEYS));
}
@@ -1570,16 +1576,16 @@ bool DsMrr_impl::check_cpk_scan(THD *thd, TABLE_SHARE *share, uint keyno,
bool DsMrr_impl::choose_mrr_impl(uint keyno, ha_rows rows, uint *flags,
- uint *bufsz, COST_VECT *cost)
+ uint *bufsz, Cost_estimate *cost)
{
- COST_VECT dsmrr_cost;
+ Cost_estimate dsmrr_cost;
bool res;
THD *thd= current_thd;
TABLE_SHARE *share= primary_file->get_table_share();
bool doing_cpk_scan= check_cpk_scan(thd, share, keyno, *flags);
- bool using_cpk= test(keyno == share->primary_key &&
- primary_file->primary_key_is_clustered());
+ bool using_cpk= MY_TEST(keyno == share->primary_key &&
+ primary_file->primary_key_is_clustered());
*flags &= ~HA_MRR_IMPLEMENTATION_FLAGS;
if (!optimizer_flag(thd, OPTIMIZER_SWITCH_MRR) ||
*flags & HA_MRR_INDEX_ONLY ||
@@ -1667,7 +1673,7 @@ int DsMrr_impl::dsmrr_explain_info(uint mrr_mode, char *str, size_t size)
used_str= rowid_ordered;
uint used_str_len= strlen(used_str);
- uint copy_len= min(used_str_len, size);
+ uint copy_len= MY_MIN(used_str_len, size);
memcpy(str, used_str, copy_len);
return copy_len;
}
@@ -1675,7 +1681,7 @@ int DsMrr_impl::dsmrr_explain_info(uint mrr_mode, char *str, size_t size)
}
-static void get_sort_and_sweep_cost(TABLE *table, ha_rows nrows, COST_VECT *cost);
+static void get_sort_and_sweep_cost(TABLE *table, ha_rows nrows, Cost_estimate *cost);
/**
@@ -1693,7 +1699,7 @@ static void get_sort_and_sweep_cost(TABLE *table, ha_rows nrows, COST_VECT *cost
*/
bool DsMrr_impl::get_disk_sweep_mrr_cost(uint keynr, ha_rows rows, uint flags,
- uint *buffer_size, COST_VECT *cost)
+ uint *buffer_size, Cost_estimate *cost)
{
ulong max_buff_entries, elem_size;
ha_rows rows_in_full_step;
@@ -1702,7 +1708,7 @@ bool DsMrr_impl::get_disk_sweep_mrr_cost(uint keynr, ha_rows rows, uint flags,
double index_read_cost;
elem_size= primary_file->ref_length +
- sizeof(void*) * (!test(flags & HA_MRR_NO_ASSOCIATION));
+ sizeof(void*) * (!MY_TEST(flags & HA_MRR_NO_ASSOCIATION));
max_buff_entries = *buffer_size / elem_size;
if (!max_buff_entries)
@@ -1727,13 +1733,13 @@ bool DsMrr_impl::get_disk_sweep_mrr_cost(uint keynr, ha_rows rows, uint flags,
}
else
{
- cost->zero();
- *buffer_size= max(*buffer_size,
+ cost->reset();
+ *buffer_size= MY_MAX(*buffer_size,
(size_t)(1.2*rows_in_last_step) * elem_size +
primary_file->ref_length + table->key_info[keynr].key_length);
}
- COST_VECT last_step_cost;
+ Cost_estimate last_step_cost;
get_sort_and_sweep_cost(table, rows_in_last_step, &last_step_cost);
cost->add(&last_step_cost);
@@ -1762,7 +1768,7 @@ bool DsMrr_impl::get_disk_sweep_mrr_cost(uint keynr, ha_rows rows, uint flags,
*/
static
-void get_sort_and_sweep_cost(TABLE *table, ha_rows nrows, COST_VECT *cost)
+void get_sort_and_sweep_cost(TABLE *table, ha_rows nrows, Cost_estimate *cost)
{
if (nrows)
{
@@ -1774,7 +1780,7 @@ void get_sort_and_sweep_cost(TABLE *table, ha_rows nrows, COST_VECT *cost)
cost->cpu_cost += cmp_op * log2(cmp_op);
}
else
- cost->zero();
+ cost->reset();
}
@@ -1822,11 +1828,11 @@ void get_sort_and_sweep_cost(TABLE *table, ha_rows nrows, COST_VECT *cost)
*/
void get_sweep_read_cost(TABLE *table, ha_rows nrows, bool interrupted,
- COST_VECT *cost)
+ Cost_estimate *cost)
{
DBUG_ENTER("get_sweep_read_cost");
- cost->zero();
+ cost->reset();
if (table->file->primary_key_is_clustered())
{
cost->io_count= table->file->read_time(table->s->primary_key,
diff --git a/sql/multi_range_read.h b/sql/multi_range_read.h
index 0819b6fe948..ffae6d63124 100644
--- a/sql/multi_range_read.h
+++ b/sql/multi_range_read.h
@@ -339,7 +339,7 @@ private:
uchar *saved_key_tuple; /* Saved current key tuple */
uchar *saved_primary_key; /* Saved current primary key tuple */
-
+
/*
TRUE<=> saved_key_tuple (and saved_primary_key when applicable) have
valid values.
@@ -568,11 +568,11 @@ public:
int dsmrr_next(range_id_t *range_info);
ha_rows dsmrr_info(uint keyno, uint n_ranges, uint keys, uint key_parts,
- uint *bufsz, uint *flags, COST_VECT *cost);
+ uint *bufsz, uint *flags, Cost_estimate *cost);
ha_rows dsmrr_info_const(uint keyno, RANGE_SEQ_IF *seq,
void *seq_init_param, uint n_ranges, uint *bufsz,
- uint *flags, COST_VECT *cost);
+ uint *flags, Cost_estimate *cost);
int dsmrr_explain_info(uint mrr_mode, char *str, size_t size);
private:
@@ -630,9 +630,9 @@ private:
Forward_lifo_buffer rowid_buffer;
bool choose_mrr_impl(uint keyno, ha_rows rows, uint *flags, uint *bufsz,
- COST_VECT *cost);
+ Cost_estimate *cost);
bool get_disk_sweep_mrr_cost(uint keynr, ha_rows rows, uint flags,
- uint *buffer_size, COST_VECT *cost);
+ uint *buffer_size, Cost_estimate *cost);
bool check_cpk_scan(THD *thd, TABLE_SHARE *share, uint keyno, uint mrr_flags);
bool setup_buffer_sharing(uint key_size_in_keybuf, key_part_map key_tuple_map);
diff --git a/sql/my_apc.cc b/sql/my_apc.cc
new file mode 100644
index 00000000000..17660688be0
--- /dev/null
+++ b/sql/my_apc.cc
@@ -0,0 +1,270 @@
+/*
+ Copyright (c) 2011, 2013 Monty Program Ab.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ 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 */
+
+
+#ifndef MY_APC_STANDALONE
+
+#include "sql_class.h"
+
+#endif
+
+/* For standalone testing of APC system, see unittest/sql/my_apc-t.cc */
+
+/*
+ Initialize the target.
+
+ @note
+ Initialization must be done prior to enabling/disabling the target, or making
+ any call requests to it.
+ Initial state after initialization is 'disabled'.
+*/
+void Apc_target::init(mysql_mutex_t *target_mutex)
+{
+ DBUG_ASSERT(!enabled);
+ LOCK_thd_data_ptr= target_mutex;
+#ifndef DBUG_OFF
+ n_calls_processed= 0;
+#endif
+}
+
+
+/*
+ Destroy the target. The target must be disabled when this call is made.
+*/
+void Apc_target::destroy()
+{
+ DBUG_ASSERT(!enabled);
+}
+
+
+/*
+ Enter ther state where the target is available for serving APC requests
+*/
+void Apc_target::enable()
+{
+ /* Ok to do without getting/releasing the mutex: */
+ enabled++;
+}
+
+
+/*
+ Make the target unavailable for serving APC requests.
+
+ @note
+ This call will serve all requests that were already enqueued
+*/
+
+void Apc_target::disable()
+{
+ bool process= FALSE;
+ DBUG_ASSERT(enabled);
+ mysql_mutex_lock(LOCK_thd_data_ptr);
+ if (!(--enabled))
+ process= TRUE;
+ mysql_mutex_unlock(LOCK_thd_data_ptr);
+ if (process)
+ process_apc_requests();
+}
+
+
+/* [internal] Put request qe into the request list */
+
+void Apc_target::enqueue_request(Call_request *qe)
+{
+ mysql_mutex_assert_owner(LOCK_thd_data_ptr);
+ if (apc_calls)
+ {
+ Call_request *after= apc_calls->prev;
+ qe->next= apc_calls;
+ apc_calls->prev= qe;
+
+ qe->prev= after;
+ after->next= qe;
+ }
+ else
+ {
+ apc_calls= qe;
+ qe->next= qe->prev= qe;
+ }
+}
+
+
+/*
+ [internal] Remove request qe from the request queue.
+
+ The request is not necessarily first in the queue.
+*/
+
+void Apc_target::dequeue_request(Call_request *qe)
+{
+ mysql_mutex_assert_owner(LOCK_thd_data_ptr);
+ if (apc_calls == qe)
+ {
+ if ((apc_calls= apc_calls->next) == qe)
+ {
+ apc_calls= NULL;
+ }
+ }
+
+ qe->prev->next= qe->next;
+ qe->next->prev= qe->prev;
+}
+
+#ifdef HAVE_PSI_INTERFACE
+
+/* One key for all conds */
+PSI_cond_key key_show_explain_request_COND;
+
+static PSI_cond_info show_explain_psi_conds[]=
+{
+ { &key_show_explain_request_COND, "show_explain", 0 /* not using PSI_FLAG_GLOBAL*/ }
+};
+
+void init_show_explain_psi_keys(void)
+{
+ if (PSI_server == NULL)
+ return;
+
+ PSI_server->register_cond("sql", show_explain_psi_conds,
+ array_elements(show_explain_psi_conds));
+}
+#endif
+
+
+/*
+ Make an APC (Async Procedure Call) to another thread.
+
+ @detail
+ Make an APC call: schedule it for execution and wait until the target
+ thread has executed it.
+
+ - The caller is responsible for making sure he's not posting request
+ to the thread he's calling this function from.
+
+ - The caller must have locked target_mutex. The function will release it.
+
+ @retval FALSE - Ok, the call has been made
+ @retval TRUE - Call wasnt made (either the target is in disabled state or
+ timeout occured)
+*/
+
+bool Apc_target::make_apc_call(THD *caller_thd, Apc_call *call,
+ int timeout_sec, bool *timed_out)
+{
+ bool res= TRUE;
+ *timed_out= FALSE;
+
+ if (enabled)
+ {
+ /* Create and post the request */
+ Call_request apc_request;
+ apc_request.call= call;
+ apc_request.processed= FALSE;
+ mysql_cond_init(key_show_explain_request_COND, &apc_request.COND_request,
+ NULL);
+ enqueue_request(&apc_request);
+ apc_request.what="enqueued by make_apc_call";
+
+ struct timespec abstime;
+ const int timeout= timeout_sec;
+ set_timespec(abstime, timeout);
+
+ int wait_res= 0;
+ PSI_stage_info old_stage;
+ caller_thd->ENTER_COND(&apc_request.COND_request, LOCK_thd_data_ptr,
+ &stage_show_explain, &old_stage);
+ /* todo: how about processing other errors here? */
+ while (!apc_request.processed && (wait_res != ETIMEDOUT))
+ {
+ /* We own LOCK_thd_data_ptr */
+ wait_res= mysql_cond_timedwait(&apc_request.COND_request,
+ LOCK_thd_data_ptr, &abstime);
+ // &apc_request.LOCK_request, &abstime);
+ if (caller_thd->killed)
+ break;
+ }
+
+ if (!apc_request.processed)
+ {
+ /*
+ The wait has timed out, or this thread was KILLed.
+ Remove the request from the queue (ok to do because we own
+ LOCK_thd_data_ptr)
+ */
+ apc_request.processed= TRUE;
+ dequeue_request(&apc_request);
+ *timed_out= TRUE;
+ res= TRUE;
+ }
+ else
+ {
+ /* Request was successfully executed and dequeued by the target thread */
+ res= FALSE;
+ }
+ /*
+ exit_cond() will call mysql_mutex_unlock(LOCK_thd_data_ptr) for us:
+ */
+ caller_thd->EXIT_COND(&old_stage);
+
+ /* Destroy all APC request data */
+ mysql_cond_destroy(&apc_request.COND_request);
+ }
+ else
+ {
+ mysql_mutex_unlock(LOCK_thd_data_ptr);
+ }
+ return res;
+}
+
+
+/*
+ Process all APC requests.
+ This should be called periodically by the APC target thread.
+*/
+
+void Apc_target::process_apc_requests()
+{
+ while (1)
+ {
+ Call_request *request;
+
+ mysql_mutex_lock(LOCK_thd_data_ptr);
+ if (!(request= get_first_in_queue()))
+ {
+ /* No requests in the queue */
+ mysql_mutex_unlock(LOCK_thd_data_ptr);
+ break;
+ }
+
+ /*
+ Remove the request from the queue (we're holding queue lock so we can be
+ sure that request owner won't try to remove it)
+ */
+ request->what="dequeued by process_apc_requests";
+ dequeue_request(request);
+ request->processed= TRUE;
+
+ request->call->call_in_target_thread();
+ request->what="func called by process_apc_requests";
+
+#ifndef DBUG_OFF
+ n_calls_processed++;
+#endif
+ mysql_cond_signal(&request->COND_request);
+ mysql_mutex_unlock(LOCK_thd_data_ptr);
+ }
+}
+
diff --git a/sql/my_apc.h b/sql/my_apc.h
new file mode 100644
index 00000000000..dfeef5eb8ac
--- /dev/null
+++ b/sql/my_apc.h
@@ -0,0 +1,142 @@
+#ifndef SQL_MY_APC_INCLUDED
+#define SQL_MY_APC_INCLUDED
+/*
+ Copyright (c) 2011, 2013 Monty Program Ab.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ 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 */
+
+/*
+ Interface
+ ~~~~~~~~~
+ (
+ - This is an APC request queue
+ - We assume there is a particular owner thread which periodically calls
+ process_apc_requests() to serve the call requests.
+ - Other threads can post call requests, and block until they are exectued.
+ )
+
+ Implementation
+ ~~~~~~~~~~~~~~
+ - The target has a mutex-guarded request queue.
+
+ - After the request has been put into queue, the requestor waits for request
+ to be satisfied. The worker satisifes the request and signals the
+ requestor.
+*/
+
+class THD;
+
+/*
+ Target for asynchronous procedure calls (APCs).
+ - A target is running in some particular thread,
+ - One can make calls to it from other threads.
+*/
+class Apc_target
+{
+ mysql_mutex_t *LOCK_thd_data_ptr;
+public:
+ Apc_target() : enabled(0), apc_calls(NULL) {}
+ ~Apc_target() { DBUG_ASSERT(!enabled && !apc_calls);}
+
+ void init(mysql_mutex_t *target_mutex);
+ void destroy();
+ void enable();
+ void disable();
+
+ void process_apc_requests();
+ /*
+ A lightweight function, intended to be used in frequent checks like this:
+
+ if (apc_target.have_requests()) apc_target.process_apc_requests()
+ */
+ inline bool have_apc_requests()
+ {
+ return MY_TEST(apc_calls);
+ }
+
+ inline bool is_enabled() { return enabled; }
+
+ /* Functor class for calls you can schedule */
+ class Apc_call
+ {
+ public:
+ /* This function will be called in the target thread */
+ virtual void call_in_target_thread()= 0;
+ virtual ~Apc_call() {}
+ };
+
+ /* Make a call in the target thread (see function definition for details) */
+ bool make_apc_call(THD *caller_thd, Apc_call *call, int timeout_sec, bool *timed_out);
+
+#ifndef DBUG_OFF
+ int n_calls_processed; /* Number of calls served by this target */
+#endif
+private:
+ class Call_request;
+
+ /*
+ Non-zero value means we're enabled. It's an int, not bool, because one can
+ call enable() N times (and then needs to call disable() N times before the
+ target is really disabled)
+ */
+ int enabled;
+
+ /*
+ Circular, double-linked list of all enqueued call requests.
+ We use this structure, because we
+ - process requests sequentially: requests are added at the end of the
+ list and removed from the front. With circular list, we can keep one
+ pointer, and access both front an back of the list with it.
+ - a thread that has posted a request may time out (or be KILLed) and
+ cancel the request, which means we need a fast request-removal
+ operation.
+ */
+ Call_request *apc_calls;
+
+ class Call_request
+ {
+ public:
+ Apc_call *call; /* Functor to be called */
+
+ /* The caller will actually wait for "processed==TRUE" */
+ bool processed;
+
+ /* Condition that will be signalled when the request has been served */
+ mysql_cond_t COND_request;
+
+ /* Double linked-list linkage */
+ Call_request *next;
+ Call_request *prev;
+
+ const char *what; /* (debug) state of the request */
+ };
+
+ void enqueue_request(Call_request *qe);
+ void dequeue_request(Call_request *qe);
+
+ /* return the first call request in queue, or NULL if there are none enqueued */
+ Call_request *get_first_in_queue()
+ {
+ return apc_calls;
+ }
+};
+
+#ifdef HAVE_PSI_INTERFACE
+void init_show_explain_psi_keys(void);
+#else
+#define init_show_explain_psi_keys() /* no-op */
+#endif
+
+#endif //SQL_MY_APC_INCLUDED
+
diff --git a/sql/my_decimal.cc b/sql/my_decimal.cc
index 21611afd87b..c11bf671cb1 100644
--- a/sql/my_decimal.cc
+++ b/sql/my_decimal.cc
@@ -45,21 +45,21 @@ int decimal_operation_results(int result, const char *value, const char *type)
case E_DEC_OK:
break;
case E_DEC_TRUNCATED:
- push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
ER_DATA_TRUNCATED, ER(ER_DATA_TRUNCATED),
value, type);
break;
case E_DEC_OVERFLOW:
- push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
ER_DATA_OVERFLOW, ER(ER_DATA_OVERFLOW),
value, type);
break;
case E_DEC_DIV_ZERO:
- push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
ER_DIVISION_BY_ZERO, ER(ER_DIVISION_BY_ZERO));
break;
case E_DEC_BAD_NUM:
- push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
ER_BAD_DATA, ER(ER_BAD_DATA),
value, type);
break;
diff --git a/sql/my_decimal.h b/sql/my_decimal.h
index 22ae38bef41..fa85b41d70c 100644
--- a/sql/my_decimal.h
+++ b/sql/my_decimal.h
@@ -343,6 +343,10 @@ bool my_decimal2seconds(const my_decimal *d, ulonglong *sec, ulong *microsec);
my_decimal *seconds2my_decimal(bool sign, ulonglong sec, ulong microsec,
my_decimal *d);
+#define TIME_to_my_decimal(TIME, DECIMAL) \
+ seconds2my_decimal((TIME)->neg, TIME_to_ulonglong(TIME), \
+ (TIME)->second_part, (DECIMAL))
+
int my_decimal2int(uint mask, const decimal_t *d, bool unsigned_flag,
longlong *l);
diff --git a/sql/mysql_install_db.cc b/sql/mysql_install_db.cc
index 9c1a234241f..9b4f45a9971 100644
--- a/sql/mysql_install_db.cc
+++ b/sql/mysql_install_db.cc
@@ -24,7 +24,6 @@
#include <m_string.h>
#include <windows.h>
-#include <assert.h>
#include <shellapi.h>
#include <accctrl.h>
#include <aclapi.h>
@@ -37,7 +36,7 @@
"Usage: mysql_install_db.exe [OPTIONS]\n" \
"OPTIONS:"
-extern "C" const char mysql_bootstrap_sql[];
+extern "C" const char* mysql_bootstrap_sql[];
char default_os_user[]= "NT AUTHORITY\\NetworkService";
static int create_db_instance();
@@ -250,7 +249,7 @@ static char *init_bootstrap_command_line(char *cmdline, size_t size)
"\"\"%s\" --no-defaults %s --bootstrap"
" \"--lc-messages-dir=%s/share\""
" --basedir=. --datadir=. --default-storage-engine=myisam"
- " --max_allowed_packet=9M --loose-skip-innodb"
+ " --max_allowed_packet=9M "
" --net-buffer-length=16k\"", mysqld_path,
opt_verbose_bootstrap?"--console":"", basedir );
return cmdline;
@@ -564,6 +563,10 @@ static int create_db_instance()
if (!in)
goto end;
+ if (setvbuf(in, NULL, _IONBF, 0))
+ {
+ verbose("WARNING: Cannot disable buffering on mysqld's stdin");
+ }
if (fwrite("use mysql;\n",11,1, in) != 1)
{
verbose("ERROR: Cannot write to mysqld's stdin");
@@ -571,12 +574,16 @@ static int create_db_instance()
goto end;
}
- /* Write the bootstrap script to stdin. */
- if (fwrite(mysql_bootstrap_sql, strlen(mysql_bootstrap_sql), 1, in) != 1)
+ int i;
+ for (i=0; mysql_bootstrap_sql[i]; i++)
{
- verbose("ERROR: Cannot write to mysqld's stdin");
- ret= 1;
- goto end;
+ /* Write the bootstrap script to stdin. */
+ if (fwrite(mysql_bootstrap_sql[i], strlen(mysql_bootstrap_sql[i]), 1, in) != 1)
+ {
+ verbose("ERROR: Cannot write to mysqld's stdin");
+ ret= 1;
+ goto end;
+ }
}
/* Remove default user, if requested. */
@@ -627,6 +634,14 @@ static int create_db_instance()
goto end;
}
+ /*
+ Remove innodb log files if they exist (this works around "different size logs"
+ error in MSI installation). TODO : remove this with the next Innodb, where
+ different size is handled gracefully.
+ */
+ DeleteFile("ib_logfile0");
+ DeleteFile("ib_logfile1");
+
/* Create my.ini file in data directory.*/
ret= create_myini();
if (ret)
diff --git a/sql/mysqld.cc b/sql/mysqld.cc
index a9359ba047e..0abb394f89f 100644
--- a/sql/mysqld.cc
+++ b/sql/mysqld.cc
@@ -1,5 +1,5 @@
/* Copyright (c) 2000, 2015, Oracle and/or its affiliates.
- Copyright (c) 2008, 2015, SkySQL Ab.
+ Copyright (c) 2008, 2016, MariaDB
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
@@ -14,7 +14,7 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */
-#include "my_global.h" /* NO_EMBEDDED_ACCESS_CHECKS */
+#include "sql_plugin.h" // Includes my_global.h
#include "sql_priv.h"
#include "unireg.h"
#include <signal.h>
@@ -40,9 +40,7 @@
#include "hostname.h" // hostname_cache_free, hostname_cache_init
#include "sql_acl.h" // acl_free, grant_free, acl_init,
// grant_init
-#include "sql_base.h" // table_def_free, table_def_init,
- // cached_open_tables,
- // cached_table_definitions
+#include "sql_base.h"
#include "sql_test.h" // mysql_print_status
#include "item_create.h" // item_create_cleanup, item_create_init
#include "sql_servers.h" // servers_free, servers_init
@@ -52,6 +50,7 @@
#include "des_key_file.h" // load_des_key_file
#include "sql_manager.h" // stop_handle_manager, start_handle_manager
#include "sql_expression_cache.h" // subquery_cache_miss, subquery_cache_hit
+#include "sys_vars_shared.h"
#include <m_ctype.h>
#include <my_dir.h>
@@ -85,6 +84,10 @@ ulong wsrep_running_threads = 0; // # of currently running wsrep threads
#ifdef WITH_PERFSCHEMA_STORAGE_ENGINE
#include "../storage/perfschema/pfs_server.h"
#endif /* WITH_PERFSCHEMA_STORAGE_ENGINE */
+#include <mysql/psi/mysql_idle.h>
+#include <mysql/psi/mysql_socket.h>
+#include <mysql/psi/mysql_statement.h>
+#include "mysql_com_server.h"
#include "keycaches.h"
#include "../storage/myisam/ha_myisam.h"
@@ -118,7 +121,6 @@ ulong wsrep_running_threads = 0; // # of currently running wsrep threads
#endif
extern "C" { // Because of SCO 3.2V4.2
-#include <errno.h>
#include <sys/stat.h>
#ifndef __GNU_LIBRARY__
#define __GNU_LIBRARY__ // Skip warnings in getopt.h
@@ -342,6 +344,13 @@ static PSI_rwlock_key key_rwlock_openssl;
volatile sig_atomic_t ld_assume_kernel_is_set= 0;
#endif
+/**
+ Statement instrumentation key for replication.
+*/
+#ifdef HAVE_PSI_STATEMENT_INTERFACE
+PSI_statement_info stmt_info_rpl;
+#endif
+
/* the default log output is log tables */
static bool lower_case_table_names_used= 0;
static bool max_long_data_size_used= false;
@@ -369,12 +378,14 @@ static I_List<THD> thread_cache;
static bool binlog_format_used= false;
LEX_STRING opt_init_connect, opt_init_slave;
static mysql_cond_t COND_thread_cache, COND_flush_thread_cache;
+mysql_cond_t COND_slave_init;
static DYNAMIC_ARRAY all_options;
/* Global variables */
#ifdef WITH_WSREP
ulong my_bind_addr;
+bool wsrep_new_cluster= false;
#endif /* WITH_WSREP */
bool opt_bin_log, opt_bin_log_used=0, opt_ignore_builtin_innodb= 0;
my_bool opt_log, opt_slow_log, debug_assert_if_crashed_table= 0, opt_help= 0;
@@ -483,17 +494,21 @@ ulong delay_key_write_options;
uint protocol_version;
uint lower_case_table_names;
ulong tc_heuristic_recover= 0;
-uint volatile thread_count;
+int32 thread_count, service_thread_count;
int32 thread_running;
+int32 slave_open_temp_tables;
ulong thread_created;
ulong back_log, connect_timeout, concurrency, server_id;
-ulong table_cache_size, table_def_size;
ulong what_to_log;
-ulong slow_launch_time, slave_open_temp_tables;
-ulong open_files_limit, max_binlog_size, max_relay_log_size;
+ulong slow_launch_time;
+ulong open_files_limit, max_binlog_size;
ulong slave_trans_retries;
uint slave_net_timeout;
ulong slave_exec_mode_options;
+#ifdef RBR_TRIGGERS
+ulong slave_run_triggers_for_rbr= 0;
+#endif //RBR_TRIGGERS
+ulong slave_ddl_exec_mode_options= SLAVE_EXEC_MODE_IDEMPOTENT;
ulonglong slave_type_conversions_options;
ulong thread_cache_size=0;
ulonglong binlog_cache_size=0;
@@ -502,11 +517,14 @@ ulong slave_max_allowed_packet= 0;
ulonglong binlog_stmt_cache_size=0;
ulonglong max_binlog_stmt_cache_size=0;
ulonglong query_cache_size=0;
-ulong refresh_version; /* Increments on each reload */
+ulong query_cache_limit=0;
ulong executed_events=0;
query_id_t global_query_id;
my_atomic_rwlock_t global_query_id_lock;
my_atomic_rwlock_t thread_running_lock;
+my_atomic_rwlock_t thread_count_lock;
+my_atomic_rwlock_t statistics_lock;
+my_atomic_rwlock_t slave_executed_entries_lock;
ulong aborted_threads, aborted_connects;
ulong delayed_insert_timeout, delayed_insert_limit, delayed_queue_size;
ulong delayed_insert_threads, delayed_insert_writes, delayed_rows_in_use;
@@ -516,6 +534,9 @@ ulong binlog_cache_use= 0, binlog_cache_disk_use= 0;
ulong binlog_stmt_cache_use= 0, binlog_stmt_cache_disk_use= 0;
ulong max_connections, max_connect_errors;
ulong extra_max_connections;
+ulong max_digest_length= 0;
+ulong slave_retried_transactions;
+ulong feature_files_opened_with_delayed_keys;
ulonglong denied_connections;
my_decimal decimal_zero;
@@ -558,6 +579,13 @@ ulong rpl_recovery_rank=0;
*/
ulong stored_program_cache_size= 0;
+ulong opt_slave_parallel_threads= 0;
+ulong opt_slave_domain_parallel_threads= 0;
+ulong opt_binlog_commit_wait_count= 0;
+ulong opt_binlog_commit_wait_usec= 0;
+ulong opt_slave_parallel_max_queued= 131072;
+my_bool opt_gtid_ignore_duplicates= FALSE;
+
const double log_10[] = {
1e000, 1e001, 1e002, 1e003, 1e004, 1e005, 1e006, 1e007, 1e008, 1e009,
1e010, 1e011, 1e012, 1e013, 1e014, 1e015, 1e016, 1e017, 1e018, 1e019,
@@ -623,6 +651,19 @@ const char *in_left_expr_name= "<left expr>";
const char *in_additional_cond= "<IN COND>";
const char *in_having_cond= "<IN HAVING>";
+/** Number of connection errors when selecting on the listening port */
+ulong connection_errors_select= 0;
+/** Number of connection errors when accepting sockets in the listening port. */
+ulong connection_errors_accept= 0;
+/** Number of connection errors from TCP wrappers. */
+ulong connection_errors_tcpwrap= 0;
+/** Number of connection errors from internal server errors. */
+ulong connection_errors_internal= 0;
+/** Number of connection errors from the server max_connection limit. */
+ulong connection_errors_max_connection= 0;
+/** Number of errors when reading the peer address. */
+ulong connection_errors_peer_addr= 0;
+
/* classes for comparation parsing/processing */
Eq_creator eq_creator;
Ne_creator ne_creator;
@@ -635,7 +676,8 @@ MYSQL_FILE *bootstrap_file;
int bootstrap_error;
I_List<THD> threads;
-Rpl_filter* rpl_filter;
+Rpl_filter* cur_rpl_filter;
+Rpl_filter* global_rpl_filter;
Rpl_filter* binlog_filter;
THD *first_global_thread()
@@ -672,19 +714,20 @@ SHOW_COMP_OPTION have_ssl, have_symlink, have_dlopen, have_query_cache;
SHOW_COMP_OPTION have_geometry, have_rtree_keys;
SHOW_COMP_OPTION have_crypt, have_compress;
SHOW_COMP_OPTION have_profiling;
+SHOW_COMP_OPTION have_openssl;
/* Thread specific variables */
pthread_key(MEM_ROOT**,THR_MALLOC);
pthread_key(THD*, THR_THD);
-mysql_mutex_t LOCK_thread_count;
+mysql_mutex_t LOCK_thread_count, LOCK_thread_cache;
mysql_mutex_t
- LOCK_status, LOCK_error_log, LOCK_short_uuid_generator,
+ LOCK_status, LOCK_show_status, LOCK_error_log, LOCK_short_uuid_generator,
LOCK_delayed_insert, LOCK_delayed_status, LOCK_delayed_create,
LOCK_crypt,
LOCK_global_system_variables,
LOCK_user_conn, LOCK_slave_list, LOCK_active_mi,
- LOCK_connection_count, LOCK_error_messages;
+ LOCK_connection_count, LOCK_error_messages, LOCK_slave_init;
mysql_mutex_t LOCK_stats, LOCK_global_user_client_stats,
LOCK_global_table_stats, LOCK_global_index_stats;
@@ -708,6 +751,8 @@ pthread_attr_t connection_attrib;
mysql_mutex_t LOCK_server_started;
mysql_cond_t COND_server_started;
+int mysqld_server_started=0, mysqld_server_initialized= 0;
+File_parser_dummy_hook file_parser_dummy_hook;
#ifdef WITH_WSREP
mysql_mutex_t LOCK_wsrep_ready;
mysql_cond_t COND_wsrep_ready;
@@ -725,9 +770,6 @@ mysql_mutex_t LOCK_wsrep_desync;
int wsrep_replaying= 0;
static void wsrep_close_threads(THD* thd);
#endif /* WITH_WSREP */
-int mysqld_server_started= 0;
-
-File_parser_dummy_hook file_parser_dummy_hook;
/* replication parameters, if master_host is not NULL, we are a slave */
uint report_port= 0;
@@ -766,32 +808,106 @@ static char **remaining_argv;
int orig_argc;
char **orig_argv;
+static struct my_option pfs_early_options[]=
+{
+#ifdef WITH_PERFSCHEMA_STORAGE_ENGINE
+ {"performance_schema_instrument", OPT_PFS_INSTRUMENT,
+ "Default startup value for a performance schema instrument.",
+ &pfs_param.m_pfs_instrument, &pfs_param.m_pfs_instrument, 0, GET_STR,
+ OPT_ARG, 0, 0, 0, 0, 0, 0},
+ {"performance_schema_consumer_events_stages_current", 0,
+ "Default startup value for the events_stages_current consumer.",
+ &pfs_param.m_consumer_events_stages_current_enabled,
+ &pfs_param.m_consumer_events_stages_current_enabled, 0, GET_BOOL,
+ OPT_ARG, FALSE, 0, 0, 0, 0, 0},
+ {"performance_schema_consumer_events_stages_history", 0,
+ "Default startup value for the events_stages_history consumer.",
+ &pfs_param.m_consumer_events_stages_history_enabled,
+ &pfs_param.m_consumer_events_stages_history_enabled, 0,
+ GET_BOOL, OPT_ARG, FALSE, 0, 0, 0, 0, 0},
+ {"performance_schema_consumer_events_stages_history_long", 0,
+ "Default startup value for the events_stages_history_long consumer.",
+ &pfs_param.m_consumer_events_stages_history_long_enabled,
+ &pfs_param.m_consumer_events_stages_history_long_enabled, 0,
+ GET_BOOL, OPT_ARG, FALSE, 0, 0, 0, 0, 0},
+ {"performance_schema_consumer_events_statements_current", 0,
+ "Default startup value for the events_statements_current consumer.",
+ &pfs_param.m_consumer_events_statements_current_enabled,
+ &pfs_param.m_consumer_events_statements_current_enabled, 0,
+ GET_BOOL, OPT_ARG, TRUE, 0, 0, 0, 0, 0},
+ {"performance_schema_consumer_events_statements_history", 0,
+ "Default startup value for the events_statements_history consumer.",
+ &pfs_param.m_consumer_events_statements_history_enabled,
+ &pfs_param.m_consumer_events_statements_history_enabled, 0,
+ GET_BOOL, OPT_ARG, FALSE, 0, 0, 0, 0, 0},
+ {"performance_schema_consumer_events_statements_history_long", 0,
+ "Default startup value for the events_statements_history_long consumer.",
+ &pfs_param.m_consumer_events_statements_history_long_enabled,
+ &pfs_param.m_consumer_events_statements_history_long_enabled, 0,
+ GET_BOOL, OPT_ARG, FALSE, 0, 0, 0, 0, 0},
+ {"performance_schema_consumer_events_waits_current", 0,
+ "Default startup value for the events_waits_current consumer.",
+ &pfs_param.m_consumer_events_waits_current_enabled,
+ &pfs_param.m_consumer_events_waits_current_enabled, 0,
+ GET_BOOL, OPT_ARG, FALSE, 0, 0, 0, 0, 0},
+ {"performance_schema_consumer_events_waits_history", 0,
+ "Default startup value for the events_waits_history consumer.",
+ &pfs_param.m_consumer_events_waits_history_enabled,
+ &pfs_param.m_consumer_events_waits_history_enabled, 0,
+ GET_BOOL, OPT_ARG, FALSE, 0, 0, 0, 0, 0},
+ {"performance_schema_consumer_events_waits_history_long", 0,
+ "Default startup value for the events_waits_history_long consumer.",
+ &pfs_param.m_consumer_events_waits_history_long_enabled,
+ &pfs_param.m_consumer_events_waits_history_long_enabled, 0,
+ GET_BOOL, OPT_ARG, FALSE, 0, 0, 0, 0, 0},
+ {"performance_schema_consumer_global_instrumentation", 0,
+ "Default startup value for the global_instrumentation consumer.",
+ &pfs_param.m_consumer_global_instrumentation_enabled,
+ &pfs_param.m_consumer_global_instrumentation_enabled, 0,
+ GET_BOOL, OPT_ARG, TRUE, 0, 0, 0, 0, 0},
+ {"performance_schema_consumer_thread_instrumentation", 0,
+ "Default startup value for the thread_instrumentation consumer.",
+ &pfs_param.m_consumer_thread_instrumentation_enabled,
+ &pfs_param.m_consumer_thread_instrumentation_enabled, 0,
+ GET_BOOL, OPT_ARG, TRUE, 0, 0, 0, 0, 0},
+ {"performance_schema_consumer_statements_digest", 0,
+ "Default startup value for the statements_digest consumer.",
+ &pfs_param.m_consumer_statement_digest_enabled,
+ &pfs_param.m_consumer_statement_digest_enabled, 0,
+ GET_BOOL, OPT_ARG, TRUE, 0, 0, 0, 0, 0}
+#endif /* WITH_PERFSCHEMA_STORAGE_ENGINE */
+};
+
#ifdef HAVE_PSI_INTERFACE
#ifdef HAVE_MMAP
-PSI_mutex_key key_PAGE_lock, key_LOCK_sync, key_LOCK_active, key_LOCK_pool;
+PSI_mutex_key key_PAGE_lock, key_LOCK_sync, key_LOCK_active, key_LOCK_pool,
+ key_LOCK_pending_checkpoint;
#endif /* HAVE_MMAP */
#ifdef HAVE_OPENSSL
PSI_mutex_key key_LOCK_des_key_file;
#endif /* HAVE_OPENSSL */
-PSI_mutex_key key_BINLOG_LOCK_index, key_BINLOG_LOCK_prep_xids,
+PSI_mutex_key key_BINLOG_LOCK_index, key_BINLOG_LOCK_xid_list,
+ key_BINLOG_LOCK_binlog_background_thread,
key_delayed_insert_mutex, key_hash_filo_lock, key_LOCK_active_mi,
key_LOCK_connection_count, key_LOCK_crypt, key_LOCK_delayed_create,
key_LOCK_delayed_insert, key_LOCK_delayed_status, key_LOCK_error_log,
key_LOCK_gdl, key_LOCK_global_system_variables,
key_LOCK_manager,
key_LOCK_prepared_stmt_count,
- key_LOCK_rpl_status, key_LOCK_server_started, key_LOCK_status,
- key_LOCK_system_variables_hash, key_LOCK_table_share, key_LOCK_thd_data,
+ key_LOCK_rpl_status, key_LOCK_server_started,
+ key_LOCK_status, key_LOCK_show_status,
+ key_LOCK_system_variables_hash, key_LOCK_thd_data,
key_LOCK_user_conn, key_LOCK_uuid_short_generator, key_LOG_LOCK_log,
key_master_info_data_lock, key_master_info_run_lock,
key_master_info_sleep_lock,
key_mutex_slave_reporting_capability_err_lock, key_relay_log_info_data_lock,
+ key_rpl_group_info_sleep_lock,
key_relay_log_info_log_space_lock, key_relay_log_info_run_lock,
- key_relay_log_info_sleep_lock,
key_structure_guard_mutex, key_TABLE_SHARE_LOCK_ha_data,
- key_LOCK_error_messages, key_LOG_INFO_lock, key_LOCK_thread_count,
+ key_LOCK_error_messages, key_LOG_INFO_lock,
+ key_LOCK_thread_count, key_LOCK_thread_cache,
key_PARTITION_LOCK_auto_inc;
#ifdef WITH_WSREP
PSI_mutex_key key_LOCK_wsrep_rollback, key_LOCK_wsrep_thd,
@@ -800,13 +916,18 @@ PSI_mutex_key key_LOCK_wsrep_rollback, key_LOCK_wsrep_thd,
key_LOCK_wsrep_slave_threads, key_LOCK_wsrep_desync;
#endif
PSI_mutex_key key_RELAYLOG_LOCK_index;
+PSI_mutex_key key_LOCK_slave_state, key_LOCK_binlog_state,
+ key_LOCK_rpl_thread, key_LOCK_rpl_thread_pool, key_LOCK_parallel_entry;
PSI_mutex_key key_LOCK_stats,
key_LOCK_global_user_client_stats, key_LOCK_global_table_stats,
key_LOCK_global_index_stats,
- key_LOCK_wakeup_ready;
+ key_LOCK_wakeup_ready, key_LOCK_wait_commit;
+PSI_mutex_key key_LOCK_gtid_waiting;
-PSI_mutex_key key_LOCK_prepare_ordered, key_LOCK_commit_ordered;
+PSI_mutex_key key_LOCK_prepare_ordered, key_LOCK_commit_ordered,
+ key_LOCK_slave_init;
+PSI_mutex_key key_TABLE_SHARE_LOCK_share;
static PSI_mutex_info all_server_mutexes[]=
{
@@ -815,6 +936,7 @@ static PSI_mutex_info all_server_mutexes[]=
{ &key_LOCK_sync, "TC_LOG_MMAP::LOCK_sync", 0},
{ &key_LOCK_active, "TC_LOG_MMAP::LOCK_active", 0},
{ &key_LOCK_pool, "TC_LOG_MMAP::LOCK_pool", 0},
+ { &key_LOCK_pool, "TC_LOG_MMAP::LOCK_pending_checkpoint", 0},
#endif /* HAVE_MMAP */
#ifdef HAVE_OPENSSL
@@ -822,7 +944,8 @@ static PSI_mutex_info all_server_mutexes[]=
#endif /* HAVE_OPENSSL */
{ &key_BINLOG_LOCK_index, "MYSQL_BIN_LOG::LOCK_index", 0},
- { &key_BINLOG_LOCK_prep_xids, "MYSQL_BIN_LOG::LOCK_prep_xids", 0},
+ { &key_BINLOG_LOCK_xid_list, "MYSQL_BIN_LOG::LOCK_xid_list", 0},
+ { &key_BINLOG_LOCK_binlog_background_thread, "MYSQL_BIN_LOG::LOCK_binlog_background_thread", 0},
{ &key_RELAYLOG_LOCK_index, "MYSQL_RELAY_LOG::LOCK_index", 0},
{ &key_delayed_insert_mutex, "Delayed_insert::mutex", 0},
{ &key_hash_filo_lock, "hash_filo::lock", 0},
@@ -840,13 +963,15 @@ static PSI_mutex_info all_server_mutexes[]=
{ &key_LOCK_rpl_status, "LOCK_rpl_status", PSI_FLAG_GLOBAL},
{ &key_LOCK_server_started, "LOCK_server_started", PSI_FLAG_GLOBAL},
{ &key_LOCK_status, "LOCK_status", PSI_FLAG_GLOBAL},
+ { &key_LOCK_show_status, "LOCK_show_status", PSI_FLAG_GLOBAL},
{ &key_LOCK_system_variables_hash, "LOCK_system_variables_hash", PSI_FLAG_GLOBAL},
- { &key_LOCK_table_share, "LOCK_table_share", PSI_FLAG_GLOBAL},
{ &key_LOCK_stats, "LOCK_stats", PSI_FLAG_GLOBAL},
{ &key_LOCK_global_user_client_stats, "LOCK_global_user_client_stats", PSI_FLAG_GLOBAL},
{ &key_LOCK_global_table_stats, "LOCK_global_table_stats", PSI_FLAG_GLOBAL},
{ &key_LOCK_global_index_stats, "LOCK_global_index_stats", PSI_FLAG_GLOBAL},
{ &key_LOCK_wakeup_ready, "THD::LOCK_wakeup_ready", 0},
+ { &key_LOCK_wait_commit, "wait_for_commit::LOCK_wait_commit", 0},
+ { &key_LOCK_gtid_waiting, "gtid_waiting::LOCK_gtid_waiting", 0},
{ &key_LOCK_thd_data, "THD::LOCK_thd_data", 0},
{ &key_LOCK_user_conn, "LOCK_user_conn", PSI_FLAG_GLOBAL},
{ &key_LOCK_uuid_short_generator, "LOCK_uuid_short_generator", PSI_FLAG_GLOBAL},
@@ -858,14 +983,15 @@ static PSI_mutex_info all_server_mutexes[]=
{ &key_relay_log_info_data_lock, "Relay_log_info::data_lock", 0},
{ &key_relay_log_info_log_space_lock, "Relay_log_info::log_space_lock", 0},
{ &key_relay_log_info_run_lock, "Relay_log_info::run_lock", 0},
- { &key_relay_log_info_sleep_lock, "Relay_log_info::sleep_lock", 0},
+ { &key_rpl_group_info_sleep_lock, "Rpl_group_info::sleep_lock", 0},
{ &key_structure_guard_mutex, "Query_cache::structure_guard_mutex", 0},
{ &key_TABLE_SHARE_LOCK_ha_data, "TABLE_SHARE::LOCK_ha_data", 0},
+ { &key_TABLE_SHARE_LOCK_share, "TABLE_SHARE::LOCK_share", 0},
{ &key_LOCK_error_messages, "LOCK_error_messages", PSI_FLAG_GLOBAL},
{ &key_LOCK_prepare_ordered, "LOCK_prepare_ordered", PSI_FLAG_GLOBAL},
{ &key_LOCK_commit_ordered, "LOCK_commit_ordered", PSI_FLAG_GLOBAL},
+ { &key_LOCK_slave_init, "LOCK_slave_init", PSI_FLAG_GLOBAL},
{ &key_LOG_INFO_lock, "LOG_INFO::lock", 0},
- { &key_LOCK_thread_count, "LOCK_thread_count", PSI_FLAG_GLOBAL},
#ifdef WITH_WSREP
{ &key_LOCK_wsrep_ready, "LOCK_wsrep_ready", PSI_FLAG_GLOBAL},
{ &key_LOCK_wsrep_sst, "LOCK_wsrep_sst", PSI_FLAG_GLOBAL},
@@ -878,7 +1004,14 @@ static PSI_mutex_info all_server_mutexes[]=
{ &key_LOCK_wsrep_slave_threads, "LOCK_wsrep_slave_threads", PSI_FLAG_GLOBAL},
{ &key_LOCK_wsrep_desync, "LOCK_wsrep_desync", PSI_FLAG_GLOBAL},
#endif
- { &key_PARTITION_LOCK_auto_inc, "HA_DATA_PARTITION::LOCK_auto_inc", 0}
+ { &key_LOCK_thread_count, "LOCK_thread_count", PSI_FLAG_GLOBAL},
+ { &key_LOCK_thread_cache, "LOCK_thread_cache", PSI_FLAG_GLOBAL},
+ { &key_PARTITION_LOCK_auto_inc, "HA_DATA_PARTITION::LOCK_auto_inc", 0},
+ { &key_LOCK_slave_state, "LOCK_slave_state", 0},
+ { &key_LOCK_binlog_state, "LOCK_binlog_state", 0},
+ { &key_LOCK_rpl_thread, "LOCK_rpl_thread", 0},
+ { &key_LOCK_rpl_thread_pool, "LOCK_rpl_thread_pool", 0},
+ { &key_LOCK_parallel_entry, "LOCK_parallel_entry", 0}
};
PSI_rwlock_key key_rwlock_LOCK_grant, key_rwlock_LOCK_logger,
@@ -902,7 +1035,9 @@ static PSI_rwlock_info all_server_rwlocks[]=
PSI_cond_key key_PAGE_cond, key_COND_active, key_COND_pool;
#endif /* HAVE_MMAP */
-PSI_cond_key key_BINLOG_COND_prep_xids, key_BINLOG_update_cond,
+PSI_cond_key key_BINLOG_COND_xid_list, key_BINLOG_update_cond,
+ key_BINLOG_COND_binlog_background_thread,
+ key_BINLOG_COND_binlog_background_thread_end,
key_COND_cache_status_changed, key_COND_manager,
key_COND_rpl_status, key_COND_server_started,
key_delayed_insert_cond, key_delayed_insert_cond_client,
@@ -911,7 +1046,7 @@ PSI_cond_key key_BINLOG_COND_prep_xids, key_BINLOG_update_cond,
key_master_info_sleep_cond,
key_relay_log_info_data_cond, key_relay_log_info_log_space_cond,
key_relay_log_info_start_cond, key_relay_log_info_stop_cond,
- key_relay_log_info_sleep_cond,
+ key_rpl_group_info_sleep_cond,
key_TABLE_SHARE_cond, key_user_level_lock_cond,
key_COND_thread_count, key_COND_thread_cache, key_COND_flush_thread_cache,
key_BINLOG_COND_queue_busy;
@@ -920,9 +1055,15 @@ PSI_cond_key key_COND_wsrep_rollback,
key_COND_wsrep_replaying, key_COND_wsrep_ready, key_COND_wsrep_sst,
key_COND_wsrep_sst_init, key_COND_wsrep_sst_thread;
#endif /* WITH_WSREP */
-PSI_cond_key key_RELAYLOG_update_cond, key_COND_wakeup_ready;
+PSI_cond_key key_RELAYLOG_update_cond, key_COND_wakeup_ready,
+ key_COND_wait_commit;
PSI_cond_key key_RELAYLOG_COND_queue_busy;
PSI_cond_key key_TC_LOG_MMAP_COND_queue_busy;
+PSI_cond_key key_COND_rpl_thread_queue, key_COND_rpl_thread,
+ key_COND_rpl_thread_stop, key_COND_rpl_thread_pool,
+ key_COND_parallel_entry, key_COND_group_commit_orderer,
+ key_COND_prepare_ordered, key_COND_slave_init;
+PSI_cond_key key_COND_wait_gtid, key_COND_gtid_ignore_duplicates;
static PSI_cond_info all_server_conds[]=
{
@@ -935,15 +1076,17 @@ static PSI_cond_info all_server_conds[]=
{ &key_COND_pool, "TC_LOG_MMAP::COND_pool", 0},
{ &key_TC_LOG_MMAP_COND_queue_busy, "TC_LOG_MMAP::COND_queue_busy", 0},
#endif /* HAVE_MMAP */
- { &key_BINLOG_COND_prep_xids, "MYSQL_BIN_LOG::COND_prep_xids", 0},
+ { &key_BINLOG_COND_xid_list, "MYSQL_BIN_LOG::COND_xid_list", 0},
{ &key_BINLOG_update_cond, "MYSQL_BIN_LOG::update_cond", 0},
+ { &key_BINLOG_COND_binlog_background_thread, "MYSQL_BIN_LOG::COND_binlog_background_thread", 0},
+ { &key_BINLOG_COND_binlog_background_thread_end, "MYSQL_BIN_LOG::COND_binlog_background_thread_end", 0},
{ &key_BINLOG_COND_queue_busy, "MYSQL_BIN_LOG::COND_queue_busy", 0},
{ &key_RELAYLOG_update_cond, "MYSQL_RELAY_LOG::update_cond", 0},
{ &key_RELAYLOG_COND_queue_busy, "MYSQL_RELAY_LOG::COND_queue_busy", 0},
{ &key_COND_wakeup_ready, "THD::COND_wakeup_ready", 0},
+ { &key_COND_wait_commit, "wait_for_commit::COND_wait_commit", 0},
{ &key_COND_cache_status_changed, "Query_cache::COND_cache_status_changed", 0},
{ &key_COND_manager, "COND_manager", PSI_FLAG_GLOBAL},
- { &key_COND_rpl_status, "COND_rpl_status", PSI_FLAG_GLOBAL},
{ &key_COND_server_started, "COND_server_started", PSI_FLAG_GLOBAL},
{ &key_delayed_insert_cond, "Delayed_insert::cond", 0},
{ &key_delayed_insert_cond_client, "Delayed_insert::cond_client", 0},
@@ -956,7 +1099,7 @@ static PSI_cond_info all_server_conds[]=
{ &key_relay_log_info_log_space_cond, "Relay_log_info::log_space_cond", 0},
{ &key_relay_log_info_start_cond, "Relay_log_info::start_cond", 0},
{ &key_relay_log_info_stop_cond, "Relay_log_info::stop_cond", 0},
- { &key_relay_log_info_sleep_cond, "Relay_log_info::sleep_cond", 0},
+ { &key_rpl_group_info_sleep_cond, "Rpl_group_info::sleep_cond", 0},
{ &key_TABLE_SHARE_cond, "TABLE_SHARE::cond", 0},
{ &key_user_level_lock_cond, "User_level_lock::cond", 0},
{ &key_COND_thread_count, "COND_thread_count", PSI_FLAG_GLOBAL},
@@ -969,12 +1112,23 @@ static PSI_cond_info all_server_conds[]=
{ &key_COND_wsrep_rollback, "COND_wsrep_rollback", PSI_FLAG_GLOBAL},
{ &key_COND_wsrep_replaying, "COND_wsrep_replaying", PSI_FLAG_GLOBAL},
#endif
- { &key_COND_flush_thread_cache, "COND_flush_thread_cache", PSI_FLAG_GLOBAL}
+ { &key_COND_flush_thread_cache, "COND_flush_thread_cache", PSI_FLAG_GLOBAL},
+ { &key_COND_rpl_thread, "COND_rpl_thread", 0},
+ { &key_COND_rpl_thread_queue, "COND_rpl_thread_queue", 0},
+ { &key_COND_rpl_thread_stop, "COND_rpl_thread_stop", 0},
+ { &key_COND_rpl_thread_pool, "COND_rpl_thread_pool", 0},
+ { &key_COND_parallel_entry, "COND_parallel_entry", 0},
+ { &key_COND_group_commit_orderer, "COND_group_commit_orderer", 0},
+ { &key_COND_prepare_ordered, "COND_prepare_ordered", 0},
+ { &key_COND_slave_init, "COND_slave_init", 0},
+ { &key_COND_wait_gtid, "COND_wait_gtid", 0},
+ { &key_COND_gtid_ignore_duplicates, "COND_gtid_ignore_duplicates", 0}
};
PSI_thread_key key_thread_bootstrap, key_thread_delayed_insert,
key_thread_handle_manager, key_thread_main,
- key_thread_one_connection, key_thread_signal_hand;
+ key_thread_one_connection, key_thread_signal_hand,
+ key_thread_slave_init, key_rpl_parallel_thread;
static PSI_thread_info all_server_threads[]=
{
@@ -999,9 +1153,15 @@ static PSI_thread_info all_server_threads[]=
{ &key_thread_handle_manager, "manager", PSI_FLAG_GLOBAL},
{ &key_thread_main, "main", PSI_FLAG_GLOBAL},
{ &key_thread_one_connection, "one_connection", 0},
- { &key_thread_signal_hand, "signal_handler", PSI_FLAG_GLOBAL}
+ { &key_thread_signal_hand, "signal_handler", PSI_FLAG_GLOBAL},
+ { &key_thread_slave_init, "slave_init", PSI_FLAG_GLOBAL},
+ { &key_rpl_parallel_thread, "rpl_parallel_thread", 0}
};
+#ifdef HAVE_MMAP
+PSI_file_key key_file_map;
+#endif /* HAVE_MMAP */
+
PSI_file_key key_file_binlog, key_file_binlog_index, key_file_casetest,
key_file_dbopt, key_file_des_key_file, key_file_ERRMSG, key_select_to_file,
key_file_fileparser, key_file_frm, key_file_global_ddl_log, key_file_load,
@@ -1011,75 +1171,84 @@ PSI_file_key key_file_binlog, key_file_binlog_index, key_file_casetest,
key_file_trg, key_file_trn, key_file_init;
PSI_file_key key_file_query_log, key_file_slow_log;
PSI_file_key key_file_relaylog, key_file_relaylog_index;
+PSI_file_key key_file_binlog_state;
-static PSI_file_info all_server_files[]=
-{
- { &key_file_binlog, "binlog", 0},
- { &key_file_binlog_index, "binlog_index", 0},
- { &key_file_relaylog, "relaylog", 0},
- { &key_file_relaylog_index, "relaylog_index", 0},
- { &key_file_casetest, "casetest", 0},
- { &key_file_dbopt, "dbopt", 0},
- { &key_file_des_key_file, "des_key_file", 0},
- { &key_file_ERRMSG, "ERRMSG", 0},
- { &key_select_to_file, "select_to_file", 0},
- { &key_file_fileparser, "file_parser", 0},
- { &key_file_frm, "FRM", 0},
- { &key_file_global_ddl_log, "global_ddl_log", 0},
- { &key_file_load, "load", 0},
- { &key_file_loadfile, "LOAD_FILE", 0},
- { &key_file_log_event_data, "log_event_data", 0},
- { &key_file_log_event_info, "log_event_info", 0},
- { &key_file_master_info, "master_info", 0},
- { &key_file_misc, "misc", 0},
- { &key_file_partition, "partition", 0},
- { &key_file_pid, "pid", 0},
- { &key_file_query_log, "query_log", 0},
- { &key_file_relay_log_info, "relay_log_info", 0},
- { &key_file_send_file, "send_file", 0},
- { &key_file_slow_log, "slow_log", 0},
- { &key_file_tclog, "tclog", 0},
- { &key_file_trg, "trigger_name", 0},
- { &key_file_trn, "trigger", 0},
- { &key_file_init, "init", 0}
-};
+#endif /* HAVE_PSI_INTERFACE */
-/**
- Initialise all the performance schema instrumentation points
- used by the server.
-*/
-void init_server_psi_keys(void)
+#ifdef HAVE_PSI_STATEMENT_INTERFACE
+PSI_statement_info stmt_info_new_packet;
+#endif
+
+#ifndef EMBEDDED_LIBRARY
+void net_before_header_psi(struct st_net *net, void *user_data, size_t /* unused: count */)
{
- const char* category= "sql";
- int count;
+ THD *thd;
+ thd= static_cast<THD*> (user_data);
+ DBUG_ASSERT(thd != NULL);
- if (PSI_server == NULL)
- return;
+ /*
+ We only come where when the server is IDLE, waiting for the next command.
+ Technically, it is a wait on a socket, which may take a long time,
+ because the call is blocking.
+ Disable the socket instrumentation, to avoid recording a SOCKET event.
+ Instead, start explicitly an IDLE event.
+ */
+ MYSQL_SOCKET_SET_STATE(net->vio->mysql_socket, PSI_SOCKET_STATE_IDLE);
+ MYSQL_START_IDLE_WAIT(thd->m_idle_psi, &thd->m_idle_state);
+}
- count= array_elements(all_server_mutexes);
- PSI_server->register_mutex(category, all_server_mutexes, count);
+void net_after_header_psi(struct st_net *net, void *user_data,
+ size_t /* unused: count */, my_bool rc)
+{
+ THD *thd;
+ thd= static_cast<THD*> (user_data);
+ DBUG_ASSERT(thd != NULL);
- count= array_elements(all_server_rwlocks);
- PSI_server->register_rwlock(category, all_server_rwlocks, count);
+ /*
+ The server just got data for a network packet header,
+ from the network layer.
+ The IDLE event is now complete, since we now have a message to process.
+ We need to:
+ - start a new STATEMENT event
+ - start a new STAGE event, within this statement,
+ - start recording SOCKET WAITS events, within this stage.
+ The proper order is critical to get events numbered correctly,
+ and nested in the proper parent.
+ */
+ MYSQL_END_IDLE_WAIT(thd->m_idle_psi);
- count= array_elements(all_server_conds);
- PSI_server->register_cond(category, all_server_conds, count);
+ if (! rc)
+ {
+ thd->m_statement_psi= MYSQL_START_STATEMENT(&thd->m_statement_state,
+ stmt_info_new_packet.m_key,
+ thd->db, thd->db_length,
+ thd->charset());
- count= array_elements(all_server_threads);
- PSI_server->register_thread(category, all_server_threads, count);
+ THD_STAGE_INFO(thd, stage_init);
+ }
- count= array_elements(all_server_files);
- PSI_server->register_file(category, all_server_files, count);
+ /*
+ TODO: consider recording a SOCKET event for the bytes just read,
+ by also passing count here.
+ */
+ MYSQL_SOCKET_SET_STATE(net->vio->mysql_socket, PSI_SOCKET_STATE_ACTIVE);
}
-#endif /* HAVE_PSI_INTERFACE */
-/*
- Since buffered_option_error_reporter is only used currently
- for parsing performance schema options, this code is not needed
- when the performance schema is not compiled in.
-*/
-#ifdef WITH_PERFSCHEMA_STORAGE_ENGINE
+void init_net_server_extension(THD *thd)
+{
+ /* Start with a clean state for connection events. */
+ thd->m_idle_psi= NULL;
+ thd->m_statement_psi= NULL;
+ /* Hook up the NET_SERVER callback in the net layer. */
+ thd->m_net_server_extension.m_user_data= thd;
+ thd->m_net_server_extension.m_before_header= net_before_header_psi;
+ thd->m_net_server_extension.m_after_header= net_after_header_psi;
+ /* Activate this private extension for the mysqld server. */
+ thd->net.extension= & thd->m_net_server_extension;
+}
+#endif /* EMBEDDED_LIBRARY */
+
/**
A log message for the error log, buffered in memory.
Log messages are temporarily buffered when generated before the error log
@@ -1176,7 +1345,7 @@ private:
void Buffered_logs::init()
{
- init_alloc_root(&m_root, 1024, 0);
+ init_alloc_root(&m_root, 1024, 0, MYF(0));
}
void Buffered_logs::cleanup()
@@ -1215,6 +1384,9 @@ void Buffered_logs::print()
/** Logs reported before a logger is available. */
static Buffered_logs buffered_logs;
+static MYSQL_SOCKET unix_sock, base_ip_sock, extra_ip_sock;
+struct my_rnd_struct sql_rand; ///< used by sql_class.cc:THD::THD()
+
#ifndef EMBEDDED_LIBRARY
/**
Error reporter that buffer log messages.
@@ -1233,14 +1405,34 @@ static void buffered_option_error_reporter(enum loglevel level,
va_end(args);
buffered_logs.buffer(level, buffer);
}
-C_MODE_END
-#endif /* !EMBEDDED_LIBRARY */
-#endif /* WITH_PERFSCHEMA_STORAGE_ENGINE */
-static my_socket unix_sock, base_ip_sock, extra_ip_sock;
-struct my_rnd_struct sql_rand; ///< used by sql_class.cc:THD::THD()
-#ifndef EMBEDDED_LIBRARY
+/**
+ Character set and collation error reporter that prints to sql error log.
+ @param level log message level
+ @param format log message format string
+
+ This routine is used to print character set and collation
+ warnings and errors inside an already running mysqld server,
+ e.g. when a character set or collation is requested for the very first time
+ and its initialization does not go well for some reasons.
+
+ Note: At early mysqld initialization stage,
+ when error log is not yet available,
+ we use buffered_option_error_reporter() instead,
+ to print general character set subsystem initialization errors,
+ such as Index.xml syntax problems, bad XML tag hierarchy, etc.
+*/
+static void charset_error_reporter(enum loglevel level,
+ const char *format, ...)
+{
+ va_list args;
+ va_start(args, format);
+ vprint_msg_to_log(level, format, args);
+ va_end(args);
+}
+C_MODE_END
+
struct passwd *user_info;
static pthread_t select_thread;
#endif
@@ -1278,7 +1470,9 @@ bool mysqld_embedded=0;
bool mysqld_embedded=1;
#endif
+#ifndef EMBEDDED_LIBRARY
static my_bool plugins_are_initialized= FALSE;
+#endif
#ifndef DBUG_OFF
static const char* default_dbug_option;
@@ -1301,7 +1495,9 @@ HANDLE smem_event_connect_request= 0;
my_bool opt_use_ssl = 0;
char *opt_ssl_ca= NULL, *opt_ssl_capath= NULL, *opt_ssl_cert= NULL,
- *opt_ssl_cipher= NULL, *opt_ssl_key= NULL;
+ *opt_ssl_cipher= NULL, *opt_ssl_key= NULL, *opt_ssl_crl= NULL,
+ *opt_ssl_crlpath= NULL;
+
static scheduler_functions thread_scheduler_struct, extra_thread_scheduler_struct;
scheduler_functions *thread_scheduler= &thread_scheduler_struct,
@@ -1333,12 +1529,16 @@ struct st_VioSSLFd *ssl_acceptor_fd;
*/
uint connection_count= 0, extra_connection_count= 0;
+my_bool opt_gtid_strict_mode= FALSE;
+
+
/* Function declarations */
pthread_handler_t signal_hand(void *arg);
static int mysql_init_variables(void);
static int get_options(int *argc_ptr, char ***argv_ptr);
static bool add_terminator(DYNAMIC_ARRAY *options);
+static bool add_many_options(DYNAMIC_ARRAY *, my_option *, size_t);
extern "C" my_bool mysqld_get_one_option(int, const struct my_option *, char *);
static int init_thread_environment();
static char *get_relative_path(const char *path);
@@ -1429,17 +1629,17 @@ static void close_connections(void)
DBUG_PRINT("quit",("Closing sockets"));
if (!opt_disable_networking )
{
- if (base_ip_sock != INVALID_SOCKET)
+ if (mysql_socket_getfd(base_ip_sock) != INVALID_SOCKET)
{
(void) mysql_socket_shutdown(base_ip_sock, SHUT_RDWR);
- (void) closesocket(base_ip_sock);
- base_ip_sock= INVALID_SOCKET;
+ (void) mysql_socket_close(base_ip_sock);
+ base_ip_sock= MYSQL_INVALID_SOCKET;
}
- if (extra_ip_sock != INVALID_SOCKET)
+ if (mysql_socket_getfd(extra_ip_sock) != INVALID_SOCKET)
{
(void) mysql_socket_shutdown(extra_ip_sock, SHUT_RDWR);
- (void) closesocket(extra_ip_sock);
- extra_ip_sock= INVALID_SOCKET;
+ (void) mysql_socket_close(extra_ip_sock);
+ extra_ip_sock= MYSQL_INVALID_SOCKET;
}
}
#ifdef _WIN32
@@ -1467,12 +1667,12 @@ static void close_connections(void)
}
#endif
#ifdef HAVE_SYS_UN_H
- if (unix_sock != INVALID_SOCKET)
+ if (mysql_socket_getfd(unix_sock) != INVALID_SOCKET)
{
(void) mysql_socket_shutdown(unix_sock, SHUT_RDWR);
- (void) closesocket(unix_sock);
+ (void) mysql_socket_close(unix_sock);
(void) unlink(mysqld_unix_port);
- unix_sock= INVALID_SOCKET;
+ unix_sock= MYSQL_INVALID_SOCKET;
}
#endif
end_thr_alarm(0); // Abort old alarms.
@@ -1532,8 +1732,21 @@ static void close_connections(void)
Events::deinit();
end_slave();
- /* Give threads time to die. */
- for (int i= 0; thread_count && i < 100; i++)
+ /*
+ Give threads time to die.
+
+ In 5.5, this was waiting 100 rounds @ 20 milliseconds/round, so as little
+ as 2 seconds, depending on thread scheduling.
+
+ From 10.0, we increase this to 1000 rounds / 20 seconds. The rationale is
+ that on a server with heavy I/O load, it is quite possible for eg. an
+ fsync() of the binlog or whatever to cause something like LOCK_log to be
+ held for more than 2 seconds. We do not want to force kill threads in
+ such cases, if it can be avoided. Note that normally, the wait will be
+ much smaller than even 2 seconds, this is only a safety fallback against
+ stuck threads so server shutdown is not held up forever.
+ */
+ for (int i= 0; *(volatile int32*) &thread_count && i < 1000; i++)
my_sleep(20000);
/*
@@ -1596,7 +1809,7 @@ static void close_connections(void)
/* All threads has now been aborted */
DBUG_PRINT("quit",("Waiting for threads to die (count=%u)",thread_count));
mysql_mutex_lock(&LOCK_thread_count);
- while (thread_count)
+ while (thread_count || service_thread_count)
{
mysql_cond_wait(&COND_thread_count, &LOCK_thread_count);
DBUG_PRINT("quit",("One thread died (count=%u)",thread_count));
@@ -1609,22 +1822,14 @@ static void close_connections(void)
#ifdef HAVE_CLOSE_SERVER_SOCK
-static void close_socket(my_socket sock, const char *info)
+static void close_socket(MYSQL_SOCKET sock, const char *info)
{
DBUG_ENTER("close_socket");
- if (sock != INVALID_SOCKET)
+ if (mysql_socket_getfd(sock) != INVALID_SOCKET)
{
DBUG_PRINT("info", ("calling shutdown on %s socket", info));
(void) mysql_socket_shutdown(sock, SHUT_RDWR);
-#if defined(__NETWARE__)
- /*
- The following code is disabled for normal systems as it causes MySQL
- to hang on AIX 4.3 during shutdown
- */
- DBUG_PRINT("info", ("calling closesocket on %s socket", info));
- (void) closesocket(tmp_sock);
-#endif
}
DBUG_VOID_RETURN;
}
@@ -1640,9 +1845,9 @@ static void close_server_sock()
close_socket(extra_ip_sock, "TCP/IP");
close_socket(unix_sock, "unix/IP");
- if (unix_sock != INVALID_SOCKET)
+ if (mysql_socket_getfd(unix_sock) != INVALID_SOCKET)
(void) unlink(mysqld_unix_port);
- base_ip_sock= extra_ip_sock= unix_sock= INVALID_SOCKET;
+ base_ip_sock= extra_ip_sock= unix_sock= MYSQL_INVALID_SOCKET;
DBUG_VOID_RETURN;
#endif
@@ -1874,7 +2079,6 @@ extern "C" void unireg_abort(int exit_code)
wsrep_deinit(true);
}
#endif // WITH_WSREP
-
clean_up(!opt_abort && (exit_code || !opt_bootstrap)); /* purecov: inspected */
DBUG_PRINT("quit",("done with cleanup in unireg_abort"));
mysqld_exit(exit_code);
@@ -1882,11 +2086,14 @@ extern "C" void unireg_abort(int exit_code)
static void mysqld_exit(int exit_code)
{
+ DBUG_ENTER("mysqld_exit");
/*
Important note: we wait for the signal thread to end,
but if a kill -15 signal was sent, the signal thread did
spawn the kill_server_thread thread, which is running concurrently.
*/
+ rpl_deinit_gtid_waiting();
+ rpl_deinit_gtid_slave_state();
wait_for_signal_thread_to_end();
mysql_audit_finalize();
clean_up_mutexes();
@@ -1895,6 +2102,7 @@ static void mysqld_exit(int exit_code)
#ifdef WITH_PERFSCHEMA_STORAGE_ENGINE
shutdown_performance_schema(); // we do it as late as possible
#endif
+ DBUG_LEAVE;
exit(exit_code); /* purecov: inspected */
}
@@ -1906,7 +2114,12 @@ void clean_up(bool print_message)
if (cleanup_done++)
return; /* purecov: inspected */
- close_active_mi();
+#ifdef HAVE_REPLICATION
+ // We must call end_slave() as clean_up may have been called during startup
+ end_slave();
+ if (use_slave_mask)
+ my_bitmap_free(&slave_error_mask);
+#endif
stop_handle_manager();
release_ddl_log();
@@ -1921,24 +2134,20 @@ void clean_up(bool print_message)
injector::free_instance();
mysql_bin_log.cleanup();
-#ifdef HAVE_REPLICATION
- if (use_slave_mask)
- bitmap_free(&slave_error_mask);
-#endif
my_tz_free();
my_dboptions_cache_free();
ignore_db_dirs_free();
-#ifndef NO_EMBEDDED_ACCESS_CHECKS
servers_free(1);
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
acl_free(1);
grant_free();
#endif
query_cache_destroy();
hostname_cache_free();
- item_user_lock_free();
+ item_func_sleep_free();
lex_free(); /* Free some memory */
item_create_cleanup();
- table_def_start_shutdown();
+ tdc_start_shutdown();
plugin_shutdown();
udf_free();
ha_end();
@@ -1946,7 +2155,7 @@ void clean_up(bool print_message)
tc_log->close();
delegates_destroy();
xid_cache_free();
- table_def_free();
+ tdc_deinit();
mdl_destroy();
key_caches.delete_elements((void (*)(const char*, uchar*)) free_key_cache);
wt_end();
@@ -1958,24 +2167,24 @@ void clean_up(bool print_message)
if (defaults_argv)
free_defaults(defaults_argv);
free_tmpdir(&mysql_tmpdir_list);
- bitmap_free(&temp_pool);
+ my_bitmap_free(&temp_pool);
free_max_user_conn();
free_global_user_stats();
free_global_client_stats();
free_global_table_stats();
free_global_index_stats();
delete_dynamic(&all_options);
+ free_all_rpl_filters();
#ifdef HAVE_REPLICATION
end_slave_list();
#endif
my_uuid_end();
delete binlog_filter;
- delete rpl_filter;
+ delete global_rpl_filter;
end_ssl();
#ifndef EMBEDDED_LIBRARY
vio_end();
#endif /*!EMBEDDED_LIBRARY*/
- my_regex_end();
#if defined(ENABLED_DEBUG_SYNC)
/* End the debug sync facility. See debug_sync.cc. */
debug_sync_end();
@@ -1996,6 +2205,9 @@ void clean_up(bool print_message)
sys_var_end();
my_atomic_rwlock_destroy(&global_query_id_lock);
my_atomic_rwlock_destroy(&thread_running_lock);
+ my_atomic_rwlock_destroy(&thread_count_lock);
+ my_atomic_rwlock_destroy(&statistics_lock);
+ my_atomic_rwlock_destroy(&slave_executed_entries_lock);
free_charsets();
mysql_mutex_lock(&LOCK_thread_count);
DBUG_PRINT("quit", ("got thread count lock"));
@@ -2004,6 +2216,14 @@ void clean_up(bool print_message)
mysql_cond_broadcast(&COND_thread_count);
mysql_mutex_unlock(&LOCK_thread_count);
+ free_list(opt_plugin_load_list_ptr);
+
+ if (THR_THD)
+ (void) pthread_key_delete(THR_THD);
+
+ if (THR_MALLOC)
+ (void) pthread_key_delete(THR_MALLOC);
+
/*
The following lines may never be executed as the main thread may have
killed us
@@ -2039,7 +2259,9 @@ static void clean_up_mutexes()
DBUG_ENTER("clean_up_mutexes");
mysql_rwlock_destroy(&LOCK_grant);
mysql_mutex_destroy(&LOCK_thread_count);
+ mysql_mutex_destroy(&LOCK_thread_cache);
mysql_mutex_destroy(&LOCK_status);
+ mysql_mutex_destroy(&LOCK_show_status);
mysql_mutex_destroy(&LOCK_delayed_insert);
mysql_mutex_destroy(&LOCK_delayed_status);
mysql_mutex_destroy(&LOCK_delayed_create);
@@ -2060,7 +2282,6 @@ static void clean_up_mutexes()
#endif /* HAVE_OPENSSL */
#ifdef HAVE_REPLICATION
mysql_mutex_destroy(&LOCK_rpl_status);
- mysql_cond_destroy(&COND_rpl_status);
#endif /* HAVE_REPLICATION */
mysql_mutex_destroy(&LOCK_active_mi);
mysql_rwlock_destroy(&LOCK_sys_init_connect);
@@ -2090,7 +2311,10 @@ static void clean_up_mutexes()
mysql_mutex_destroy(&LOCK_server_started);
mysql_cond_destroy(&COND_server_started);
mysql_mutex_destroy(&LOCK_prepare_ordered);
+ mysql_cond_destroy(&COND_prepare_ordered);
mysql_mutex_destroy(&LOCK_commit_ordered);
+ mysql_mutex_destroy(&LOCK_slave_init);
+ mysql_cond_destroy(&COND_slave_init);
DBUG_VOID_RETURN;
}
@@ -2276,14 +2500,15 @@ static void set_root(const char *path)
Activate usage of a tcp port
*/
-static my_socket activate_tcp_port(uint port)
+static MYSQL_SOCKET activate_tcp_port(uint port)
{
struct addrinfo *ai, *a;
struct addrinfo hints;
int error;
int arg;
char port_buf[NI_MAXSERV];
- my_socket ip_sock= INVALID_SOCKET;
+ const char *real_bind_addr_str;
+ MYSQL_SOCKET ip_sock= MYSQL_INVALID_SOCKET;
DBUG_ENTER("activate_tcp_port");
DBUG_PRINT("general",("IP Socket is %d",port));
@@ -2291,34 +2516,55 @@ static my_socket activate_tcp_port(uint port)
hints.ai_flags= AI_PASSIVE;
hints.ai_socktype= SOCK_STREAM;
hints.ai_family= AF_UNSPEC;
+
+ if (my_bind_addr_str && strcmp(my_bind_addr_str, "*") == 0)
+ real_bind_addr_str= NULL; // windows doesn't seem to support * here
+ else
+ real_bind_addr_str= my_bind_addr_str;
my_snprintf(port_buf, NI_MAXSERV, "%d", port);
- error= getaddrinfo(my_bind_addr_str, port_buf, &hints, &ai);
+ error= getaddrinfo(real_bind_addr_str, port_buf, &hints, &ai);
if (error != 0)
{
DBUG_PRINT("error",("Got error: %d from getaddrinfo()", error));
- sql_perror(ER_DEFAULT(ER_IPSOCK_ERROR)); /* purecov: tested */
+
+ sql_print_error("%s: %s", ER_DEFAULT(ER_IPSOCK_ERROR), gai_strerror(error));
unireg_abort(1); /* purecov: tested */
}
+ /*
+ special case: for wildcard addresses prefer ipv6 over ipv4,
+ because we later switch off IPV6_V6ONLY, so ipv6 wildcard
+ addresses will work for ipv4 too
+ */
+ if (!real_bind_addr_str && ai->ai_family == AF_INET && ai->ai_next
+ && ai->ai_next->ai_family == AF_INET6)
+ {
+ a= ai;
+ ai= ai->ai_next;
+ a->ai_next= ai->ai_next;
+ ai->ai_next= a;
+ }
+
for (a= ai; a != NULL; a= a->ai_next)
{
- ip_sock= socket(a->ai_family, a->ai_socktype, a->ai_protocol);
-
- char ip_addr[INET6_ADDRSTRLEN];
+ ip_sock= mysql_socket_socket(key_socket_tcpip, a->ai_family,
+ a->ai_socktype, a->ai_protocol);
+ char ip_addr[INET6_ADDRSTRLEN];
if (vio_get_normalized_ip_string(a->ai_addr, a->ai_addrlen,
ip_addr, sizeof (ip_addr)))
{
ip_addr[0]= 0;
}
- if (ip_sock == INVALID_SOCKET)
+ if (mysql_socket_getfd(ip_sock) == INVALID_SOCKET)
{
- sql_print_error("Failed to create a socket for %s '%s': errno: %d.",
- (a->ai_family == AF_INET) ? "IPv4" : "IPv6",
- (const char *) ip_addr,
- (int) socket_errno);
+ sql_print_message_func func= real_bind_addr_str ? sql_print_error
+ : sql_print_warning;
+ func("Failed to create a socket for %s '%s': errno: %d.",
+ (a->ai_family == AF_INET) ? "IPv4" : "IPv6",
+ (const char *) ip_addr, (int) socket_errno);
}
else
{
@@ -2328,20 +2574,23 @@ static my_socket activate_tcp_port(uint port)
}
}
- if (ip_sock == INVALID_SOCKET)
+ if (mysql_socket_getfd(ip_sock) == INVALID_SOCKET)
{
DBUG_PRINT("error",("Got error: %d from socket()",socket_errno));
sql_perror(ER_DEFAULT(ER_IPSOCK_ERROR)); /* purecov: tested */
unireg_abort(1); /* purecov: tested */
}
+ mysql_socket_set_thread_owner(ip_sock);
+
#ifndef __WIN__
/*
We should not use SO_REUSEADDR on windows as this would enable a
user to open two mysqld servers with the same TCP/IP port.
*/
arg= 1;
- (void) setsockopt(ip_sock,SOL_SOCKET,SO_REUSEADDR,(char*)&arg,sizeof(arg));
+ (void) mysql_socket_setsockopt(ip_sock,SOL_SOCKET,SO_REUSEADDR,(char*)&arg,
+ sizeof(arg));
#endif /* __WIN__ */
#ifdef IPV6_V6ONLY
@@ -2357,8 +2606,8 @@ static my_socket activate_tcp_port(uint port)
if (a->ai_family == AF_INET6)
{
arg= 0;
- (void) setsockopt(ip_sock, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&arg,
- sizeof(arg));
+ (void) mysql_socket_setsockopt(ip_sock, IPPROTO_IPV6, IPV6_V6ONLY,
+ (char*)&arg, sizeof(arg));
}
#endif
/*
@@ -2373,7 +2622,7 @@ static my_socket activate_tcp_port(uint port)
uint waited, retry, this_wait;
for (waited= 0, retry= 1; ; retry++, waited+= this_wait)
{
- if (((ret= bind(ip_sock, a->ai_addr, a->ai_addrlen)) >= 0 ) ||
+ if (((ret= mysql_socket_bind(ip_sock, a->ai_addr, a->ai_addrlen)) >= 0 ) ||
(socket_errno != SOCKET_EADDRINUSE) ||
(waited >= mysqld_port_timeout))
break;
@@ -2392,7 +2641,7 @@ static my_socket activate_tcp_port(uint port)
"port: %u ?", port);
unireg_abort(1);
}
- if (listen(ip_sock,(int) back_log) < 0)
+ if (mysql_socket_listen(ip_sock,(int) back_log) < 0)
{
sql_perror("Can't start server: listen() on TCP/IP port");
sql_print_error("listen() on TCP/IP failed with error %d",
@@ -2400,8 +2649,9 @@ static my_socket activate_tcp_port(uint port)
unireg_abort(1);
}
#if defined(WITH_WSREP) && defined(HAVE_FCNTL) && defined(FD_CLOEXEC)
- (void) fcntl(ip_sock, F_SETFD, FD_CLOEXEC);
+ (void) fcntl(mysql_socket_getfd(ip_sock), F_SETFD, FD_CLOEXEC);
#endif /* WITH_WSREP */
+
DBUG_RETURN(ip_sock);
}
@@ -2496,21 +2746,26 @@ static void network_init(void)
(uint) sizeof(UNIXaddr.sun_path) - 1, mysqld_unix_port);
unireg_abort(1);
}
- if ((unix_sock= socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
+ unix_sock= mysql_socket_socket(key_socket_unix, AF_UNIX, SOCK_STREAM, 0);
+ if (mysql_socket_getfd(unix_sock) < 0)
{
sql_perror("Can't start server : UNIX Socket "); /* purecov: inspected */
unireg_abort(1); /* purecov: inspected */
}
+
+ mysql_socket_set_thread_owner(unix_sock);
+
bzero((char*) &UNIXaddr, sizeof(UNIXaddr));
UNIXaddr.sun_family = AF_UNIX;
strmov(UNIXaddr.sun_path, mysqld_unix_port);
(void) unlink(mysqld_unix_port);
arg= 1;
- (void) setsockopt(unix_sock,SOL_SOCKET,SO_REUSEADDR,(char*)&arg,
- sizeof(arg));
+ (void) mysql_socket_setsockopt(unix_sock,SOL_SOCKET,SO_REUSEADDR,
+ (char*)&arg, sizeof(arg));
umask(0);
- if (bind(unix_sock, reinterpret_cast<struct sockaddr *>(&UNIXaddr),
- sizeof(UNIXaddr)) < 0)
+ if (mysql_socket_bind(unix_sock,
+ reinterpret_cast<struct sockaddr *>(&UNIXaddr),
+ sizeof(UNIXaddr)) < 0)
{
sql_perror("Can't start server : Bind on unix socket"); /* purecov: tested */
sql_print_error("Do you already have another mysqld server running on socket: %s ?",mysqld_unix_port);
@@ -2520,11 +2775,11 @@ static void network_init(void)
#if defined(S_IFSOCK) && defined(SECURE_SOCKETS)
(void) chmod(mysqld_unix_port,S_IFSOCK); /* Fix solaris 2.6 bug */
#endif
- if (listen(unix_sock,(int) back_log) < 0)
+ if (mysql_socket_listen(unix_sock,(int) back_log) < 0)
sql_print_warning("listen() on Unix socket failed with error %d",
socket_errno);
-#if defined(WITH_WSREP) && defined(HAVE_FCNTL) && defined(FD_CLOEXEC)
- (void) fcntl(unix_sock, F_SETFD, FD_CLOEXEC);
+#if defined(WITH_WSREP) && defined(HAVE_FCNTL)
+ (void) fcntl(mysql_socket_getfd(unix_sock), F_SETFD, FD_CLOEXEC);
#endif /* WITH_WSREP */
}
#endif
@@ -2560,7 +2815,7 @@ void close_connection(THD *thd, uint sql_errno)
{
sleep(0); /* Workaround to avoid tailcall optimisation */
}
- MYSQL_AUDIT_NOTIFY_CONNECTION_DISCONNECT(thd, sql_errno);
+ mysql_audit_notify_connection_disconnect(thd, sql_errno);
DBUG_VOID_RETURN;
}
#endif /* EMBEDDED_LIBRARY */
@@ -2617,6 +2872,47 @@ void dec_connection_count(THD *thd)
/*
+ Delete THD and decrement thread counters, including thread_running
+*/
+
+void delete_running_thd(THD *thd)
+{
+ mysql_mutex_lock(&LOCK_thread_count);
+ thd->unlink();
+ mysql_mutex_unlock(&LOCK_thread_count);
+
+ delete thd;
+ dec_thread_running();
+ thread_safe_decrement32(&thread_count, &thread_count_lock);
+ signal_thd_deleted();
+}
+
+
+/*
+ Send a signal to unblock close_conneciton() if there is no more
+ threads running with a THD attached
+
+ It's safe to check for thread_count and service_thread_count outside
+ of a mutex as we are only interested to see if they where decremented
+ to 0 by a previous unlink_thd() call.
+
+ We should only signal COND_thread_count if both variables are 0,
+ false positives are ok.
+*/
+
+void signal_thd_deleted()
+{
+ if (!thread_count && ! service_thread_count)
+ {
+ /* Signal close_connections() that all THD's are freed */
+ mysql_mutex_lock(&LOCK_thread_count);
+ mysql_cond_broadcast(&COND_thread_count);
+ mysql_mutex_unlock(&LOCK_thread_count);
+ }
+}
+
+
+/*
Unlink thd from global list of available connections and free thd
SYNOPSIS
@@ -2645,19 +2941,11 @@ void unlink_thd(THD *thd)
sync feature has been shut down at this point.
*/
DBUG_EXECUTE_IF("sleep_after_lock_thread_count_before_delete_thd", sleep(5););
- if (unlikely(abort_loop))
- {
- /*
- During shutdown, we have to delete thd inside the mutex
- to not refer to mutexes that may be deleted during shutdown
- */
- delete thd;
- thd= 0;
- }
- thread_count--;
mysql_mutex_unlock(&LOCK_thread_count);
delete thd;
+ thread_safe_decrement32(&thread_count, &thread_count_lock);
+
DBUG_VOID_RETURN;
}
@@ -2669,7 +2957,7 @@ void unlink_thd(THD *thd)
cache_thread()
NOTES
- LOCK_thread_count has to be locked
+ LOCK_thread_cache is used to protect the cache variables
RETURN
0 Thread was not put in cache
@@ -2680,7 +2968,9 @@ void unlink_thd(THD *thd)
static bool cache_thread()
{
- mysql_mutex_assert_owner(&LOCK_thread_count);
+ DBUG_ENTER("cache_thread");
+
+ mysql_mutex_lock(&LOCK_thread_cache);
if (cached_thread_count < thread_cache_size &&
! abort_loop && !kill_cached_threads)
{
@@ -2688,17 +2978,16 @@ static bool cache_thread()
DBUG_PRINT("info", ("Adding thread to cache"));
cached_thread_count++;
-#ifdef HAVE_PSI_INTERFACE
+#ifdef HAVE_PSI_THREAD_INTERFACE
/*
Delete the instrumentation for the job that just completed,
before parking this pthread in the cache (blocked on COND_thread_cache).
*/
- if (likely(PSI_server != NULL))
- PSI_server->delete_current_thread();
+ PSI_THREAD_CALL(delete_current_thread)();
#endif
while (!abort_loop && ! wake_thread && ! kill_cached_threads)
- mysql_cond_wait(&COND_thread_cache, &LOCK_thread_count);
+ mysql_cond_wait(&COND_thread_cache, &LOCK_thread_cache);
cached_thread_count--;
if (kill_cached_threads)
mysql_cond_signal(&COND_flush_thread_cache);
@@ -2707,21 +2996,19 @@ static bool cache_thread()
THD *thd;
wake_thread--;
thd= thread_cache.get();
+ mysql_mutex_unlock(&LOCK_thread_cache);
+
thd->thread_stack= (char*) &thd; // For store_globals
(void) thd->store_globals();
-#ifdef HAVE_PSI_INTERFACE
+#ifdef HAVE_PSI_THREAD_INTERFACE
/*
Create new instrumentation for the new THD job,
and attach it to this running pthread.
*/
- if (likely(PSI_server != NULL))
- {
- PSI_thread *psi= PSI_server->new_thread(key_thread_one_connection,
- thd, thd->thread_id);
- if (likely(psi != NULL))
- PSI_server->set_thread(psi);
- }
+ PSI_thread *psi= PSI_THREAD_CALL(new_thread)(key_thread_one_connection,
+ thd, thd->thread_id);
+ PSI_THREAD_CALL(set_thread)(psi);
#endif
/*
@@ -2732,11 +3019,16 @@ static bool cache_thread()
thd->mysys_var->abort= 0;
thd->thr_create_utime= microsecond_interval_timer();
thd->start_utime= thd->thr_create_utime;
+
+ /* Link thd into list of all active threads (THD's) */
+ mysql_mutex_lock(&LOCK_thread_count);
threads.append(thd);
- return(1);
+ mysql_mutex_unlock(&LOCK_thread_count);
+ DBUG_RETURN(1);
}
}
- return(0);
+ mysql_mutex_unlock(&LOCK_thread_cache);
+ DBUG_RETURN(0);
}
@@ -2764,33 +3056,25 @@ bool one_thread_per_connection_end(THD *thd, bool put_in_cache)
DBUG_ENTER("one_thread_per_connection_end");
#ifdef WITH_WSREP
const bool wsrep_applier(thd->wsrep_applier);
-#endif /* WITH_WSREP */
+#endif
unlink_thd(thd);
/* Mark that current_thd is not valid anymore */
- my_pthread_setspecific_ptr(THR_THD, 0);
+ set_current_thd(0);
#ifdef WITH_WSREP
- if (put_in_cache && !wsrep_applier)
+ if (!wsrep_applier && put_in_cache && cache_thread())
#else
- if (put_in_cache)
+ if (put_in_cache && cache_thread())
#endif /* WITH_WSREP */
- {
- mysql_mutex_lock(&LOCK_thread_count);
- put_in_cache= cache_thread();
- mysql_mutex_unlock(&LOCK_thread_count);
- if (put_in_cache)
- DBUG_RETURN(0); // Thread is reused
- }
+ DBUG_RETURN(0); // Thread is reused
- /* It's safe to broadcast outside a lock (COND... is not deleted here) */
- DBUG_PRINT("signal", ("Broadcasting COND_thread_count"));
+ signal_thd_deleted();
DBUG_LEAVE; // Must match DBUG_ENTER()
#if defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY)
ERR_remove_state(0);
#endif
my_thread_end();
- mysql_cond_broadcast(&COND_thread_count);
pthread_exit(0);
return 0; // Avoid compiler warnings
@@ -2799,15 +3083,17 @@ bool one_thread_per_connection_end(THD *thd, bool put_in_cache)
void flush_thread_cache()
{
- mysql_mutex_lock(&LOCK_thread_count);
+ DBUG_ENTER("flush_thread_cache");
+ mysql_mutex_lock(&LOCK_thread_cache);
kill_cached_threads++;
while (cached_thread_count)
{
mysql_cond_broadcast(&COND_thread_cache);
- mysql_cond_wait(&COND_flush_thread_cache, &LOCK_thread_count);
+ mysql_cond_wait(&COND_flush_thread_cache, &LOCK_thread_cache);
}
kill_cached_threads--;
- mysql_mutex_unlock(&LOCK_thread_count);
+ mysql_mutex_unlock(&LOCK_thread_cache);
+ DBUG_VOID_RETURN;
}
@@ -3214,8 +3500,8 @@ pthread_handler_t signal_hand(void *arg __attribute__((unused)))
should not be any other mysql_cond_signal() calls.
*/
mysql_mutex_lock(&LOCK_thread_count);
- mysql_mutex_unlock(&LOCK_thread_count);
mysql_cond_broadcast(&COND_thread_count);
+ mysql_mutex_unlock(&LOCK_thread_count);
(void) pthread_sigmask(SIG_BLOCK,&set,NULL);
for (;;)
@@ -3251,10 +3537,9 @@ pthread_handler_t signal_hand(void *arg __attribute__((unused)))
if (!abort_loop)
{
abort_loop=1; // mark abort for threads
-#ifdef HAVE_PSI_INTERFACE
+#ifdef HAVE_PSI_THREAD_INTERFACE
/* Delete the instrumentation for the signal thread */
- if (likely(PSI_server != NULL))
- PSI_server->delete_current_thread();
+ PSI_THREAD_CALL(delete_current_thread)();
#endif
#ifdef USE_ONE_SIGNAL_HAND
pthread_t tmp;
@@ -3326,28 +3611,28 @@ extern "C" void my_message_sql(uint error, const char *str, myf MyFlags);
void my_message_sql(uint error, const char *str, myf MyFlags)
{
THD *thd= current_thd;
- MYSQL_ERROR::enum_warning_level level;
+ Sql_condition::enum_warning_level level;
sql_print_message_func func;
-
DBUG_ENTER("my_message_sql");
- DBUG_PRINT("error", ("error: %u message: '%s' Flag: %d", error, str, MyFlags));
+ DBUG_PRINT("error", ("error: %u message: '%s' Flag: %lu", error, str,
+ MyFlags));
DBUG_ASSERT(str != NULL);
DBUG_ASSERT(error != 0);
if (MyFlags & ME_JUST_INFO)
{
- level= MYSQL_ERROR::WARN_LEVEL_NOTE;
+ level= Sql_condition::WARN_LEVEL_NOTE;
func= sql_print_information;
}
else if (MyFlags & ME_JUST_WARNING)
{
- level= MYSQL_ERROR::WARN_LEVEL_WARN;
+ level= Sql_condition::WARN_LEVEL_WARN;
func= sql_print_warning;
}
else
{
- level= MYSQL_ERROR::WARN_LEVEL_ERROR;
+ level= Sql_condition::WARN_LEVEL_ERROR;
func= sql_print_error;
}
@@ -3369,9 +3654,9 @@ void my_message_sql(uint error, const char *str, myf MyFlags)
}
-#ifndef EMBEDDED_LIBRARY
extern "C" void *my_str_malloc_mysqld(size_t size);
extern "C" void my_str_free_mysqld(void *ptr);
+extern "C" void *my_str_realloc_mysqld(void *ptr, size_t size);
void *my_str_malloc_mysqld(size_t size)
{
@@ -3383,7 +3668,11 @@ void my_str_free_mysqld(void *ptr)
{
my_free(ptr);
}
-#endif /* EMBEDDED_LIBRARY */
+
+void *my_str_realloc_mysqld(void *ptr, size_t size)
+{
+ return my_realloc(ptr, size, MYF(MY_FAE));
+}
#ifdef __WIN__
@@ -3415,24 +3704,57 @@ sizeof(load_default_groups)/sizeof(load_default_groups[0]);
/**
This function is used to check for stack overrun for pathological
cases of regular expressions and 'like' expressions.
- The call to current_thd is quite expensive, so we try to avoid it
- for the normal cases.
+*/
+extern "C" int
+check_enough_stack_size_slow()
+{
+ uchar stack_top;
+ THD *my_thd= current_thd;
+ if (my_thd != NULL)
+ return check_stack_overrun(my_thd, STACK_MIN_SIZE * 2, &stack_top);
+ return 0;
+}
+
+
+/*
+ The call to current_thd in check_enough_stack_size_slow is quite expensive,
+ so we try to avoid it for the normal cases.
The size of each stack frame for the wildcmp() routines is ~128 bytes,
so checking *every* recursive call is not necessary.
*/
extern "C" int
check_enough_stack_size(int recurse_level)
{
- uchar stack_top;
if (recurse_level % 16 != 0)
return 0;
+ return check_enough_stack_size_slow();
+}
+#endif
- THD *my_thd= current_thd;
- if (my_thd != NULL)
- return check_stack_overrun(my_thd, STACK_MIN_SIZE * 2, &stack_top);
- return 0;
+
+
+/*
+ Initialize my_str_malloc() and my_str_free()
+*/
+static void init_libstrings()
+{
+ my_str_malloc= &my_str_malloc_mysqld;
+ my_str_free= &my_str_free_mysqld;
+ my_str_realloc= &my_str_realloc_mysqld;
+#ifndef EMBEDDED_LIBRARY
+ my_string_stack_guard= check_enough_stack_size;
+#endif
}
+
+
+static void init_pcre()
+{
+ pcre_malloc= pcre_stack_malloc= my_str_malloc_mysqld;
+ pcre_free= pcre_stack_free= my_str_free_mysqld;
+#ifndef EMBEDDED_LIBRARY
+ pcre_stack_guard= check_enough_stack_size_slow;
#endif
+}
/**
@@ -3490,6 +3812,7 @@ SHOW_VAR com_status_vars[]= {
{"create_function", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_CREATE_SPFUNCTION]), SHOW_LONG_STATUS},
{"create_index", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_CREATE_INDEX]), SHOW_LONG_STATUS},
{"create_procedure", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_CREATE_PROCEDURE]), SHOW_LONG_STATUS},
+ {"create_role", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_CREATE_ROLE]), SHOW_LONG_STATUS},
{"create_server", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_CREATE_SERVER]), SHOW_LONG_STATUS},
{"create_table", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_CREATE_TABLE]), SHOW_LONG_STATUS},
{"create_trigger", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_CREATE_TRIGGER]), SHOW_LONG_STATUS},
@@ -3505,6 +3828,7 @@ SHOW_VAR com_status_vars[]= {
{"drop_function", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_DROP_FUNCTION]), SHOW_LONG_STATUS},
{"drop_index", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_DROP_INDEX]), SHOW_LONG_STATUS},
{"drop_procedure", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_DROP_PROCEDURE]), SHOW_LONG_STATUS},
+ {"drop_role", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_DROP_ROLE]), SHOW_LONG_STATUS},
{"drop_server", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_DROP_SERVER]), SHOW_LONG_STATUS},
{"drop_table", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_DROP_TABLE]), SHOW_LONG_STATUS},
{"drop_trigger", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_DROP_TRIGGER]), SHOW_LONG_STATUS},
@@ -3513,7 +3837,9 @@ SHOW_VAR com_status_vars[]= {
{"empty_query", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_EMPTY_QUERY]), SHOW_LONG_STATUS},
{"execute_sql", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_EXECUTE]), SHOW_LONG_STATUS},
{"flush", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_FLUSH]), SHOW_LONG_STATUS},
+ {"get_diagnostics", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_GET_DIAGNOSTICS]), SHOW_LONG_STATUS},
{"grant", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_GRANT]), SHOW_LONG_STATUS},
+ {"grant_role", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_GRANT_ROLE]), SHOW_LONG_STATUS},
{"ha_close", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_HA_CLOSE]), SHOW_LONG_STATUS},
{"ha_open", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_HA_OPEN]), SHOW_LONG_STATUS},
{"ha_read", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_HA_READ]), SHOW_LONG_STATUS},
@@ -3539,6 +3865,7 @@ SHOW_VAR com_status_vars[]= {
{"resignal", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_RESIGNAL]), SHOW_LONG_STATUS},
{"revoke", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_REVOKE]), SHOW_LONG_STATUS},
{"revoke_all", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_REVOKE_ALL]), SHOW_LONG_STATUS},
+ {"revoke_role", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_REVOKE_ROLE]), SHOW_LONG_STATUS},
{"rollback", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_ROLLBACK]), SHOW_LONG_STATUS},
{"rollback_to_savepoint",(char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_ROLLBACK_TO_SAVEPOINT]), SHOW_LONG_STATUS},
{"savepoint", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SAVEPOINT]), SHOW_LONG_STATUS},
@@ -3563,6 +3890,7 @@ SHOW_VAR com_status_vars[]= {
{"show_engine_status", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_ENGINE_STATUS]), SHOW_LONG_STATUS},
{"show_errors", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_ERRORS]), SHOW_LONG_STATUS},
{"show_events", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_EVENTS]), SHOW_LONG_STATUS},
+ {"show_explain", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_EXPLAIN]), SHOW_LONG_STATUS},
{"show_fields", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_FIELDS]), SHOW_LONG_STATUS},
#ifndef DBUG_OFF
{"show_function_code", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_FUNC_CODE]), SHOW_LONG_STATUS},
@@ -3594,9 +3922,10 @@ SHOW_VAR com_status_vars[]= {
{"show_user_statistics", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_USER_STATS]), SHOW_LONG_STATUS},
{"show_variables", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_VARIABLES]), SHOW_LONG_STATUS},
{"show_warnings", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_WARNS]), SHOW_LONG_STATUS},
+ {"shutdown", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHUTDOWN]), SHOW_LONG_STATUS},
{"signal", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SIGNAL]), SHOW_LONG_STATUS},
- {"slave_start", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SLAVE_START]), SHOW_LONG_STATUS},
- {"slave_stop", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SLAVE_STOP]), SHOW_LONG_STATUS},
+ {"start_all_slaves", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SLAVE_ALL_START]), SHOW_LONG_STATUS},
+ {"start_slave", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SLAVE_START]), SHOW_LONG_STATUS},
{"stmt_close", (char*) offsetof(STATUS_VAR, com_stmt_close), SHOW_LONG_STATUS},
{"stmt_execute", (char*) offsetof(STATUS_VAR, com_stmt_execute), SHOW_LONG_STATUS},
{"stmt_fetch", (char*) offsetof(STATUS_VAR, com_stmt_fetch), SHOW_LONG_STATUS},
@@ -3604,6 +3933,8 @@ SHOW_VAR com_status_vars[]= {
{"stmt_reprepare", (char*) offsetof(STATUS_VAR, com_stmt_reprepare), SHOW_LONG_STATUS},
{"stmt_reset", (char*) offsetof(STATUS_VAR, com_stmt_reset), SHOW_LONG_STATUS},
{"stmt_send_long_data", (char*) offsetof(STATUS_VAR, com_stmt_send_long_data), SHOW_LONG_STATUS},
+ {"stop_all_slaves", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SLAVE_ALL_STOP]), SHOW_LONG_STATUS},
+ {"stop_slave", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SLAVE_STOP]), SHOW_LONG_STATUS},
{"truncate", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_TRUNCATE]), SHOW_LONG_STATUS},
{"uninstall_plugin", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_UNINSTALL_PLUGIN]), SHOW_LONG_STATUS},
{"unlock_tables", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_UNLOCK_TABLES]), SHOW_LONG_STATUS},
@@ -3618,21 +3949,154 @@ SHOW_VAR com_status_vars[]= {
{NullS, NullS, SHOW_LONG}
};
+
+#ifdef HAVE_PSI_STATEMENT_INTERFACE
+PSI_statement_info sql_statement_info[(uint) SQLCOM_END + 1];
+PSI_statement_info com_statement_info[(uint) COM_END + 1];
+
+/**
+ Initialize the command names array.
+ Since we do not want to maintain a separate array,
+ this is populated from data mined in com_status_vars,
+ which already has one name for each command.
+*/
+void init_sql_statement_info()
+{
+ char *first_com= (char*) offsetof(STATUS_VAR, com_stat[0]);
+ char *last_com= (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_END]);
+ int record_size= (char*) offsetof(STATUS_VAR, com_stat[1])
+ - (char*) offsetof(STATUS_VAR, com_stat[0]);
+ char *ptr;
+ uint i;
+ uint com_index;
+
+ static const char* dummy= "";
+ for (i= 0; i < ((uint) SQLCOM_END + 1); i++)
+ {
+ sql_statement_info[i].m_name= dummy;
+ sql_statement_info[i].m_flags= 0;
+ }
+
+ SHOW_VAR *var= &com_status_vars[0];
+ while (var->name != NULL)
+ {
+ ptr= var->value;
+ if ((first_com <= ptr) && (ptr <= last_com))
+ {
+ com_index= ((int)(ptr - first_com))/record_size;
+ DBUG_ASSERT(com_index < (uint) SQLCOM_END);
+ sql_statement_info[com_index].m_name= var->name;
+ }
+ var++;
+ }
+
+ DBUG_ASSERT(strcmp(sql_statement_info[(uint) SQLCOM_SELECT].m_name, "select") == 0);
+ DBUG_ASSERT(strcmp(sql_statement_info[(uint) SQLCOM_SIGNAL].m_name, "signal") == 0);
+
+ sql_statement_info[(uint) SQLCOM_END].m_name= "error";
+}
+
+void init_com_statement_info()
+{
+ uint index;
+
+ for (index= 0; index < (uint) COM_END + 1; index++)
+ {
+ com_statement_info[index].m_name= command_name[index].str;
+ com_statement_info[index].m_flags= 0;
+ }
+
+ /* "statement/abstract/query" can mutate into "statement/sql/..." */
+ com_statement_info[(uint) COM_QUERY].m_flags= PSI_FLAG_MUTABLE;
+}
+#endif
+
+
+#ifdef SAFEMALLOC
+/*
+ Return the id for the current THD, to allow safemalloc to associate
+ the memory with the right id.
+*/
+
+extern "C" my_thread_id mariadb_dbug_id()
+{
+ THD *thd;
+ if ((thd= current_thd))
+ {
+ return thd->thread_id;
+ }
+ return my_thread_dbug_id();
+}
+#endif /* SAFEMALLOC */
+
+/* Thread Mem Usage By P.Linux */
+extern "C" {
+static void my_malloc_size_cb_func(long long size, my_bool is_thread_specific)
+{
+ /* If thread specific memory */
+ if (is_thread_specific)
+ {
+ THD *thd= current_thd;
+ if (mysqld_server_initialized || thd)
+ {
+ /*
+ THD may not be set if we are called from my_net_init() before THD
+ thread has started.
+ However, this should never happen, so better to assert and
+ fix this.
+ */
+ DBUG_ASSERT(thd);
+ if (thd)
+ {
+ DBUG_PRINT("info", ("memory_used: %lld size: %lld",
+ (longlong) thd->status_var.memory_used, size));
+ thd->status_var.memory_used+= size;
+ DBUG_ASSERT((longlong) thd->status_var.memory_used >= 0);
+ }
+ }
+ }
+ // workaround for gcc 4.2.4-1ubuntu4 -fPIE (from DEB_BUILD_HARDENING=1)
+ int64 volatile * volatile ptr=&global_status_var.memory_used;
+ my_atomic_add64(ptr, size);
+}
+}
+
+
static int init_common_variables()
{
umask(((~my_umask) & 0666));
+ connection_errors_select= 0;
+ connection_errors_accept= 0;
+ connection_errors_tcpwrap= 0;
+ connection_errors_internal= 0;
+ connection_errors_max_connection= 0;
+ connection_errors_peer_addr= 0;
my_decimal_set_zero(&decimal_zero); // set decimal_zero constant;
+ if (pthread_key_create(&THR_THD,NULL) ||
+ pthread_key_create(&THR_MALLOC,NULL))
+ {
+ sql_print_error("Can't create thread-keys");
+ return 1;
+ }
+
+ set_current_thd(0);
+ set_malloc_size_cb(my_malloc_size_cb_func);
+
+ init_libstrings();
tzset(); // Set tzname
sf_leaking_memory= 0; // no memory leaks from now on
+#ifdef SAFEMALLOC
+ sf_malloc_dbug_id= mariadb_dbug_id;
+#endif
max_system_variables.pseudo_thread_id= (ulong)~0;
server_start_time= flush_status_time= my_time(0);
- rpl_filter= new Rpl_filter;
+ global_rpl_filter= new Rpl_filter;
binlog_filter= new Rpl_filter;
- if (!rpl_filter || !binlog_filter)
+ if (!global_rpl_filter || !binlog_filter)
{
sql_perror("Could not allocate replication and binlog filters");
return 1;
@@ -3674,8 +4138,9 @@ static int init_common_variables()
#ifdef HAVE_PSI_INTERFACE
/*
Complete the mysql_bin_log initialization.
- Instrumentation keys are known only after the performance schema initialization,
- and can not be set in the MYSQL_BIN_LOG constructor (called before main()).
+ Instrumentation keys are known only after the performance schema
+ initialization, and can not be set in the MYSQL_BIN_LOG
+ constructor (called before main()).
*/
mysql_bin_log.set_psi_keys(key_BINLOG_LOCK_index,
key_BINLOG_update_cond,
@@ -3866,7 +4331,7 @@ static int init_common_variables()
/* MyISAM requires two file handles per table. */
wanted_files= (10 + max_connections + extra_max_connections +
- table_cache_size*2);
+ tc_size * 2);
/*
We are trying to allocate no less than max_connections*5 file
handles (i.e. we are trying to set the limit so that they will
@@ -3877,7 +4342,7 @@ static int init_common_variables()
can't get max_connections*5 but still got no less than was
requested (value of wanted_files).
*/
- max_open_files= max(max(wanted_files,
+ max_open_files= MY_MAX(MY_MAX(wanted_files,
(max_connections + extra_max_connections)*5),
open_files_limit);
files= my_set_max_open_files(max_open_files);
@@ -3890,23 +4355,22 @@ static int init_common_variables()
If we have requested too much file handles than we bring
max_connections in supported bounds.
*/
- max_connections= (ulong) min(files-10-TABLE_OPEN_CACHE_MIN*2,
+ max_connections= (ulong) MY_MIN(files-10-TABLE_OPEN_CACHE_MIN*2,
max_connections);
/*
- Decrease table_cache_size according to max_connections, but
- not below TABLE_OPEN_CACHE_MIN. Outer min() ensures that we
- never increase table_cache_size automatically (that could
+ Decrease tc_size according to max_connections, but
+ not below TABLE_OPEN_CACHE_MIN. Outer MY_MIN() ensures that we
+ never increase tc_size automatically (that could
happen if max_connections is decreased above).
*/
- table_cache_size= (ulong) min(max((files-10-max_connections)/2,
- TABLE_OPEN_CACHE_MIN),
- table_cache_size);
+ tc_size= (ulong) MY_MIN(MY_MAX((files - 10 - max_connections) / 2,
+ TABLE_OPEN_CACHE_MIN), tc_size);
DBUG_PRINT("warning",
("Changed limits: max_open_files: %u max_connections: %ld table_cache: %ld",
- files, max_connections, table_cache_size));
+ files, max_connections, tc_size));
if (global_system_variables.log_warnings)
sql_print_warning("Changed limits: max_open_files: %u max_connections: %ld table_cache: %ld",
- files, max_connections, table_cache_size);
+ files, max_connections, tc_size);
}
else if (global_system_variables.log_warnings)
sql_print_warning("Could not increase number of max_open_files to more than %u (request: %u)", files, wanted_files);
@@ -3929,12 +4393,7 @@ static int init_common_variables()
if (item_create_init())
return 1;
item_init();
-#ifndef EMBEDDED_LIBRARY
- my_regex_init(&my_charset_latin1, check_enough_stack_size);
- my_string_stack_guard= check_enough_stack_size;
-#else
- my_regex_init(&my_charset_latin1, NULL);
-#endif
+ init_pcre();
/*
Process a comma-separated character set list and choose
the first available character set. This is mostly for
@@ -3968,6 +4427,10 @@ static int init_common_variables()
default_collation= get_charset_by_name(default_collation_name, MYF(0));
if (!default_collation)
{
+#ifdef WITH_PERFSCHEMA_STORAGE_ENGINE
+ buffered_logs.print();
+ buffered_logs.cleanup();
+#endif
sql_print_error(ER_DEFAULT(ER_UNKNOWN_COLLATION), default_collation_name);
return 1;
}
@@ -3985,7 +4448,15 @@ static int init_common_variables()
global_system_variables.collation_database= default_charset_info;
global_system_variables.collation_connection= default_charset_info;
global_system_variables.character_set_results= default_charset_info;
- global_system_variables.character_set_client= default_charset_info;
+ if (default_charset_info->mbminlen > 1)
+ {
+ global_system_variables.character_set_client= &my_charset_latin1;
+ sql_print_warning("Cannot use %s as character_set_client, %s will be used instead",
+ default_charset_info->csname,
+ global_system_variables.character_set_client->csname);
+ }
+ else
+ global_system_variables.character_set_client= default_charset_info;
if (!(character_set_filesystem=
get_charset_by_csname(character_set_filesystem_name,
@@ -4026,7 +4497,7 @@ static int init_common_variables()
#endif /* defined(ENABLED_DEBUG_SYNC) */
#if (ENABLE_TEMP_POOL)
- if (use_temp_pool && bitmap_init(&temp_pool,0,1024,1))
+ if (use_temp_pool && my_bitmap_init(&temp_pool,0,1024,1))
return 1;
#else
use_temp_pool= 0;
@@ -4104,8 +4575,11 @@ static int init_common_variables()
static int init_thread_environment()
{
+ DBUG_ENTER("init_thread_environment");
mysql_mutex_init(key_LOCK_thread_count, &LOCK_thread_count, MY_MUTEX_INIT_FAST);
+ mysql_mutex_init(key_LOCK_thread_cache, &LOCK_thread_cache, MY_MUTEX_INIT_FAST);
mysql_mutex_init(key_LOCK_status, &LOCK_status, MY_MUTEX_INIT_FAST);
+ mysql_mutex_init(key_LOCK_show_status, &LOCK_show_status, MY_MUTEX_INIT_SLOW);
mysql_mutex_init(key_LOCK_delayed_insert,
&LOCK_delayed_insert, MY_MUTEX_INIT_FAST);
mysql_mutex_init(key_LOCK_delayed_status,
@@ -4117,6 +4591,7 @@ static int init_thread_environment()
mysql_mutex_init(key_LOCK_active_mi, &LOCK_active_mi, MY_MUTEX_INIT_FAST);
mysql_mutex_init(key_LOCK_global_system_variables,
&LOCK_global_system_variables, MY_MUTEX_INIT_FAST);
+ mysql_mutex_record_order(&LOCK_active_mi, &LOCK_global_system_variables);
mysql_rwlock_init(key_rwlock_LOCK_system_variables_hash,
&LOCK_system_variables_hash);
mysql_mutex_init(key_LOCK_prepared_stmt_count,
@@ -4136,8 +4611,12 @@ static int init_thread_environment()
&LOCK_global_index_stats, MY_MUTEX_INIT_FAST);
mysql_mutex_init(key_LOCK_prepare_ordered, &LOCK_prepare_ordered,
MY_MUTEX_INIT_SLOW);
+ mysql_cond_init(key_COND_prepare_ordered, &COND_prepare_ordered, NULL);
mysql_mutex_init(key_LOCK_commit_ordered, &LOCK_commit_ordered,
MY_MUTEX_INIT_SLOW);
+ mysql_mutex_init(key_LOCK_slave_init, &LOCK_slave_init,
+ MY_MUTEX_INIT_SLOW);
+ mysql_cond_init(key_COND_slave_init, &COND_slave_init, NULL);
#ifdef HAVE_OPENSSL
mysql_mutex_init(key_LOCK_des_key_file,
@@ -4161,7 +4640,6 @@ static int init_thread_environment()
mysql_cond_init(key_COND_flush_thread_cache, &COND_flush_thread_cache, NULL);
#ifdef HAVE_REPLICATION
mysql_mutex_init(key_LOCK_rpl_status, &LOCK_rpl_status, MY_MUTEX_INIT_FAST);
- mysql_cond_init(key_COND_rpl_status, &COND_rpl_status, NULL);
#endif
mysql_mutex_init(key_LOCK_server_started,
&LOCK_server_started, MY_MUTEX_INIT_FAST);
@@ -4170,18 +4648,18 @@ static int init_thread_environment()
#ifdef HAVE_EVENT_SCHEDULER
Events::init_mutexes();
#endif
+ init_show_explain_psi_keys();
/* Parameter for threads created for connections */
(void) pthread_attr_init(&connection_attrib);
(void) pthread_attr_setdetachstate(&connection_attrib,
PTHREAD_CREATE_DETACHED);
pthread_attr_setscope(&connection_attrib, PTHREAD_SCOPE_SYSTEM);
- if (pthread_key_create(&THR_THD,NULL) ||
- pthread_key_create(&THR_MALLOC,NULL))
- {
- sql_print_error("Can't create thread-keys");
- return 1;
- }
+#ifdef HAVE_REPLICATION
+ rpl_init_gtid_slave_state();
+ rpl_init_gtid_waiting();
+#endif
+
#ifdef WITH_WSREP
mysql_mutex_init(key_LOCK_wsrep_ready,
&LOCK_wsrep_ready, MY_MUTEX_INIT_FAST);
@@ -4203,7 +4681,8 @@ static int init_thread_environment()
mysql_mutex_init(key_LOCK_wsrep_desync,
&LOCK_wsrep_desync, MY_MUTEX_INIT_FAST);
#endif
- return 0;
+
+ DBUG_RETURN(0);
}
@@ -4280,9 +4759,9 @@ static void init_ssl()
/* having ssl_acceptor_fd != 0 signals the use of SSL */
ssl_acceptor_fd= new_VioSSLAcceptorFd(opt_ssl_key, opt_ssl_cert,
opt_ssl_ca, opt_ssl_capath,
- opt_ssl_cipher, &error);
+ opt_ssl_cipher, &error,
+ opt_ssl_crl, opt_ssl_crlpath);
DBUG_PRINT("info",("ssl_acceptor_fd: 0x%lx", (long) ssl_acceptor_fd));
- ERR_remove_state(0);
if (!ssl_acceptor_fd)
{
sql_print_warning("Failed to setup SSL");
@@ -4290,6 +4769,14 @@ static void init_ssl()
opt_use_ssl = 0;
have_ssl= SHOW_OPTION_DISABLED;
}
+ if (global_system_variables.log_warnings > 0)
+ {
+ ulong err;
+ while ((err= ERR_get_error()))
+ sql_print_warning("SSL error: %s", ERR_error_string(err, NULL));
+ }
+ else
+ ERR_remove_state(0);
}
else
{
@@ -4349,12 +4836,13 @@ static int init_server_components()
all things are initialized so that unireg_abort() doesn't fail
*/
mdl_init();
- if (table_def_init() | hostname_cache_init())
+ if (tdc_init() | hostname_cache_init())
unireg_abort(1);
query_cache_set_min_res_unit(query_cache_min_res_unit);
query_cache_init();
query_cache_resize(query_cache_size);
+ query_cache_result_size_limit(query_cache_limit);
my_rnd_init(&sql_rand,(ulong) server_start_time,(ulong) server_start_time/2);
setup_fpu();
init_thr_lock();
@@ -4407,7 +4895,7 @@ static int init_server_components()
/* set up the hook before initializing plugins which may use it */
error_handler_hook= my_message_sql;
- proc_info_hook= set_thd_proc_info;
+ proc_info_hook= set_thd_stage_info;
#ifdef WITH_PERFSCHEMA_STORAGE_ENGINE
/*
@@ -4421,6 +4909,15 @@ static int init_server_components()
buffered_logs.cleanup();
#endif /* WITH_PERFSCHEMA_STORAGE_ENGINE */
+#ifndef EMBEDDED_LIBRARY
+ /*
+ Now that the logger is available, redirect character set
+ errors directly to the logger
+ (instead of the buffered_logs used at the server startup time).
+ */
+ my_charset_error_reporter= charset_error_reporter;
+#endif
+
if (xid_cache_init())
{
sql_print_error("Out of memory");
@@ -4462,56 +4959,19 @@ static int init_server_components()
{
if (opt_bin_log)
{
- sql_print_error("using --replicate-same-server-id in conjunction with \
---log-slave-updates is impossible, it would lead to infinite loops in this \
-server.");
+ sql_print_error("using --replicate-same-server-id in conjunction with "
+ "--log-slave-updates is impossible, it would lead to "
+ "infinite loops in this server.");
unireg_abort(1);
}
else
- sql_print_warning("using --replicate-same-server-id in conjunction with \
---log-slave-updates would lead to infinite loops in this server. However this \
-will be ignored as the --log-bin option is not defined.");
+ sql_print_warning("using --replicate-same-server-id in conjunction with "
+ "--log-slave-updates would lead to infinite loops in "
+ "this server. However this will be ignored as the "
+ "--log-bin option is not defined.");
}
#endif
-#ifdef WITH_WSREP /* WSREP BEFORE SE */
- if (!wsrep_recovery && !opt_help)
- {
- if (opt_bootstrap) // bootsrap option given - disable wsrep functionality
- {
- wsrep_provider_init(WSREP_NONE);
- if (wsrep_init()) unireg_abort(1);
- }
- else // full wsrep initialization
- {
- // add basedir/bin to PATH to resolve wsrep script names
- char* const tmp_path((char*)alloca(strlen(mysql_home) +
- strlen("/bin") + 1));
- if (tmp_path)
- {
- strcpy(tmp_path, mysql_home);
- strcat(tmp_path, "/bin");
- wsrep_prepend_PATH(tmp_path);
- }
- else
- {
- WSREP_ERROR("Could not append %s/bin to PATH", mysql_home);
- }
-
- if (wsrep_before_SE())
- {
-#ifndef EMBEDDED_LIBRARY
- set_ports(); // this is also called in network_init() later but we need
- // to know mysqld_port now - lp:1071882
-#endif /* !EMBEDDED_LIBRARY */
-
- wsrep_init_startup(true);
- }
- }
- }
-#endif /* WITH_WSREP */
- DBUG_ASSERT(!opt_bin_log || opt_bin_logname);
-
if (opt_bin_log)
{
/* Reports an error and aborts, if the --log-bin's path
@@ -4519,8 +4979,8 @@ will be ignored as the --log-bin option is not defined.");
if (opt_bin_logname[0] &&
opt_bin_logname[strlen(opt_bin_logname) - 1] == FN_LIBCHAR)
{
- sql_print_error("Path '%s' is a directory name, please specify \
-a file name for --log-bin option", opt_bin_logname);
+ sql_print_error("Path '%s' is a directory name, please specify "
+ "a file name for --log-bin option", opt_bin_logname);
unireg_abort(1);
}
@@ -4530,8 +4990,9 @@ a file name for --log-bin option", opt_bin_logname);
opt_binlog_index_name[strlen(opt_binlog_index_name) - 1]
== FN_LIBCHAR)
{
- sql_print_error("Path '%s' is a directory name, please specify \
-a file name for --log-bin-index option", opt_binlog_index_name);
+ sql_print_error("Path '%s' is a directory name, please specify "
+ "a file name for --log-bin-index option",
+ opt_binlog_index_name);
unireg_abort(1);
}
@@ -4559,10 +5020,72 @@ a file name for --log-bin-index option", opt_binlog_index_name);
{
opt_bin_logname= my_once_strdup(buf, MYF(MY_WME));
}
+#ifdef WITH_WSREP /* WSREP BEFORE SE */
+ /*
+ Wsrep initialization must happen at this point, because:
+ - opt_bin_logname must be known when starting replication
+ since SST may need it
+ - SST may modify binlog index file, so it must be opened
+ after SST has happened
+ */
+ }
+ if (!wsrep_recovery && !opt_help)
+ {
+ if (opt_bootstrap) // bootsrap option given - disable wsrep functionality
+ {
+ wsrep_provider_init(WSREP_NONE);
+ if (wsrep_init()) unireg_abort(1);
+ }
+ else // full wsrep initialization
+ {
+ // add basedir/bin to PATH to resolve wsrep script names
+ char* const tmp_path((char*)alloca(strlen(mysql_home) +
+ strlen("/bin") + 1));
+ if (tmp_path)
+ {
+ strcpy(tmp_path, mysql_home);
+ strcat(tmp_path, "/bin");
+ wsrep_prepend_PATH(tmp_path);
+ }
+ else
+ {
+ WSREP_ERROR("Could not append %s/bin to PATH", mysql_home);
+ }
+
+ if (wsrep_before_SE())
+ {
+ set_ports(); // this is also called in network_init() later but we need
+ // to know mysqld_port now - lp:1071882
+ /*
+ Plugin initialization (plugin_init()) hasn't happened yet, set
+ maria_hton to 0.
+ */
+ maria_hton= 0;
+ wsrep_init_startup(true);
+ }
+ }
+ }
+ if (opt_bin_log)
+ {
+ /*
+ Variable ln is not defined at this scope. We use opt_bin_logname instead.
+ It should be the same as ln since
+ - mysql_bin_log.generate_name() returns first argument if new log name
+ is not generated
+ - if new log name is generated, return value is assigned to ln and copied
+ to opt_bin_logname above
+ */
+ if (mysql_bin_log.open_index_file(opt_binlog_index_name, opt_bin_logname,
+ TRUE))
+ {
+ unireg_abort(1);
+ }
+#else
if (mysql_bin_log.open_index_file(opt_binlog_index_name, ln, TRUE))
{
unireg_abort(1);
}
+#endif /* WITH_WSREP */
}
/* call ha_init_key_cache() on all key caches to init them */
@@ -4614,12 +5137,12 @@ a file name for --log-bin-index option", opt_binlog_index_name);
variables that allocates memory for this THD.
*/
THD *current_thd_saved= current_thd;
- my_pthread_setspecific_ptr(THR_THD, tmp);
+ set_current_thd(tmp);
tmp->init_for_queries();
/* Restore current_thd. */
- my_pthread_setspecific_ptr(THR_THD, current_thd_saved);
+ set_current_thd(current_thd_saved);
tmp->server_status= server_status_saved;
tmp->variables.option_bits= option_bits_saved;
@@ -4629,13 +5152,6 @@ a file name for --log-bin-index option", opt_binlog_index_name);
}
#endif
- have_csv= plugin_status(STRING_WITH_LEN("csv"),
- MYSQL_STORAGE_ENGINE_PLUGIN);
- have_ndbcluster= plugin_status(STRING_WITH_LEN("ndbcluster"),
- MYSQL_STORAGE_ENGINE_PLUGIN);
- have_partitioning= plugin_status(STRING_WITH_LEN("partition"),
- MYSQL_STORAGE_ENGINE_PLUGIN);
-
/* we do want to exit if there are any other unknown options */
if (remaining_argc > 1)
{
@@ -4721,7 +5237,7 @@ a file name for --log-bin-index option", opt_binlog_index_name);
plugin_ref plugin;
handlerton *hton;
if ((plugin= ha_resolve_by_name(0, &name)))
- hton= plugin_data(plugin, handlerton*);
+ hton= plugin_hton(plugin);
else
{
sql_print_error("Unknown/unsupported storage engine: %s",
@@ -4749,7 +5265,7 @@ a file name for --log-bin-index option", opt_binlog_index_name);
global_system_variables.table_plugin= plugin;
mysql_mutex_unlock(&LOCK_global_system_variables);
}
-#if defined(WITH_ARIA_STORAGE_ENGINE) && defined(USE_ARIA_FOR_TMP_TABLES)
+#ifdef USE_ARIA_FOR_TMP_TABLES
if (!ha_storage_engine_is_enabled(maria_hton) && !opt_bootstrap)
{
sql_print_error("Aria engine is not enabled or did not start. The Aria engine must be enabled to continue as mysqld was configured with --with-aria-tmp-tables");
@@ -4774,6 +5290,12 @@ a file name for --log-bin-index option", opt_binlog_index_name);
#ifdef WITH_WSREP
if (WSREP_ON && tc_log == &tc_log_mmap)
tc_log= &tc_log_dummy;
+
+ WSREP_DEBUG("Initial TC log open: %s",
+ (tc_log == &mysql_bin_log) ? "binlog" :
+ (tc_log == &tc_log_mmap) ? "mmap" :
+ (tc_log == &tc_log_dummy) ? "dummy" : "unknown"
+ );
#endif
if (tc_log->open(opt_bin_log ? opt_bin_logname : opt_tc_log_file))
@@ -4788,7 +5310,7 @@ a file name for --log-bin-index option", opt_binlog_index_name);
}
if (opt_bin_log && mysql_bin_log.open(opt_bin_logname, LOG_BIN, 0,
- WRITE_CACHE, 0, max_binlog_size, 0, TRUE))
+ WRITE_CACHE, max_binlog_size, 0, TRUE))
unireg_abort(1);
#ifdef HAVE_REPLICATION
@@ -4830,8 +5352,13 @@ a file name for --log-bin-index option", opt_binlog_index_name);
init_update_queries();
init_global_user_stats();
init_global_client_stats();
+ if (!opt_bootstrap)
+ servers_init(0);
+ init_status_vars();
DBUG_RETURN(0);
}
+
+
#ifndef EMBEDDED_LIBRARY
static void create_shutdown_thread()
@@ -4874,7 +5401,7 @@ pthread_handler_t start_wsrep_THD(void *arg)
thread_created++;
threads.append(thd);
- my_net_init(&thd->net,(st_vio*) 0);
+ my_net_init(&thd->net,(st_vio*) 0, MYF(0));
DBUG_PRINT("wsrep",(("creating thread %lld"), (long long)thd->thread_id));
thd->prior_thr_create_utime= thd->start_utime= microsecond_interval_timer();
@@ -4928,7 +5455,7 @@ pthread_handler_t start_wsrep_THD(void *arg)
/* handle_one_connection() again... */
//thd->version= refresh_version;
thd->proc_info= 0;
- thd->command= COM_SLEEP;
+ thd->set_command(COM_SLEEP);
if (wsrep_creating_startup_threads == 0)
{
@@ -4998,7 +5525,7 @@ static bool abort_replicated(THD *thd)
bool ret_code= false;
if (thd->wsrep_query_state== QUERY_COMMITTING)
{
- if (wsrep_debug) WSREP_INFO("aborting replicated trx: %lu", thd->real_id);
+ WSREP_DEBUG("aborting replicated trx: %lu", thd->real_id);
(void)wsrep_abort_thd(thd, thd, TRUE);
ret_code= true;
@@ -5099,7 +5626,6 @@ static my_bool have_committing_connections()
if (is_committing_connection(tmp))
{
- mysql_mutex_unlock(&LOCK_thread_count);
return TRUE;
}
}
@@ -5184,8 +5710,7 @@ void wsrep_close_client_connections(my_bool wait_to_end)
}
DBUG_PRINT("quit",("Waiting for threads to die (count=%u)",thread_count));
- if (wsrep_debug)
- WSREP_INFO("waiting for client connections to close: %u", thread_count);
+ WSREP_DEBUG("waiting for client connections to close: %u", thread_count);
while (wait_to_end && have_client_connections())
{
@@ -5227,28 +5752,6 @@ static void wsrep_close_threads(THD *thd)
mysql_mutex_unlock(&LOCK_thread_count);
}
-void wsrep_close_applier_threads(int count)
-{
- THD *tmp;
- mysql_mutex_lock(&LOCK_thread_count); // For unlink from list
-
- I_List_iterator<THD> it(threads);
- while ((tmp=it++) && count)
- {
- DBUG_PRINT("quit",("Informing thread %ld that it's time to die",
- tmp->thread_id));
- /* We skip slave threads & scheduler on this first loop through. */
- if (tmp->wsrep_applier)
- {
- WSREP_DEBUG("closing wsrep applier thread %ld", tmp->thread_id);
- tmp->wsrep_applier_closing= TRUE;
- count--;
- }
- }
-
- mysql_mutex_unlock(&LOCK_thread_count);
-}
-
void wsrep_wait_appliers_close(THD *thd)
{
/* Wait for wsrep appliers to gracefully exit */
@@ -5458,6 +5961,7 @@ static void test_lc_time_sz()
}
#endif//DBUG_OFF
+
#ifdef __WIN__
int win_main(int argc, char **argv)
#else
@@ -5470,6 +5974,8 @@ int mysqld_main(int argc, char **argv)
*/
my_progname= argv[0];
sf_leaking_memory= 1; // no safemalloc memory leak reports if we exit early
+ mysqld_server_started= mysqld_server_initialized= 0;
+
#ifdef HAVE_NPTL
ld_assume_kernel_is_set= (getenv("LD_ASSUME_KERNEL") != 0);
#endif
@@ -5481,9 +5987,6 @@ int mysqld_main(int argc, char **argv)
return 1;
}
#endif
-#ifdef WITH_WSREP
- wsrep_filter_new_cluster (&argc, argv);
-#endif /* WITH_WSREP */
orig_argc= argc;
orig_argv= argv;
@@ -5504,38 +6007,33 @@ int mysqld_main(int argc, char **argv)
#ifdef WITH_PERFSCHEMA_STORAGE_ENGINE
/*
- The performance schema needs to be initialized as early as possible,
- before to-be-instrumented objects of the server are initialized.
+ Initialize the array of performance schema instrument configurations.
*/
- int ho_error;
- DYNAMIC_ARRAY all_early_options;
-
- my_getopt_register_get_addr(NULL);
- /* Skip unknown options so that they may be processed later */
- my_getopt_skip_unknown= TRUE;
-
- /* prepare all_early_options array */
- my_init_dynamic_array(&all_early_options, sizeof(my_option), 100, 25);
- sys_var_add_options(&all_early_options, sys_var::PARSE_EARLY);
- add_terminator(&all_early_options);
-
+ init_pfs_instrument_array();
+#endif /* WITH_PERFSCHEMA_STORAGE_ENGINE */
/*
Logs generated while parsing the command line
options are buffered and printed later.
*/
buffered_logs.init();
my_getopt_error_reporter= buffered_option_error_reporter;
+ my_charset_error_reporter= buffered_option_error_reporter;
+#ifdef WITH_PERFSCHEMA_STORAGE_ENGINE
+ pfs_param.m_pfs_instrument= const_cast<char*>("");
+#endif /* WITH_PERFSCHEMA_STORAGE_ENGINE */
- ho_error= handle_options(&remaining_argc, &remaining_argv,
- (my_option*)(all_early_options.buffer), NULL);
- delete_dynamic(&all_early_options);
+ int ho_error __attribute__((unused))= handle_early_options();
+
+#ifdef WITH_PERFSCHEMA_STORAGE_ENGINE
if (ho_error == 0)
{
- /* Add back the program name handle_options removes */
- remaining_argc++;
- remaining_argv--;
- if (pfs_param.m_enabled)
+ if (pfs_param.m_enabled && !opt_help && !opt_bootstrap)
{
+ /* Add sizing hints from the server sizing parameters. */
+ pfs_param.m_hints.m_table_definition_cache= tdc_size;
+ pfs_param.m_hints.m_table_open_cache= tc_size;
+ pfs_param.m_hints.m_max_connections= max_connections;
+ pfs_param.m_hints.m_open_files_limit= open_files_limit;
PSI_hook= initialize_performance_schema(&pfs_param);
if (PSI_hook == NULL)
{
@@ -5563,27 +6061,29 @@ int mysqld_main(int argc, char **argv)
if available.
*/
if (PSI_hook)
- PSI_server= (PSI*) PSI_hook->get_interface(PSI_CURRENT_VERSION);
-
- if (PSI_server)
{
- /*
- Now that we have parsed the command line arguments, and have initialized
- the performance schema itself, the next step is to register all the
- server instruments.
- */
- init_server_psi_keys();
- /* Instrument the main thread */
- PSI_thread *psi= PSI_server->new_thread(key_thread_main, NULL, 0);
- if (psi)
- PSI_server->set_thread(psi);
+ PSI *psi_server= (PSI*) PSI_hook->get_interface(PSI_CURRENT_VERSION);
+ if (likely(psi_server != NULL))
+ {
+ set_psi_server(psi_server);
- /*
- Now that some instrumentation is in place,
- recreate objects which were initialised early,
- so that they are instrumented as well.
- */
- my_thread_global_reinit();
+ /*
+ Now that we have parsed the command line arguments, and have
+ initialized the performance schema itself, the next step is to
+ register all the server instruments.
+ */
+ init_server_psi_keys();
+ /* Instrument the main thread */
+ PSI_thread *psi= PSI_THREAD_CALL(new_thread)(key_thread_main, NULL, 0);
+ PSI_THREAD_CALL(set_thread)(psi);
+
+ /*
+ Now that some instrumentation is in place,
+ recreate objects which were initialised early,
+ so that they are instrumented as well.
+ */
+ my_thread_global_reinit();
+ }
}
#endif /* HAVE_PSI_INTERFACE */
@@ -5668,9 +6168,9 @@ int mysqld_main(int argc, char **argv)
set_user(mysqld_user, user_info);
}
- if (opt_bin_log && !server_id)
+ if (opt_bin_log && !global_system_variables.server_id)
{
- server_id= 1;
+ global_system_variables.server_id= ::server_id= 1;
#ifdef EXTRA_DEBUG
sql_print_warning("You have enabled the binary log, but you haven't set "
"server-id to a non-zero value: we force server id to 1; "
@@ -5703,12 +6203,6 @@ int mysqld_main(int argc, char **argv)
}
#endif
- /*
- Initialize my_str_malloc() and my_str_free()
- */
- my_str_malloc= &my_str_malloc_mysqld;
- my_str_free= &my_str_free_mysqld;
-
#ifdef WITH_WSREP /* WSREP AFTER SE */
if (wsrep_recovery)
{
@@ -5717,6 +6211,7 @@ int mysqld_main(int argc, char **argv)
unireg_abort(0);
}
#endif /* WITH_WSREP */
+
/*
init signals & alarm
After this we can't quit by a simple unireg_abort
@@ -5733,7 +6228,7 @@ int mysqld_main(int argc, char **argv)
delete_pid_file(MYF(MY_WME));
- if (unix_sock != INVALID_SOCKET)
+ if (mysql_socket_getfd(unix_sock) != INVALID_SOCKET)
unlink(mysqld_unix_port);
exit(1);
}
@@ -5741,26 +6236,12 @@ int mysqld_main(int argc, char **argv)
if (!opt_noacl)
(void) grant_init();
- if (!opt_bootstrap)
- servers_init(0);
-
udf_init();
- init_status_vars();
if (opt_bootstrap) /* If running with bootstrap, do not start replication. */
opt_skip_slave_start= 1;
binlog_unsafe_map_init();
- /*
- init_slave() must be called after the thread keys are created.
- Some parts of the code (e.g. SHOW STATUS LIKE 'slave_running' and other
- places) assume that active_mi != 0, so let's fail if it's 0 (out of
- memory); a message has already been printed.
- */
- if (init_slave() && !active_mi)
- {
- unireg_abort(1);
- }
#ifdef WITH_PERFSCHEMA_STORAGE_ENGINE
initialize_performance_schema_acl(opt_bootstrap);
@@ -5778,17 +6259,18 @@ int mysqld_main(int argc, char **argv)
execute_ddl_log_recovery();
- if (Events::init(opt_noacl || opt_bootstrap))
+ /*
+ Change EVENTS_ORIGINAL to EVENTS_OFF (the default value) as there is no
+ point in using ORIGINAL during startup
+ */
+ if (Events::opt_event_scheduler == Events::EVENTS_ORIGINAL)
+ Events::opt_event_scheduler= Events::EVENTS_OFF;
+
+ Events::set_original_state(Events::opt_event_scheduler);
+ if (Events::init((THD*) 0, opt_noacl || opt_bootstrap))
unireg_abort(1);
#ifdef WITH_WSREP /* WSREP AFTER SE */
- if (wsrep_recovery)
- {
- select_thread_in_use= 0;
- wsrep_recover();
- unireg_abort(0);
- }
-
if (opt_bootstrap)
{
/*! bootstrap wsrep init was taken care of above */
@@ -5813,7 +6295,7 @@ int mysqld_main(int argc, char **argv)
wsrep_create_appliers(wsrep_slave_threads - 1);
}
#endif /* WITH_WSREP */
- if (opt_bootstrap)
+ if (opt_bootstrap)
{
select_thread_in_use= 0; // Allow 'kill' to work
bootstrap(mysql_stdin);
@@ -5825,31 +6307,58 @@ int mysqld_main(int argc, char **argv)
exit(0);
}
}
+
+ /* It's now safe to use thread specific memory */
+ mysqld_server_initialized= 1;
+
+ create_shutdown_thread();
+ start_handle_manager();
+
+ /* Copy default global rpl_filter to global_rpl_filter */
+ copy_filter_setting(global_rpl_filter, get_or_create_rpl_filter("", 0));
+
+ /*
+ init_slave() must be called after the thread keys are created.
+ Some parts of the code (e.g. SHOW STATUS LIKE 'slave_running' and other
+ places) assume that active_mi != 0, so let's fail if it's 0 (out of
+ memory); a message has already been printed.
+ */
+ if (init_slave() && !active_mi)
+ {
+ unireg_abort(1);
+ }
+
if (opt_init_file && *opt_init_file)
{
if (read_init_file(opt_init_file))
unireg_abort(1);
}
- create_shutdown_thread();
- start_handle_manager();
-
sql_print_information(ER_DEFAULT(ER_STARTUP),my_progname,server_version,
- ((unix_sock == INVALID_SOCKET) ? (char*) ""
- : mysqld_unix_port),
+ ((mysql_socket_getfd(unix_sock) == INVALID_SOCKET) ?
+ (char*) "" : mysqld_unix_port),
mysqld_port,
MYSQL_COMPILATION_COMMENT);
+
+ // try to keep fd=0 busy
+ if (!freopen(IF_WIN("NUL","/dev/null"), "r", stdin))
+ {
+ // fall back on failure
+ fclose(stdin);
+ }
+
#if defined(_WIN32) && !defined(EMBEDDED_LIBRARY)
Service.SetRunning();
#endif
-
/* Signal threads waiting for server to be started */
mysql_mutex_lock(&LOCK_server_started);
mysqld_server_started= 1;
mysql_cond_signal(&COND_server_started);
mysql_mutex_unlock(&LOCK_server_started);
+ MYSQL_SET_STAGE(0 ,__FILE__, __LINE__);
+
#if defined(_WIN32) || defined(HAVE_SMEM)
handle_connections_methods();
#else
@@ -5877,13 +6386,12 @@ int mysqld_main(int argc, char **argv)
#endif
#endif /* __WIN__ */
-#ifdef HAVE_PSI_INTERFACE
+#ifdef HAVE_PSI_THREAD_INTERFACE
/*
Disable the main thread instrumentation,
to avoid recording events during the shutdown.
*/
- if (PSI_server)
- PSI_server->delete_current_thread();
+ PSI_THREAD_CALL(delete_current_thread)();
#endif
/* Wait until cleanup is done */
@@ -6137,11 +6645,11 @@ static void bootstrap(MYSQL_FILE *file)
thd->variables.wsrep_on= 0;
#endif
thd->bootstrap=1;
- my_net_init(&thd->net,(st_vio*) 0);
+ my_net_init(&thd->net,(st_vio*) 0, MYF(0));
thd->max_client_packet_length= thd->net.max_packet;
thd->security_ctx->master_access= ~(ulong)0;
thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;
- thread_count++;
+ thread_count++; // Safe as only one thread running
in_bootstrap= TRUE;
bootstrap_file=file;
@@ -6225,55 +6733,70 @@ void handle_connection_in_main_thread(THD *thd)
void create_thread_to_handle_connection(THD *thd)
{
+ DBUG_ENTER("create_thread_to_handle_connection");
+ mysql_mutex_assert_owner(&LOCK_thread_count);
+
+ /* Check if we can get thread from the cache */
if (cached_thread_count > wake_thread)
{
- /* Get thread from cache */
- thread_cache.push_back(thd);
- wake_thread++;
- mysql_cond_signal(&COND_thread_cache);
+ mysql_mutex_lock(&LOCK_thread_cache);
+ /* Recheck condition when we have the lock */
+ if (cached_thread_count > wake_thread)
+ {
+ mysql_mutex_unlock(&LOCK_thread_count);
+ /* Get thread from cache */
+ thread_cache.push_back(thd);
+ wake_thread++;
+ mysql_cond_signal(&COND_thread_cache);
+ mysql_mutex_unlock(&LOCK_thread_cache);
+ DBUG_PRINT("info",("Thread created"));
+ DBUG_VOID_RETURN;
+ }
+ mysql_mutex_unlock(&LOCK_thread_cache);
}
- else
+
+ char error_message_buff[MYSQL_ERRMSG_SIZE];
+ /* Create new thread to handle connection */
+ int error;
+ thread_created++;
+ threads.append(thd);
+ DBUG_PRINT("info",(("creating thread %lu"), thd->thread_id));
+ thd->prior_thr_create_utime= microsecond_interval_timer();
+ if ((error= mysql_thread_create(key_thread_one_connection,
+ &thd->real_id, &connection_attrib,
+ handle_one_connection,
+ (void*) thd)))
{
- char error_message_buff[MYSQL_ERRMSG_SIZE];
- /* Create new thread to handle connection */
- int error;
- thread_created++;
- threads.append(thd);
- DBUG_PRINT("info",(("creating thread %lu"), thd->thread_id));
- thd->prior_thr_create_utime= microsecond_interval_timer();
- if ((error= mysql_thread_create(key_thread_one_connection,
- &thd->real_id, &connection_attrib,
- handle_one_connection,
- (void*) thd)))
- {
- /* purecov: begin inspected */
- DBUG_PRINT("error",
- ("Can't create thread to handle request (error %d)",
- error));
+ /* purecov: begin inspected */
+ DBUG_PRINT("error",
+ ("Can't create thread to handle request (error %d)",
+ error));
+ thd->killed= KILL_CONNECTION; // Safety
+ mysql_mutex_unlock(&LOCK_thread_count);
- thread_count--;
- thd->killed= KILL_CONNECTION; // Safety
- mysql_mutex_unlock(&LOCK_thread_count);
+ mysql_mutex_lock(&LOCK_connection_count);
+ (*thd->scheduler->connection_count)--;
+ mysql_mutex_unlock(&LOCK_connection_count);
- mysql_mutex_lock(&LOCK_connection_count);
- (*thd->scheduler->connection_count)--;
- mysql_mutex_unlock(&LOCK_connection_count);
+ statistic_increment(aborted_connects,&LOCK_status);
+ statistic_increment(connection_errors_internal, &LOCK_status);
+ /* Can't use my_error() since store_globals has not been called. */
+ my_snprintf(error_message_buff, sizeof(error_message_buff),
+ ER_THD(thd, ER_CANT_CREATE_THREAD), error);
+ net_send_error(thd, ER_CANT_CREATE_THREAD, error_message_buff, NULL);
+ close_connection(thd, ER_OUT_OF_RESOURCES);
- 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),
- ER_THD(thd, ER_CANT_CREATE_THREAD), error);
- net_send_error(thd, ER_CANT_CREATE_THREAD, error_message_buff, NULL);
- close_connection(thd, ER_OUT_OF_RESOURCES);
- mysql_mutex_lock(&LOCK_thread_count);
- delete thd;
- mysql_mutex_unlock(&LOCK_thread_count);
- return;
- /* purecov: end */
- }
+ mysql_mutex_lock(&LOCK_thread_count);
+ thd->unlink();
+ mysql_mutex_unlock(&LOCK_thread_count);
+ delete thd;
+ thread_safe_decrement32(&thread_count, &thread_count_lock);
+ return;
+ /* purecov: end */
}
mysql_mutex_unlock(&LOCK_thread_count);
DBUG_PRINT("info",("Thread created"));
+ DBUG_VOID_RETURN;
}
@@ -6310,6 +6833,7 @@ static void create_new_thread(THD *thd)
close_connection(thd, ER_CON_COUNT_ERROR);
statistic_increment(denied_connections, &LOCK_status);
delete thd;
+ statistic_increment(connection_errors_max_connection, &LOCK_status);
DBUG_VOID_RETURN;
}
@@ -6320,10 +6844,10 @@ static void create_new_thread(THD *thd)
mysql_mutex_unlock(&LOCK_connection_count);
- /* Start a new thread to handle connection. */
+ thread_safe_increment32(&thread_count, &thread_count_lock);
+ /* Start a new thread to handle connection. */
mysql_mutex_lock(&LOCK_thread_count);
-
/*
The initialization of thread_id is done in create_embedded_thd() for
the embedded library.
@@ -6331,8 +6855,6 @@ static void create_new_thread(THD *thd)
*/
thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;
- thread_count++;
-
MYSQL_CALLBACK(thd->scheduler, add_connection, (thd));
DBUG_VOID_RETURN;
@@ -6344,8 +6866,9 @@ static void create_new_thread(THD *thd)
inline void kill_broken_server()
{
/* hack to get around signals ignored in syscalls for problem OS's */
- if (unix_sock == INVALID_SOCKET ||
- (!opt_disable_networking && base_ip_sock == INVALID_SOCKET))
+ if (mysql_socket_getfd(unix_sock) == INVALID_SOCKET ||
+ (!opt_disable_networking &&
+ mysql_socket_getfd(base_ip_sock) == INVALID_SOCKET))
{
select_thread_in_use = 0;
/* The following call will never return */
@@ -6364,7 +6887,8 @@ inline void kill_broken_server()
void handle_connections_sockets()
{
- my_socket UNINIT_VAR(sock), UNINIT_VAR(new_sock);
+ MYSQL_SOCKET sock= mysql_socket_invalid();
+ MYSQL_SOCKET new_sock= mysql_socket_invalid();
uint error_count=0;
THD *thd;
struct sockaddr_storage cAddr;
@@ -6373,36 +6897,38 @@ void handle_connections_sockets()
int extra_ip_flags __attribute__((unused))=0;
int flags=0,retval;
st_vio *vio_tmp;
+ bool is_unix_sock;
#ifdef HAVE_POLL
int socket_count= 0;
struct pollfd fds[3]; // for ip_sock, unix_sock and extra_ip_sock
+ MYSQL_SOCKET pfs_fds[3]; // for performance schema
#define setup_fds(X) \
- fds[socket_count].fd= X; \
+ mysql_socket_set_thread_owner(X); \
+ pfs_fds[socket_count]= (X); \
+ fds[socket_count].fd= mysql_socket_getfd(X); \
fds[socket_count].events= POLLIN; \
socket_count++
#else
+#define setup_fds(X) FD_SET(mysql_socket_getfd(X),&clientFDs)
fd_set readFDs,clientFDs;
- uint max_used_connection= (uint)
- max(max(base_ip_sock, unix_sock), extra_ip_sock) + 1;
-#define setup_fds(X) FD_SET(X,&clientFDs)
FD_ZERO(&clientFDs);
#endif
DBUG_ENTER("handle_connections_sockets");
- if (base_ip_sock != INVALID_SOCKET)
+ if (mysql_socket_getfd(base_ip_sock) != INVALID_SOCKET)
{
setup_fds(base_ip_sock);
- ip_flags = fcntl(base_ip_sock, F_GETFL, 0);
+ ip_flags = fcntl(mysql_socket_getfd(base_ip_sock), F_GETFL, 0);
}
- if (extra_ip_sock != INVALID_SOCKET)
+ if (mysql_socket_getfd(extra_ip_sock) != INVALID_SOCKET)
{
setup_fds(extra_ip_sock);
- extra_ip_flags = fcntl(extra_ip_sock, F_GETFL, 0);
+ extra_ip_flags = fcntl(mysql_socket_getfd(extra_ip_sock), F_GETFL, 0);
}
#ifdef HAVE_SYS_UN_H
setup_fds(unix_sock);
- socket_flags=fcntl(unix_sock, F_GETFL, 0);
+ socket_flags=fcntl(mysql_socket_getfd(unix_sock), F_GETFL, 0);
#endif
DBUG_PRINT("general",("Waiting for connections."));
@@ -6413,14 +6939,19 @@ void handle_connections_sockets()
retval= poll(fds, socket_count, -1);
#else
readFDs=clientFDs;
-
- retval= select((int) max_used_connection,&readFDs,0,0,0);
+ retval= select((int) 0,&readFDs,0,0,0);
#endif
if (retval < 0)
{
if (socket_errno != SOCKET_EINTR)
{
+ /*
+ select(2)/poll(2) failed on the listening port.
+ There is not much details to report about the client,
+ increment the server global status variable.
+ */
+ statistic_increment(connection_errors_accept, &LOCK_status);
if (!select_errors++ && !abort_loop) /* purecov: inspected */
sql_print_error("mysqld: Got error %d from select",socket_errno); /* purecov: inspected */
}
@@ -6440,19 +6971,19 @@ void handle_connections_sockets()
{
if (fds[i].revents & POLLIN)
{
- sock= fds[i].fd;
- flags= fcntl(sock, F_GETFL, 0);
+ sock= pfs_fds[i];
+ flags= fcntl(mysql_socket_getfd(sock), F_GETFL, 0);
break;
}
}
#else // HAVE_POLL
- if (FD_ISSET(base_ip_sock,&readFDs))
+ if (FD_ISSET(mysql_socket_getfd(base_ip_sock),&readFDs))
{
sock= base_ip_sock;
flags= ip_flags;
}
else
- if (FD_ISSET(extra_ip_sock,&readFDs))
+ if (FD_ISSET(mysql_socket_getfd(extra_ip_sock),&readFDs))
{
sock= extra_ip_sock;
flags= extra_ip_flags;
@@ -6468,18 +6999,19 @@ void handle_connections_sockets()
if (!(test_flags & TEST_BLOCKING))
{
#if defined(O_NONBLOCK)
- fcntl(sock, F_SETFL, flags | O_NONBLOCK);
+ fcntl(mysql_socket_getfd(sock), F_SETFL, flags | O_NONBLOCK);
#elif defined(O_NDELAY)
- fcntl(sock, F_SETFL, flags | O_NDELAY);
+ fcntl(mysql_socket_getfd(sock), F_SETFL, flags | O_NDELAY);
#endif
}
#endif /* NO_FCNTL_NONBLOCK */
for (uint retry=0; retry < MAX_ACCEPT_RETRY; retry++)
{
size_socket length= sizeof(struct sockaddr_storage);
- new_sock= accept(sock, (struct sockaddr *)(&cAddr),
- &length);
- if (new_sock != INVALID_SOCKET ||
+ new_sock= mysql_socket_accept(key_socket_client_connection, sock,
+ (struct sockaddr *)(&cAddr),
+ &length);
+ if (mysql_socket_getfd(new_sock) != INVALID_SOCKET ||
(socket_errno != SOCKET_EINTR && socket_errno != SOCKET_EAGAIN))
break;
MAYBE_BROKEN_SYSCALL;
@@ -6487,16 +7019,25 @@ void handle_connections_sockets()
if (!(test_flags & TEST_BLOCKING))
{
if (retry == MAX_ACCEPT_RETRY - 1)
- fcntl(sock, F_SETFL, flags); // Try without O_NONBLOCK
+ {
+ // Try without O_NONBLOCK
+ fcntl(mysql_socket_getfd(sock), F_SETFL, flags);
+ }
}
#endif
}
#if !defined(NO_FCNTL_NONBLOCK)
if (!(test_flags & TEST_BLOCKING))
- fcntl(sock, F_SETFL, flags);
+ fcntl(mysql_socket_getfd(sock), F_SETFL, flags);
#endif
- if (new_sock == INVALID_SOCKET)
+ if (mysql_socket_getfd(new_sock) == INVALID_SOCKET)
{
+ /*
+ accept(2) failed on the listening port, after many retries.
+ There is not much details to report about the client,
+ increment the server global status variable.
+ */
+ statistic_increment(connection_errors_accept, &LOCK_status);
if ((error_count++ & 255) == 0) // This can happen often
sql_perror("Error in accept");
MAYBE_BROKEN_SYSCALL;
@@ -6505,16 +7046,18 @@ void handle_connections_sockets()
continue;
}
#if defined(WITH_WSREP) && defined(HAVE_FCNTL) && defined(FD_CLOEXEC)
- (void) fcntl(new_sock, F_SETFD, FD_CLOEXEC);
+ (void) fcntl(mysql_socket_getfd(new_sock), F_SETFD, FD_CLOEXEC);
#endif /* WITH_WSREP */
#ifdef HAVE_LIBWRAP
{
- if (sock == base_ip_sock || sock == extra_ip_sock)
+ if (mysql_socket_getfd(sock) == mysql_socket_getfd(base_ip_sock) ||
+ mysql_socket_getfd(sock) == mysql_socket_getfd(extra_ip_sock))
{
struct request_info req;
signal(SIGCHLD, SIG_DFL);
- request_init(&req, RQ_DAEMON, libwrapName, RQ_FILE, new_sock, NULL);
+ request_init(&req, RQ_DAEMON, libwrapName, RQ_FILE,
+ mysql_socket_getfd(new_sock), NULL);
my_fromhost(&req);
if (!my_hosts_access(&req))
{
@@ -6536,42 +7079,42 @@ void handle_connections_sockets()
((void (*)(int))req.sink)(req.fd);
(void) mysql_socket_shutdown(new_sock, SHUT_RDWR);
- (void) closesocket(new_sock);
+ (void) mysql_socket_close(new_sock);
+ /*
+ The connection was refused by TCP wrappers.
+ There are no details (by client IP) available to update the
+ host_cache.
+ */
+ statistic_increment(connection_errors_tcpwrap, &LOCK_status);
continue;
}
}
}
#endif /* HAVE_LIBWRAP */
- {
- size_socket dummyLen;
- struct sockaddr_storage dummy;
- dummyLen = sizeof(dummy);
- if ( getsockname(new_sock,(struct sockaddr *)&dummy,
- (SOCKET_SIZE_TYPE *)&dummyLen) < 0 )
- {
- sql_perror("Error on new connection socket");
- (void) mysql_socket_shutdown(new_sock, SHUT_RDWR);
- (void) closesocket(new_sock);
- continue;
- }
- }
-
/*
** Don't allow too many connections
*/
+ DBUG_PRINT("info", ("Creating THD for new connection"));
if (!(thd= new THD))
{
(void) mysql_socket_shutdown(new_sock, SHUT_RDWR);
- (void) closesocket(new_sock);
+ (void) mysql_socket_close(new_sock);
+ statistic_increment(connection_errors_internal, &LOCK_status);
continue;
}
- if (!(vio_tmp=vio_new(new_sock,
- sock == unix_sock ? VIO_TYPE_SOCKET :
- VIO_TYPE_TCPIP,
- sock == unix_sock ? VIO_LOCALHOST: 0)) ||
- my_net_init(&thd->net,vio_tmp))
+ /* Set to get io buffers to be part of THD */
+ set_current_thd(thd);
+
+ is_unix_sock= (mysql_socket_getfd(sock) ==
+ mysql_socket_getfd(unix_sock));
+
+ if (!(vio_tmp=
+ mysql_socket_vio_new(new_sock,
+ is_unix_sock ? VIO_TYPE_SOCKET : VIO_TYPE_TCPIP,
+ is_unix_sock ? VIO_LOCALHOST: 0)) ||
+ my_net_init(&thd->net, vio_tmp, MYF(MY_THREAD_SPECIFIC)))
{
/*
Only delete the temporary vio if we didn't already attach it to the
@@ -6583,20 +7126,24 @@ void handle_connections_sockets()
else
{
(void) mysql_socket_shutdown(new_sock, SHUT_RDWR);
- (void) closesocket(new_sock);
+ (void) mysql_socket_close(new_sock);
}
delete thd;
+ statistic_increment(connection_errors_internal, &LOCK_status);
continue;
}
- if (sock == unix_sock)
+
+ init_net_server_extension(thd);
+ if (is_unix_sock)
thd->security_ctx->host=(char*) my_localhost;
- if (sock == extra_ip_sock)
+ if (mysql_socket_getfd(sock) == mysql_socket_getfd(extra_ip_sock))
{
thd->extra_port= 1;
thd->scheduler= extra_thread_scheduler;
}
create_new_thread(thd);
+ set_current_thd(0);
}
DBUG_VOID_RETURN;
}
@@ -6690,8 +7237,9 @@ pthread_handler_t handle_connections_namedpipes(void *arg)
CloseHandle(hConnectedPipe);
continue;
}
+ set_current_thd(thd);
if (!(thd->net.vio= vio_new_win32pipe(hConnectedPipe)) ||
- my_net_init(&thd->net, thd->net.vio))
+ my_net_init(&thd->net, thd->net.vio, MYF(MY_THREAD_SPECIFIC)))
{
close_connection(thd, ER_OUT_OF_RESOURCES);
delete thd;
@@ -6700,6 +7248,7 @@ pthread_handler_t handle_connections_namedpipes(void *arg)
/* Host is unknown */
thd->security_ctx->host= my_strdup(my_localhost, MYF(0));
create_new_thread(thd);
+ set_current_thd(0);
}
CloseHandle(connectOverlapped.hEvent);
DBUG_LEAVE;
@@ -6879,6 +7428,7 @@ pthread_handler_t handle_connections_shared_memory(void *arg)
errmsg= "Could not set client to read mode";
goto errorconn;
}
+ set_current_thd(thd);
if (!(thd->net.vio= vio_new_win32shared_memory(handle_client_file_map,
handle_client_map,
event_client_wrote,
@@ -6886,7 +7436,7 @@ pthread_handler_t handle_connections_shared_memory(void *arg)
event_server_wrote,
event_server_read,
event_conn_closed)) ||
- my_net_init(&thd->net, thd->net.vio))
+ my_net_init(&thd->net, thd->net.vio, MYF(MY_THREAD_SPECIFIC)))
{
close_connection(thd, ER_OUT_OF_RESOURCES);
errmsg= 0;
@@ -6895,6 +7445,7 @@ pthread_handler_t handle_connections_shared_memory(void *arg)
thd->security_ctx->host= my_strdup(my_localhost, MYF(0)); /* Host is unknown */
create_new_thread(thd);
connect_number++;
+ set_current_thd(thd);
continue;
errorconn:
@@ -6922,6 +7473,7 @@ errorconn:
CloseHandle(event_conn_closed);
delete thd;
}
+ set_current_thd(0);
/* End shared memory handling */
error:
@@ -6952,6 +7504,64 @@ error:
Handle start options
******************************************************************************/
+
+/**
+ Process command line options flagged as 'early'.
+ Some components needs to be initialized as early as possible,
+ because the rest of the server initialization depends on them.
+ Options that needs to be parsed early includes:
+ - the performance schema, when compiled in,
+ - options related to the help,
+ - options related to the bootstrap
+ The performance schema needs to be initialized as early as possible,
+ before to-be-instrumented objects of the server are initialized.
+*/
+
+int handle_early_options()
+{
+ int ho_error;
+ DYNAMIC_ARRAY all_early_options;
+
+ my_getopt_register_get_addr(NULL);
+ /* Skip unknown options so that they may be processed later */
+ my_getopt_skip_unknown= TRUE;
+
+ /* prepare all_early_options array */
+ my_init_dynamic_array(&all_early_options, sizeof(my_option), 100, 25, MYF(0));
+ add_many_options(&all_early_options, pfs_early_options,
+ array_elements(pfs_early_options));
+ sys_var_add_options(&all_early_options, sys_var::PARSE_EARLY);
+ add_terminator(&all_early_options);
+
+ ho_error= handle_options(&remaining_argc, &remaining_argv,
+ (my_option*)(all_early_options.buffer),
+ mysqld_get_one_option);
+ if (ho_error == 0)
+ {
+ /* Add back the program name handle_options removes */
+ remaining_argc++;
+ remaining_argv--;
+ }
+
+ delete_dynamic(&all_early_options);
+
+ return ho_error;
+}
+
+
+#define MYSQL_COMPATIBILITY_OPTION(option) \
+ { option, OPT_MYSQL_COMPATIBILITY, \
+ 0, 0, 0, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0 }
+
+#define MYSQL_TO_BE_IMPLEMENTED_OPTION(option) \
+ { option, OPT_MYSQL_TO_BE_IMPLEMENTED, \
+ 0, 0, 0, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0 }
+
+#define MYSQL_SUGGEST_ANALOG_OPTION(option, str) \
+ { option, OPT_MYSQL_COMPATIBILITY, \
+ 0, 0, 0, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0 }
+
+
/**
System variables are automatically command-line options (few
exceptions are documented in sys_var.h), so don't need
@@ -6980,7 +7590,7 @@ struct my_option my_long_options[]=
{"autocommit", 0, "Set default value for autocommit (0 or 1)",
&opt_autocommit, &opt_autocommit, 0,
GET_BOOL, OPT_ARG, 1, 0, 0, 0, 0, NULL},
- {"bind-address", OPT_BIND_ADDRESS, "IP address to bind to.",
+ {"bind-address", 0, "IP address to bind to.",
&my_bind_addr_str, &my_bind_addr_str, 0, GET_STR,
REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"binlog-do-db", OPT_BINLOG_DO_DB,
@@ -7140,9 +7750,6 @@ struct my_option my_long_options[]=
"Set the language used for the month names and the days of the week.",
&lc_time_names_name, &lc_time_names_name,
0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
- {"log", 'l', "Log connections and queries to file (deprecated option, use "
- "--general-log/--general-log-file instead).", &opt_logname, &opt_logname,
- 0, GET_STR_ALLOC, OPT_ARG, 0, 0, 0, 0, 0, 0},
{"log-basename", OPT_LOG_BASENAME,
"Basename for all log files and the .pid file. This sets all log file "
"names at once (in 'datadir') and is normally the only option you need "
@@ -7179,25 +7786,11 @@ struct my_option my_long_options[]=
"Log slow statements executed by slave thread to the slow log if it is open.",
&opt_log_slow_slave_statements, &opt_log_slow_slave_statements,
0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
- {"log-slow-queries", OPT_SLOW_QUERY_LOG,
- "Enable logging of slow queries (longer than --long-query-time) to log file "
- "or table. Optional argument is a file name for the slow log. If not given, "
- "'log-basename'-slow.log will be used. Use --log-output=TABLE if you want "
- "to have the log in the table mysql.slow_log. "
- "Deprecated option, use --slow-query-log/--slow-query-log-file instead.",
- &opt_slow_logname, &opt_slow_logname, 0, GET_STR_ALLOC, OPT_ARG,
- 0, 0, 0, 0, 0, 0},
{"log-tc", 0,
"Path to transaction coordinator log (used for transactions that affect "
"more than one storage engine, when binary log is disabled).",
&opt_tc_log_file, &opt_tc_log_file, 0, GET_STR,
REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
-#ifdef HAVE_MMAP
- {"log-tc-size", 0, "Size of transaction coordinator log.",
- &opt_tc_log_size, &opt_tc_log_size, 0, GET_ULONG,
- REQUIRED_ARG, TC_LOG_MIN_SIZE, TC_LOG_MIN_SIZE, (ulonglong) ULONG_MAX, 0,
- TC_LOG_PAGE_SIZE, 0},
-#endif
{"master-info-file", 0,
"The location and name of the file that remembers the master and where "
"the I/O replication thread is in the master's binlogs. Defaults to "
@@ -7215,10 +7808,6 @@ struct my_option my_long_options[]=
#endif /* HAVE_REPLICATION */
{"memlock", 0, "Lock mysqld in memory.", &locked_in_memory,
&locked_in_memory, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
- {"one-thread", OPT_ONE_THREAD,
- "(Deprecated): Only use one thread (for debugging under Linux). Use "
- "thread-handling=no-threads instead.",
- 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
{"old-style-user-limits", 0,
"Enable old-style user limits (before 5.0.3, user resources were counted "
"per each user+host vs. per account).",
@@ -7236,28 +7825,28 @@ struct my_option my_long_options[]=
"while having selected a different or no database. If you need cross "
"database updates to work, make sure you have 3.23.28 or later, and use "
"replicate-wild-do-table=db_name.%.",
- 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ 0, 0, 0, GET_STR | GET_ASK_ADDR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"replicate-do-table", OPT_REPLICATE_DO_TABLE,
"Tells the slave thread to restrict replication to the specified table. "
"To specify more than one table, use the directive multiple times, once "
"for each table. This will work for cross-database updates, in contrast "
- "to replicate-do-db.", 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ "to replicate-do-db.", 0, 0, 0, GET_STR | GET_ASK_ADDR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"replicate-ignore-db", OPT_REPLICATE_IGNORE_DB,
"Tells the slave thread to not replicate to the specified database. To "
"specify more than one database to ignore, use the directive multiple "
"times, once for each database. This option will not work if you use "
"cross database updates. If you need cross database updates to work, "
"make sure you have 3.23.28 or later, and use replicate-wild-ignore-"
- "table=db_name.%. ", 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ "table=db_name.%. ", 0, 0, 0, GET_STR | GET_ASK_ADDR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"replicate-ignore-table", OPT_REPLICATE_IGNORE_TABLE,
"Tells the slave thread to not replicate to the specified table. To specify "
"more than one table to ignore, use the directive multiple times, once for "
"each table. This will work for cross-database updates, in contrast to "
- "replicate-ignore-db.", 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ "replicate-ignore-db.", 0, 0, 0, GET_STR | GET_ASK_ADDR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"replicate-rewrite-db", OPT_REPLICATE_REWRITE_DB,
"Updates to a database with a different name than the original. Example: "
"replicate-rewrite-db=master_db_name->slave_db_name.",
- 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ 0, 0, 0, GET_STR | GET_ASK_ADDR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
#ifdef HAVE_REPLICATION
{"replicate-same-server-id", 0,
"In replication, if set to 1, do not skip events having our server id. "
@@ -7273,7 +7862,7 @@ struct my_option my_long_options[]=
"database updates. Example: replicate-wild-do-table=foo%.bar% will "
"replicate only updates to tables in all databases that start with foo "
"and whose table names start with bar.",
- 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ 0, 0, 0, GET_STR | GET_ASK_ADDR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"replicate-wild-ignore-table", OPT_REPLICATE_WILD_IGNORE_TABLE,
"Tells the slave thread to not replicate to the tables that match the "
"given wildcard pattern. To specify more than one table to ignore, use "
@@ -7281,7 +7870,7 @@ struct my_option my_long_options[]=
"cross-database updates. Example: replicate-wild-ignore-table=foo%.bar% "
"will not do updates to tables in databases that start with foo and whose "
"table names start with bar.",
- 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ 0, 0, 0, GET_STR | GET_ASK_ADDR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"safe-mode", OPT_SAFE, "Skip some optimize stages (for testing). Deprecated.",
0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
{"safe-user-create", 0,
@@ -7306,10 +7895,6 @@ struct my_option my_long_options[]=
{"skip-slave-start", 0,
"If set, slave is not autostarted.", &opt_skip_slave_start,
&opt_skip_slave_start, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
- {"skip-thread-priority", OPT_SKIP_PRIOR,
- "Don't give threads different priorities. This option is deprecated "
- "because it has no effect; the implied behavior is already the default.",
- 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
#if defined(_WIN32) && !defined(EMBEDDED_LIBRARY)
{"slow-start-timeout", 0,
"Maximum number of milliseconds that the service control manager should wait "
@@ -7319,7 +7904,7 @@ struct my_option my_long_options[]=
#endif
#ifdef HAVE_OPENSSL
{"ssl", 0,
- "Enable SSL for connection (automatically enabled with other flags).",
+ "Enable SSL for connection (automatically enabled if an ssl option is used).",
&opt_use_ssl, &opt_use_ssl, 0, GET_BOOL, OPT_ARG, 0, 0, 0,
0, 0, 0},
#endif
@@ -7362,24 +7947,85 @@ struct my_option my_long_options[]=
&global_system_variables.tx_isolation,
&global_system_variables.tx_isolation, &tx_isolation_typelib,
GET_ENUM, REQUIRED_ARG, ISO_REPEATABLE_READ, 0, 0, 0, 0, 0},
+ {"transaction-read-only", 0,
+ "Default transaction access mode. "
+ "True if transactions are read-only.",
+ &global_system_variables.tx_read_only,
+ &global_system_variables.tx_read_only, 0,
+ GET_BOOL, OPT_ARG, 0, 0, 0, 0, 0, 0},
{"user", 'u', "Run mysqld daemon as user.", 0, 0, 0, GET_STR, REQUIRED_ARG,
0, 0, 0, 0, 0, 0},
{"verbose", 'v', "Used with --help option for detailed help.",
&opt_verbose, &opt_verbose, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
{"version", 'V', "Output version information and exit.", 0, 0, 0, GET_NO_ARG,
NO_ARG, 0, 0, 0, 0, 0, 0},
- {"plugin-load", 0,
+ {"plugin-load", OPT_PLUGIN_LOAD,
"Semicolon-separated list of plugins to load, where each plugin is "
"specified as ether a plugin_name=library_file pair or only a library_file. "
"If the latter case, all plugins from a given library_file will be loaded.",
- &opt_plugin_load, &opt_plugin_load, 0,
+ 0, 0, 0,
GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"plugin-load-add", OPT_PLUGIN_LOAD_ADD,
+ "Optional semicolon-separated list of plugins to load. This option adds "
+ "to the list specified by --plugin-load in an incremental way. "
+ "It can be specified many times, adding more plugins every time.",
+ 0, 0, 0,
+ GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"table_cache", 0, "Deprecated; use --table-open-cache instead.",
- &table_cache_size, &table_cache_size, 0, GET_ULONG,
+ &tc_size, &tc_size, 0, GET_ULONG,
REQUIRED_ARG, TABLE_OPEN_CACHE_DEFAULT, 1, 512*1024L, 0, 1, 0},
- {0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
-};
+#ifdef WITH_WSREP
+ {"wsrep-new-cluster", 0, "Bootstrap a cluster. It works by overriding the "
+ "current value of wsrep_cluster_address. It is recommended not to add this "
+ "option to the config file as this will trigger bootstrap on every server "
+ "start.", &wsrep_new_cluster, &wsrep_new_cluster, 0, GET_BOOL, NO_ARG,
+ 0, 0, 0, 0, 0, 0},
+#endif
+ /* The following options exist in 5.6 but not in 10.0 */
+ MYSQL_TO_BE_IMPLEMENTED_OPTION("default-tmp-storage-engine"),
+ MYSQL_COMPATIBILITY_OPTION("log-raw"),
+ MYSQL_COMPATIBILITY_OPTION("log-bin-use-v1-row-events"),
+ MYSQL_TO_BE_IMPLEMENTED_OPTION("default-authentication-plugin"),
+ MYSQL_COMPATIBILITY_OPTION("binlog-max-flush-queue-time"),
+ MYSQL_TO_BE_IMPLEMENTED_OPTION("binlog-row-image"),
+ MYSQL_TO_BE_IMPLEMENTED_OPTION("explicit-defaults-for-timestamp"),
+ MYSQL_COMPATIBILITY_OPTION("master-info-repository"),
+ MYSQL_COMPATIBILITY_OPTION("relay-log-info-repository"),
+ MYSQL_SUGGEST_ANALOG_OPTION("binlog-rows-query-log-events", "--binlog-annotate-row-events"),
+ MYSQL_COMPATIBILITY_OPTION("binlog-order-commits"),
+ MYSQL_TO_BE_IMPLEMENTED_OPTION("log-throttle-queries-not-using-indexes"),
+ MYSQL_TO_BE_IMPLEMENTED_OPTION("end-markers-in-json"),
+ MYSQL_TO_BE_IMPLEMENTED_OPTION("optimizer-trace"), // OPTIMIZER_TRACE
+ MYSQL_TO_BE_IMPLEMENTED_OPTION("optimizer-trace-features"), // OPTIMIZER_TRACE
+ MYSQL_TO_BE_IMPLEMENTED_OPTION("optimizer-trace-offset"), // OPTIMIZER_TRACE
+ MYSQL_TO_BE_IMPLEMENTED_OPTION("optimizer-trace-limit"), // OPTIMIZER_TRACE
+ MYSQL_TO_BE_IMPLEMENTED_OPTION("optimizer-trace-max-mem-size"), // OPTIMIZER_TRACE
+ MYSQL_TO_BE_IMPLEMENTED_OPTION("eq-range-index-dive-limit"),
+ MYSQL_COMPATIBILITY_OPTION("server-id-bits"),
+ MYSQL_TO_BE_IMPLEMENTED_OPTION("slave-rows-search-algorithms"), // HAVE_REPLICATION
+ MYSQL_COMPATIBILITY_OPTION("table-open-cache-instances"),
+ MYSQL_TO_BE_IMPLEMENTED_OPTION("slave-allow-batching"), // HAVE_REPLICATION
+ MYSQL_COMPATIBILITY_OPTION("slave-checkpoint-period"), // HAVE_REPLICATION
+ MYSQL_COMPATIBILITY_OPTION("slave-checkpoint-group"), // HAVE_REPLICATION
+ MYSQL_SUGGEST_ANALOG_OPTION("slave-parallel-workers", "--slave-parallel-threads"), // HAVE_REPLICATION
+ MYSQL_SUGGEST_ANALOG_OPTION("slave-pending-jobs-size-max", "--slave-parallel-max-queued"), // HAVE_REPLICATION
+ MYSQL_TO_BE_IMPLEMENTED_OPTION("disconnect-on-expired-password"),
+ MYSQL_TO_BE_IMPLEMENTED_OPTION("sha256-password-private-key-path"), // HAVE_OPENSSL && !HAVE_YASSL
+ MYSQL_TO_BE_IMPLEMENTED_OPTION("sha256-password-public-key-path"), // HAVE_OPENSSL && !HAVE_YASSL
+
+ /* The following options exist in 5.5 and 5.6 but not in 10.0 */
+ MYSQL_SUGGEST_ANALOG_OPTION("abort-slave-event-count", "--debug-abort-slave-event-count"),
+ MYSQL_SUGGEST_ANALOG_OPTION("disconnect-slave-event-count", "--debug-disconnect-slave-event-count"),
+ MYSQL_SUGGEST_ANALOG_OPTION("exit-info", "--debug-exit-info"),
+ MYSQL_SUGGEST_ANALOG_OPTION("max-binlog-dump-events", "--debug-max-binlog-dump-events"),
+ MYSQL_SUGGEST_ANALOG_OPTION("sporadic-binlog-dump-fail", "--debug-sporadic-binlog-dump-fail"),
+ MYSQL_COMPATIBILITY_OPTION("new"),
+
+ /* The following options were added after 5.6.10 */
+ MYSQL_TO_BE_IMPLEMENTED_OPTION("rpl-stop-slave-timeout"),
+ MYSQL_TO_BE_IMPLEMENTED_OPTION("validate-user-plugins") // NO_EMBEDDED_ACCESS_CHECKS
+};
static int show_queries(THD *thd, SHOW_VAR *var, char *buff)
{
@@ -7424,52 +8070,109 @@ static int show_rpl_status(THD *thd, SHOW_VAR *var, char *buff)
static int show_slave_running(THD *thd, SHOW_VAR *var, char *buff)
{
+ Master_info *mi= NULL;
+ bool tmp;
+ LINT_INIT(tmp);
+
var->type= SHOW_MY_BOOL;
var->value= buff;
- *((my_bool *)buff)= (my_bool) (active_mi &&
- active_mi->slave_running == MYSQL_SLAVE_RUN_CONNECT &&
- active_mi->rli.slave_running);
+ mysql_mutex_lock(&LOCK_active_mi);
+ if (master_info_index)
+ {
+ mi= master_info_index->
+ get_master_info(&thd->variables.default_master_connection,
+ Sql_condition::WARN_LEVEL_NOTE);
+ if (mi)
+ tmp= (my_bool) (mi->slave_running == MYSQL_SLAVE_RUN_READING &&
+ mi->rli.slave_running != MYSQL_SLAVE_NOT_RUN);
+ }
+ mysql_mutex_unlock(&LOCK_active_mi);
+ if (mi)
+ *((my_bool *)buff)= tmp;
+ else
+ var->type= SHOW_UNDEF;
return 0;
}
-static int show_slave_retried_trans(THD *thd, SHOW_VAR *var, char *buff)
+
+/* How many slaves are connected to this master */
+
+static int show_slaves_connected(THD *thd, SHOW_VAR *var, char *buff)
{
- /*
- TODO: with multimaster, have one such counter per line in
- SHOW SLAVE STATUS, and have the sum over all lines here.
- */
- if (active_mi)
- {
- var->type= SHOW_LONG;
- var->value= buff;
- *((long *)buff)= (long)active_mi->rli.retried_trans;
- }
- else
- var->type= SHOW_UNDEF;
+
+ var->type= SHOW_LONGLONG;
+ var->value= buff;
+ mysql_mutex_lock(&LOCK_slave_list);
+
+ *((longlong *)buff)= slave_list.records;
+
+ mysql_mutex_unlock(&LOCK_slave_list);
+ return 0;
+}
+
+
+/* How many masters this slave is connected to */
+
+
+static int show_slaves_running(THD *thd, SHOW_VAR *var, char *buff)
+{
+ var->type= SHOW_LONGLONG;
+ var->value= buff;
+ mysql_mutex_lock(&LOCK_active_mi);
+
+ *((longlong *)buff)= master_info_index->any_slave_sql_running();
+
+ mysql_mutex_unlock(&LOCK_active_mi);
return 0;
}
+
static int show_slave_received_heartbeats(THD *thd, SHOW_VAR *var, char *buff)
{
- if (active_mi)
- {
- var->type= SHOW_LONGLONG;
- var->value= buff;
- *((longlong *)buff)= active_mi->received_heartbeats;
- }
+ Master_info *mi= NULL;
+ longlong tmp;
+ LINT_INIT(tmp);
+
+ var->type= SHOW_LONGLONG;
+ var->value= buff;
+ mysql_mutex_lock(&LOCK_active_mi);
+ if (master_info_index)
+ {
+ mi= master_info_index->
+ get_master_info(&thd->variables.default_master_connection,
+ Sql_condition::WARN_LEVEL_NOTE);
+ if (mi)
+ tmp= mi->received_heartbeats;
+ }
+ mysql_mutex_unlock(&LOCK_active_mi);
+ if (mi)
+ *((longlong *)buff)= tmp;
else
var->type= SHOW_UNDEF;
return 0;
}
+
static int show_heartbeat_period(THD *thd, SHOW_VAR *var, char *buff)
{
- if (active_mi)
- {
- var->type= SHOW_CHAR;
- var->value= buff;
- sprintf(buff, "%.3f", active_mi->heartbeat_period);
- }
+ Master_info *mi= NULL;
+ float tmp;
+ LINT_INIT(tmp);
+
+ var->type= SHOW_CHAR;
+ var->value= buff;
+ mysql_mutex_lock(&LOCK_active_mi);
+ if (master_info_index)
+ {
+ mi= master_info_index->
+ get_master_info(&thd->variables.default_master_connection,
+ Sql_condition::WARN_LEVEL_NOTE);
+ if (mi)
+ tmp= mi->heartbeat_period;
+ }
+ mysql_mutex_unlock(&LOCK_active_mi);
+ if (mi)
+ sprintf(buff, "%.3f", tmp);
else
var->type= SHOW_UNDEF;
return 0;
@@ -7482,7 +8185,7 @@ static int show_open_tables(THD *thd, SHOW_VAR *var, char *buff)
{
var->type= SHOW_LONG;
var->value= buff;
- *((long *)buff)= (long)cached_open_tables();
+ *((long *) buff)= (long) tc_records();
return 0;
}
@@ -7500,10 +8203,20 @@ static int show_table_definitions(THD *thd, SHOW_VAR *var, char *buff)
{
var->type= SHOW_LONG;
var->value= buff;
- *((long *)buff)= (long)cached_table_definitions();
+ *((long *) buff)= (long) tdc_records();
return 0;
}
+
+static int show_flush_commands(THD *thd, SHOW_VAR *var, char *buff)
+{
+ var->type= SHOW_LONG;
+ var->value= buff;
+ *((long *) buff)= (long) tdc_refresh_version();
+ return 0;
+}
+
+
#if defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY)
/* Functions relying on CTX */
static int show_ssl_ctx_sess_accept(THD *thd, SHOW_VAR *var, char *buff)
@@ -7760,6 +8473,110 @@ static int show_ssl_get_cipher_list(THD *thd, SHOW_VAR *var, char *buff)
return 0;
}
+
+#ifdef HAVE_YASSL
+
+static char *
+my_asn1_time_to_string(ASN1_TIME *time, char *buf, size_t len)
+{
+ return yaSSL_ASN1_TIME_to_string(time, buf, len);
+}
+
+#else /* openssl */
+
+static char *
+my_asn1_time_to_string(ASN1_TIME *time, char *buf, size_t len)
+{
+ int n_read;
+ char *res= NULL;
+ BIO *bio= BIO_new(BIO_s_mem());
+
+ if (bio == NULL)
+ return NULL;
+
+ if (!ASN1_TIME_print(bio, time))
+ goto end;
+
+ n_read= BIO_read(bio, buf, (int) (len - 1));
+
+ if (n_read > 0)
+ {
+ buf[n_read]= 0;
+ res= buf;
+ }
+
+end:
+ BIO_free(bio);
+ return res;
+}
+
+#endif
+
+
+/**
+ Handler function for the 'ssl_get_server_not_before' variable
+
+ @param thd the mysql thread structure
+ @param var the data for the variable
+ @param[out] buf the string to put the value of the variable into
+
+ @return status
+ @retval 0 success
+*/
+
+static int
+show_ssl_get_server_not_before(THD *thd, SHOW_VAR *var, char *buff)
+{
+ var->type= SHOW_CHAR;
+ if(thd->vio_ok() && thd->net.vio->ssl_arg)
+ {
+ SSL *ssl= (SSL*) thd->net.vio->ssl_arg;
+ X509 *cert= SSL_get_certificate(ssl);
+ ASN1_TIME *not_before= X509_get_notBefore(cert);
+
+ var->value= my_asn1_time_to_string(not_before, buff,
+ SHOW_VAR_FUNC_BUFF_SIZE);
+ if (!var->value)
+ return 1;
+ var->value= buff;
+ }
+ else
+ var->value= empty_c_string;
+ return 0;
+}
+
+
+/**
+ Handler function for the 'ssl_get_server_not_after' variable
+
+ @param thd the mysql thread structure
+ @param var the data for the variable
+ @param[out] buf the string to put the value of the variable into
+
+ @return status
+ @retval 0 success
+*/
+
+static int
+show_ssl_get_server_not_after(THD *thd, SHOW_VAR *var, char *buff)
+{
+ var->type= SHOW_CHAR;
+ if(thd->vio_ok() && thd->net.vio->ssl_arg)
+ {
+ SSL *ssl= (SSL*) thd->net.vio->ssl_arg;
+ X509 *cert= SSL_get_certificate(ssl);
+ ASN1_TIME *not_after= X509_get_notAfter(cert);
+
+ var->value= my_asn1_time_to_string(not_after, buff,
+ SHOW_VAR_FUNC_BUFF_SIZE);
+ if (!var->value)
+ return 1;
+ }
+ else
+ var->value= empty_c_string;
+ return 0;
+}
+
#endif /* HAVE_OPENSSL && !EMBEDDED_LIBRARY */
static int show_default_keycache(THD *thd, SHOW_VAR *var, char *buff)
@@ -7802,6 +8619,42 @@ static int show_default_keycache(THD *thd, SHOW_VAR *var, char *buff)
return 0;
}
+#ifndef DBUG_OFF
+static int debug_status_func(THD *thd, SHOW_VAR *var, char *buff)
+{
+#define add_var(X,Y,Z) \
+ v->name= X; \
+ v->value= (char*)Y; \
+ v->type= Z; \
+ v++;
+
+ var->type= SHOW_ARRAY;
+ var->value= buff;
+
+ SHOW_VAR *v= (SHOW_VAR *)buff;
+
+ if (_db_keyword_(0, "role_merge_stats", 1))
+ {
+ static SHOW_VAR roles[]= {
+ {"global", (char*) &role_global_merges, SHOW_ULONG},
+ {"db", (char*) &role_db_merges, SHOW_ULONG},
+ {"table", (char*) &role_table_merges, SHOW_ULONG},
+ {"column", (char*) &role_column_merges, SHOW_ULONG},
+ {"routine", (char*) &role_routine_merges, SHOW_ULONG},
+ {NullS, NullS, SHOW_LONG}
+ };
+
+ add_var("role_merges", roles, SHOW_ARRAY);
+ }
+
+ v->name= 0;
+
+#undef add_var
+
+ return 0;
+}
+#endif
+
#ifdef HAVE_POOL_OF_THREADS
int show_threadpool_idle_threads(THD *thd, SHOW_VAR *var, char *buff)
{
@@ -7829,35 +8682,46 @@ SHOW_VAR status_vars[]= {
{"Bytes_received", (char*) offsetof(STATUS_VAR, bytes_received), SHOW_LONGLONG_STATUS},
{"Bytes_sent", (char*) offsetof(STATUS_VAR, bytes_sent), SHOW_LONGLONG_STATUS},
{"Com", (char*) com_status_vars, SHOW_ARRAY},
- {"Compression", (char*) &show_net_compression, SHOW_FUNC},
+ {"Compression", (char*) &show_net_compression, SHOW_SIMPLE_FUNC},
{"Connections", (char*) &thread_id, SHOW_LONG_NOFLUSH},
+ {"Connection_errors_accept", (char*) &connection_errors_accept, SHOW_LONG},
+ {"Connection_errors_internal", (char*) &connection_errors_internal, SHOW_LONG},
+ {"Connection_errors_max_connections", (char*) &connection_errors_max_connection, SHOW_LONG},
+ {"Connection_errors_peer_address", (char*) &connection_errors_peer_addr, SHOW_LONG},
+ {"Connection_errors_select", (char*) &connection_errors_select, SHOW_LONG},
+ {"Connection_errors_tcpwrap", (char*) &connection_errors_tcpwrap, SHOW_LONG},
{"Cpu_time", (char*) offsetof(STATUS_VAR, cpu_time), SHOW_DOUBLE_STATUS},
- {"Created_tmp_disk_tables", (char*) offsetof(STATUS_VAR, created_tmp_disk_tables), SHOW_LONG_STATUS},
+ {"Created_tmp_disk_tables", (char*) offsetof(STATUS_VAR, created_tmp_disk_tables_), SHOW_LONG_STATUS},
{"Created_tmp_files", (char*) &my_tmp_file_created, SHOW_LONG},
- {"Created_tmp_tables", (char*) offsetof(STATUS_VAR, created_tmp_tables), SHOW_LONG_STATUS},
+ {"Created_tmp_tables", (char*) offsetof(STATUS_VAR, created_tmp_tables_), SHOW_LONG_STATUS},
+#ifndef DBUG_OFF
+ {"Debug", (char*) &debug_status_func, SHOW_FUNC},
+#endif
{"Delayed_errors", (char*) &delayed_insert_errors, SHOW_LONG},
{"Delayed_insert_threads", (char*) &delayed_insert_threads, SHOW_LONG_NOFLUSH},
{"Delayed_writes", (char*) &delayed_insert_writes, SHOW_LONG},
{"Empty_queries", (char*) offsetof(STATUS_VAR, empty_queries), SHOW_LONG_STATUS},
{"Executed_events", (char*) &executed_events, SHOW_LONG_NOFLUSH },
{"Executed_triggers", (char*) offsetof(STATUS_VAR, executed_triggers), SHOW_LONG_STATUS},
+ {"Feature_delay_key_write", (char*) &feature_files_opened_with_delayed_keys, SHOW_LONG },
{"Feature_dynamic_columns", (char*) offsetof(STATUS_VAR, feature_dynamic_columns), SHOW_LONG_STATUS},
{"Feature_fulltext", (char*) offsetof(STATUS_VAR, feature_fulltext), SHOW_LONG_STATUS},
{"Feature_gis", (char*) offsetof(STATUS_VAR, feature_gis), SHOW_LONG_STATUS},
{"Feature_locale", (char*) offsetof(STATUS_VAR, feature_locale), SHOW_LONG_STATUS},
{"Feature_subquery", (char*) offsetof(STATUS_VAR, feature_subquery), SHOW_LONG_STATUS},
{"Feature_timezone", (char*) offsetof(STATUS_VAR, feature_timezone), SHOW_LONG_STATUS},
- {"Feature_trigger", (char*) offsetof(STATUS_VAR, feature_trigger), SHOW_LONG_STATUS},
- {"Feature_xml", (char*) offsetof(STATUS_VAR, feature_xml), SHOW_LONG_STATUS},
- {"Flush_commands", (char*) &refresh_version, SHOW_LONG_NOFLUSH},
+ {"Feature_trigger", (char*) offsetof(STATUS_VAR, feature_trigger), SHOW_LONG_STATUS},
+ {"Feature_xml", (char*) offsetof(STATUS_VAR, feature_xml), SHOW_LONG_STATUS},
+ {"Flush_commands", (char*) &show_flush_commands, SHOW_SIMPLE_FUNC},
{"Handler_commit", (char*) offsetof(STATUS_VAR, ha_commit_count), SHOW_LONG_STATUS},
{"Handler_delete", (char*) offsetof(STATUS_VAR, ha_delete_count), SHOW_LONG_STATUS},
{"Handler_discover", (char*) offsetof(STATUS_VAR, ha_discover_count), SHOW_LONG_STATUS},
+ {"Handler_external_lock", (char*) offsetof(STATUS_VAR, ha_external_lock_count), SHOW_LONGLONG_STATUS},
{"Handler_icp_attempts", (char*) offsetof(STATUS_VAR, ha_icp_attempts), SHOW_LONG_STATUS},
{"Handler_icp_match", (char*) offsetof(STATUS_VAR, ha_icp_match), SHOW_LONG_STATUS},
{"Handler_mrr_init", (char*) offsetof(STATUS_VAR, ha_mrr_init_count), SHOW_LONG_STATUS},
- {"Handler_mrr_key_refills", (char*) offsetof(STATUS_VAR, ha_mrr_key_refills_count), SHOW_LONG_STATUS},
- {"Handler_mrr_rowid_refills", (char*) offsetof(STATUS_VAR, ha_mrr_rowid_refills_count), SHOW_LONG_STATUS},
+ {"Handler_mrr_key_refills", (char*) offsetof(STATUS_VAR, ha_mrr_key_refills_count), SHOW_LONG_STATUS},
+ {"Handler_mrr_rowid_refills",(char*) offsetof(STATUS_VAR, ha_mrr_rowid_refills_count), SHOW_LONG_STATUS},
{"Handler_prepare", (char*) offsetof(STATUS_VAR, ha_prepare_count), SHOW_LONG_STATUS},
{"Handler_read_first", (char*) offsetof(STATUS_VAR, ha_read_first_count), SHOW_LONG_STATUS},
{"Handler_read_key", (char*) offsetof(STATUS_VAR, ha_read_key_count), SHOW_LONG_STATUS},
@@ -7877,18 +8741,20 @@ SHOW_VAR status_vars[]= {
{"Key", (char*) &show_default_keycache, SHOW_FUNC},
{"Last_query_cost", (char*) offsetof(STATUS_VAR, last_query_cost), SHOW_DOUBLE_STATUS},
{"Max_used_connections", (char*) &max_used_connections, SHOW_LONG},
+ {"Memory_used", (char*) offsetof(STATUS_VAR, memory_used), SHOW_LONGLONG_STATUS},
{"Not_flushed_delayed_rows", (char*) &delayed_rows_in_use, SHOW_LONG_NOFLUSH},
{"Open_files", (char*) &my_file_opened, SHOW_LONG_NOFLUSH},
{"Open_streams", (char*) &my_stream_opened, SHOW_LONG_NOFLUSH},
- {"Open_table_definitions", (char*) &show_table_definitions, SHOW_FUNC},
- {"Open_tables", (char*) &show_open_tables, SHOW_FUNC},
+ {"Open_table_definitions", (char*) &show_table_definitions, SHOW_SIMPLE_FUNC},
+ {"Open_tables", (char*) &show_open_tables, SHOW_SIMPLE_FUNC},
{"Opened_files", (char*) &my_file_total_opened, SHOW_LONG_NOFLUSH},
+ {"Opened_plugin_libraries", (char*) &dlopen_count, SHOW_LONG},
{"Opened_table_definitions", (char*) offsetof(STATUS_VAR, opened_shares), SHOW_LONG_STATUS},
{"Opened_tables", (char*) offsetof(STATUS_VAR, opened_tables), SHOW_LONG_STATUS},
- {"Opened_views", (char*) offsetof(STATUS_VAR, opened_views), SHOW_LONG_STATUS},
- {"Prepared_stmt_count", (char*) &show_prepared_stmt_count, SHOW_FUNC},
- {"Rows_read", (char*) offsetof(STATUS_VAR, rows_read), SHOW_LONGLONG_STATUS},
+ {"Opened_views", (char*) offsetof(STATUS_VAR, opened_views), SHOW_LONG_STATUS},
+ {"Prepared_stmt_count", (char*) &show_prepared_stmt_count, SHOW_SIMPLE_FUNC},
{"Rows_sent", (char*) offsetof(STATUS_VAR, rows_sent), SHOW_LONGLONG_STATUS},
+ {"Rows_read", (char*) offsetof(STATUS_VAR, rows_read), SHOW_LONGLONG_STATUS},
{"Rows_tmp_read", (char*) offsetof(STATUS_VAR, rows_tmp_read), SHOW_LONGLONG_STATUS},
#ifdef HAVE_QUERY_CACHE
{"Qcache_free_blocks", (char*) &query_cache.free_memory_blocks, SHOW_LONG_NOFLUSH},
@@ -7900,54 +8766,60 @@ SHOW_VAR status_vars[]= {
{"Qcache_queries_in_cache", (char*) &query_cache.queries_in_cache, SHOW_LONG_NOFLUSH},
{"Qcache_total_blocks", (char*) &query_cache.total_blocks, SHOW_LONG_NOFLUSH},
#endif /*HAVE_QUERY_CACHE*/
- {"Queries", (char*) &show_queries, SHOW_FUNC},
+ {"Queries", (char*) &show_queries, SHOW_SIMPLE_FUNC},
{"Questions", (char*) offsetof(STATUS_VAR, questions), SHOW_LONG_STATUS},
#ifdef HAVE_REPLICATION
- {"Rpl_status", (char*) &show_rpl_status, SHOW_FUNC},
-#endif
- {"Select_full_join", (char*) offsetof(STATUS_VAR, select_full_join_count), SHOW_LONG_STATUS},
- {"Select_full_range_join", (char*) offsetof(STATUS_VAR, select_full_range_join_count), SHOW_LONG_STATUS},
- {"Select_range", (char*) offsetof(STATUS_VAR, select_range_count), SHOW_LONG_STATUS},
- {"Select_range_check", (char*) offsetof(STATUS_VAR, select_range_check_count), SHOW_LONG_STATUS},
- {"Select_scan", (char*) offsetof(STATUS_VAR, select_scan_count), SHOW_LONG_STATUS},
- {"Slave_open_temp_tables", (char*) &slave_open_temp_tables, SHOW_LONG},
+ {"Rpl_status", (char*) &show_rpl_status, SHOW_SIMPLE_FUNC},
+#endif
+ {"Select_full_join", (char*) offsetof(STATUS_VAR, select_full_join_count_), SHOW_LONG_STATUS},
+ {"Select_full_range_join", (char*) offsetof(STATUS_VAR, select_full_range_join_count_), SHOW_LONG_STATUS},
+ {"Select_range", (char*) offsetof(STATUS_VAR, select_range_count_), SHOW_LONG_STATUS},
+ {"Select_range_check", (char*) offsetof(STATUS_VAR, select_range_check_count_), SHOW_LONG_STATUS},
+ {"Select_scan", (char*) offsetof(STATUS_VAR, select_scan_count_), SHOW_LONG_STATUS},
+ {"Slave_open_temp_tables", (char*) &slave_open_temp_tables, SHOW_INT},
#ifdef HAVE_REPLICATION
- {"Slave_heartbeat_period", (char*) &show_heartbeat_period, SHOW_FUNC},
- {"Slave_received_heartbeats",(char*) &show_slave_received_heartbeats, SHOW_FUNC},
- {"Slave_retried_transactions",(char*) &show_slave_retried_trans, SHOW_FUNC},
- {"Slave_running", (char*) &show_slave_running, SHOW_FUNC},
+ {"Slaves_connected", (char*) &show_slaves_connected, SHOW_SIMPLE_FUNC },
+ {"Slaves_running", (char*) &show_slaves_running, SHOW_SIMPLE_FUNC },
+ {"Slave_connections", (char*) offsetof(STATUS_VAR, com_register_slave), SHOW_LONG_STATUS},
+ {"Slave_heartbeat_period", (char*) &show_heartbeat_period, SHOW_SIMPLE_FUNC},
+ {"Slave_received_heartbeats",(char*) &show_slave_received_heartbeats, SHOW_SIMPLE_FUNC},
+ {"Slave_retried_transactions",(char*)&slave_retried_transactions, SHOW_LONG},
+ {"Slave_running", (char*) &show_slave_running, SHOW_SIMPLE_FUNC},
#endif
{"Slow_launch_threads", (char*) &slow_launch_threads, SHOW_LONG},
{"Slow_queries", (char*) offsetof(STATUS_VAR, long_query_count), SHOW_LONG_STATUS},
- {"Sort_merge_passes", (char*) offsetof(STATUS_VAR, filesort_merge_passes), SHOW_LONG_STATUS},
- {"Sort_range", (char*) offsetof(STATUS_VAR, filesort_range_count), SHOW_LONG_STATUS},
- {"Sort_rows", (char*) offsetof(STATUS_VAR, filesort_rows), SHOW_LONG_STATUS},
- {"Sort_scan", (char*) offsetof(STATUS_VAR, filesort_scan_count), SHOW_LONG_STATUS},
+ {"Sort_merge_passes", (char*) offsetof(STATUS_VAR, filesort_merge_passes_), SHOW_LONG_STATUS},
+ {"Sort_priority_queue_sorts",(char*) offsetof(STATUS_VAR, filesort_pq_sorts_), SHOW_LONG_STATUS},
+ {"Sort_range", (char*) offsetof(STATUS_VAR, filesort_range_count_), SHOW_LONG_STATUS},
+ {"Sort_rows", (char*) offsetof(STATUS_VAR, filesort_rows_), SHOW_LONG_STATUS},
+ {"Sort_scan", (char*) offsetof(STATUS_VAR, filesort_scan_count_), SHOW_LONG_STATUS},
#ifdef HAVE_OPENSSL
#ifndef EMBEDDED_LIBRARY
- {"Ssl_accept_renegotiates", (char*) &show_ssl_ctx_sess_accept_renegotiate, SHOW_FUNC},
- {"Ssl_accepts", (char*) &show_ssl_ctx_sess_accept, SHOW_FUNC},
- {"Ssl_callback_cache_hits", (char*) &show_ssl_ctx_sess_cb_hits, SHOW_FUNC},
- {"Ssl_cipher", (char*) &show_ssl_get_cipher, SHOW_FUNC},
- {"Ssl_cipher_list", (char*) &show_ssl_get_cipher_list, SHOW_FUNC},
- {"Ssl_client_connects", (char*) &show_ssl_ctx_sess_connect, SHOW_FUNC},
- {"Ssl_connect_renegotiates", (char*) &show_ssl_ctx_sess_connect_renegotiate, SHOW_FUNC},
- {"Ssl_ctx_verify_depth", (char*) &show_ssl_ctx_get_verify_depth, SHOW_FUNC},
- {"Ssl_ctx_verify_mode", (char*) &show_ssl_ctx_get_verify_mode, SHOW_FUNC},
- {"Ssl_default_timeout", (char*) &show_ssl_get_default_timeout, SHOW_FUNC},
- {"Ssl_finished_accepts", (char*) &show_ssl_ctx_sess_accept_good, SHOW_FUNC},
- {"Ssl_finished_connects", (char*) &show_ssl_ctx_sess_connect_good, SHOW_FUNC},
- {"Ssl_session_cache_hits", (char*) &show_ssl_ctx_sess_hits, SHOW_FUNC},
- {"Ssl_session_cache_misses", (char*) &show_ssl_ctx_sess_misses, SHOW_FUNC},
- {"Ssl_session_cache_mode", (char*) &show_ssl_ctx_get_session_cache_mode, SHOW_FUNC},
- {"Ssl_session_cache_overflows", (char*) &show_ssl_ctx_sess_cache_full, SHOW_FUNC},
- {"Ssl_session_cache_size", (char*) &show_ssl_ctx_sess_get_cache_size, SHOW_FUNC},
- {"Ssl_session_cache_timeouts", (char*) &show_ssl_ctx_sess_timeouts, SHOW_FUNC},
- {"Ssl_sessions_reused", (char*) &show_ssl_session_reused, SHOW_FUNC},
- {"Ssl_used_session_cache_entries",(char*) &show_ssl_ctx_sess_number, SHOW_FUNC},
- {"Ssl_verify_depth", (char*) &show_ssl_get_verify_depth, SHOW_FUNC},
- {"Ssl_verify_mode", (char*) &show_ssl_get_verify_mode, SHOW_FUNC},
- {"Ssl_version", (char*) &show_ssl_get_version, SHOW_FUNC},
+ {"Ssl_accept_renegotiates", (char*) &show_ssl_ctx_sess_accept_renegotiate, SHOW_SIMPLE_FUNC},
+ {"Ssl_accepts", (char*) &show_ssl_ctx_sess_accept, SHOW_SIMPLE_FUNC},
+ {"Ssl_callback_cache_hits", (char*) &show_ssl_ctx_sess_cb_hits, SHOW_SIMPLE_FUNC},
+ {"Ssl_cipher", (char*) &show_ssl_get_cipher, SHOW_SIMPLE_FUNC},
+ {"Ssl_cipher_list", (char*) &show_ssl_get_cipher_list, SHOW_SIMPLE_FUNC},
+ {"Ssl_client_connects", (char*) &show_ssl_ctx_sess_connect, SHOW_SIMPLE_FUNC},
+ {"Ssl_connect_renegotiates", (char*) &show_ssl_ctx_sess_connect_renegotiate, SHOW_SIMPLE_FUNC},
+ {"Ssl_ctx_verify_depth", (char*) &show_ssl_ctx_get_verify_depth, SHOW_SIMPLE_FUNC},
+ {"Ssl_ctx_verify_mode", (char*) &show_ssl_ctx_get_verify_mode, SHOW_SIMPLE_FUNC},
+ {"Ssl_default_timeout", (char*) &show_ssl_get_default_timeout, SHOW_SIMPLE_FUNC},
+ {"Ssl_finished_accepts", (char*) &show_ssl_ctx_sess_accept_good, SHOW_SIMPLE_FUNC},
+ {"Ssl_finished_connects", (char*) &show_ssl_ctx_sess_connect_good, SHOW_SIMPLE_FUNC},
+ {"Ssl_server_not_after", (char*) &show_ssl_get_server_not_after, SHOW_SIMPLE_FUNC},
+ {"Ssl_server_not_before", (char*) &show_ssl_get_server_not_before, SHOW_SIMPLE_FUNC},
+ {"Ssl_session_cache_hits", (char*) &show_ssl_ctx_sess_hits, SHOW_SIMPLE_FUNC},
+ {"Ssl_session_cache_misses", (char*) &show_ssl_ctx_sess_misses, SHOW_SIMPLE_FUNC},
+ {"Ssl_session_cache_mode", (char*) &show_ssl_ctx_get_session_cache_mode, SHOW_SIMPLE_FUNC},
+ {"Ssl_session_cache_overflows", (char*) &show_ssl_ctx_sess_cache_full, SHOW_SIMPLE_FUNC},
+ {"Ssl_session_cache_size", (char*) &show_ssl_ctx_sess_get_cache_size, SHOW_SIMPLE_FUNC},
+ {"Ssl_session_cache_timeouts", (char*) &show_ssl_ctx_sess_timeouts, SHOW_SIMPLE_FUNC},
+ {"Ssl_sessions_reused", (char*) &show_ssl_session_reused, SHOW_SIMPLE_FUNC},
+ {"Ssl_used_session_cache_entries",(char*) &show_ssl_ctx_sess_number, SHOW_SIMPLE_FUNC},
+ {"Ssl_verify_depth", (char*) &show_ssl_get_verify_depth, SHOW_SIMPLE_FUNC},
+ {"Ssl_verify_mode", (char*) &show_ssl_get_verify_mode, SHOW_SIMPLE_FUNC},
+ {"Ssl_version", (char*) &show_ssl_get_version, SHOW_SIMPLE_FUNC},
#endif
#endif /* HAVE_OPENSSL */
{"Syncs", (char*) &my_sync_count, SHOW_LONG_NOFLUSH},
@@ -7965,16 +8837,16 @@ SHOW_VAR status_vars[]= {
{"Tc_log_page_waits", (char*) &tc_log_page_waits, SHOW_LONG},
#endif
#ifdef HAVE_POOL_OF_THREADS
- {"Threadpool_idle_threads", (char *) &show_threadpool_idle_threads, SHOW_FUNC},
+ {"Threadpool_idle_threads", (char *) &show_threadpool_idle_threads, SHOW_SIMPLE_FUNC},
{"Threadpool_threads", (char *) &tp_stats.num_worker_threads, SHOW_INT},
#endif
{"Threads_cached", (char*) &cached_thread_count, SHOW_LONG_NOFLUSH},
{"Threads_connected", (char*) &connection_count, SHOW_INT},
{"Threads_created", (char*) &thread_created, SHOW_LONG_NOFLUSH},
{"Threads_running", (char*) &thread_running, SHOW_INT},
- {"Uptime", (char*) &show_starttime, SHOW_FUNC},
+ {"Uptime", (char*) &show_starttime, SHOW_SIMPLE_FUNC},
#ifdef ENABLED_PROFILING
- {"Uptime_since_flush_status",(char*) &show_flushstatustime, SHOW_FUNC},
+ {"Uptime_since_flush_status",(char*) &show_flushstatustime, SHOW_SIMPLE_FUNC},
#endif
#ifdef WITH_WSREP
{"wsrep_connected", (char*) &wsrep_connected, SHOW_BOOL},
@@ -7984,7 +8856,7 @@ SHOW_VAR status_vars[]= {
{"wsrep_cluster_status", (char*) &wsrep_cluster_status, SHOW_CHAR_PTR},
{"wsrep_cluster_size", (char*) &wsrep_cluster_size, SHOW_LONG_NOFLUSH},
{"wsrep_local_index", (char*) &wsrep_local_index, SHOW_LONG_NOFLUSH},
- {"wsrep_local_bf_aborts", (char*) &wsrep_show_bf_aborts, SHOW_FUNC},
+ {"wsrep_local_bf_aborts", (char*) &wsrep_show_bf_aborts, SHOW_SIMPLE_FUNC},
{"wsrep_provider_name", (char*) &wsrep_provider_name, SHOW_CHAR_PTR},
{"wsrep_provider_version", (char*) &wsrep_provider_version, SHOW_CHAR_PTR},
{"wsrep_provider_vendor", (char*) &wsrep_provider_vendor, SHOW_CHAR_PTR},
@@ -7994,12 +8866,21 @@ SHOW_VAR status_vars[]= {
{NullS, NullS, SHOW_LONG}
};
-bool add_terminator(DYNAMIC_ARRAY *options)
+static bool add_terminator(DYNAMIC_ARRAY *options)
{
my_option empty_element= {0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0};
return insert_dynamic(options, (uchar *)&empty_element);
}
+static bool add_many_options(DYNAMIC_ARRAY *options, my_option *list,
+ size_t elements)
+{
+ for (my_option *opt= list; opt < list + elements; opt++)
+ if (insert_dynamic(options, opt))
+ return 1;
+ return 0;
+}
+
#ifndef EMBEDDED_LIBRARY
static void print_version(void)
{
@@ -8031,22 +8912,30 @@ static int option_cmp(my_option *a, my_option *b)
return 1;
}
}
- DBUG_ASSERT(a->name == b->name);
return 0;
}
static void print_help()
{
MEM_ROOT mem_root;
- init_alloc_root(&mem_root, 4096, 4096);
+ init_alloc_root(&mem_root, 4096, 4096, MYF(0));
pop_dynamic(&all_options);
+ add_many_options(&all_options, pfs_early_options,
+ array_elements(pfs_early_options));
sys_var_add_options(&all_options, sys_var::PARSE_EARLY);
add_plugin_options(&all_options, &mem_root);
sort_dynamic(&all_options, (qsort_cmp) option_cmp);
+ sort_dynamic(&all_options, (qsort_cmp) option_cmp);
add_terminator(&all_options);
my_print_help((my_option*) all_options.buffer);
+
+ /* Add variables that can be shown but not changed, like version numbers */
+ pop_dynamic(&all_options);
+ sys_var_add_options(&all_options, sys_var::SHOW_VALUE_IN_HELP);
+ sort_dynamic(&all_options, (qsort_cmp) option_cmp);
+ add_terminator(&all_options);
my_print_variables((my_option*) all_options.buffer);
free_root(&mem_root, MYF(0));
@@ -8070,16 +8959,15 @@ static void usage(void)
else
{
#ifdef __WIN__
- puts("NT and Win32 specific options:\n\
- --install Install the default service (NT).\n\
- --install-manual Install the default service started manually (NT).\n\
- --install service_name Install an optional service (NT).\n\
- --install-manual service_name Install an optional service started manually (NT).\n\
- --remove Remove the default service from the service list (NT).\n\
- --remove service_name Remove the service_name from the service list (NT).\n\
- --enable-named-pipe Only to be used for the default server (NT).\n\
- --standalone Dummy option to start as a standalone server (NT).\
-");
+ puts("NT and Win32 specific options:\n"
+ " --install Install the default service (NT).\n"
+ " --install-manual Install the default service started manually (NT).\n"
+ " --install service_name Install an optional service (NT).\n"
+ " --install-manual service_name Install an optional service started manually (NT).\n"
+ " --remove Remove the default service from the service list (NT).\n"
+ " --remove service_name Remove the service_name from the service list (NT).\n"
+ " --enable-named-pipe Only to be used for the default server (NT).\n"
+ " --standalone Dummy option to start as a standalone server (NT).");
puts("");
#endif
print_defaults(MYSQL_CONFIG_NAME,load_default_groups);
@@ -8091,14 +8979,12 @@ static void usage(void)
if (! plugins_are_initialized)
{
- puts("\n\
-Plugins have parameters that are not reflected in this list\n\
-because execution stopped before plugins were initialized.");
+ puts("\nPlugins have parameters that are not reflected in this list"
+ "\nbecause execution stopped before plugins were initialized.");
}
- puts("\n\
-To see what values a running MySQL server is using, type\n\
-'mysqladmin variables' instead of 'mysqld --verbose --help'.");
+ puts("\nTo see what values a running MySQL server is using, type"
+ "\n'mysqladmin variables' instead of 'mysqld --verbose --help'.");
}
DBUG_VOID_RETURN;
}
@@ -8141,7 +9027,8 @@ static int mysql_init_variables(void)
cleanup_done= 0;
server_id_supplied= 0;
test_flags= select_errors= dropping_tables= ha_open_options=0;
- thread_count= thread_running= kill_cached_threads= wake_thread=0;
+ thread_count= thread_running= kill_cached_threads= wake_thread= 0;
+ service_thread_count= 0;
slave_open_temp_tables= 0;
cached_thread_count= 0;
opt_endinfo= using_udf_functions= 0;
@@ -8175,17 +9062,20 @@ static int mysql_init_variables(void)
character_set_filesystem= &my_charset_bin;
opt_specialflag= SPECIAL_ENGLISH;
- unix_sock= base_ip_sock= extra_ip_sock= INVALID_SOCKET;
+ unix_sock= base_ip_sock= extra_ip_sock= MYSQL_INVALID_SOCKET;
mysql_home_ptr= mysql_home;
pidfile_name_ptr= pidfile_name;
log_error_file_ptr= log_error_file;
protocol_version= PROTOCOL_VERSION;
what_to_log= ~ (1L << (uint) COM_TIME);
- refresh_version= 2L; /* Increments on each reload. 0 and 1 are reserved */
+ denied_connections= 0;
executed_events= 0;
global_query_id= thread_id= 1L;
my_atomic_rwlock_init(&global_query_id_lock);
my_atomic_rwlock_init(&thread_running_lock);
+ my_atomic_rwlock_init(&thread_count_lock);
+ my_atomic_rwlock_init(&statistics_lock);
+ my_atomic_rwlock_init(&slave_executed_entries_lock);
strnmov(server_version, MYSQL_SERVER_VERSION, sizeof(server_version)-1);
threads.empty();
thread_cache.empty();
@@ -8209,6 +9099,7 @@ static int mysql_init_variables(void)
relay_log_info_file= (char*) "relay-log.info";
report_user= report_password = report_host= 0; /* TO BE DELETED */
opt_relay_logname= opt_relaylog_index_name= 0;
+ slave_retried_transactions= 0;
/* Variables in libraries */
charsets_dir= 0;
@@ -8233,8 +9124,13 @@ static int mysql_init_variables(void)
#if defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY)
have_ssl=SHOW_OPTION_YES;
+#if HAVE_YASSL
+ have_openssl= SHOW_OPTION_NO;
+#else
+ have_openssl= SHOW_OPTION_YES;
+#endif
#else
- have_ssl=SHOW_OPTION_NO;
+ have_openssl= have_ssl= SHOW_OPTION_NO;
#endif
#ifdef HAVE_BROKEN_REALPATH
have_symlink=SHOW_OPTION_NO;
@@ -8344,6 +9240,14 @@ mysqld_get_one_option(int optid,
"for compatiblity with old my.cnf files.",
opt->name);
break;
+ case OPT_MYSQL_COMPATIBILITY:
+ sql_print_warning("'%s' is MySQL 5.6 compatible option. Not used or needed "
+ "in MariaDB.", opt->name);
+ break;
+ case OPT_MYSQL_TO_BE_IMPLEMENTED:
+ sql_print_warning("'%s' is MySQL 5.6 compatible option. To be implemented "
+ "in later versions.", opt->name);
+ break;
case 'a':
global_system_variables.sql_mode= MODE_ANSI;
global_system_variables.tx_isolation= ISO_SERIALIZABLE;
@@ -8355,10 +9259,6 @@ mysqld_get_one_option(int optid,
if (default_collation_name == compiled_default_collation_name)
default_collation_name= 0;
break;
- case 'l':
- WARN_DEPRECATED(NULL, 7, 0, "--log", "'--general-log'/'--general-log-file'");
- opt_log=1;
- break;
case 'h':
strmake_buf(mysql_real_data_home, argument);
/* Correct pointer set by my_getopt (for embedded library) */
@@ -8402,7 +9302,7 @@ mysqld_get_one_option(int optid,
opt_myisam_log=1;
break;
case (int) OPT_BIN_LOG:
- opt_bin_log= test(argument != disabled_my_option);
+ opt_bin_log= MY_TEST(argument != disabled_my_option);
opt_bin_log_used= 1;
break;
case (int) OPT_LOG_BASENAME:
@@ -8436,12 +9336,12 @@ mysqld_get_one_option(int optid,
#ifdef HAVE_REPLICATION
case (int)OPT_REPLICATE_IGNORE_DB:
{
- rpl_filter->add_ignore_db(argument);
+ cur_rpl_filter->add_ignore_db(argument);
break;
}
case (int)OPT_REPLICATE_DO_DB:
{
- rpl_filter->add_do_db(argument);
+ cur_rpl_filter->add_do_db(argument);
break;
}
case (int)OPT_REPLICATE_REWRITE_DB:
@@ -8472,7 +9372,7 @@ mysqld_get_one_option(int optid,
return 1;
}
- rpl_filter->add_db_rewrite(key, val);
+ cur_rpl_filter->add_db_rewrite(key, val);
break;
}
@@ -8488,7 +9388,7 @@ mysqld_get_one_option(int optid,
}
case (int)OPT_REPLICATE_DO_TABLE:
{
- if (rpl_filter->add_do_table(argument))
+ if (cur_rpl_filter->add_do_table(argument))
{
sql_print_error("Could not add do table rule '%s'!\n", argument);
return 1;
@@ -8497,7 +9397,7 @@ mysqld_get_one_option(int optid,
}
case (int)OPT_REPLICATE_WILD_DO_TABLE:
{
- if (rpl_filter->add_wild_do_table(argument))
+ if (cur_rpl_filter->add_wild_do_table(argument))
{
sql_print_error("Could not add do table rule '%s'!\n", argument);
return 1;
@@ -8506,7 +9406,7 @@ mysqld_get_one_option(int optid,
}
case (int)OPT_REPLICATE_WILD_IGNORE_TABLE:
{
- if (rpl_filter->add_wild_ignore_table(argument))
+ if (cur_rpl_filter->add_wild_ignore_table(argument))
{
sql_print_error("Could not add ignore table rule '%s'!\n", argument);
return 1;
@@ -8515,7 +9415,7 @@ mysqld_get_one_option(int optid,
}
case (int)OPT_REPLICATE_IGNORE_TABLE:
{
- if (rpl_filter->add_ignore_table(argument))
+ if (cur_rpl_filter->add_ignore_table(argument))
{
sql_print_error("Could not add ignore table rule '%s'!\n", argument);
return 1;
@@ -8523,10 +9423,6 @@ mysqld_get_one_option(int optid,
break;
}
#endif /* HAVE_REPLICATION */
- case (int) OPT_SLOW_QUERY_LOG:
- WARN_DEPRECATED(NULL, 7, 0, "--log-slow-queries", "'--slow-query-log'/'--slow-query-log-file'");
- opt_slow_log= 1;
- break;
case (int) OPT_SAFE:
opt_specialflag|= SPECIAL_SAFE_MODE | SPECIAL_NO_NEW_FUNC;
delay_key_write_options= (uint) DELAY_KEY_WRITE_NONE;
@@ -8538,12 +9434,6 @@ mysqld_get_one_option(int optid,
sql_print_warning("The syntax '--safe-mode' is deprecated and will be "
"removed in a future release.");
break;
- case (int) OPT_SKIP_PRIOR:
- opt_specialflag|= SPECIAL_NO_PRIOR;
- sql_print_warning("The --skip-thread-priority startup option is deprecated "
- "and will be removed in MySQL 7.0. This option has no effect "
- "as the implied behavior is already the default.");
- break;
case (int) OPT_SKIP_HOST_CACHE:
opt_specialflag|= SPECIAL_NO_HOST_CACHE;
break;
@@ -8563,9 +9453,7 @@ mysqld_get_one_option(int optid,
break;
case OPT_SERVER_ID:
server_id_supplied = 1;
- break;
- case OPT_ONE_THREAD:
- thread_handling= SCHEDULER_NO_THREADS;
+ ::server_id= global_system_variables.server_id;
break;
case OPT_LOWER_CASE_TABLE_NAMES:
lower_case_table_names_used= 1;
@@ -8594,18 +9482,6 @@ mysqld_get_one_option(int optid,
}
break;
#endif /* defined(ENABLED_DEBUG_SYNC) */
- case OPT_ENGINE_CONDITION_PUSHDOWN:
- /*
- The last of --engine-condition-pushdown and --optimizer_switch on
- command line wins (see get_options().
- */
- if (global_system_variables.engine_condition_pushdown)
- global_system_variables.optimizer_switch|=
- OPTIMIZER_SWITCH_ENGINE_CONDITION_PUSHDOWN;
- else
- global_system_variables.optimizer_switch&=
- ~OPTIMIZER_SWITCH_ENGINE_CONDITION_PUSHDOWN;
- break;
case OPT_LOG_ERROR:
/*
"No --log-error" == "write errors to stderr",
@@ -8614,11 +9490,6 @@ mysqld_get_one_option(int optid,
if (argument == NULL) /* no argument */
log_error_file_ptr= const_cast<char*>("");
break;
- case OPT_MAX_LONG_DATA_SIZE:
- max_long_data_size_used= true;
- break;
-
-
case OPT_IGNORE_DB_DIRECTORY:
if (*argument == 0)
ignore_db_dirs_reset();
@@ -8633,6 +9504,85 @@ mysqld_get_one_option(int optid,
}
}
break;
+
+ case OPT_PLUGIN_LOAD:
+ free_list(opt_plugin_load_list_ptr);
+ /* fall through */
+ case OPT_PLUGIN_LOAD_ADD:
+ opt_plugin_load_list_ptr->push_back(new i_string(argument));
+ break;
+ case OPT_MAX_LONG_DATA_SIZE:
+ max_long_data_size_used= true;
+ break;
+ case OPT_PFS_INSTRUMENT:
+#ifdef WITH_PERFSCHEMA_STORAGE_ENGINE
+#ifndef EMBEDDED_LIBRARY
+ /* Parse instrument name and value from argument string */
+ char* name = argument,*p, *val;
+
+ /* Assignment required */
+ if (!(p= strchr(argument, '=')))
+ {
+ my_getopt_error_reporter(WARNING_LEVEL,
+ "Missing value for performance_schema_instrument "
+ "'%s'", argument);
+ return 0;
+ }
+
+ /* Option value */
+ val= p + 1;
+ if (!*val)
+ {
+ my_getopt_error_reporter(WARNING_LEVEL,
+ "Missing value for performance_schema_instrument "
+ "'%s'", argument);
+ return 0;
+ }
+
+ /* Trim leading spaces from instrument name */
+ while (*name && my_isspace(mysqld_charset, *name))
+ name++;
+
+ /* Trim trailing spaces and slashes from instrument name */
+ while (p > argument && (my_isspace(mysqld_charset, p[-1]) || p[-1] == '/'))
+ p--;
+ *p= 0;
+
+ if (!*name)
+ {
+ my_getopt_error_reporter(WARNING_LEVEL,
+ "Invalid instrument name for "
+ "performance_schema_instrument '%s'", argument);
+ return 0;
+ }
+
+ /* Trim leading spaces from option value */
+ while (*val && my_isspace(mysqld_charset, *val))
+ val++;
+
+ /* Trim trailing spaces from option value */
+ if ((p= my_strchr(mysqld_charset, val, val+strlen(val), ' ')) != NULL)
+ *p= 0;
+
+ if (!*val)
+ {
+ my_getopt_error_reporter(WARNING_LEVEL,
+ "Invalid value for performance_schema_instrument "
+ "'%s'", argument);
+ return 0;
+ }
+
+ /* Add instrument name and value to array of configuration options */
+ if (add_pfs_instr_to_array(name, val))
+ {
+ my_getopt_error_reporter(WARNING_LEVEL,
+ "Invalid value for performance_schema_instrument "
+ "'%s'", argument);
+ return 0;
+ }
+#endif /* EMBEDDED_LIBRARY */
+#endif
+ break;
}
return 0;
}
@@ -8643,7 +9593,7 @@ mysqld_get_one_option(int optid,
C_MODE_START
static void*
-mysql_getopt_value(const char *keyname, uint key_length,
+mysql_getopt_value(const char *name, uint length,
const struct my_option *option, int *error)
{
if (error)
@@ -8654,9 +9604,10 @@ mysql_getopt_value(const char *keyname, uint key_length,
case OPT_KEY_CACHE_DIVISION_LIMIT:
case OPT_KEY_CACHE_AGE_THRESHOLD:
case OPT_KEY_CACHE_PARTITIONS:
+ case OPT_KEY_CACHE_CHANGED_BLOCKS_HASH_SIZE:
{
KEY_CACHE *key_cache;
- if (!(key_cache= get_or_create_key_cache(keyname, key_length)))
+ if (!(key_cache= get_or_create_key_cache(name, length)))
{
if (error)
*error= EXIT_OUT_OF_MEMORY;
@@ -8673,8 +9624,26 @@ mysql_getopt_value(const char *keyname, uint key_length,
return &key_cache->param_age_threshold;
case OPT_KEY_CACHE_PARTITIONS:
return (uchar**) &key_cache->param_partitions;
+ case OPT_KEY_CACHE_CHANGED_BLOCKS_HASH_SIZE:
+ return (uchar**) &key_cache->changed_blocks_hash_size;
}
}
+ case OPT_REPLICATE_DO_DB:
+ case OPT_REPLICATE_DO_TABLE:
+ case OPT_REPLICATE_IGNORE_DB:
+ case OPT_REPLICATE_IGNORE_TABLE:
+ case OPT_REPLICATE_WILD_DO_TABLE:
+ case OPT_REPLICATE_WILD_IGNORE_TABLE:
+ case OPT_REPLICATE_REWRITE_DB:
+ {
+ /* Store current filter for mysqld_get_one_option() */
+ if (!(cur_rpl_filter= get_or_create_rpl_filter(name, length)))
+ {
+ if (error)
+ *error= EXIT_OUT_OF_MEMORY;
+ }
+ return 0;
+ }
}
return option->value;
}
@@ -8715,11 +9684,8 @@ static int get_options(int *argc_ptr, char ***argv_ptr)
/* prepare all_options array */
my_init_dynamic_array(&all_options, sizeof(my_option),
array_elements(my_long_options),
- array_elements(my_long_options)/4);
- for (my_option *opt= my_long_options;
- opt < my_long_options + array_elements(my_long_options) - 1;
- opt++)
- insert_dynamic(&all_options, (uchar*) opt);
+ array_elements(my_long_options)/4, MYF(0));
+ add_many_options(&all_options, my_long_options, array_elements(my_long_options));
sys_var_add_options(&all_options, 0);
add_terminator(&all_options);
@@ -8839,7 +9805,7 @@ static int get_options(int *argc_ptr, char ***argv_ptr)
global_system_variables.sql_mode=
expand_sql_mode(global_system_variables.sql_mode);
-#if defined(HAVE_BROKEN_REALPATH)
+#if !defined(HAVE_REALPATH) || defined(HAVE_BROKEN_REALPATH)
my_use_symdir=0;
my_disable_symlinks=1;
have_symlink=SHOW_OPTION_NO;
@@ -8875,7 +9841,7 @@ static int get_options(int *argc_ptr, char ***argv_ptr)
Set some global variables from the global_system_variables
In most cases the global variables will not be used
*/
- my_disable_locking= myisam_single_user= test(opt_external_locking == 0);
+ my_disable_locking= myisam_single_user= MY_TEST(opt_external_locking == 0);
my_default_record_cache_size=global_system_variables.read_buff_size;
/*
@@ -8932,8 +9898,8 @@ static int get_options(int *argc_ptr, char ***argv_ptr)
#endif
global_system_variables.engine_condition_pushdown=
- test(global_system_variables.optimizer_switch &
- OPTIMIZER_SWITCH_ENGINE_CONDITION_PUSHDOWN);
+ MY_TEST(global_system_variables.optimizer_switch &
+ OPTIMIZER_SWITCH_ENGINE_CONDITION_PUSHDOWN);
opt_readonly= read_only;
@@ -8944,8 +9910,37 @@ static int get_options(int *argc_ptr, char ***argv_ptr)
if (!max_long_data_size_used)
max_long_data_size= global_system_variables.max_allowed_packet;
- /* Rember if max_user_connections was 0 at startup */
+ /* Remember if max_user_connections was 0 at startup */
max_user_connections_checking= global_system_variables.max_user_connections != 0;
+
+ {
+ sys_var *max_relay_log_size_var, *max_binlog_size_var;
+ /* If max_relay_log_size is 0, then set it to max_binlog_size */
+ if (!global_system_variables.max_relay_log_size)
+ global_system_variables.max_relay_log_size= max_binlog_size;
+
+ /*
+ Fix so that DEFAULT and limit checking works with max_relay_log_size
+ (Yes, this is a hack, but it's required as the definition of
+ max_relay_log_size allows it to be set to 0).
+ */
+ max_relay_log_size_var= intern_find_sys_var("max_relay_log_size", 0);
+ max_binlog_size_var= intern_find_sys_var("max_binlog_size", 0);
+ if (max_binlog_size_var && max_relay_log_size_var)
+ {
+ max_relay_log_size_var->option.min_value=
+ max_binlog_size_var->option.min_value;
+ max_relay_log_size_var->option.def_value=
+ max_binlog_size_var->option.def_value;
+ }
+ }
+
+ /* Ensure that some variables are not set higher than needed */
+ if (back_log > max_connections)
+ back_log= max_connections;
+ if (thread_cache_size > max_connections)
+ thread_cache_size= max_connections;
+
return 0;
}
@@ -9197,8 +10192,6 @@ static int test_if_case_insensitive(const char *dir_name)
DBUG_PRINT("exit", ("result: %d", result));
DBUG_RETURN(result);
}
-
-
#ifndef EMBEDDED_LIBRARY
/**
@@ -9256,7 +10249,7 @@ void refresh_status(THD *thd)
add_to_status(&global_status_var, &thd->status_var);
/* Reset thread's status variables */
- bzero((uchar*) &thd->status_var, sizeof(thd->status_var));
+ thd->set_status_var_init();
bzero((uchar*) &thd->org_status_var, sizeof(thd->org_status_var));
thd->start_bytes_received= 0;
@@ -9273,32 +10266,394 @@ void refresh_status(THD *thd)
/*
Set max_used_connections to the number of currently open
- connections. Lock LOCK_thread_count out of LOCK_status to avoid
- deadlocks. Status reset becomes not atomic, but status data is
- not exact anyway.
+ connections. This is not perfect, but status data is not exact anyway.
*/
- mysql_mutex_lock(&LOCK_thread_count);
- max_used_connections= thread_count-delayed_insert_threads;
- mysql_mutex_unlock(&LOCK_thread_count);
+ max_used_connections= connection_count + extra_connection_count;
}
+#ifdef HAVE_PSI_INTERFACE
+static PSI_file_info all_server_files[]=
+{
+#ifdef HAVE_MMAP
+ { &key_file_map, "map", 0},
+#endif /* HAVE_MMAP */
+ { &key_file_binlog, "binlog", 0},
+ { &key_file_binlog_index, "binlog_index", 0},
+ { &key_file_relaylog, "relaylog", 0},
+ { &key_file_relaylog_index, "relaylog_index", 0},
+ { &key_file_casetest, "casetest", 0},
+ { &key_file_dbopt, "dbopt", 0},
+ { &key_file_des_key_file, "des_key_file", 0},
+ { &key_file_ERRMSG, "ERRMSG", 0},
+ { &key_select_to_file, "select_to_file", 0},
+ { &key_file_fileparser, "file_parser", 0},
+ { &key_file_frm, "FRM", 0},
+ { &key_file_global_ddl_log, "global_ddl_log", 0},
+ { &key_file_load, "load", 0},
+ { &key_file_loadfile, "LOAD_FILE", 0},
+ { &key_file_log_event_data, "log_event_data", 0},
+ { &key_file_log_event_info, "log_event_info", 0},
+ { &key_file_master_info, "master_info", 0},
+ { &key_file_misc, "misc", 0},
+ { &key_file_partition, "partition", 0},
+ { &key_file_pid, "pid", 0},
+ { &key_file_query_log, "query_log", 0},
+ { &key_file_relay_log_info, "relay_log_info", 0},
+ { &key_file_send_file, "send_file", 0},
+ { &key_file_slow_log, "slow_log", 0},
+ { &key_file_tclog, "tclog", 0},
+ { &key_file_trg, "trigger_name", 0},
+ { &key_file_trn, "trigger", 0},
+ { &key_file_init, "init", 0},
+ { &key_file_binlog_state, "binlog_state", 0}
+};
+#endif /* HAVE_PSI_INTERFACE */
+
+PSI_stage_info stage_after_create= { 0, "After create", 0};
+PSI_stage_info stage_after_opening_tables= { 0, "After opening tables", 0};
+PSI_stage_info stage_after_table_lock= { 0, "After table lock", 0};
+PSI_stage_info stage_allocating_local_table= { 0, "allocating local table", 0};
+PSI_stage_info stage_alter_inplace_prepare= { 0, "preparing for alter table", 0};
+PSI_stage_info stage_alter_inplace= { 0, "altering table", 0};
+PSI_stage_info stage_alter_inplace_commit= { 0, "committing alter table to storage engine", 0};
+PSI_stage_info stage_changing_master= { 0, "Changing master", 0};
+PSI_stage_info stage_checking_master_version= { 0, "Checking master version", 0};
+PSI_stage_info stage_checking_permissions= { 0, "checking permissions", 0};
+PSI_stage_info stage_checking_privileges_on_cached_query= { 0, "checking privileges on cached query", 0};
+PSI_stage_info stage_checking_query_cache_for_query= { 0, "checking query cache for query", 0};
+PSI_stage_info stage_cleaning_up= { 0, "cleaning up", 0};
+PSI_stage_info stage_closing_tables= { 0, "closing tables", 0};
+PSI_stage_info stage_connecting_to_master= { 0, "Connecting to master", 0};
+PSI_stage_info stage_converting_heap_to_myisam= { 0, "converting HEAP to " TMP_ENGINE_NAME, 0};
+PSI_stage_info stage_copying_to_group_table= { 0, "Copying to group table", 0};
+PSI_stage_info stage_copying_to_tmp_table= { 0, "Copying to tmp table", 0};
+PSI_stage_info stage_copy_to_tmp_table= { 0, "copy to tmp table", 0};
+PSI_stage_info stage_creating_delayed_handler= { 0, "Creating delayed handler", 0};
+PSI_stage_info stage_creating_sort_index= { 0, "Creating sort index", 0};
+PSI_stage_info stage_creating_table= { 0, "creating table", 0};
+PSI_stage_info stage_creating_tmp_table= { 0, "Creating tmp table", 0};
+PSI_stage_info stage_deleting_from_main_table= { 0, "deleting from main table", 0};
+PSI_stage_info stage_deleting_from_reference_tables= { 0, "deleting from reference tables", 0};
+PSI_stage_info stage_discard_or_import_tablespace= { 0, "discard_or_import_tablespace", 0};
+PSI_stage_info stage_enabling_keys= { 0, "enabling keys", 0};
+PSI_stage_info stage_end= { 0, "end", 0};
+PSI_stage_info stage_executing= { 0, "executing", 0};
+PSI_stage_info stage_execution_of_init_command= { 0, "Execution of init_command", 0};
+PSI_stage_info stage_explaining= { 0, "explaining", 0};
+PSI_stage_info stage_finding_key_cache= { 0, "Finding key cache", 0};
+PSI_stage_info stage_finished_reading_one_binlog_switching_to_next_binlog= { 0, "Finished reading one binlog; switching to next binlog", 0};
+PSI_stage_info stage_flushing_relay_log_and_master_info_repository= { 0, "Flushing relay log and master info repository.", 0};
+PSI_stage_info stage_flushing_relay_log_info_file= { 0, "Flushing relay-log info file.", 0};
+PSI_stage_info stage_freeing_items= { 0, "freeing items", 0};
+PSI_stage_info stage_fulltext_initialization= { 0, "FULLTEXT initialization", 0};
+PSI_stage_info stage_got_handler_lock= { 0, "got handler lock", 0};
+PSI_stage_info stage_got_old_table= { 0, "got old table", 0};
+PSI_stage_info stage_init= { 0, "init", 0};
+PSI_stage_info stage_insert= { 0, "insert", 0};
+PSI_stage_info stage_invalidating_query_cache_entries_table= { 0, "invalidating query cache entries (table)", 0};
+PSI_stage_info stage_invalidating_query_cache_entries_table_list= { 0, "invalidating query cache entries (table list)", 0};
+PSI_stage_info stage_killing_slave= { 0, "Killing slave", 0};
+PSI_stage_info stage_logging_slow_query= { 0, "logging slow query", 0};
+PSI_stage_info stage_making_temp_file_append_before_load_data= { 0, "Making temporary file (append) before replaying LOAD DATA INFILE.", 0};
+PSI_stage_info stage_making_temp_file_create_before_load_data= { 0, "Making temporary file (create) before replaying LOAD DATA INFILE.", 0};
+PSI_stage_info stage_manage_keys= { 0, "manage keys", 0};
+PSI_stage_info stage_master_has_sent_all_binlog_to_slave= { 0, "Master has sent all binlog to slave; waiting for binlog to be updated", 0};
+PSI_stage_info stage_opening_tables= { 0, "Opening tables", 0};
+PSI_stage_info stage_optimizing= { 0, "optimizing", 0};
+PSI_stage_info stage_preparing= { 0, "preparing", 0};
+PSI_stage_info stage_purging_old_relay_logs= { 0, "Purging old relay logs", 0};
+PSI_stage_info stage_query_end= { 0, "query end", 0};
+PSI_stage_info stage_queueing_master_event_to_the_relay_log= { 0, "Queueing master event to the relay log", 0};
+PSI_stage_info stage_reading_event_from_the_relay_log= { 0, "Reading event from the relay log", 0};
+PSI_stage_info stage_recreating_table= { 0, "recreating table", 0};
+PSI_stage_info stage_registering_slave_on_master= { 0, "Registering slave on master", 0};
+PSI_stage_info stage_removing_duplicates= { 0, "Removing duplicates", 0};
+PSI_stage_info stage_removing_tmp_table= { 0, "removing tmp table", 0};
+PSI_stage_info stage_rename= { 0, "rename", 0};
+PSI_stage_info stage_rename_result_table= { 0, "rename result table", 0};
+PSI_stage_info stage_requesting_binlog_dump= { 0, "Requesting binlog dump", 0};
+PSI_stage_info stage_reschedule= { 0, "reschedule", 0};
+PSI_stage_info stage_searching_rows_for_update= { 0, "Searching rows for update", 0};
+PSI_stage_info stage_sending_binlog_event_to_slave= { 0, "Sending binlog event to slave", 0};
+PSI_stage_info stage_sending_cached_result_to_client= { 0, "sending cached result to client", 0};
+PSI_stage_info stage_sending_data= { 0, "Sending data", 0};
+PSI_stage_info stage_setup= { 0, "setup", 0};
+PSI_stage_info stage_show_explain= { 0, "show explain", 0};
+PSI_stage_info stage_slave_has_read_all_relay_log= { 0, "Slave has read all relay log; waiting for the slave I/O thread to update it", 0};
+PSI_stage_info stage_sorting= { 0, "Sorting", 0};
+PSI_stage_info stage_sorting_for_group= { 0, "Sorting for group", 0};
+PSI_stage_info stage_sorting_for_order= { 0, "Sorting for order", 0};
+PSI_stage_info stage_sorting_result= { 0, "Sorting result", 0};
+PSI_stage_info stage_statistics= { 0, "statistics", 0};
+PSI_stage_info stage_sql_thd_waiting_until_delay= { 0, "Waiting until MASTER_DELAY seconds after master executed event", 0 };
+PSI_stage_info stage_storing_result_in_query_cache= { 0, "storing result in query cache", 0};
+PSI_stage_info stage_storing_row_into_queue= { 0, "storing row into queue", 0};
+PSI_stage_info stage_system_lock= { 0, "System lock", 0};
+PSI_stage_info stage_table_lock= { 0, "Table lock", 0};
+PSI_stage_info stage_filling_schema_table= { 0, "Filling schema table", 0};
+PSI_stage_info stage_update= { 0, "update", 0};
+PSI_stage_info stage_updating= { 0, "updating", 0};
+PSI_stage_info stage_updating_main_table= { 0, "updating main table", 0};
+PSI_stage_info stage_updating_reference_tables= { 0, "updating reference tables", 0};
+PSI_stage_info stage_upgrading_lock= { 0, "upgrading lock", 0};
+PSI_stage_info stage_user_lock= { 0, "User lock", 0};
+PSI_stage_info stage_user_sleep= { 0, "User sleep", 0};
+PSI_stage_info stage_verifying_table= { 0, "verifying table", 0};
+PSI_stage_info stage_waiting_for_delay_list= { 0, "waiting for delay_list", 0};
+PSI_stage_info stage_waiting_for_gtid_to_be_written_to_binary_log= { 0, "waiting for GTID to be written to binary log", 0};
+PSI_stage_info stage_waiting_for_handler_insert= { 0, "waiting for handler insert", 0};
+PSI_stage_info stage_waiting_for_handler_lock= { 0, "waiting for handler lock", 0};
+PSI_stage_info stage_waiting_for_handler_open= { 0, "waiting for handler open", 0};
+PSI_stage_info stage_waiting_for_insert= { 0, "Waiting for INSERT", 0};
+PSI_stage_info stage_waiting_for_master_to_send_event= { 0, "Waiting for master to send event", 0};
+PSI_stage_info stage_waiting_for_master_update= { 0, "Waiting for master update", 0};
+PSI_stage_info stage_waiting_for_relay_log_space= { 0, "Waiting for the slave SQL thread to free enough relay log space", 0};
+PSI_stage_info stage_waiting_for_slave_mutex_on_exit= { 0, "Waiting for slave mutex on exit", 0};
+PSI_stage_info stage_waiting_for_slave_thread_to_start= { 0, "Waiting for slave thread to start", 0};
+PSI_stage_info stage_waiting_for_table_flush= { 0, "Waiting for table flush", 0};
+PSI_stage_info stage_waiting_for_query_cache_lock= { 0, "Waiting for query cache lock", 0};
+PSI_stage_info stage_waiting_for_the_next_event_in_relay_log= { 0, "Waiting for the next event in relay log", 0};
+PSI_stage_info stage_waiting_for_the_slave_thread_to_advance_position= { 0, "Waiting for the slave SQL thread to advance position", 0};
+PSI_stage_info stage_waiting_to_finalize_termination= { 0, "Waiting to finalize termination", 0};
+PSI_stage_info stage_waiting_to_get_readlock= { 0, "Waiting to get readlock", 0};
+PSI_stage_info stage_slave_waiting_workers_to_exit= { 0, "Waiting for workers to exit", 0};
+PSI_stage_info stage_slave_waiting_worker_to_release_partition= { 0, "Waiting for Slave Worker to release partition", 0};
+PSI_stage_info stage_slave_waiting_worker_to_free_events= { 0, "Waiting for Slave Workers to free pending events", 0};
+PSI_stage_info stage_slave_waiting_worker_queue= { 0, "Waiting for Slave Worker queue", 0};
+PSI_stage_info stage_slave_waiting_event_from_coordinator= { 0, "Waiting for an event from Coordinator", 0};
+PSI_stage_info stage_binlog_waiting_background_tasks= { 0, "Waiting for background binlog tasks", 0};
+PSI_stage_info stage_binlog_processing_checkpoint_notify= { 0, "Processing binlog checkpoint notification", 0};
+PSI_stage_info stage_binlog_stopping_background_thread= { 0, "Stopping binlog background thread", 0};
+PSI_stage_info stage_waiting_for_work_from_sql_thread= { 0, "Waiting for work from SQL thread", 0};
+PSI_stage_info stage_waiting_for_prior_transaction_to_commit= { 0, "Waiting for prior transaction to commit", 0};
+PSI_stage_info stage_waiting_for_prior_transaction_to_start_commit= { 0, "Waiting for prior transaction to start commit before starting next transaction", 0};
+PSI_stage_info stage_waiting_for_room_in_worker_thread= { 0, "Waiting for room in worker thread event queue", 0};
+PSI_stage_info stage_waiting_for_workers_idle= { 0, "Waiting for worker threads to be idle", 0};
+PSI_stage_info stage_waiting_for_ftwrl= { 0, "Waiting due to global read lock", 0};
+PSI_stage_info stage_waiting_for_ftwrl_threads_to_pause= { 0, "Waiting for worker threads to pause for global read lock", 0};
+PSI_stage_info stage_waiting_for_rpl_thread_pool= { 0, "Waiting while replication worker thread pool is busy", 0};
+PSI_stage_info stage_master_gtid_wait_primary= { 0, "Waiting in MASTER_GTID_WAIT() (primary waiter)", 0};
+PSI_stage_info stage_master_gtid_wait= { 0, "Waiting in MASTER_GTID_WAIT()", 0};
+PSI_stage_info stage_gtid_wait_other_connection= { 0, "Waiting for other master connection to process GTID received on multiple master connections", 0};
+
+#ifdef HAVE_PSI_INTERFACE
+
+PSI_stage_info *all_server_stages[]=
+{
+ & stage_after_create,
+ & stage_after_opening_tables,
+ & stage_after_table_lock,
+ & stage_allocating_local_table,
+ & stage_alter_inplace,
+ & stage_alter_inplace_commit,
+ & stage_alter_inplace_prepare,
+ & stage_binlog_processing_checkpoint_notify,
+ & stage_binlog_stopping_background_thread,
+ & stage_binlog_waiting_background_tasks,
+ & stage_changing_master,
+ & stage_checking_master_version,
+ & stage_checking_permissions,
+ & stage_checking_privileges_on_cached_query,
+ & stage_checking_query_cache_for_query,
+ & stage_cleaning_up,
+ & stage_closing_tables,
+ & stage_connecting_to_master,
+ & stage_converting_heap_to_myisam,
+ & stage_copy_to_tmp_table,
+ & stage_copying_to_group_table,
+ & stage_copying_to_tmp_table,
+ & stage_creating_delayed_handler,
+ & stage_creating_sort_index,
+ & stage_creating_table,
+ & stage_creating_tmp_table,
+ & stage_deleting_from_main_table,
+ & stage_deleting_from_reference_tables,
+ & stage_discard_or_import_tablespace,
+ & stage_enabling_keys,
+ & stage_end,
+ & stage_executing,
+ & stage_execution_of_init_command,
+ & stage_explaining,
+ & stage_finding_key_cache,
+ & stage_finished_reading_one_binlog_switching_to_next_binlog,
+ & stage_flushing_relay_log_and_master_info_repository,
+ & stage_flushing_relay_log_info_file,
+ & stage_freeing_items,
+ & stage_fulltext_initialization,
+ & stage_got_handler_lock,
+ & stage_got_old_table,
+ & stage_init,
+ & stage_insert,
+ & stage_invalidating_query_cache_entries_table,
+ & stage_invalidating_query_cache_entries_table_list,
+ & stage_killing_slave,
+ & stage_logging_slow_query,
+ & stage_making_temp_file_append_before_load_data,
+ & stage_making_temp_file_create_before_load_data,
+ & stage_manage_keys,
+ & stage_master_has_sent_all_binlog_to_slave,
+ & stage_opening_tables,
+ & stage_optimizing,
+ & stage_preparing,
+ & stage_purging_old_relay_logs,
+ & stage_query_end,
+ & stage_queueing_master_event_to_the_relay_log,
+ & stage_reading_event_from_the_relay_log,
+ & stage_recreating_table,
+ & stage_registering_slave_on_master,
+ & stage_removing_duplicates,
+ & stage_removing_tmp_table,
+ & stage_rename,
+ & stage_rename_result_table,
+ & stage_requesting_binlog_dump,
+ & stage_reschedule,
+ & stage_searching_rows_for_update,
+ & stage_sending_binlog_event_to_slave,
+ & stage_sending_cached_result_to_client,
+ & stage_sending_data,
+ & stage_setup,
+ & stage_show_explain,
+ & stage_slave_has_read_all_relay_log,
+ & stage_slave_waiting_event_from_coordinator,
+ & stage_slave_waiting_worker_queue,
+ & stage_slave_waiting_worker_to_free_events,
+ & stage_slave_waiting_worker_to_release_partition,
+ & stage_slave_waiting_workers_to_exit,
+ & stage_sorting,
+ & stage_sorting_for_group,
+ & stage_sorting_for_order,
+ & stage_sorting_result,
+ & stage_sql_thd_waiting_until_delay,
+ & stage_statistics,
+ & stage_storing_result_in_query_cache,
+ & stage_storing_row_into_queue,
+ & stage_system_lock,
+ & stage_table_lock,
+ & stage_filling_schema_table,
+ & stage_update,
+ & stage_updating,
+ & stage_updating_main_table,
+ & stage_updating_reference_tables,
+ & stage_upgrading_lock,
+ & stage_user_lock,
+ & stage_user_sleep,
+ & stage_verifying_table,
+ & stage_waiting_for_delay_list,
+ & stage_waiting_for_gtid_to_be_written_to_binary_log,
+ & stage_waiting_for_handler_insert,
+ & stage_waiting_for_handler_lock,
+ & stage_waiting_for_handler_open,
+ & stage_waiting_for_insert,
+ & stage_waiting_for_master_to_send_event,
+ & stage_waiting_for_master_update,
+ & stage_waiting_for_prior_transaction_to_commit,
+ & stage_waiting_for_prior_transaction_to_start_commit,
+ & stage_waiting_for_query_cache_lock,
+ & stage_waiting_for_relay_log_space,
+ & stage_waiting_for_room_in_worker_thread,
+ & stage_waiting_for_slave_mutex_on_exit,
+ & stage_waiting_for_slave_thread_to_start,
+ & stage_waiting_for_table_flush,
+ & stage_waiting_for_the_next_event_in_relay_log,
+ & stage_waiting_for_the_slave_thread_to_advance_position,
+ & stage_waiting_for_work_from_sql_thread,
+ & stage_waiting_to_finalize_termination,
+ & stage_waiting_to_get_readlock,
+ & stage_master_gtid_wait_primary,
+ & stage_master_gtid_wait,
+ & stage_gtid_wait_other_connection
+};
-/*****************************************************************************
- Instantiate variables for missing storage engines
- This section should go away soon
-*****************************************************************************/
+PSI_socket_key key_socket_tcpip, key_socket_unix, key_socket_client_connection;
-/*****************************************************************************
- Instantiate templates
-*****************************************************************************/
+static PSI_socket_info all_server_sockets[]=
+{
+ { &key_socket_tcpip, "server_tcpip_socket", PSI_FLAG_GLOBAL},
+ { &key_socket_unix, "server_unix_socket", PSI_FLAG_GLOBAL},
+ { &key_socket_client_connection, "client_connection", 0}
+};
-#ifdef HAVE_EXPLICIT_TEMPLATE_INSTANTIATION
-/* Used templates */
-template class I_List<THD>;
-template class I_List_iterator<THD>;
-template class I_List<i_string>;
-template class I_List<i_string_pair>;
-template class I_List<Statement>;
-template class I_List_iterator<Statement>;
+/**
+ Initialise all the performance schema instrumentation points
+ used by the server.
+*/
+void init_server_psi_keys(void)
+{
+ const char* category= "sql";
+ int count;
+
+ count= array_elements(all_server_mutexes);
+ mysql_mutex_register(category, all_server_mutexes, count);
+
+ count= array_elements(all_server_rwlocks);
+ mysql_rwlock_register(category, all_server_rwlocks, count);
+
+ count= array_elements(all_server_conds);
+ mysql_cond_register(category, all_server_conds, count);
+
+ count= array_elements(all_server_threads);
+ mysql_thread_register(category, all_server_threads, count);
+
+ count= array_elements(all_server_files);
+ mysql_file_register(category, all_server_files, count);
+
+ count= array_elements(all_server_stages);
+ mysql_stage_register(category, all_server_stages, count);
+
+ count= array_elements(all_server_sockets);
+ mysql_socket_register(category, all_server_sockets, count);
+
+#ifdef HAVE_PSI_STATEMENT_INTERFACE
+ init_sql_statement_info();
+ count= array_elements(sql_statement_info);
+ mysql_statement_register(category, sql_statement_info, count);
+
+ category= "com";
+ init_com_statement_info();
+
+ /*
+ Register [0 .. COM_QUERY - 1] as "statement/com/..."
+ */
+ count= (int) COM_QUERY;
+ mysql_statement_register(category, com_statement_info, count);
+
+ /*
+ Register [COM_QUERY + 1 .. COM_END] as "statement/com/..."
+ */
+ count= (int) COM_END - (int) COM_QUERY;
+ mysql_statement_register(category, & com_statement_info[(int) COM_QUERY + 1], count);
+
+ category= "abstract";
+ /*
+ Register [COM_QUERY] as "statement/abstract/com_query"
+ */
+ mysql_statement_register(category, & com_statement_info[(int) COM_QUERY], 1);
+
+ /*
+ When a new packet is received,
+ it is instrumented as "statement/abstract/new_packet".
+ Based on the packet type found, it later mutates to the
+ proper narrow type, for example
+ "statement/abstract/query" or "statement/com/ping".
+ In cases of "statement/abstract/query", SQL queries are given to
+ the parser, which mutates the statement type to an even more
+ narrow classification, for example "statement/sql/select".
+ */
+ stmt_info_new_packet.m_key= 0;
+ stmt_info_new_packet.m_name= "new_packet";
+ stmt_info_new_packet.m_flags= PSI_FLAG_MUTABLE;
+ mysql_statement_register(category, &stmt_info_new_packet, 1);
+
+ /*
+ Statements processed from the relay log are initially instrumented as
+ "statement/abstract/relay_log". The parser will mutate the statement type to
+ a more specific classification, for example "statement/sql/insert".
+ */
+ stmt_info_rpl.m_key= 0;
+ stmt_info_rpl.m_name= "relay_log";
+ stmt_info_rpl.m_flags= PSI_FLAG_MUTABLE;
+ mysql_statement_register(category, &stmt_info_rpl, 1);
#endif
+}
+#endif /* HAVE_PSI_INTERFACE */
diff --git a/sql/mysqld.h b/sql/mysqld.h
index cfe22101971..d549013cd32 100644
--- a/sql/mysqld.h
+++ b/sql/mysqld.h
@@ -24,6 +24,8 @@
#include "my_atomic.h" /* my_atomic_rwlock_t */
#include "mysql/psi/mysql_file.h" /* MYSQL_FILE */
#include "sql_list.h" /* I_List */
+#include "sql_cmd.h"
+#include <my_rnd.h>
class THD;
struct handlerton;
@@ -57,6 +59,8 @@ void kill_mysql(void);
void close_connection(THD *thd, uint sql_errno= 0);
void handle_connection_in_main_thread(THD *thd);
void create_thread_to_handle_connection(THD *thd);
+void delete_running_thd(THD *thd);
+void signal_thd_deleted();
void unlink_thd(THD *thd);
bool one_thread_per_connection_end(THD *thd, bool put_in_cache);
void flush_thread_cache();
@@ -90,12 +94,17 @@ extern bool opt_ignore_builtin_innodb;
extern my_bool opt_character_set_client_handshake;
extern bool volatile abort_loop;
extern bool in_bootstrap;
-extern uint volatile thread_count;
extern uint connection_count;
extern my_bool opt_safe_user_create;
extern my_bool opt_safe_show_db, opt_local_infile, opt_myisam_use_mmap;
extern my_bool opt_slave_compressed_protocol, use_temp_pool;
-extern ulong slave_exec_mode_options;
+extern ulong slave_exec_mode_options, slave_ddl_exec_mode_options;
+extern ulong slave_retried_transactions;
+#ifdef RBR_TRIGGERS
+extern ulong slave_run_triggers_for_rbr;
+#else
+#define slave_run_triggers_for_rbr 0
+#endif //RBR_TRIGGERS
extern ulonglong slave_type_conversions_options;
extern my_bool read_only, opt_readonly;
extern my_bool lower_case_file_system;
@@ -106,6 +115,7 @@ extern char* opt_secure_backup_file_priv;
extern size_t opt_secure_backup_file_priv_len;
extern my_bool opt_log_slow_admin_statements, opt_log_slow_slave_statements;
extern my_bool sp_automatic_privileges, opt_noacl;
+extern ulong use_stat_tables;
extern my_bool opt_old_style_user_limits, trust_function_creators;
extern uint opt_crash_binlog_innodb;
extern char *shared_memory_base_name, *mysqld_unix_port;
@@ -137,7 +147,8 @@ extern char *opt_backup_history_logname, *opt_backup_progress_logname,
extern const char *log_output_str;
extern const char *log_backup_output_str;
extern char *mysql_home_ptr, *pidfile_name_ptr;
-extern char glob_hostname[FN_REFLEN], mysql_home[FN_REFLEN];
+extern MYSQL_PLUGIN_IMPORT char glob_hostname[FN_REFLEN];
+extern char mysql_home[FN_REFLEN];
extern char pidfile_name[FN_REFLEN], system_time_zone[30], *opt_init_file;
extern char default_logfile_name[FN_REFLEN];
extern char log_error_file[FN_REFLEN], *opt_tc_log_file;
@@ -152,12 +163,13 @@ extern ulong delayed_insert_timeout;
extern ulong delayed_insert_limit, delayed_queue_size;
extern ulong delayed_insert_threads, delayed_insert_writes;
extern ulong delayed_rows_in_use,delayed_insert_errors;
-extern ulong slave_open_temp_tables;
+extern int32 slave_open_temp_tables;
extern ulonglong query_cache_size;
+extern ulong query_cache_limit;
extern ulong query_cache_min_res_unit;
extern ulong slow_launch_threads, slow_launch_time;
-extern ulong table_cache_size, table_def_size;
extern MYSQL_PLUGIN_IMPORT ulong max_connections;
+extern ulong max_digest_length;
extern ulong max_connect_errors, connect_timeout;
extern my_bool slave_allow_batching;
extern my_bool allow_slave_start;
@@ -170,11 +182,17 @@ extern ulong max_prepared_stmt_count, prepared_stmt_count;
extern ulong open_files_limit;
extern ulonglong binlog_cache_size, binlog_stmt_cache_size;
extern ulonglong max_binlog_cache_size, max_binlog_stmt_cache_size;
-extern ulong max_binlog_size, max_relay_log_size;
+extern ulong max_binlog_size;
extern ulong slave_max_allowed_packet;
extern ulong opt_binlog_rows_event_max_size;
extern ulong rpl_recovery_rank, thread_cache_size;
extern ulong stored_program_cache_size;
+extern ulong opt_slave_parallel_threads;
+extern ulong opt_slave_domain_parallel_threads;
+extern ulong opt_slave_parallel_max_queued;
+extern ulong opt_binlog_commit_wait_count;
+extern ulong opt_binlog_commit_wait_usec;
+extern my_bool opt_gtid_ignore_duplicates;
extern ulong back_log;
extern ulong executed_events;
extern char language[FN_REFLEN];
@@ -199,7 +217,8 @@ extern handlerton *myisam_hton;
extern handlerton *heap_hton;
extern const char *load_default_groups[];
extern struct my_option my_long_options[];
-extern int mysqld_server_started;
+int handle_early_options();
+extern int mysqld_server_started, mysqld_server_initialized;
extern "C" MYSQL_PLUGIN_IMPORT int orig_argc;
extern "C" MYSQL_PLUGIN_IMPORT char **orig_argv;
extern pthread_attr_t connection_attrib;
@@ -209,6 +228,13 @@ extern LEX_STRING opt_init_connect, opt_init_slave;
extern int bootstrap_error;
extern I_List<THD> threads;
extern char err_shared_dir[];
+extern ulong connection_errors_select;
+extern ulong connection_errors_accept;
+extern ulong connection_errors_tcpwrap;
+extern ulong connection_errors_internal;
+extern ulong connection_errors_max_connection;
+extern ulong connection_errors_peer_addr;
+extern ulong log_warnings;
/*
THR_MALLOC is a key which will be used to set/get MEM_ROOT** for a thread,
@@ -219,38 +245,44 @@ extern pthread_key(MEM_ROOT**,THR_MALLOC);
#ifdef HAVE_PSI_INTERFACE
#ifdef HAVE_MMAP
extern PSI_mutex_key key_PAGE_lock, key_LOCK_sync, key_LOCK_active,
- key_LOCK_pool;
+ key_LOCK_pool, key_LOCK_pending_checkpoint;
#endif /* HAVE_MMAP */
+
#ifdef WITH_WSREP
extern PSI_mutex_key key_LOCK_wsrep_thd;
-#endif /* HAVE_WSREP */
+#endif /* WITH_WSREP */
#ifdef HAVE_OPENSSL
extern PSI_mutex_key key_LOCK_des_key_file;
#endif
-extern PSI_mutex_key key_BINLOG_LOCK_index, key_BINLOG_LOCK_prep_xids,
+extern PSI_mutex_key key_BINLOG_LOCK_index, key_BINLOG_LOCK_xid_list,
+ key_BINLOG_LOCK_binlog_background_thread,
key_delayed_insert_mutex, key_hash_filo_lock, key_LOCK_active_mi,
key_LOCK_connection_count, key_LOCK_crypt, key_LOCK_delayed_create,
key_LOCK_delayed_insert, key_LOCK_delayed_status, key_LOCK_error_log,
key_LOCK_gdl, key_LOCK_global_system_variables,
key_LOCK_logger, key_LOCK_manager,
key_LOCK_prepared_stmt_count,
- key_LOCK_rpl_status, key_LOCK_server_started, key_LOCK_status,
- key_LOCK_table_share, key_LOCK_thd_data,
+ key_LOCK_rpl_status, key_LOCK_server_started,
+ key_LOCK_status, key_LOCK_show_status,
+ key_LOCK_thd_data,
key_LOCK_user_conn, key_LOG_LOCK_log,
key_master_info_data_lock, key_master_info_run_lock,
key_master_info_sleep_lock,
key_mutex_slave_reporting_capability_err_lock, key_relay_log_info_data_lock,
key_relay_log_info_log_space_lock, key_relay_log_info_run_lock,
- key_relay_log_info_sleep_lock,
+ key_rpl_group_info_sleep_lock,
key_structure_guard_mutex, key_TABLE_SHARE_LOCK_ha_data,
key_LOCK_error_messages, key_LOCK_thread_count, key_PARTITION_LOCK_auto_inc;
extern PSI_mutex_key key_RELAYLOG_LOCK_index;
+extern PSI_mutex_key key_LOCK_slave_state, key_LOCK_binlog_state,
+ key_LOCK_rpl_thread, key_LOCK_rpl_thread_pool, key_LOCK_parallel_entry;
-extern PSI_mutex_key key_LOCK_stats,
+extern PSI_mutex_key key_TABLE_SHARE_LOCK_share, key_LOCK_stats,
key_LOCK_global_user_client_stats, key_LOCK_global_table_stats,
- key_LOCK_global_index_stats, key_LOCK_wakeup_ready;
+ key_LOCK_global_index_stats, key_LOCK_wakeup_ready, key_LOCK_wait_commit;
+extern PSI_mutex_key key_LOCK_gtid_waiting;
extern PSI_rwlock_key key_rwlock_LOCK_grant, key_rwlock_LOCK_logger,
key_rwlock_LOCK_sys_init_connect, key_rwlock_LOCK_sys_init_slave,
@@ -260,7 +292,9 @@ extern PSI_rwlock_key key_rwlock_LOCK_grant, key_rwlock_LOCK_logger,
extern PSI_cond_key key_PAGE_cond, key_COND_active, key_COND_pool;
#endif /* HAVE_MMAP */
-extern PSI_cond_key key_BINLOG_COND_prep_xids, key_BINLOG_update_cond,
+extern PSI_cond_key key_BINLOG_COND_xid_list, key_BINLOG_update_cond,
+ key_BINLOG_COND_binlog_background_thread,
+ key_BINLOG_COND_binlog_background_thread_end,
key_COND_cache_status_changed, key_COND_manager,
key_COND_rpl_status, key_COND_server_started,
key_delayed_insert_cond, key_delayed_insert_cond_client,
@@ -269,16 +303,22 @@ extern PSI_cond_key key_BINLOG_COND_prep_xids, key_BINLOG_update_cond,
key_master_info_sleep_cond,
key_relay_log_info_data_cond, key_relay_log_info_log_space_cond,
key_relay_log_info_start_cond, key_relay_log_info_stop_cond,
- key_relay_log_info_sleep_cond,
+ key_rpl_group_info_sleep_cond,
key_TABLE_SHARE_cond, key_user_level_lock_cond,
key_COND_thread_count, key_COND_thread_cache, key_COND_flush_thread_cache;
-extern PSI_cond_key key_RELAYLOG_update_cond, key_COND_wakeup_ready;
+extern PSI_cond_key key_RELAYLOG_update_cond, key_COND_wakeup_ready,
+ key_COND_wait_commit;
extern PSI_cond_key key_RELAYLOG_COND_queue_busy;
extern PSI_cond_key key_TC_LOG_MMAP_COND_queue_busy;
+extern PSI_cond_key key_COND_rpl_thread, key_COND_rpl_thread_queue,
+ key_COND_rpl_thread_stop, key_COND_rpl_thread_pool,
+ key_COND_parallel_entry, key_COND_group_commit_orderer;
+extern PSI_cond_key key_COND_wait_gtid, key_COND_gtid_ignore_duplicates;
extern PSI_thread_key key_thread_bootstrap, key_thread_delayed_insert,
key_thread_handle_manager, key_thread_kill_server, key_thread_main,
- key_thread_one_connection, key_thread_signal_hand;
+ key_thread_one_connection, key_thread_signal_hand, key_thread_slave_init,
+ key_rpl_parallel_thread;
extern PSI_file_key key_file_binlog, key_file_binlog_index, key_file_casetest,
key_file_dbopt, key_file_des_key_file, key_file_ERRMSG, key_select_to_file,
@@ -289,10 +329,166 @@ extern PSI_file_key key_file_binlog, key_file_binlog_index, key_file_casetest,
key_file_trg, key_file_trn, key_file_init;
extern PSI_file_key key_file_query_log, key_file_slow_log;
extern PSI_file_key key_file_relaylog, key_file_relaylog_index;
+extern PSI_socket_key key_socket_tcpip, key_socket_unix,
+ key_socket_client_connection;
+extern PSI_file_key key_file_binlog_state;
void init_server_psi_keys();
#endif /* HAVE_PSI_INTERFACE */
+/*
+ MAINTAINER: Please keep this list in order, to limit merge collisions.
+ Hint: grep PSI_stage_info | sort -u
+*/
+extern PSI_stage_info stage_after_create;
+extern PSI_stage_info stage_after_opening_tables;
+extern PSI_stage_info stage_after_table_lock;
+extern PSI_stage_info stage_allocating_local_table;
+extern PSI_stage_info stage_alter_inplace_prepare;
+extern PSI_stage_info stage_alter_inplace;
+extern PSI_stage_info stage_alter_inplace_commit;
+extern PSI_stage_info stage_changing_master;
+extern PSI_stage_info stage_checking_master_version;
+extern PSI_stage_info stage_checking_permissions;
+extern PSI_stage_info stage_checking_privileges_on_cached_query;
+extern PSI_stage_info stage_checking_query_cache_for_query;
+extern PSI_stage_info stage_cleaning_up;
+extern PSI_stage_info stage_closing_tables;
+extern PSI_stage_info stage_connecting_to_master;
+extern PSI_stage_info stage_converting_heap_to_myisam;
+extern PSI_stage_info stage_copying_to_group_table;
+extern PSI_stage_info stage_copying_to_tmp_table;
+extern PSI_stage_info stage_copy_to_tmp_table;
+extern PSI_stage_info stage_creating_delayed_handler;
+extern PSI_stage_info stage_creating_sort_index;
+extern PSI_stage_info stage_creating_table;
+extern PSI_stage_info stage_creating_tmp_table;
+extern PSI_stage_info stage_deleting_from_main_table;
+extern PSI_stage_info stage_deleting_from_reference_tables;
+extern PSI_stage_info stage_discard_or_import_tablespace;
+extern PSI_stage_info stage_end;
+extern PSI_stage_info stage_enabling_keys;
+extern PSI_stage_info stage_executing;
+extern PSI_stage_info stage_execution_of_init_command;
+extern PSI_stage_info stage_explaining;
+extern PSI_stage_info stage_finding_key_cache;
+extern PSI_stage_info stage_finished_reading_one_binlog_switching_to_next_binlog;
+extern PSI_stage_info stage_flushing_relay_log_and_master_info_repository;
+extern PSI_stage_info stage_flushing_relay_log_info_file;
+extern PSI_stage_info stage_freeing_items;
+extern PSI_stage_info stage_fulltext_initialization;
+extern PSI_stage_info stage_got_handler_lock;
+extern PSI_stage_info stage_got_old_table;
+extern PSI_stage_info stage_init;
+extern PSI_stage_info stage_insert;
+extern PSI_stage_info stage_invalidating_query_cache_entries_table;
+extern PSI_stage_info stage_invalidating_query_cache_entries_table_list;
+extern PSI_stage_info stage_killing_slave;
+extern PSI_stage_info stage_logging_slow_query;
+extern PSI_stage_info stage_making_temp_file_append_before_load_data;
+extern PSI_stage_info stage_making_temp_file_create_before_load_data;
+extern PSI_stage_info stage_manage_keys;
+extern PSI_stage_info stage_master_has_sent_all_binlog_to_slave;
+extern PSI_stage_info stage_opening_tables;
+extern PSI_stage_info stage_optimizing;
+extern PSI_stage_info stage_preparing;
+extern PSI_stage_info stage_purging_old_relay_logs;
+extern PSI_stage_info stage_query_end;
+extern PSI_stage_info stage_queueing_master_event_to_the_relay_log;
+extern PSI_stage_info stage_reading_event_from_the_relay_log;
+extern PSI_stage_info stage_recreating_table;
+extern PSI_stage_info stage_registering_slave_on_master;
+extern PSI_stage_info stage_removing_duplicates;
+extern PSI_stage_info stage_removing_tmp_table;
+extern PSI_stage_info stage_rename;
+extern PSI_stage_info stage_rename_result_table;
+extern PSI_stage_info stage_requesting_binlog_dump;
+extern PSI_stage_info stage_reschedule;
+extern PSI_stage_info stage_searching_rows_for_update;
+extern PSI_stage_info stage_sending_binlog_event_to_slave;
+extern PSI_stage_info stage_sending_cached_result_to_client;
+extern PSI_stage_info stage_sending_data;
+extern PSI_stage_info stage_setup;
+extern PSI_stage_info stage_slave_has_read_all_relay_log;
+extern PSI_stage_info stage_show_explain;
+extern PSI_stage_info stage_sorting;
+extern PSI_stage_info stage_sorting_for_group;
+extern PSI_stage_info stage_sorting_for_order;
+extern PSI_stage_info stage_sorting_result;
+extern PSI_stage_info stage_sql_thd_waiting_until_delay;
+extern PSI_stage_info stage_statistics;
+extern PSI_stage_info stage_storing_result_in_query_cache;
+extern PSI_stage_info stage_storing_row_into_queue;
+extern PSI_stage_info stage_system_lock;
+extern PSI_stage_info stage_table_lock;
+extern PSI_stage_info stage_filling_schema_table;
+extern PSI_stage_info stage_update;
+extern PSI_stage_info stage_updating;
+extern PSI_stage_info stage_updating_main_table;
+extern PSI_stage_info stage_updating_reference_tables;
+extern PSI_stage_info stage_upgrading_lock;
+extern PSI_stage_info stage_user_lock;
+extern PSI_stage_info stage_user_sleep;
+extern PSI_stage_info stage_verifying_table;
+extern PSI_stage_info stage_waiting_for_delay_list;
+extern PSI_stage_info stage_waiting_for_gtid_to_be_written_to_binary_log;
+extern PSI_stage_info stage_waiting_for_handler_insert;
+extern PSI_stage_info stage_waiting_for_handler_lock;
+extern PSI_stage_info stage_waiting_for_handler_open;
+extern PSI_stage_info stage_waiting_for_insert;
+extern PSI_stage_info stage_waiting_for_master_to_send_event;
+extern PSI_stage_info stage_waiting_for_master_update;
+extern PSI_stage_info stage_waiting_for_relay_log_space;
+extern PSI_stage_info stage_waiting_for_slave_mutex_on_exit;
+extern PSI_stage_info stage_waiting_for_slave_thread_to_start;
+extern PSI_stage_info stage_waiting_for_query_cache_lock;
+extern PSI_stage_info stage_waiting_for_table_flush;
+extern PSI_stage_info stage_waiting_for_the_next_event_in_relay_log;
+extern PSI_stage_info stage_waiting_for_the_slave_thread_to_advance_position;
+extern PSI_stage_info stage_waiting_to_finalize_termination;
+extern PSI_stage_info stage_waiting_to_get_readlock;
+extern PSI_stage_info stage_slave_waiting_worker_to_release_partition;
+extern PSI_stage_info stage_slave_waiting_worker_to_free_events;
+extern PSI_stage_info stage_slave_waiting_worker_queue;
+extern PSI_stage_info stage_slave_waiting_event_from_coordinator;
+extern PSI_stage_info stage_slave_waiting_workers_to_exit;
+extern PSI_stage_info stage_binlog_waiting_background_tasks;
+extern PSI_stage_info stage_binlog_processing_checkpoint_notify;
+extern PSI_stage_info stage_binlog_stopping_background_thread;
+extern PSI_stage_info stage_waiting_for_work_from_sql_thread;
+extern PSI_stage_info stage_waiting_for_prior_transaction_to_commit;
+extern PSI_stage_info stage_waiting_for_prior_transaction_to_start_commit;
+extern PSI_stage_info stage_waiting_for_room_in_worker_thread;
+extern PSI_stage_info stage_waiting_for_workers_idle;
+extern PSI_stage_info stage_waiting_for_ftwrl;
+extern PSI_stage_info stage_waiting_for_ftwrl_threads_to_pause;
+extern PSI_stage_info stage_waiting_for_rpl_thread_pool;
+extern PSI_stage_info stage_master_gtid_wait_primary;
+extern PSI_stage_info stage_master_gtid_wait;
+extern PSI_stage_info stage_gtid_wait_other_connection;
+
+#ifdef HAVE_PSI_STATEMENT_INTERFACE
+/**
+ Statement instrumentation keys (sql).
+ The last entry, at [SQLCOM_END], is for parsing errors.
+*/
+extern PSI_statement_info sql_statement_info[(uint) SQLCOM_END + 1];
+
+/**
+ Statement instrumentation keys (com).
+ The last entry, at [COM_END], is for packet errors.
+*/
+extern PSI_statement_info com_statement_info[(uint) COM_END + 1];
+
+/**
+ Statement instrumentation key for replication.
+*/
+extern PSI_statement_info stmt_info_rpl;
+
+void init_sql_statement_info();
+void init_com_statement_info();
+#endif /* HAVE_PSI_STATEMENT_INTERFACE */
+
#ifndef __WIN__
extern pthread_t signal_thread;
#endif
@@ -332,12 +528,13 @@ extern MYSQL_PLUGIN_IMPORT key_map key_map_full; /* Should be threaded
Server mutex locks and condition variables.
*/
extern mysql_mutex_t
- LOCK_user_locks, LOCK_status,
+ LOCK_item_func_sleep, LOCK_status, LOCK_show_status,
LOCK_error_log, LOCK_delayed_insert, LOCK_short_uuid_generator,
LOCK_delayed_status, LOCK_delayed_create, LOCK_crypt, LOCK_timezone,
LOCK_slave_list, LOCK_active_mi, LOCK_manager,
LOCK_global_system_variables, LOCK_user_conn,
- LOCK_prepared_stmt_count, LOCK_error_messages, LOCK_connection_count;
+ LOCK_prepared_stmt_count, LOCK_error_messages, LOCK_connection_count,
+ LOCK_slave_init;
extern MYSQL_PLUGIN_IMPORT mysql_mutex_t LOCK_thread_count;
#ifdef HAVE_OPENSSL
extern char* des_key_file;
@@ -349,11 +546,14 @@ extern mysql_rwlock_t LOCK_grant, LOCK_sys_init_connect, LOCK_sys_init_slave;
extern mysql_rwlock_t LOCK_system_variables_hash;
extern mysql_cond_t COND_thread_count;
extern mysql_cond_t COND_manager;
+extern mysql_cond_t COND_slave_init;
extern int32 thread_running;
-extern my_atomic_rwlock_t thread_running_lock;
+extern int32 thread_count, service_thread_count;
+extern my_atomic_rwlock_t thread_running_lock, thread_count_lock;
+extern my_atomic_rwlock_t slave_executed_entries_lock;
extern char *opt_ssl_ca, *opt_ssl_capath, *opt_ssl_cert, *opt_ssl_cipher,
- *opt_ssl_key;
+ *opt_ssl_key, *opt_ssl_crl, *opt_ssl_crlpath;
extern MYSQL_PLUGIN_IMPORT pthread_key(THD*, THR_THD);
@@ -366,7 +566,6 @@ extern MYSQL_PLUGIN_IMPORT pthread_key(THD*, THR_THD);
enum options_mysqld
{
OPT_to_set_the_start_number=256,
- OPT_BIND_ADDRESS,
OPT_BINLOG_DO_DB,
OPT_BINLOG_FORMAT,
OPT_BINLOG_IGNORE_DB,
@@ -374,9 +573,7 @@ enum options_mysqld
OPT_BOOTSTRAP,
OPT_CONSOLE,
OPT_DEBUG_SYNC_TIMEOUT,
- OPT_DELAY_KEY_WRITE_ALL,
OPT_DEPRECATED_OPTION,
- OPT_ENGINE_CONDITION_PUSHDOWN,
OPT_IGNORE_DB_DIRECTORY,
OPT_ISAM_LOG,
OPT_KEY_BUFFER_SIZE,
@@ -384,12 +581,14 @@ enum options_mysqld
OPT_KEY_CACHE_BLOCK_SIZE,
OPT_KEY_CACHE_DIVISION_LIMIT,
OPT_KEY_CACHE_PARTITIONS,
+ OPT_KEY_CACHE_CHANGED_BLOCKS_HASH_SIZE,
OPT_LOG_BASENAME,
OPT_LOG_ERROR,
OPT_LOWER_CASE_TABLE_NAMES,
OPT_MAX_LONG_DATA_SIZE,
- OPT_ONE_THREAD,
- OPT_POOL_OF_THREADS,
+ OPT_PLUGIN_LOAD,
+ OPT_PLUGIN_LOAD_ADD,
+ OPT_PFS_INSTRUMENT,
OPT_REPLICATE_DO_DB,
OPT_REPLICATE_DO_TABLE,
OPT_REPLICATE_IGNORE_DB,
@@ -400,20 +599,18 @@ enum options_mysqld
OPT_SAFE,
OPT_SERVER_ID,
OPT_SKIP_HOST_CACHE,
- OPT_SKIP_LOCK,
- OPT_SKIP_PRIOR,
OPT_SKIP_RESOLVE,
- OPT_SKIP_STACK_TRACE,
- OPT_SKIP_SYMLINKS,
- OPT_SLOW_QUERY_LOG,
OPT_SSL_CA,
OPT_SSL_CAPATH,
OPT_SSL_CERT,
OPT_SSL_CIPHER,
+ OPT_SSL_CRL,
+ OPT_SSL_CRLPATH,
OPT_SSL_KEY,
OPT_THREAD_CONCURRENCY,
- OPT_UPDATE_LOG,
OPT_WANT_CORE,
+ OPT_MYSQL_COMPATIBILITY,
+ OPT_MYSQL_TO_BE_IMPLEMENTED,
#ifdef WITH_WSREP
OPT_WSREP_PROVIDER,
OPT_WSREP_PROVIDER_OPTIONS,
@@ -422,6 +619,7 @@ enum options_mysqld
OPT_WSREP_SST_AUTH,
OPT_WSREP_RECOVER,
#endif /* WITH_WSREP */
+
OPT_which_is_always_the_last
};
#endif
@@ -445,6 +643,7 @@ enum enum_query_type
typedef int64 query_id_t;
extern query_id_t global_query_id;
extern my_atomic_rwlock_t global_query_id_lock;
+extern my_atomic_rwlock_t statistics_lock;
void unireg_end(void) __attribute__((noreturn));
@@ -455,7 +654,7 @@ inline __attribute__((warn_unused_result)) query_id_t next_query_id()
my_atomic_rwlock_wrlock(&global_query_id_lock);
id= my_atomic_add64(&global_query_id, 1);
my_atomic_rwlock_wrunlock(&global_query_id_lock);
- return (id+1);
+ return (id);
}
inline query_id_t get_query_id()
@@ -485,42 +684,44 @@ inline void table_case_convert(char * name, uint length)
name, length, name, length);
}
-inline ulong sql_rnd_with_mutex()
+inline void thread_safe_increment32(int32 *value, my_atomic_rwlock_t *lock)
{
- mysql_mutex_lock(&LOCK_thread_count);
- ulong tmp=(ulong) (my_rnd(&sql_rand) * 0xffffffff); /* make all bits random */
- mysql_mutex_unlock(&LOCK_thread_count);
- return tmp;
+ my_atomic_rwlock_wrlock(lock);
+ (void) my_atomic_add32(value, 1);
+ my_atomic_rwlock_wrunlock(lock);
}
-inline int32
-inc_thread_running()
+inline void thread_safe_decrement32(int32 *value, my_atomic_rwlock_t *lock)
{
- int32 num_thread_running;
- my_atomic_rwlock_wrlock(&thread_running_lock);
- num_thread_running= my_atomic_add32(&thread_running, 1);
- my_atomic_rwlock_wrunlock(&thread_running_lock);
- return (num_thread_running+1);
+ my_atomic_rwlock_wrlock(lock);
+ (void) my_atomic_add32(value, -1);
+ my_atomic_rwlock_wrunlock(lock);
}
-inline int32
-dec_thread_running()
+inline void thread_safe_increment64(int64 *value, my_atomic_rwlock_t *lock)
+{
+ my_atomic_rwlock_wrlock(lock);
+ (void) my_atomic_add64(value, 1);
+ my_atomic_rwlock_wrunlock(lock);
+}
+
+inline void thread_safe_decrement64(int64 *value, my_atomic_rwlock_t *lock)
{
- int32 num_thread_running;
- my_atomic_rwlock_wrlock(&thread_running_lock);
- num_thread_running= my_atomic_add32(&thread_running, -1);
- my_atomic_rwlock_wrunlock(&thread_running_lock);
- return (num_thread_running-1);
+ my_atomic_rwlock_wrlock(lock);
+ (void) my_atomic_add64(value, -1);
+ my_atomic_rwlock_wrunlock(lock);
}
-inline int32
-get_thread_running()
+inline void
+inc_thread_running()
{
- int32 num_thread_running;
- my_atomic_rwlock_wrlock(&thread_running_lock);
- num_thread_running= my_atomic_load32(&thread_running);
- my_atomic_rwlock_wrunlock(&thread_running_lock);
- return num_thread_running;
+ thread_safe_increment32(&thread_running, &thread_running_lock);
+}
+
+inline void
+dec_thread_running()
+{
+ thread_safe_decrement32(&thread_running, &thread_running_lock);
}
void set_server_version(void);
@@ -540,6 +741,10 @@ inline THD *_current_thd(void)
}
#endif
#define current_thd _current_thd()
+inline int set_current_thd(THD *thd)
+{
+ return my_pthread_setspecific_ptr(THR_THD, thd);
+}
/*
@todo remove, make it static in ha_maria.cc
@@ -548,6 +753,8 @@ inline THD *_current_thd(void)
extern handlerton *maria_hton;
extern uint extra_connection_count;
+extern uint64 global_gtid_counter;
+extern my_bool opt_gtid_strict_mode;
extern my_bool opt_userstat_running, debug_assert_if_crashed_table;
extern uint mysqld_extra_port;
extern ulong opt_progress_report_time;
diff --git a/sql/net_serv.cc b/sql/net_serv.cc
index 4b78492c857..91a17606d68 100644
--- a/sql/net_serv.cc
+++ b/sql/net_serv.cc
@@ -1,5 +1,5 @@
/* Copyright (c) 2000, 2013, Oracle and/or its affiliates.
- Copyright (c) 2012, Monty Program Ab
+ Copyright (c) 2010, 2014, SkySQL 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
@@ -34,6 +34,7 @@
HFTODO this must be hidden if we don't want client capabilities in
embedded library
*/
+
#include <my_global.h>
#include <mysql.h>
#include <mysql_com.h>
@@ -43,7 +44,6 @@
#include <my_net.h>
#include <violite.h>
#include <signal.h>
-#include <errno.h>
#include "probes_mysql.h"
#ifdef EMBEDDED_LIBRARY
@@ -108,26 +108,30 @@ extern void query_cache_insert(const char *packet, ulong length,
unsigned pkt_nr);
#endif // HAVE_QUERY_CACHE
#define update_statistics(A) A
+extern my_bool thd_net_is_killed();
+/* Additional instrumentation hooks for the server */
+#include "mysql_com_server.h"
#else
#define update_statistics(A)
+#define thd_net_is_killed() 0
#endif
#define TEST_BLOCKING 8
#define MAX_PACKET_LENGTH (256L*256L*256L-1)
-static my_bool net_write_buff(NET *net,const uchar *packet,ulong len);
-
+static my_bool net_write_buff(NET *, const uchar *, ulong);
/** Init with packet info. */
-my_bool my_net_init(NET *net, Vio* vio)
+my_bool my_net_init(NET *net, Vio* vio, uint my_flags)
{
DBUG_ENTER("my_net_init");
+ DBUG_PRINT("enter", ("my_flags: %u", my_flags));
net->vio = vio;
my_net_local_init(net); /* Set some limits */
if (!(net->buff=(uchar*) my_malloc((size_t) net->max_packet+
NET_HEADER_SIZE + COMP_HEADER_SIZE +1,
- MYF(MY_WME))))
+ MYF(MY_WME | my_flags))))
DBUG_RETURN(1);
net->buff_end=net->buff+net->max_packet;
net->error=0; net->return_status=0;
@@ -139,10 +143,15 @@ my_bool my_net_init(NET *net, Vio* vio)
net->net_skip_rest_factor= 0;
net->last_errno=0;
net->unused= 0;
+ net->thread_specific_malloc= MY_TEST(my_flags & MY_THREAD_SPECIFIC);
+#ifdef MYSQL_SERVER
+ net->extension= NULL;
+#endif
- if (vio != 0) /* If real connection */
+ if (vio)
{
- net->fd = vio_fd(vio); /* For perl DBI/DBD */
+ /* For perl DBI/DBD. */
+ net->fd= vio_fd(vio);
#if defined(MYSQL_SERVER) && !defined(__WIN__)
if (!(test_flags & TEST_BLOCKING))
{
@@ -193,7 +202,9 @@ my_bool net_realloc(NET *net, size_t length)
*/
if (!(buff= (uchar*) my_realloc((char*) net->buff, pkt_length +
NET_HEADER_SIZE + COMP_HEADER_SIZE + 1,
- MYF(MY_WME))))
+ MYF(MY_WME |
+ (net->thread_specific_malloc ?
+ MY_THREAD_SPECIFIC : 0)))))
{
/* @todo: 1 and 2 codes are identical. */
net->error= 1;
@@ -255,14 +266,17 @@ static int net_data_is_ready(my_socket sd)
if ((res= select((int) (sd + 1), &sfds, NULL, NULL, &tv)) < 0)
return 0;
else
- return test(res ? FD_ISSET(sd, &sfds) : 0);
+ return MY_TEST(res ? FD_ISSET(sd, &sfds) : 0);
#endif /* HAVE_POLL */
}
#endif /* EMBEDDED_LIBRARY */
/**
- Intialize NET handler for new reads:
+ Clear (reinitialize) the NET structure for a new command.
+
+ @remark Performs debug checking of the socket buffer to
+ ensure that the protocol sequence is correct.
- Read from socket until there is nothing more to read. Discard
what is read.
@@ -295,7 +309,7 @@ void net_clear(NET *net, my_bool clear_buffer __attribute__((unused)))
{
size_t count;
int ready;
- while ((ready= net_data_is_ready(net->vio->sd)) > 0)
+ while ((ready= net_data_is_ready(vio_fd(net->vio))) > 0)
{
/* The socket is ready */
if ((long) (count= vio_read(net->vio, net->buff,
@@ -345,9 +359,9 @@ my_bool net_flush(NET *net)
DBUG_ENTER("net_flush");
if (net->buff != net->write_pos)
{
- error=test(net_real_write(net, net->buff,
- (size_t) (net->write_pos - net->buff)));
- net->write_pos=net->buff;
+ error= MY_TEST(net_real_write(net, net->buff,
+ (size_t) (net->write_pos - net->buff)));
+ net->write_pos= net->buff;
}
/* Sync packet number if using compression */
if (net->compress)
@@ -363,15 +377,13 @@ my_bool net_flush(NET *net)
/**
Write a logical packet with packet header.
- Format: Packet length (3 bytes), packet number(1 byte)
- When compression is used a 3 byte compression length is added
+ Format: Packet length (3 bytes), packet number (1 byte)
+ When compression is used, a 3 byte compression length is added.
- @note
- If compression is used the original package is modified!
+ @note If compression is used, the original packet is modified!
*/
-my_bool
-my_net_write(NET *net,const uchar *packet,size_t len)
+my_bool my_net_write(NET *net, const uchar *packet, size_t len)
{
uchar buff[NET_HEADER_SIZE];
int rc;
@@ -411,11 +423,12 @@ my_net_write(NET *net,const uchar *packet,size_t len)
#ifndef DEBUG_DATA_PACKETS
DBUG_DUMP("packet_header", buff, NET_HEADER_SIZE);
#endif
- rc= test(net_write_buff(net,packet,len));
+ rc= MY_TEST(net_write_buff(net, packet, len));
MYSQL_NET_WRITE_DONE(rc);
return rc;
}
+
/**
Send a command to the server.
@@ -484,9 +497,9 @@ net_write_command(NET *net,uchar command,
}
int3store(buff,length);
buff[3]= (uchar) net->pkt_nr++;
- rc= test(net_write_buff(net, buff, header_size) ||
- (head_len && net_write_buff(net, header, head_len)) ||
- net_write_buff(net, packet, len) || net_flush(net));
+ rc= MY_TEST(net_write_buff(net, buff, header_size) ||
+ (head_len && net_write_buff(net, header, head_len)) ||
+ net_write_buff(net, packet, len) || net_flush(net));
MYSQL_NET_WRITE_DONE(rc);
DBUG_RETURN(rc);
}
@@ -603,7 +616,10 @@ net_real_write(NET *net,const uchar *packet, size_t len)
uchar *b;
uint header_length=NET_HEADER_SIZE+COMP_HEADER_SIZE;
if (!(b= (uchar*) my_malloc(len + NET_HEADER_SIZE +
- COMP_HEADER_SIZE + 1, MYF(MY_WME))))
+ COMP_HEADER_SIZE + 1,
+ MYF(MY_WME |
+ (net->thread_specific_malloc ?
+ MY_THREAD_SPECIFIC : 0)))))
{
net->error= 2;
net->last_errno= ER_OUT_OF_RESOURCES;
@@ -775,7 +791,7 @@ static my_bool my_net_skip_rest(NET *net, uint32 remain, thr_alarm_t *alarmed,
{
while (remain > 0)
{
- size_t length= min(remain, net->max_packet);
+ size_t length= MY_MIN(remain, net->max_packet);
if (net_safe_read(net, net->buff, length, alarmed))
DBUG_RETURN(1);
update_statistics(thd_increment_bytes_received(length));
@@ -807,7 +823,8 @@ static my_bool my_net_skip_rest(NET *net, uint32 remain, thr_alarm_t *alarmed,
*/
static ulong
-my_real_read(NET *net, size_t *complen)
+my_real_read(NET *net, size_t *complen,
+ my_bool header __attribute__((unused)))
{
uchar *pos;
size_t length;
@@ -820,6 +837,21 @@ my_real_read(NET *net, size_t *complen)
my_bool net_blocking=vio_is_blocking(net->vio);
uint32 remain= (net->compress ? NET_HEADER_SIZE+COMP_HEADER_SIZE :
NET_HEADER_SIZE);
+#ifdef MYSQL_SERVER
+ size_t count= remain;
+ struct st_net_server *server_extension= 0;
+
+ if (header)
+ {
+ server_extension= static_cast<st_net_server*> (net->extension);
+ if (server_extension != NULL)
+ {
+ void *user_data= server_extension->m_user_data;
+ server_extension->m_before_header(net, user_data, count);
+ }
+ }
+#endif
+
*complen = 0;
net->reading_or_writing=1;
@@ -843,6 +875,16 @@ my_real_read(NET *net, size_t *complen)
DBUG_PRINT("info",("vio_read returned %ld errno: %d",
(long) length, vio_errno(net->vio)));
+
+ if (i== 0 && thd_net_is_killed())
+ {
+ len= packet_error;
+ net->error= 0;
+ net->last_errno= ER_CONNECTION_KILLED;
+ MYSQL_SERVER_my_error(net->last_errno, MYF(0));
+ goto end;
+ }
+
#if !defined(__WIN__) && defined(MYSQL_SERVER)
/*
We got an error that there was no data on the socket. We now set up
@@ -885,7 +927,7 @@ my_real_read(NET *net, size_t *complen)
my_progname,vio_errno(net->vio));
}
#ifndef MYSQL_SERVER
- if (vio_errno(net->vio) == SOCKET_EINTR)
+ if (length != 0 && vio_errno(net->vio) == SOCKET_EINTR)
{
DBUG_PRINT("warning",("Interrupted read. Retrying..."));
continue;
@@ -895,7 +937,7 @@ my_real_read(NET *net, size_t *complen)
remain, vio_errno(net->vio), (long) length));
len= packet_error;
net->error= 2; /* Close socket */
- net->last_errno= (vio_was_interrupted(net->vio) ?
+ net->last_errno= (vio_was_timeout(net->vio) ?
ER_NET_READ_INTERRUPTED :
ER_NET_READ_ERROR);
MYSQL_SERVER_my_error(net->last_errno, MYF(0));
@@ -959,7 +1001,7 @@ my_real_read(NET *net, size_t *complen)
len=uint3korr(net->buff+net->where_b);
if (!len) /* End of big multi-packet */
goto end;
- helping = max(len,*complen) + net->where_b;
+ helping = MY_MAX(len,*complen) + net->where_b;
/* The necessary size of net->buff */
if (helping >= net->max_packet)
{
@@ -976,6 +1018,14 @@ my_real_read(NET *net, size_t *complen)
}
pos=net->buff + net->where_b;
remain = (uint32) len;
+#ifdef MYSQL_SERVER
+ if (server_extension != NULL)
+ {
+ void *user_data= server_extension->m_user_data;
+ server_extension->m_after_header(net, user_data, count, 0);
+ server_extension= NULL;
+ }
+#endif
}
}
@@ -992,10 +1042,28 @@ end:
if (len != packet_error)
DBUG_DUMP("data", net->buff+net->where_b, len);
#endif
+#ifdef MYSQL_SERVER
+ if (server_extension != NULL)
+ {
+ void *user_data= server_extension->m_user_data;
+ server_extension->m_after_header(net, user_data, count, 1);
+ DBUG_ASSERT(len == packet_error || len == 0);
+ }
+#endif
return(len);
}
+/* Old interface. See my_net_read_packet() for function description */
+
+#undef my_net_read
+
+ulong my_net_read(NET *net)
+{
+ return my_net_read_packet(net, 0);
+}
+
+
/**
Read a packet from the client/server and return it without the internal
package header.
@@ -1007,13 +1075,17 @@ end:
If the packet was compressed, its uncompressed and the length of the
uncompressed packet is returned.
+ read_from_server is set when the server is reading a new command
+ from the client.
+
@return
The function returns the length of the found packet or packet_error.
net->read_pos points to the read data.
*/
+
ulong
-my_net_read(NET *net)
+my_net_read_packet(NET *net, my_bool read_from_server)
{
size_t len, complen;
@@ -1023,7 +1095,7 @@ my_net_read(NET *net)
if (!net->compress)
{
#endif
- len = my_real_read(net,&complen);
+ len = my_real_read(net,&complen, read_from_server);
if (len == MAX_PACKET_LENGTH)
{
/* First packet of a multi-packet. Concatenate the packets */
@@ -1033,7 +1105,7 @@ my_net_read(NET *net)
{
net->where_b += len;
total_length += len;
- len = my_real_read(net,&complen);
+ len = my_real_read(net,&complen, 0);
} while (len == MAX_PACKET_LENGTH);
if (len != packet_error)
len+= total_length;
@@ -1125,11 +1197,13 @@ my_net_read(NET *net)
}
net->where_b=buf_length;
- if ((packet_len = my_real_read(net,&complen)) == packet_error)
+ if ((packet_len = my_real_read(net,&complen, read_from_server))
+ == packet_error)
{
MYSQL_NET_READ_DONE(1, 0);
return packet_error;
}
+ read_from_server= 0;
if (my_uncompress(net->buff + net->where_b, packet_len,
&complen))
{
@@ -1160,13 +1234,12 @@ void my_net_set_read_timeout(NET *net, uint timeout)
{
DBUG_ENTER("my_net_set_read_timeout");
DBUG_PRINT("enter", ("timeout: %d", timeout));
- if (net->read_timeout == timeout)
- DBUG_VOID_RETURN;
- net->read_timeout= timeout;
-#ifdef NO_ALARM
- if (net->vio)
- vio_timeout(net->vio, 0, timeout);
-#endif
+ if (net->read_timeout != timeout)
+ {
+ net->read_timeout= timeout;
+ if (net->vio)
+ vio_timeout(net->vio, 0, timeout);
+ }
DBUG_VOID_RETURN;
}
@@ -1175,12 +1248,11 @@ void my_net_set_write_timeout(NET *net, uint timeout)
{
DBUG_ENTER("my_net_set_write_timeout");
DBUG_PRINT("enter", ("timeout: %d", timeout));
- if (net->write_timeout == timeout)
- DBUG_VOID_RETURN;
- net->write_timeout= timeout;
-#ifdef NO_ALARM
- if (net->vio)
- vio_timeout(net->vio, 1, timeout);
-#endif
+ if (net->write_timeout != timeout)
+ {
+ net->write_timeout= timeout;
+ if (net->vio)
+ vio_timeout(net->vio, 1, timeout);
+ }
DBUG_VOID_RETURN;
}
diff --git a/sql/opt_index_cond_pushdown.cc b/sql/opt_index_cond_pushdown.cc
index df9dae8e442..be33e46bf94 100644
--- a/sql/opt_index_cond_pushdown.cc
+++ b/sql/opt_index_cond_pushdown.cc
@@ -117,7 +117,7 @@ bool uses_index_fields_only(Item *item, TABLE *tbl, uint keyno,
return FALSE;
KEY *key_info= tbl->key_info + keyno;
KEY_PART_INFO *key_part= key_info->key_part;
- KEY_PART_INFO *key_part_end= key_part + key_info->key_parts;
+ KEY_PART_INFO *key_part_end= key_part + key_info->user_defined_key_parts;
for ( ; key_part < key_part_end; key_part++)
{
if (field->eq(key_part->field))
@@ -129,7 +129,7 @@ bool uses_index_fields_only(Item *item, TABLE *tbl, uint keyno,
{
key_info= tbl->key_info + tbl->s->primary_key;
key_part= key_info->key_part;
- key_part_end= key_part + key_info->key_parts;
+ key_part_end= key_part + key_info->user_defined_key_parts;
for ( ; key_part < key_part_end; key_part++)
{
/*
@@ -205,7 +205,7 @@ Item *make_cond_for_index(Item *cond, TABLE *table, uint keyno,
new_cond->argument_list()->push_back(fix);
used_tables|= fix->used_tables();
}
- if (test(item->marker == ICP_COND_USES_INDEX_ONLY))
+ if (MY_TEST(item->marker == ICP_COND_USES_INDEX_ONLY))
{
n_marked++;
item->marker= 0;
@@ -238,7 +238,7 @@ Item *make_cond_for_index(Item *cond, TABLE *table, uint keyno,
if (!fix)
return (COND*) 0;
new_cond->argument_list()->push_back(fix);
- if (test(item->marker == ICP_COND_USES_INDEX_ONLY))
+ if (MY_TEST(item->marker == ICP_COND_USES_INDEX_ONLY))
{
n_marked++;
item->marker= 0;
diff --git a/sql/opt_range.cc b/sql/opt_range.cc
index f4ac47fee96..f051ed07a7e 100644
--- a/sql/opt_range.cc
+++ b/sql/opt_range.cc
@@ -108,15 +108,17 @@
#pragma implementation // gcc: Class implementation
#endif
+#include <my_global.h>
#include "sql_priv.h"
#include "key.h" // is_key_used, key_copy, key_cmp, key_restore
#include "sql_parse.h" // check_stack_overrun
#include "sql_partition.h" // get_part_id_func, PARTITION_ITERATOR,
- // struct partition_info
+ // struct partition_info, NOT_A_PARTITION_ID
#include "sql_base.h" // free_io_cache
#include "records.h" // init_read_record, end_read_record
#include <m_ctype.h>
#include "sql_select.h"
+#include "sql_statistics.h"
#include "filesort.h" // filesort_free_buffers
#ifndef EXTRA_DEBUG
@@ -430,7 +432,7 @@ public:
new_max=arg->max_value; flag_max=arg->max_flag;
}
return new SEL_ARG(field, part, new_min, new_max, flag_min, flag_max,
- test(maybe_flag && arg->maybe_flag));
+ MY_TEST(maybe_flag && arg->maybe_flag));
}
SEL_ARG *clone_first(SEL_ARG *arg)
{ // min <= X < arg->min
@@ -874,8 +876,8 @@ public:
Used to store 'current key tuples', in both range analysis and
partitioning (list) analysis
*/
- uchar min_key[MAX_KEY_LENGTH+MAX_FIELD_WIDTH],
- max_key[MAX_KEY_LENGTH+MAX_FIELD_WIDTH];
+ uchar *min_key;
+ uchar *max_key;
/* Number of SEL_ARG objects allocated by SEL_ARG::clone_tree operations */
uint alloced_sel_args;
@@ -896,6 +898,14 @@ class PARAM : public RANGE_OPT_PARAM
{
public:
ha_rows quick_rows[MAX_KEY];
+
+ /*
+ This will collect 'possible keys' based on the range optimization.
+
+ Queries with a JOIN object actually use ref optimizer (see add_key_field)
+ to collect possible_keys. This is used by single table UPDATE/DELETE.
+ */
+ key_map possible_keys;
longlong baseflag;
uint max_key_part, range_count;
@@ -941,7 +951,7 @@ static bool is_key_scan_ror(PARAM *param, uint keynr, uint8 nparts);
static ha_rows check_quick_select(PARAM *param, uint idx, bool index_only,
SEL_ARG *tree, bool update_tbl_stats,
uint *mrr_flags, uint *bufsize,
- COST_VECT *cost);
+ Cost_estimate *cost);
QUICK_RANGE_SELECT *get_quick_select(PARAM *param,uint index,
SEL_ARG *key_tree, uint mrr_flags,
@@ -1823,7 +1833,8 @@ QUICK_RANGE_SELECT::QUICK_RANGE_SELECT(THD *thd, TABLE *table, uint key_nr,
index= key_nr;
head= table;
key_part_info= head->key_info[index].key_part;
- my_init_dynamic_array(&ranges, sizeof(QUICK_RANGE*), 16, 16);
+ my_init_dynamic_array(&ranges, sizeof(QUICK_RANGE*), 16, 16,
+ MYF(MY_THREAD_SPECIFIC));
/* 'thd' is not accessible in QUICK_RANGE_SELECT::reset(). */
mrr_buf_size= thd->variables.mrr_buff_size;
@@ -1832,7 +1843,8 @@ QUICK_RANGE_SELECT::QUICK_RANGE_SELECT(THD *thd, TABLE *table, uint key_nr,
if (!no_alloc && !parent_alloc)
{
// Allocates everything through the internal memroot
- init_sql_alloc(&alloc, thd->variables.range_alloc_block_size, 0);
+ init_sql_alloc(&alloc, thd->variables.range_alloc_block_size, 0,
+ MYF(MY_THREAD_SPECIFIC));
thd->mem_root= &alloc;
}
else
@@ -1842,13 +1854,13 @@ QUICK_RANGE_SELECT::QUICK_RANGE_SELECT(THD *thd, TABLE *table, uint key_nr,
/* Allocate a bitmap for used columns (Q: why not on MEM_ROOT?) */
if (!(bitmap= (my_bitmap_map*) my_malloc(head->s->column_bitmap_size,
- MYF(MY_WME))))
+ MYF(MY_WME | MY_THREAD_SPECIFIC))))
{
column_bitmap.bitmap= 0;
*create_error= 1;
}
else
- bitmap_init(&column_bitmap, bitmap, head->s->fields, FALSE);
+ my_bitmap_init(&column_bitmap, bitmap, head->s->fields, FALSE);
DBUG_VOID_RETURN;
}
@@ -1927,7 +1939,8 @@ QUICK_INDEX_SORT_SELECT::QUICK_INDEX_SORT_SELECT(THD *thd_param,
index= MAX_KEY;
head= table;
bzero(&read_record, sizeof(read_record));
- init_sql_alloc(&alloc, thd->variables.range_alloc_block_size, 0);
+ init_sql_alloc(&alloc, thd->variables.range_alloc_block_size, 0,
+ MYF(MY_THREAD_SPECIFIC));
DBUG_VOID_RETURN;
}
@@ -1998,7 +2011,8 @@ QUICK_ROR_INTERSECT_SELECT::QUICK_ROR_INTERSECT_SELECT(THD *thd_param,
head= table;
record= head->record[0];
if (!parent_alloc)
- init_sql_alloc(&alloc, thd->variables.range_alloc_block_size, 0);
+ init_sql_alloc(&alloc, thd->variables.range_alloc_block_size, 0,
+ MYF(MY_THREAD_SPECIFIC));
else
bzero(&alloc, sizeof(MEM_ROOT));
last_rowid= (uchar*) alloc_root(parent_alloc? parent_alloc : &alloc,
@@ -2204,7 +2218,7 @@ int QUICK_ROR_INTERSECT_SELECT::init_ror_merged_scan(bool reuse_handler,
quick->record= head->record[0];
}
- if (need_to_fetch_row && head->file->ha_rnd_init_with_error(1))
+ if (need_to_fetch_row && head->file->ha_rnd_init_with_error(false))
{
DBUG_PRINT("error", ("ROR index_merge rnd_init call failed"));
DBUG_RETURN(1);
@@ -2284,7 +2298,8 @@ QUICK_ROR_UNION_SELECT::QUICK_ROR_UNION_SELECT(THD *thd_param,
head= table;
rowid_length= table->file->ref_length;
record= head->record[0];
- init_sql_alloc(&alloc, thd->variables.range_alloc_block_size, 0);
+ init_sql_alloc(&alloc, thd->variables.range_alloc_block_size, 0,
+ MYF(MY_THREAD_SPECIFIC));
thd_param->mem_root= &alloc;
}
@@ -2385,8 +2400,13 @@ int QUICK_ROR_UNION_SELECT::reset()
quick->save_last_pos();
queue_insert(&queue, (uchar*)quick);
}
-
- if ((error= head->file->ha_rnd_init(1)))
+ /* Prepare for ha_rnd_pos calls. */
+ if (head->file->inited && (error= head->file->ha_rnd_end()))
+ {
+ DBUG_PRINT("error", ("ROR index_merge rnd_end call failed"));
+ DBUG_RETURN(error);
+ }
+ if ((error= head->file->ha_rnd_init(false)))
{
DBUG_PRINT("error", ("ROR index_merge rnd_init call failed"));
DBUG_RETURN(error);
@@ -2881,7 +2901,7 @@ static int fill_used_fields_bitmap(PARAM *param)
param->fields_bitmap_size= table->s->column_bitmap_size;
if (!(tmp= (my_bitmap_map*) alloc_root(param->mem_root,
param->fields_bitmap_size)) ||
- bitmap_init(&param->needed_fields, tmp, table->s->fields, FALSE))
+ my_bitmap_init(&param->needed_fields, tmp, table->s->fields, FALSE))
return 1;
bitmap_copy(&param->needed_fields, table->read_set);
@@ -2893,7 +2913,7 @@ static int fill_used_fields_bitmap(PARAM *param)
/* The table uses clustered PK and it is not internally generated */
KEY_PART_INFO *key_part= param->table->key_info[pk].key_part;
KEY_PART_INFO *key_part_end= key_part +
- param->table->key_info[pk].key_parts;
+ param->table->key_info[pk].user_defined_key_parts;
for (;key_part != key_part_end; ++key_part)
bitmap_clear_bit(&param->needed_fields, key_part->fieldnr-1);
}
@@ -2978,7 +2998,7 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
DBUG_PRINT("enter",("keys_to_use: %lu prev_tables: %lu const_tables: %lu",
(ulong) keys_to_use.to_ulonglong(), (ulong) prev_tables,
(ulong) const_tables));
- DBUG_PRINT("info", ("records: %lu", (ulong) head->file->stats.records));
+ DBUG_PRINT("info", ("records: %lu", (ulong) head->stat_records()));
delete quick;
quick=0;
needed_reg.clear_all();
@@ -2986,7 +3006,7 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
DBUG_ASSERT(!head->is_filled_at_execution());
if (keys_to_use.is_clear_all() || head->is_filled_at_execution())
DBUG_RETURN(0);
- records= head->file->stats.records;
+ records= head->stat_records();
if (!records)
records++; /* purecov: inspected */
scan_time= (double) records / TIME_FOR_COMPARE + 1;
@@ -2997,6 +3017,8 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
read_time= (double) records + scan_time + 1; // Force to use index
else if (read_time <= 2.0 && !force_quick_range)
DBUG_RETURN(0); /* No need for quick select */
+
+ possible_keys.clear_all();
DBUG_PRINT("info",("Time to scan table: %g", read_time));
@@ -3028,9 +3050,11 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
param.using_real_indexes= TRUE;
param.remove_jump_scans= TRUE;
param.force_default_mrr= ordered_output;
+ param.possible_keys.clear_all();
thd->no_errors=1; // Don't warn about NULL
- init_sql_alloc(&alloc, thd->variables.range_alloc_block_size, 0);
+ init_sql_alloc(&alloc, thd->variables.range_alloc_block_size, 0,
+ MYF(MY_THREAD_SPECIFIC));
if (!(param.key_parts=
(KEY_PART*) alloc_root(&alloc,
sizeof(KEY_PART) *
@@ -3042,13 +3066,13 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
DBUG_RETURN(0); // Can't use range
}
key_parts= param.key_parts;
- thd->mem_root= &alloc;
/*
Make an array with description of all key parts of all table keys.
This is used in get_mm_parts function.
*/
key_info= head->key_info;
+ uint max_key_len= 0;
for (idx=0 ; idx < head->s->keys ; idx++, key_info++)
{
KEY_PART_INFO *key_part_info;
@@ -3061,6 +3085,7 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
param.key[param.keys]=key_parts;
key_part_info= key_info->key_part;
+ uint cur_key_len= 0;
for (uint part= 0 ; part < n_key_parts ;
part++, key_parts++, key_part_info++)
{
@@ -3068,6 +3093,7 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
key_parts->part= part;
key_parts->length= key_part_info->length;
key_parts->store_length= key_part_info->store_length;
+ cur_key_len += key_part_info->store_length;
key_parts->field= key_part_info->field;
key_parts->null_bit= key_part_info->null_bit;
key_parts->image_type =
@@ -3076,10 +3102,22 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
key_parts->flag= (uint8) key_part_info->key_part_flag;
}
param.real_keynr[param.keys++]=idx;
+ if (cur_key_len > max_key_len)
+ max_key_len= cur_key_len;
}
param.key_parts_end=key_parts;
param.alloced_sel_args= 0;
+ max_key_len++; /* Take into account the "+1" in QUICK_RANGE::QUICK_RANGE */
+ if (!(param.min_key= (uchar*)alloc_root(&alloc,max_key_len)) ||
+ !(param.max_key= (uchar*)alloc_root(&alloc,max_key_len)))
+ {
+ thd->no_errors=0;
+ free_root(&alloc,MYF(0)); // Return memory & allocator
+ DBUG_RETURN(0); // Can't use range
+ }
+
+ thd->mem_root= &alloc;
/* Calculate cost of full index read for the shortest covering index */
if (!head->covering_keys.is_clear_all())
{
@@ -3122,8 +3160,8 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
group_trp= get_best_group_min_max(&param, tree, best_read_time);
if (group_trp)
{
- param.table->quick_condition_rows= min(group_trp->records,
- head->file->stats.records);
+ param.table->quick_condition_rows= MY_MIN(group_trp->records,
+ head->stat_records());
if (group_trp->read_cost < best_read_time)
{
best_trp= group_trp;
@@ -3198,7 +3236,8 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
}
}
- if (optimizer_flag(thd, OPTIMIZER_SWITCH_INDEX_MERGE))
+ if (optimizer_flag(thd, OPTIMIZER_SWITCH_INDEX_MERGE) &&
+ head->stat_records() != 0)
{
/* Try creating index_merge/ROR-union scan. */
SEL_IMERGE *imerge;
@@ -3238,6 +3277,7 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
quick= NULL;
}
}
+ possible_keys= param.possible_keys;
free_mem:
free_root(&alloc,MYF(0)); // Return memory & allocator
@@ -3245,18 +3285,543 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
thd->no_errors=0;
}
+
DBUG_EXECUTE("info", print_quick(quick, &needed_reg););
/*
Assume that if the user is using 'limit' we will only need to scan
limit rows if we are using a key
*/
- DBUG_RETURN(records ? test(quick) : -1);
+ DBUG_RETURN(records ? MY_TEST(quick) : -1);
+}
+
+/****************************************************************************
+ * Condition selectivity module
+ ****************************************************************************/
+
+
+/*
+ Build descriptors of pseudo-indexes over columns to perform range analysis
+
+ SYNOPSIS
+ create_key_parts_for_pseudo_indexes()
+ param IN/OUT data structure for the descriptors to be built
+ used_fields bitmap of columns for which the descriptors are to be built
+
+ DESCRIPTION
+ For each column marked in the bitmap used_fields the function builds
+ a descriptor of a single-component pseudo-index over this column that
+ can be used for the range analysis of the predicates over this columns.
+ The descriptors are created in the memory of param->mem_root.
+
+ RETURN
+ FALSE in the case of success
+ TRUE otherwise
+*/
+
+static
+bool create_key_parts_for_pseudo_indexes(RANGE_OPT_PARAM *param,
+ MY_BITMAP *used_fields)
+{
+ Field **field_ptr;
+ TABLE *table= param->table;
+ uint parts= 0;
+
+ for (field_ptr= table->field; *field_ptr; field_ptr++)
+ {
+ if (bitmap_is_set(used_fields, (*field_ptr)->field_index))
+ parts++;
+ }
+
+ KEY_PART *key_part;
+ uint keys= 0;
+
+ if (!(key_part= (KEY_PART *) alloc_root(param->mem_root,
+ sizeof(KEY_PART) * parts)))
+ return TRUE;
+
+ param->key_parts= key_part;
+ uint max_key_len= 0;
+ for (field_ptr= table->field; *field_ptr; field_ptr++)
+ {
+ if (bitmap_is_set(used_fields, (*field_ptr)->field_index))
+ {
+ Field *field= *field_ptr;
+ uint16 store_length;
+ key_part->key= keys;
+ key_part->part= 0;
+ key_part->length= (uint16) field->key_length();
+ store_length= key_part->length;
+ if (field->real_maybe_null())
+ store_length+= HA_KEY_NULL_LENGTH;
+ if (field->real_type() == MYSQL_TYPE_VARCHAR)
+ store_length+= HA_KEY_BLOB_LENGTH;
+ if (max_key_len < store_length)
+ max_key_len= store_length;
+ key_part->store_length= store_length;
+ key_part->field= field;
+ key_part->image_type= Field::itRAW;
+ key_part->flag= 0;
+ param->key[keys]= key_part;
+ keys++;
+ key_part++;
+ }
+ }
+
+ max_key_len++; /* Take into account the "+1" in QUICK_RANGE::QUICK_RANGE */
+ if (!(param->min_key= (uchar*)alloc_root(param->mem_root, max_key_len)) ||
+ !(param->max_key= (uchar*)alloc_root(param->mem_root, max_key_len)))
+ {
+ return true;
+ }
+ param->keys= keys;
+ param->key_parts_end= key_part;
+
+ return FALSE;
+}
+
+
+/*
+ Estimate the number of rows in all ranges built for a column
+ by the range optimizer
+
+ SYNOPSIS
+ records_in_column_ranges()
+ param the data structure to access descriptors of pseudo indexes
+ built over columns used in the condition of the processed query
+ idx the index of the descriptor of interest in param
+ tree the tree representing ranges built for the interesting column
+
+ DESCRIPTION
+ This function retrieves the ranges represented by the SEL_ARG 'tree' and
+ for each of them r it calls the function get_column_range_cardinality()
+ that estimates the number of expected rows in r. It is assumed that param
+ is the data structure containing the descriptors of pseudo-indexes that
+ has been built to perform range analysis of the range conditions imposed
+ on the columns used in the processed query, while idx is the index of the
+ descriptor created in 'param' exactly for the column for which 'tree'
+ has been built by the range optimizer.
+
+ RETURN
+ the number of rows in the retrieved ranges
+*/
+
+static
+double records_in_column_ranges(PARAM *param, uint idx,
+ SEL_ARG *tree)
+{
+ SEL_ARG_RANGE_SEQ seq;
+ KEY_MULTI_RANGE range;
+ range_seq_t seq_it;
+ double rows;
+ Field *field;
+ uint flags= 0;
+ double total_rows= 0;
+ RANGE_SEQ_IF seq_if = {NULL, sel_arg_range_seq_init,
+ sel_arg_range_seq_next, 0, 0};
+
+ /* Handle cases when we don't have a valid non-empty list of range */
+ if (!tree)
+ return HA_POS_ERROR;
+ if (tree->type == SEL_ARG::IMPOSSIBLE)
+ return (0L);
+
+ field= tree->field;
+
+ seq.keyno= idx;
+ seq.real_keyno= MAX_KEY;
+ seq.param= param;
+ seq.start= tree;
+
+ seq_it= seq_if.init((void *) &seq, 0, flags);
+
+ while (!seq_if.next(seq_it, &range))
+ {
+ key_range *min_endp, *max_endp;
+ min_endp= range.start_key.length? &range.start_key : NULL;
+ max_endp= range.end_key.length? &range.end_key : NULL;
+ rows= get_column_range_cardinality(field, min_endp, max_endp,
+ range.range_flag);
+ if (HA_POS_ERROR == rows)
+ {
+ total_rows= HA_POS_ERROR;
+ break;
+ }
+ total_rows += rows;
+ }
+ return total_rows;
+}
+
+
+/*
+ Calculate the selectivity of the condition imposed on the rows of a table
+
+ SYNOPSIS
+ calculate_cond_selectivity_for_table()
+ thd the context handle
+ table the table of interest
+ cond conditions imposed on the rows of the table
+
+ DESCRIPTION
+ This function calculates the selectivity of range conditions cond imposed
+ on the rows of 'table' in the processed query.
+ The calculated selectivity is assigned to the field table->cond_selectivity.
+
+ Selectivity is calculated as a product of selectivities imposed by:
+
+ 1. possible range accesses. (if multiple range accesses use the same
+ restrictions on the same field, we make adjustments for that)
+ 2. Sargable conditions on fields for which we have column statistics (if
+ a field is used in a possible range access, we assume that selectivity
+ is already provided by the range access' estimates)
+ 3. Reading a few records from the table pages and checking the condition
+ selectivity (this is used for conditions like "column LIKE '%val%'"
+ where approaches #1 and #2 do not provide selectivity data).
+
+ NOTE
+ Currently the selectivities of range conditions over different columns are
+ considered independent.
+
+ RETURN
+ FALSE on success
+ TRUE otherwise
+*/
+
+bool calculate_cond_selectivity_for_table(THD *thd, TABLE *table, Item *cond)
+{
+ uint keynr;
+ uint max_quick_key_parts= 0;
+ MY_BITMAP *used_fields= &table->cond_set;
+ double table_records= table->stat_records();
+ DBUG_ENTER("calculate_cond_selectivity_for_table");
+
+ table->cond_selectivity= 1.0;
+
+ if (!cond || table_records == 0)
+ DBUG_RETURN(FALSE);
+
+ if (table->pos_in_table_list->schema_table)
+ DBUG_RETURN(FALSE);
+
+ MY_BITMAP handled_columns;
+ my_bitmap_map* buf;
+ if (!(buf= (my_bitmap_map*)thd->alloc(table->s->column_bitmap_size)))
+ DBUG_RETURN(TRUE);
+ my_bitmap_init(&handled_columns, buf, table->s->fields, FALSE);
+
+ /*
+ Calculate the selectivity of the range conditions supported by indexes.
+
+ First, take into account possible range accesses.
+ range access estimates are the most precise, we prefer them to any other
+ estimate sources.
+ */
+
+ for (keynr= 0; keynr < table->s->keys; keynr++)
+ {
+ if (table->quick_keys.is_set(keynr))
+ set_if_bigger(max_quick_key_parts, table->quick_key_parts[keynr]);
+ }
+
+ /*
+ Walk through all indexes, indexes where range access uses more keyparts
+ go first.
+ */
+ for (uint quick_key_parts= max_quick_key_parts;
+ quick_key_parts; quick_key_parts--)
+ {
+ for (keynr= 0; keynr < table->s->keys; keynr++)
+ {
+ if (table->quick_keys.is_set(keynr) &&
+ table->quick_key_parts[keynr] == quick_key_parts)
+ {
+ uint i;
+ uint used_key_parts= table->quick_key_parts[keynr];
+ double quick_cond_selectivity= table->quick_rows[keynr] /
+ table_records;
+ KEY *key_info= table->key_info + keynr;
+ KEY_PART_INFO* key_part= key_info->key_part;
+ /*
+ Suppose, there are range conditions on two keys
+ KEY1 (col1, col2)
+ KEY2 (col3, col2)
+
+ we don't want to count selectivity of condition on col2 twice.
+
+ First, find the longest key prefix that's made of columns whose
+ selectivity wasn't already accounted for.
+ */
+ for (i= 0; i < used_key_parts; i++, key_part++)
+ {
+ if (bitmap_is_set(&handled_columns, key_part->fieldnr-1))
+ break;
+ bitmap_set_bit(&handled_columns, key_part->fieldnr-1);
+ }
+ if (i)
+ {
+ double UNINIT_VAR(selectivity_mult);
+ /*
+ There is at least 1-column prefix of columns whose selectivity has
+ not yet been accounted for.
+ */
+ table->cond_selectivity*= quick_cond_selectivity;
+ if (i != used_key_parts)
+ {
+ /*
+ Range access got us estimate for #used_key_parts.
+ We need estimate for #(i-1) key parts.
+ */
+ double f1= key_info->actual_rec_per_key(i-1);
+ double f2= key_info->actual_rec_per_key(i);
+ if (f1 > 0 && f2 > 0)
+ selectivity_mult= f1 / f2;
+ else
+ {
+ /*
+ No statistics available, assume the selectivity is proportional
+ to the number of key parts.
+ (i=0 means 1 keypart, i=1 means 2 keyparts, so use i+1)
+ */
+ selectivity_mult= ((double)(i+1)) / i;
+ }
+ table->cond_selectivity*= selectivity_mult;
+ }
+ /*
+ We need to set selectivity for fields supported by indexes.
+ For single-component indexes and for some first components
+ of other indexes we do it here. For the remaining fields
+ we do it later in this function, in the same way as for the
+ fields not used in any indexes.
+ */
+ if (i == 1)
+ {
+ uint fieldnr= key_info->key_part[0].fieldnr;
+ table->field[fieldnr-1]->cond_selectivity= quick_cond_selectivity;
+ if (i != used_key_parts)
+ table->field[fieldnr-1]->cond_selectivity*= selectivity_mult;
+ bitmap_clear_bit(used_fields, fieldnr-1);
+ }
+ }
+ }
+ }
+ }
+
+ /*
+ Second step: calculate the selectivity of the range conditions not
+ supported by any index and selectivity of the range condition
+ over the fields whose selectivity has not been set yet.
+ */
+
+ if (thd->variables.optimizer_use_condition_selectivity > 2 &&
+ !bitmap_is_clear_all(used_fields))
+ {
+ PARAM param;
+ MEM_ROOT alloc;
+ SEL_TREE *tree;
+ SEL_ARG **key, **end;
+ double rows;
+ uint idx= 0;
+
+ init_sql_alloc(&alloc, thd->variables.range_alloc_block_size, 0,
+ MYF(MY_THREAD_SPECIFIC));
+ param.thd= thd;
+ param.mem_root= &alloc;
+ param.old_root= thd->mem_root;
+ param.table= table;
+ param.is_ror_scan= FALSE;
+
+ if (create_key_parts_for_pseudo_indexes(&param, used_fields))
+ goto free_alloc;
+
+ param.prev_tables= param.read_tables= 0;
+ param.current_table= table->map;
+ param.using_real_indexes= FALSE;
+ param.real_keynr[0]= 0;
+ param.alloced_sel_args= 0;
+
+ thd->no_errors=1;
+
+ tree= get_mm_tree(&param, cond);
+
+ if (!tree)
+ goto free_alloc;
+
+ table->reginfo.impossible_range= 0;
+ if (tree->type == SEL_TREE::IMPOSSIBLE)
+ {
+ rows= 0;
+ table->reginfo.impossible_range= 1;
+ goto free_alloc;
+ }
+ else if (tree->type == SEL_TREE::ALWAYS)
+ {
+ rows= table_records;
+ goto free_alloc;
+ }
+ else if (tree->type == SEL_TREE::MAYBE)
+ {
+ rows= table_records;
+ goto free_alloc;
+ }
+
+ for (key= tree->keys, end= key + param.keys; key != end; key++, idx++)
+ {
+ if (*key)
+ {
+ if ((*key)->type == SEL_ARG::IMPOSSIBLE)
+ {
+ rows= 0;
+ table->reginfo.impossible_range= 1;
+ goto free_alloc;
+ }
+ else
+ {
+ rows= records_in_column_ranges(&param, idx, *key);
+ if (rows != HA_POS_ERROR)
+ (*key)->field->cond_selectivity= rows/table_records;
+ }
+ }
+ }
+
+ for (Field **field_ptr= table->field; *field_ptr; field_ptr++)
+ {
+ Field *table_field= *field_ptr;
+ if (bitmap_is_set(used_fields, table_field->field_index) &&
+ table_field->cond_selectivity < 1.0)
+ {
+ if (!bitmap_is_set(&handled_columns, table_field->field_index))
+ table->cond_selectivity*= table_field->cond_selectivity;
+ }
+ }
+
+ free_alloc:
+ thd->mem_root= param.old_root;
+ free_root(&alloc, MYF(0));
+
+ }
+
+ bitmap_union(used_fields, &handled_columns);
+
+ /* Check if we can improve selectivity estimates by using sampling */
+ ulong check_rows=
+ MY_MIN(thd->variables.optimizer_selectivity_sampling_limit,
+ (ulong) (table_records * SELECTIVITY_SAMPLING_SHARE));
+ if (cond && check_rows > SELECTIVITY_SAMPLING_THRESHOLD &&
+ thd->variables.optimizer_use_condition_selectivity > 4)
+ {
+ find_selective_predicates_list_processor_data *dt=
+ (find_selective_predicates_list_processor_data *)
+ alloc_root(thd->mem_root,
+ sizeof(find_selective_predicates_list_processor_data));
+ if (!dt)
+ DBUG_RETURN(TRUE);
+ dt->list.empty();
+ dt->table= table;
+ if (cond->walk(&Item::find_selective_predicates_list_processor, 0,
+ (uchar*) dt))
+ DBUG_RETURN(TRUE);
+ if (dt->list.elements > 0)
+ {
+ check_rows= check_selectivity(thd, check_rows, table, &dt->list);
+ if (check_rows > SELECTIVITY_SAMPLING_THRESHOLD)
+ {
+ COND_STATISTIC *stat;
+ List_iterator_fast<COND_STATISTIC> it(dt->list);
+ double examined_rows= check_rows;
+ while ((stat= it++))
+ {
+ if (!stat->positive)
+ {
+ DBUG_PRINT("info", ("To avoid 0 assigned 1 to the counter"));
+ stat->positive= 1; // avoid 0
+ }
+ DBUG_PRINT("info", ("The predicate selectivity : %g",
+ (double)stat->positive / examined_rows));
+ double selectivity= ((double)stat->positive) / examined_rows;
+ table->cond_selectivity*= selectivity;
+ /*
+ If a field is involved then we register its selectivity in case
+ there in an equality with the field.
+ For example in case
+ t1.a LIKE "%bla%" and t1.a = t2.b
+ the selectivity we have found could be used also for t2.
+ */
+ if (stat->field_arg)
+ {
+ stat->field_arg->cond_selectivity*= selectivity;
+
+ if (stat->field_arg->next_equal_field)
+ {
+ for (Field *next_field= stat->field_arg->next_equal_field;
+ next_field != stat->field_arg;
+ next_field= next_field->next_equal_field)
+ {
+ next_field->cond_selectivity*= selectivity;
+ next_field->table->cond_selectivity*= selectivity;
+ }
+ }
+ }
+ }
+
+ }
+ /* This list and its elements put to mem_root so should not be freed */
+ table->cond_selectivity_sampling_explain= &dt->list;
+ }
+ }
+
+ DBUG_RETURN(FALSE);
}
/****************************************************************************
+ * Condition selectivity code ends
+ ****************************************************************************/
+
+/****************************************************************************
* Partition pruning module
****************************************************************************/
+
+/*
+ Store field key image to table record
+
+ SYNOPSIS
+ store_key_image_to_rec()
+ field Field which key image should be stored
+ ptr Field value in key format
+ len Length of the value, in bytes
+
+ ATTENTION
+ len is the length of the value not counting the NULL-byte (at the same
+ time, ptr points to the key image, which starts with NULL-byte for
+ nullable columns)
+
+ DESCRIPTION
+ Copy the field value from its key image to the table record. The source
+ is the value in key image format, occupying len bytes in buffer pointed
+ by ptr. The destination is table record, in "field value in table record"
+ format.
+*/
+
+void store_key_image_to_rec(Field *field, uchar *ptr, uint len)
+{
+ /* Do the same as print_key() does */
+ my_bitmap_map *old_map;
+
+ if (field->real_maybe_null())
+ {
+ if (*ptr)
+ {
+ field->set_null();
+ return;
+ }
+ field->set_notnull();
+ ptr++;
+ }
+ old_map= dbug_tmp_use_all_columns(field->table,
+ field->table->write_set);
+ field->set_key_image(ptr, len);
+ dbug_tmp_restore_column_map(field->table->write_set, old_map);
+}
+
#ifdef WITH_PARTITION_STORAGE_ENGINE
/*
@@ -3381,8 +3946,8 @@ typedef struct st_part_prune_param
int last_subpart_partno; /* Same as above for supartitioning */
/*
- is_part_keypart[i] == test(keypart #i in partitioning index is a member
- used in partitioning)
+ is_part_keypart[i] == MY_TEST(keypart #i in partitioning index is a member
+ used in partitioning)
Used to maintain current values of cur_part_fields and cur_subpart_fields
*/
my_bool *is_part_keypart;
@@ -3429,29 +3994,26 @@ static void dbug_print_singlepoint_range(SEL_ARG **start, uint num);
#endif
-/*
+/**
Perform partition pruning for a given table and condition.
- SYNOPSIS
- prune_partitions()
- thd Thread handle
- table Table to perform partition pruning for
- pprune_cond Condition to use for partition pruning
+ @param thd Thread handle
+ @param table Table to perform partition pruning for
+ @param pprune_cond Condition to use for partition pruning
- DESCRIPTION
- This function assumes that all partitions are marked as unused when it
- is invoked. The function analyzes the condition, finds partitions that
- need to be used to retrieve the records that match the condition, and
- marks them as used by setting appropriate bit in part_info->used_partitions
- In the worst case all partitions are marked as used.
-
- NOTE
- This function returns promptly if called for non-partitioned table.
-
- RETURN
- TRUE We've inferred that no partitions need to be used (i.e. no table
- records will satisfy pprune_cond)
- FALSE Otherwise
+ @note This function assumes that lock_partitions are setup when it
+ is invoked. The function analyzes the condition, finds partitions that
+ need to be used to retrieve the records that match the condition, and
+ marks them as used by setting appropriate bit in part_info->read_partitions
+ In the worst case all partitions are marked as used. If the table is not
+ yet locked, it will also unset bits in part_info->lock_partitions that is
+ not set in read_partitions.
+
+ This function returns promptly if called for non-partitioned table.
+
+ @return Operation status
+ @retval true Failure
+ @retval false Success
*/
bool prune_partitions(THD *thd, TABLE *table, Item *pprune_cond)
@@ -3475,7 +4037,8 @@ bool prune_partitions(THD *thd, TABLE *table, Item *pprune_cond)
my_bitmap_map *old_sets[2];
prune_param.part_info= part_info;
- init_sql_alloc(&alloc, thd->variables.range_alloc_block_size, 0);
+ init_sql_alloc(&alloc, thd->variables.range_alloc_block_size, 0,
+ MYF(MY_THREAD_SPECIFIC));
range_par->mem_root= &alloc;
range_par->old_root= thd->mem_root;
@@ -3503,7 +4066,7 @@ bool prune_partitions(THD *thd, TABLE *table, Item *pprune_cond)
thd->no_errors=1; // Don't warn about NULL
thd->mem_root=&alloc;
- bitmap_clear_all(&part_info->used_partitions);
+ bitmap_clear_all(&part_info->read_partitions);
prune_param.key= prune_param.range_param.key_parts;
SEL_TREE *tree;
@@ -3575,7 +4138,7 @@ bool prune_partitions(THD *thd, TABLE *table, Item *pprune_cond)
res == 1 => some used partitions => retval=FALSE
res == -1 - we jump over this line to all_used:
*/
- retval= test(!res);
+ retval= MY_TEST(!res);
goto end;
all_used:
@@ -3586,45 +4149,34 @@ end:
thd->no_errors=0;
thd->mem_root= range_par->old_root;
free_root(&alloc,MYF(0)); // Return memory & allocator
- DBUG_RETURN(retval);
-}
-
-
-/*
- Store field key image to table record
-
- SYNOPSIS
- store_key_image_to_rec()
- field Field which key image should be stored
- ptr Field value in key format
- len Length of the value, in bytes
-
- DESCRIPTION
- Copy the field value from its key image to the table record. The source
- is the value in key image format, occupying len bytes in buffer pointed
- by ptr. The destination is table record, in "field value in table record"
- format.
-*/
-
-void store_key_image_to_rec(Field *field, uchar *ptr, uint len)
-{
- /* Do the same as print_key() does */
- my_bitmap_map *old_map;
-
- if (field->real_maybe_null())
+ /*
+ Must be a subset of the locked partitions.
+ lock_partitions contains the partitions marked by explicit partition
+ selection (... t PARTITION (pX) ...) and we must only use partitions
+ within that set.
+ */
+ bitmap_intersect(&prune_param.part_info->read_partitions,
+ &prune_param.part_info->lock_partitions);
+ /*
+ If not yet locked, also prune partitions to lock if not UPDATEing
+ partition key fields. This will also prune lock_partitions if we are under
+ LOCK TABLES, so prune away calls to start_stmt().
+ TODO: enhance this prune locking to also allow pruning of
+ 'UPDATE t SET part_key = const WHERE cond_is_prunable' so it adds
+ a lock for part_key partition.
+ */
+ if (table->file->get_lock_type() == F_UNLCK &&
+ !partition_key_modified(table, table->write_set))
{
- if (*ptr)
- {
- field->set_null();
- return;
- }
- field->set_notnull();
- ptr++;
- }
- old_map= dbug_tmp_use_all_columns(field->table,
- field->table->write_set);
- field->set_key_image(ptr, len);
- dbug_tmp_restore_column_map(field->table->write_set, old_map);
+ bitmap_copy(&prune_param.part_info->lock_partitions,
+ &prune_param.part_info->read_partitions);
+ }
+ if (bitmap_is_clear_all(&(prune_param.part_info->read_partitions)))
+ {
+ table->all_partitions_pruned_away= true;
+ retval= TRUE;
+ }
+ DBUG_RETURN(retval);
}
@@ -3661,7 +4213,7 @@ static void mark_full_partition_used_no_parts(partition_info* part_info,
{
DBUG_ENTER("mark_full_partition_used_no_parts");
DBUG_PRINT("enter", ("Mark partition %u as used", part_id));
- bitmap_set_bit(&part_info->used_partitions, part_id);
+ bitmap_set_bit(&part_info->read_partitions, part_id);
DBUG_VOID_RETURN;
}
@@ -3677,7 +4229,7 @@ static void mark_full_partition_used_with_parts(partition_info *part_info,
for (; start != end; start++)
{
DBUG_PRINT("info", ("1:Mark subpartition %u as used", start));
- bitmap_set_bit(&part_info->used_partitions, start);
+ bitmap_set_bit(&part_info->read_partitions, start);
}
DBUG_VOID_RETURN;
}
@@ -3705,7 +4257,7 @@ static int find_used_partitions_imerge_list(PART_PRUNE_PARAM *ppar,
MY_BITMAP all_merges;
uint bitmap_bytes;
my_bitmap_map *bitmap_buf;
- uint n_bits= ppar->part_info->used_partitions.n_bits;
+ uint n_bits= ppar->part_info->read_partitions.n_bits;
bitmap_bytes= bitmap_buffer_size(n_bits);
if (!(bitmap_buf= (my_bitmap_map*) alloc_root(ppar->range_param.mem_root,
bitmap_bytes)))
@@ -3716,7 +4268,7 @@ static int find_used_partitions_imerge_list(PART_PRUNE_PARAM *ppar,
*/
return find_used_partitions_imerge(ppar, merges.head());
}
- bitmap_init(&all_merges, bitmap_buf, n_bits, FALSE);
+ my_bitmap_init(&all_merges, bitmap_buf, n_bits, FALSE);
bitmap_set_prefix(&all_merges, n_bits);
List_iterator<SEL_IMERGE> it(merges);
@@ -3731,14 +4283,15 @@ static int find_used_partitions_imerge_list(PART_PRUNE_PARAM *ppar,
}
if (res != -1)
- bitmap_intersect(&all_merges, &ppar->part_info->used_partitions);
+ bitmap_intersect(&all_merges, &ppar->part_info->read_partitions);
+
if (bitmap_is_clear_all(&all_merges))
return 0;
- bitmap_clear_all(&ppar->part_info->used_partitions);
+ bitmap_clear_all(&ppar->part_info->read_partitions);
}
- memcpy(ppar->part_info->used_partitions.bitmap, all_merges.bitmap,
+ memcpy(ppar->part_info->read_partitions.bitmap, all_merges.bitmap,
bitmap_bytes);
return 1;
}
@@ -4107,7 +4660,7 @@ int find_used_partitions(PART_PRUNE_PARAM *ppar, SEL_ARG *key_tree)
{
for (uint i= 0; i < ppar->part_info->num_subparts; i++)
if (bitmap_is_set(&ppar->subparts_bitmap, i))
- bitmap_set_bit(&ppar->part_info->used_partitions,
+ bitmap_set_bit(&ppar->part_info->read_partitions,
part_id * ppar->part_info->num_subparts + i);
}
goto pop_and_go_right;
@@ -4169,7 +4722,7 @@ int find_used_partitions(PART_PRUNE_PARAM *ppar, SEL_ARG *key_tree)
while ((part_id= ppar->part_iter.get_next(&ppar->part_iter)) !=
NOT_A_PARTITION_ID)
{
- bitmap_set_bit(&part_info->used_partitions,
+ bitmap_set_bit(&part_info->read_partitions,
part_id * part_info->num_subparts + subpart_id);
}
res= 1; /* Some partitions were marked as used */
@@ -4227,7 +4780,7 @@ process_next_key_part:
ppar->mark_full_partition_used(ppar->part_info, part_id);
found= TRUE;
}
- res= test(found);
+ res= MY_TEST(found);
}
/*
Restore the "used partitions iterator" to the default setting that
@@ -4255,7 +4808,8 @@ pop_and_go_right:
static void mark_all_partitions_as_used(partition_info *part_info)
{
- bitmap_set_all(&part_info->used_partitions);
+ bitmap_copy(&(part_info->read_partitions),
+ &(part_info->lock_partitions));
}
@@ -4370,19 +4924,21 @@ static bool create_partition_index_description(PART_PRUNE_PARAM *ppar)
uint32 bufsize= bitmap_buffer_size(ppar->part_info->num_subparts);
if (!(buf= (my_bitmap_map*) alloc_root(alloc, bufsize)))
return TRUE;
- bitmap_init(&ppar->subparts_bitmap, buf, ppar->part_info->num_subparts,
+ my_bitmap_init(&ppar->subparts_bitmap, buf, ppar->part_info->num_subparts,
FALSE);
}
range_par->key_parts= key_part;
Field **field= (ppar->part_fields)? part_info->part_field_array :
part_info->subpart_field_array;
bool in_subpart_fields= FALSE;
+ uint total_key_len= 0;
for (uint part= 0; part < total_parts; part++, key_part++)
{
key_part->key= 0;
key_part->part= part;
key_part->length= (uint16)(*field)->key_length();
key_part->store_length= (uint16)get_partition_field_store_length(*field);
+ total_key_len += key_part->store_length;
DBUG_PRINT("info", ("part %u length %u store_length %u", part,
key_part->length, key_part->store_length));
@@ -4412,6 +4968,13 @@ static bool create_partition_index_description(PART_PRUNE_PARAM *ppar)
}
range_par->key_parts_end= key_part;
+ total_key_len++; /* Take into account the "+1" in QUICK_RANGE::QUICK_RANGE */
+ if (!(range_par->min_key= (uchar*)alloc_root(alloc,total_key_len)) ||
+ !(range_par->max_key= (uchar*)alloc_root(alloc,total_key_len)))
+ {
+ return true;
+ }
+
DBUG_EXECUTE("info", print_partitioning_index(range_par->key_parts,
range_par->key_parts_end););
return FALSE;
@@ -4738,7 +5301,7 @@ TABLE_READ_PLAN *get_best_disjunct_quick(PARAM *param, SEL_IMERGE *imerge,
DBUG_PRINT("info", ("index_merge scans cost %g", imerge_cost));
if (imerge_too_expensive || (imerge_cost > read_time) ||
((non_cpk_scan_records+cpk_scan_records >=
- param->table->file->stats.records) &&
+ param->table->stat_records()) &&
read_time != DBL_MAX))
{
/*
@@ -4808,8 +5371,8 @@ TABLE_READ_PLAN *get_best_disjunct_quick(PARAM *param, SEL_IMERGE *imerge,
{
imerge_trp->read_cost= imerge_cost;
imerge_trp->records= non_cpk_scan_records + cpk_scan_records;
- imerge_trp->records= min(imerge_trp->records,
- param->table->file->stats.records);
+ imerge_trp->records= MY_MIN(imerge_trp->records,
+ param->table->stat_records());
imerge_trp->range_scans= range_scans;
imerge_trp->range_scans_end= range_scans + n_child_scans;
read_time= imerge_cost;
@@ -4880,7 +5443,7 @@ skip_to_ror_scan:
((TRP_ROR_INTERSECT*)(*cur_roru_plan))->index_scan_costs;
roru_total_records += (*cur_roru_plan)->records;
roru_intersect_part *= (*cur_roru_plan)->records /
- param->table->file->stats.records;
+ param->table->stat_records();
}
/*
@@ -4890,7 +5453,7 @@ skip_to_ror_scan:
in disjunction do not share key parts.
*/
roru_total_records -= (ha_rows)(roru_intersect_part*
- param->table->file->stats.records);
+ param->table->stat_records());
/* ok, got a ROR read plan for each of the disjuncts
Calculate cost:
cost(index_union_scan(scan_1, ... scan_n)) =
@@ -5130,7 +5693,7 @@ bool create_fields_bitmap(PARAM *param, MY_BITMAP *fields_bitmap)
if (!(bitmap_buf= (my_bitmap_map *) alloc_root(param->mem_root,
param->fields_bitmap_size)))
return TRUE;
- if (bitmap_init(fields_bitmap, bitmap_buf, param->table->s->fields, FALSE))
+ if (my_bitmap_init(fields_bitmap, bitmap_buf, param->table->s->fields, FALSE))
return TRUE;
return FALSE;
@@ -5171,12 +5734,12 @@ static inline
ha_rows get_table_cardinality_for_index_intersect(TABLE *table)
{
if (table->file->ha_table_flags() & HA_STATS_RECORDS_IS_EXACT)
- return table->file->stats.records;
+ return table->stat_records();
else
{
ha_rows d;
double q;
- for (q= (double)table->file->stats.records, d= 1 ; q >= 10; q/= 10, d*= 10 ) ;
+ for (q= (double)table->stat_records(), d= 1 ; q >= 10; q/= 10, d*= 10 ) ;
return (ha_rows) (floor(q+0.5) * d);
}
}
@@ -5268,7 +5831,7 @@ bool prepare_search_best_index_intersect(PARAM *param,
}
}
- i= n_index_scans - test(cpk_scan != NULL) + 1;
+ i= n_index_scans - MY_TEST(cpk_scan != NULL) + 1;
if (!(common->search_scans =
(INDEX_SCAN_INFO **) alloc_root (param->mem_root,
@@ -5338,7 +5901,7 @@ bool prepare_search_best_index_intersect(PARAM *param,
if (!(common->best_intersect=
(INDEX_SCAN_INFO **) alloc_root (param->mem_root,
sizeof(INDEX_SCAN_INFO *) *
- (i + test(cpk_scan != NULL)))))
+ (i + MY_TEST(cpk_scan != NULL)))))
return TRUE;
size_t calc_cost_buff_size=
@@ -5400,7 +5963,7 @@ bool prepare_search_best_index_intersect(PARAM *param,
this number by #r.
If we do not make any assumptions then we can only state that
- #r<=min(#r1,#r2).
+ #r<=MY_MIN(#r1,#r2).
With this estimate we can't say that the index intersection scan will be
cheaper than the cheapest index scan.
@@ -5433,7 +5996,7 @@ bool prepare_search_best_index_intersect(PARAM *param,
#rt2_0 of the same range for sub-index idx2_0(dept) of the index idx2.
The current code does not make an estimate either for #rt1_0, or for #rt2_0,
but it can be adjusted to provide those numbers.
- Alternatively, min(rec_per_key) for (dept) could be used to get an upper
+ Alternatively, MY_MIN(rec_per_key) for (dept) could be used to get an upper
bound for the value of sel(Rt1&Rt2). Yet this statistics is not provided
now.
@@ -5444,7 +6007,7 @@ bool prepare_search_best_index_intersect(PARAM *param,
sel(Rt1&Rt2)=sel(dept=5)*sel(last_name='Sm5')*sel(first_name='Robert')
=sel(Rt2)*sel(dept=5)
- Here max(rec_per_key) for (dept) could be used to get an upper bound for
+ Here MY_MAX(rec_per_key) for (dept) could be used to get an upper bound for
the value of sel(Rt1&Rt2).
When the intersected indexes have different major columns, but some
@@ -5497,9 +6060,9 @@ bool prepare_search_best_index_intersect(PARAM *param,
f_1 = rec_per_key[first_name]/rec_per_key[last_name].
The the number of records in the range tree:
Rt_0: (first_name='Robert' OR first_name='Bob')
- for the sub-index (first_name) is not greater than max(#r*f_1, #t).
+ for the sub-index (first_name) is not greater than MY_MAX(#r*f_1, #t).
Strictly speaking, we can state only that it's not greater than
- max(#r*max_f_1, #t), where
+ MY_MAX(#r*max_f_1, #t), where
max_f_1= max_rec_per_key[first_name]/min_rec_per_key[last_name].
Yet, if #r/#t is big enough (and this is the case of an index intersection,
because using this index range with a single index scan is cheaper than
@@ -5579,9 +6142,8 @@ ha_rows records_in_index_intersect_extension(PARTIAL_INDEX_INTERSECT_INFO *curr,
ha_rows ext_records= ext_index_scan->records;
if (i < used_key_parts)
{
- ulong *rec_per_key= key_info->rec_per_key+i-1;
- ulong f1= rec_per_key[0] ? rec_per_key[0] : 1;
- ulong f2= rec_per_key[1] ? rec_per_key[1] : 1;
+ double f1= key_info->actual_rec_per_key(i-1);
+ double f2= key_info->actual_rec_per_key(i);
ext_records= (ha_rows) ((double) ext_records / f2 * f1);
}
if (ext_records < table_cardinality)
@@ -5949,14 +6511,14 @@ ROR_SCAN_INFO *make_ror_scan(const PARAM *param, int idx, SEL_ARG *sel_arg)
param->fields_bitmap_size)))
DBUG_RETURN(NULL);
- if (bitmap_init(&ror_scan->covered_fields, bitmap_buf,
+ if (my_bitmap_init(&ror_scan->covered_fields, bitmap_buf,
param->table->s->fields, FALSE))
DBUG_RETURN(NULL);
bitmap_clear_all(&ror_scan->covered_fields);
KEY_PART_INFO *key_part= param->table->key_info[keynr].key_part;
KEY_PART_INFO *key_part_end= key_part +
- param->table->key_info[keynr].key_parts;
+ param->table->key_info[keynr].user_defined_key_parts;
for (;key_part != key_part_end; ++key_part)
{
if (bitmap_is_set(&param->needed_fields, key_part->fieldnr-1))
@@ -6067,13 +6629,13 @@ ROR_INTERSECT_INFO* ror_intersect_init(const PARAM *param)
if (!(buf= (my_bitmap_map*) alloc_root(param->mem_root,
param->fields_bitmap_size)))
return NULL;
- if (bitmap_init(&info->covered_fields, buf, param->table->s->fields,
+ if (my_bitmap_init(&info->covered_fields, buf, param->table->s->fields,
FALSE))
return NULL;
info->is_covering= FALSE;
info->index_scan_costs= 0.0;
info->index_records= 0;
- info->out_rows= (double) param->table->file->stats.records;
+ info->out_rows= (double) param->table->stat_records();
bitmap_clear_all(&info->covered_fields);
return info;
}
@@ -6191,23 +6753,23 @@ static double ror_scan_selectivity(const ROR_INTERSECT_INFO *info,
SEL_ARG *sel_arg, *tuple_arg= NULL;
key_part_map keypart_map= 0;
bool cur_covered;
- bool prev_covered= test(bitmap_is_set(&info->covered_fields,
- key_part->fieldnr-1));
+ bool prev_covered= MY_TEST(bitmap_is_set(&info->covered_fields,
+ key_part->fieldnr - 1));
key_range min_range;
key_range max_range;
min_range.key= key_val;
min_range.flag= HA_READ_KEY_EXACT;
max_range.key= key_val;
max_range.flag= HA_READ_AFTER_KEY;
- ha_rows prev_records= info->param->table->file->stats.records;
+ ha_rows prev_records= info->param->table->stat_records();
DBUG_ENTER("ror_scan_selectivity");
for (sel_arg= scan->sel_arg; sel_arg;
sel_arg= sel_arg->next_key_part)
{
DBUG_PRINT("info",("sel_arg step"));
- cur_covered= test(bitmap_is_set(&info->covered_fields,
- key_part[sel_arg->part].fieldnr-1));
+ cur_covered= MY_TEST(bitmap_is_set(&info->covered_fields,
+ key_part[sel_arg->part].fieldnr - 1));
if (cur_covered != prev_covered)
{
/* create (part1val, ..., part{n-1}val) tuple. */
@@ -6426,7 +6988,7 @@ TRP_ROR_INTERSECT *get_best_ror_intersect(const PARAM *param, SEL_TREE *tree,
double min_cost= DBL_MAX;
DBUG_ENTER("get_best_ror_intersect");
- if ((tree->n_ror_scans < 2) || !param->table->file->stats.records ||
+ if ((tree->n_ror_scans < 2) || !param->table->stat_records() ||
!optimizer_flag(param->thd, OPTIMIZER_SWITCH_INDEX_MERGE_INTERSECT))
DBUG_RETURN(NULL);
@@ -6629,7 +7191,7 @@ TRP_ROR_INTERSECT *get_best_covering_ror_intersect(PARAM *param,
for (ROR_SCAN_INFO **scan= tree->ror_scans; scan != ror_scans_end; ++scan)
(*scan)->key_components=
- param->table->key_info[(*scan)->keynr].key_parts;
+ param->table->key_info[(*scan)->keynr].user_defined_key_parts;
/*
Run covering-ROR-search algorithm.
@@ -6644,7 +7206,7 @@ TRP_ROR_INTERSECT *get_best_covering_ror_intersect(PARAM *param,
covered_fields->bitmap= (my_bitmap_map*)alloc_root(param->mem_root,
param->fields_bitmap_size);
if (!covered_fields->bitmap ||
- bitmap_init(covered_fields, covered_fields->bitmap,
+ my_bitmap_init(covered_fields, covered_fields->bitmap,
param->table->s->fields, FALSE))
DBUG_RETURN(0);
bitmap_clear_all(covered_fields);
@@ -6786,7 +7348,7 @@ static TRP_RANGE *get_key_scans_params(PARAM *param, SEL_TREE *tree,
if (*key)
{
ha_rows found_records;
- COST_VECT cost;
+ Cost_estimate cost;
double found_read_time;
uint mrr_flags, buf_size;
INDEX_SCAN_INFO *index_scan;
@@ -7304,7 +7866,8 @@ static SEL_TREE *get_func_mm_tree(RANGE_OPT_PARAM *param, Item_func *cond_func,
param PARAM from SQL_SELECT::test_quick_select
cond_func item for the predicate
field_item field in the predicate
- value constant in the predicate
+ value constant in the predicate (or a field already read from
+ a table in the case of dynamic range access)
(for BETWEEN it contains the number of the field argument,
for IN it's always 0)
inv TRUE <> NOT cond_func is considered
@@ -7573,24 +8136,43 @@ static SEL_TREE *get_mm_tree(RANGE_OPT_PARAM *param,COND *cond)
DBUG_RETURN(ftree);
}
default:
+
+ DBUG_ASSERT (!ftree);
if (cond_func->arguments()[0]->real_item()->type() == Item::FIELD_ITEM)
{
field_item= (Item_field*) (cond_func->arguments()[0]->real_item());
- value= cond_func->arg_count > 1 ? cond_func->arguments()[1] : 0;
+ value= cond_func->arg_count > 1 ? cond_func->arguments()[1] : NULL;
+ if (value && value->is_expensive())
+ DBUG_RETURN(0);
+ if (!cond_func->arguments()[0]->real_item()->const_item())
+ ftree= get_full_func_mm_tree(param, cond_func, field_item, value, inv);
}
- else if (cond_func->have_rev_func() &&
- cond_func->arguments()[1]->real_item()->type() ==
- Item::FIELD_ITEM)
+ /*
+ Even if get_full_func_mm_tree() was executed above and did not
+ return a range predicate it may still be possible to create one
+ by reversing the order of the operands. Note that this only
+ applies to predicates where both operands are fields. Example: A
+ query of the form
+
+ WHERE t1.a OP t2.b
+
+ In this case, arguments()[0] == t1.a and arguments()[1] == t2.b.
+ When creating range predicates for t2, get_full_func_mm_tree()
+ above will return NULL because 'field' belongs to t1 and only
+ predicates that applies to t2 are of interest. In this case a
+ call to get_full_func_mm_tree() with reversed operands (see
+ below) may succeed.
+ */
+ if (!ftree && cond_func->have_rev_func() &&
+ cond_func->arguments()[1]->real_item()->type() == Item::FIELD_ITEM)
{
field_item= (Item_field*) (cond_func->arguments()[1]->real_item());
value= cond_func->arguments()[0];
+ if (value && value->is_expensive())
+ DBUG_RETURN(0);
+ if (!cond_func->arguments()[1]->real_item()->const_item())
+ ftree= get_full_func_mm_tree(param, cond_func, field_item, value, inv);
}
- else
- DBUG_RETURN(0);
- if (value && value->is_expensive())
- DBUG_RETURN(0);
-
- ftree= get_full_func_mm_tree(param, cond_func, field_item, value, inv);
}
DBUG_RETURN(ftree);
@@ -7704,13 +8286,15 @@ get_mm_leaf(RANGE_OPT_PARAM *param, COND *conf_func, Field *field,
*/
if (field->result_type() == STRING_RESULT &&
- ((Field_str*) field)->match_collation_to_optimize_range() &&
+ field->match_collation_to_optimize_range() &&
value->result_type() == STRING_RESULT &&
key_part->image_type == Field::itRAW &&
- ((Field_str*)field)->charset() != conf_func->compare_collation() &&
+ field->charset() != conf_func->compare_collation() &&
!(conf_func->compare_collation()->state & MY_CS_BINSORT &&
(type == Item_func::EQUAL_FUNC || type == Item_func::EQ_FUNC)))
goto end;
+ if (value->cmp_type() == TIME_RESULT && field->cmp_type() != TIME_RESULT)
+ goto end;
if (key_part->image_type == Field::itMBR)
{
@@ -8748,7 +9332,7 @@ and_all_keys(RANGE_OPT_PARAM *param, SEL_ARG *key1, SEL_ARG *key2,
if (!key1)
return &null_element; // Impossible ranges
key1->use_count++;
- key1->max_part_no= max(key2->max_part_no, key2->part+1);
+ key1->max_part_no= MY_MAX(key2->max_part_no, key2->part+1);
return key1;
}
@@ -8841,7 +9425,7 @@ key_and(RANGE_OPT_PARAM *param, SEL_ARG *key1, SEL_ARG *key2, uint clone_flag)
key1->use_count--;
key2->use_count--;
SEL_ARG *e1=key1->first(), *e2=key2->first(), *new_tree=0;
- uint max_part_no= max(key1->max_part_no, key2->max_part_no);
+ uint max_part_no= MY_MAX(key1->max_part_no, key2->max_part_no);
while (e1 && e2)
{
@@ -9039,7 +9623,7 @@ key_or(RANGE_OPT_PARAM *param, SEL_ARG *key1,SEL_ARG *key2)
b: [----
*/
- uint max_part_no= max(key1->max_part_no, key2->max_part_no);
+ uint max_part_no= MY_MAX(key1->max_part_no, key2->max_part_no);
for (key2=key2->first(); key2; )
{
@@ -9249,11 +9833,11 @@ key_or(RANGE_OPT_PARAM *param, SEL_ARG *key1,SEL_ARG *key2)
are merged into one range by deleting first...last-1 from
the key1 tree. In the figure, this applies to first and the
two consecutive ranges. The range of last is then extended:
- * last.min: Set to min(key2.min, first.min)
+ * last.min: Set to MY_MIN(key2.min, first.min)
* last.max: If there is a last->next that overlaps key2 (i.e.,
last->next has a different next_key_part):
Set adjacent to last->next.min
- Otherwise: Set to max(key2.max, last.max)
+ Otherwise: Set to MY_MAX(key2.max, last.max)
Result:
key2: [****----------------------*******]
@@ -9307,7 +9891,7 @@ key_or(RANGE_OPT_PARAM *param, SEL_ARG *key1,SEL_ARG *key2)
^ ^
last different next_key_part
- Extend range of last up to max(last.max, key2.max):
+ Extend range of last up to MY_MAX(last.max, key2.max):
key2: [--------*****]
key1: [***----------**] [xxxx]
*/
@@ -10087,7 +10671,7 @@ void SEL_ARG::test_use_count(SEL_ARG *root)
static
ha_rows check_quick_select(PARAM *param, uint idx, bool index_only,
SEL_ARG *tree, bool update_tbl_stats,
- uint *mrr_flags, uint *bufsize, COST_VECT *cost)
+ uint *mrr_flags, uint *bufsize, Cost_estimate *cost)
{
SEL_ARG_RANGE_SEQ seq;
RANGE_SEQ_IF seq_if = {NULL, sel_arg_range_seq_init, sel_arg_range_seq_next, 0, 0};
@@ -10142,13 +10726,14 @@ ha_rows check_quick_select(PARAM *param, uint idx, bool index_only,
if (rows != HA_POS_ERROR)
{
param->quick_rows[keynr]= rows;
+ param->possible_keys.set_bit(keynr);
if (update_tbl_stats)
{
param->table->quick_keys.set_bit(keynr);
param->table->quick_key_parts[keynr]= param->max_key_part+1;
param->table->quick_n_ranges[keynr]= param->range_count;
param->table->quick_condition_rows=
- min(param->table->quick_condition_rows, rows);
+ MY_MIN(param->table->quick_condition_rows, rows);
param->table->quick_rows[keynr]= rows;
}
}
@@ -10226,7 +10811,7 @@ static bool is_key_scan_ror(PARAM *param, uint keynr, uint8 nparts)
KEY *table_key= param->table->key_info + keynr;
KEY_PART_INFO *key_part= table_key->key_part + nparts;
KEY_PART_INFO *key_part_end= (table_key->key_part +
- table_key->key_parts);
+ table_key->user_defined_key_parts);
uint pk_number;
for (KEY_PART_INFO *kp= table_key->key_part; kp < key_part; kp++)
@@ -10236,8 +10821,13 @@ static bool is_key_scan_ror(PARAM *param, uint keynr, uint8 nparts)
if (param->table->field[fieldnr]->key_length() != kp->length)
return FALSE;
}
-
- if (key_part == key_part_end)
+
+ /*
+ If there are equalities for all key parts, it is a ROR scan. If there are
+ equalities all keyparts and even some of key parts from "Extended Key"
+ index suffix, it is a ROR-scan, too.
+ */
+ if (key_part >= key_part_end)
return TRUE;
key_part= table_key->key_part + nparts;
@@ -10247,7 +10837,7 @@ static bool is_key_scan_ror(PARAM *param, uint keynr, uint8 nparts)
KEY_PART_INFO *pk_part= param->table->key_info[pk_number].key_part;
KEY_PART_INFO *pk_part_end= pk_part +
- param->table->key_info[pk_number].key_parts;
+ param->table->key_info[pk_number].user_defined_key_parts;
for (;(key_part!=key_part_end) && (pk_part != pk_part_end);
++key_part, ++pk_part)
{
@@ -10293,12 +10883,12 @@ get_quick_select(PARAM *param,uint idx,SEL_ARG *key_tree, uint mrr_flags,
if (param->table->key_info[param->real_keynr[idx]].flags & HA_SPATIAL)
quick=new QUICK_RANGE_SELECT_GEOM(param->thd, param->table,
param->real_keynr[idx],
- test(parent_alloc),
+ MY_TEST(parent_alloc),
parent_alloc, &create_err);
else
quick=new QUICK_RANGE_SELECT(param->thd, param->table,
param->real_keynr[idx],
- test(parent_alloc), NULL, &create_err);
+ MY_TEST(parent_alloc), NULL, &create_err);
if (quick)
{
@@ -10409,7 +10999,7 @@ get_quick_keys(PARAM *param,QUICK_RANGE_SELECT *quick,KEY_PART *key,
KEY *table_key=quick->head->key_info+quick->index;
flag=EQ_RANGE;
if ((table_key->flags & HA_NOSAME) &&
- key_tree->part == table_key->key_parts-1)
+ key_tree->part == table_key->user_defined_key_parts-1)
{
if ((table_key->flags & HA_NULL_PART_KEY) &&
null_part_in_key(key,
@@ -10577,7 +11167,7 @@ QUICK_RANGE_SELECT *get_quick_select_for_ref(THD *thd, TABLE *table,
QUICK_RANGE *range;
uint part;
bool create_err= FALSE;
- COST_VECT cost;
+ Cost_estimate cost;
uint max_used_key_len;
old_root= thd->mem_root;
@@ -11126,9 +11716,6 @@ int QUICK_RANGE_SELECT::reset()
last_range= NULL;
cur_range= (QUICK_RANGE**) ranges.buffer;
RANGE_SEQ_IF seq_funcs= {NULL, quick_range_seq_init, quick_range_seq_next, 0, 0};
-
- if (in_ror_merged_scan)
- head->column_bitmaps_set_no_signal(&column_bitmap, &column_bitmap);
if (file->inited == handler::RND)
{
@@ -11137,6 +11724,9 @@ int QUICK_RANGE_SELECT::reset()
DBUG_RETURN(error);
}
+ if (in_ror_merged_scan)
+ head->column_bitmaps_set_no_signal(&column_bitmap, &column_bitmap);
+
if (file->inited == handler::NONE)
{
DBUG_EXECUTE_IF("bug14365043_2",
@@ -11300,7 +11890,7 @@ int QUICK_RANGE_SELECT::get_next_prefix(uint prefix_length,
result= file->read_range_first(last_range->min_keypart_map ? &start_key : 0,
last_range->max_keypart_map ? &end_key : 0,
- test(last_range->flag & EQ_RANGE),
+ MY_TEST(last_range->flag & EQ_RANGE),
TRUE);
if (last_range->flag == (UNIQUE_RANGE | EQ_RANGE))
last_range= 0; // Stop searching
@@ -11452,7 +12042,7 @@ int QUICK_SELECT_DESC::get_next()
if (last_range)
{ // Already read through key
result = ((last_range->flag & EQ_RANGE &&
- used_key_parts <= head->key_info[index].key_parts) ?
+ used_key_parts <= head->key_info[index].user_defined_key_parts) ?
file->ha_index_next_same(record, last_range->min_key,
last_range->min_length) :
file->ha_index_prev(record));
@@ -11500,7 +12090,7 @@ int QUICK_SELECT_DESC::get_next()
}
if (last_range->flag & EQ_RANGE &&
- used_key_parts <= head->key_info[index].key_parts)
+ used_key_parts <= head->key_info[index].user_defined_key_parts)
{
result= file->ha_index_read_map(record, last_range->max_key,
@@ -11511,7 +12101,7 @@ int QUICK_SELECT_DESC::get_next()
{
DBUG_ASSERT(last_range->flag & NEAR_MAX ||
(last_range->flag & EQ_RANGE &&
- used_key_parts > head->key_info[index].key_parts) ||
+ used_key_parts > head->key_info[index].user_defined_key_parts) ||
range_reads_after_key(last_range));
result= file->ha_index_read_map(record, last_range->max_key,
last_range->max_keypart_map,
@@ -11643,78 +12233,134 @@ void QUICK_SELECT_I::add_key_name(String *str, bool *first)
}
-void QUICK_RANGE_SELECT::add_info_string(String *str)
+Explain_quick_select* QUICK_RANGE_SELECT::get_explain(MEM_ROOT *alloc)
{
- bool first= TRUE;
-
- add_key_name(str, &first);
+ Explain_quick_select *res;
+ if ((res= new (alloc) Explain_quick_select(QS_TYPE_RANGE)))
+ res->range.set(alloc, head->key_info[index].name, max_used_key_length);
+ return res;
}
-void QUICK_INDEX_MERGE_SELECT::add_info_string(String *str)
+
+Explain_quick_select* QUICK_GROUP_MIN_MAX_SELECT::get_explain(MEM_ROOT *alloc)
{
+ Explain_quick_select *res;
+ if ((res= new (alloc) Explain_quick_select(QS_TYPE_GROUP_MIN_MAX)))
+ res->range.set(alloc, head->key_info[index].name, max_used_key_length);
+ return res;
+}
+
+
+Explain_quick_select* QUICK_INDEX_SORT_SELECT::get_explain(MEM_ROOT *alloc)
+{
+ Explain_quick_select *res;
+ if (!(res= new (alloc) Explain_quick_select(get_type())))
+ return NULL;
+
QUICK_RANGE_SELECT *quick;
- bool first= TRUE;
+ Explain_quick_select *child_explain;
List_iterator_fast<QUICK_RANGE_SELECT> it(quick_selects);
-
- str->append(STRING_WITH_LEN("sort_union("));
while ((quick= it++))
{
- quick->add_key_name(str, &first);
+ if ((child_explain= quick->get_explain(alloc)))
+ res->children.push_back(child_explain);
+ else
+ return NULL;
}
+
if (pk_quick_select)
- pk_quick_select->add_key_name(str, &first);
- str->append(')');
+ {
+ if ((child_explain= pk_quick_select->get_explain(alloc)))
+ res->children.push_back(child_explain);
+ else
+ return NULL;
+ }
+ return res;
}
-void QUICK_INDEX_INTERSECT_SELECT::add_info_string(String *str)
+
+/*
+ Same as QUICK_INDEX_SORT_SELECT::get_explain(), but primary key is printed
+ first
+*/
+
+Explain_quick_select* QUICK_INDEX_INTERSECT_SELECT::get_explain(MEM_ROOT *alloc)
{
- QUICK_RANGE_SELECT *quick;
- bool first= TRUE;
- List_iterator_fast<QUICK_RANGE_SELECT> it(quick_selects);
+ Explain_quick_select *res;
+ Explain_quick_select *child_explain;
+
+ if (!(res= new (alloc) Explain_quick_select(get_type())))
+ return NULL;
- str->append(STRING_WITH_LEN("sort_intersect("));
if (pk_quick_select)
- pk_quick_select->add_key_name(str, &first);
+ {
+ if ((child_explain= pk_quick_select->get_explain(alloc)))
+ res->children.push_back(child_explain);
+ else
+ return NULL;
+ }
+
+ QUICK_RANGE_SELECT *quick;
+ List_iterator_fast<QUICK_RANGE_SELECT> it(quick_selects);
while ((quick= it++))
{
- quick->add_key_name(str, &first);
+ if ((child_explain= quick->get_explain(alloc)))
+ res->children.push_back(child_explain);
+ else
+ return NULL;
}
- str->append(')');
+ return res;
}
-void QUICK_ROR_INTERSECT_SELECT::add_info_string(String *str)
+
+Explain_quick_select* QUICK_ROR_INTERSECT_SELECT::get_explain(MEM_ROOT *alloc)
{
- bool first= TRUE;
+ Explain_quick_select *res;
+ Explain_quick_select *child_explain;
+
+ if (!(res= new (alloc) Explain_quick_select(get_type())))
+ return NULL;
+
QUICK_SELECT_WITH_RECORD *qr;
List_iterator_fast<QUICK_SELECT_WITH_RECORD> it(quick_selects);
-
- str->append(STRING_WITH_LEN("intersect("));
while ((qr= it++))
{
- qr->quick->add_key_name(str, &first);
+ if ((child_explain= qr->quick->get_explain(alloc)))
+ res->children.push_back(child_explain);
+ else
+ return NULL;
}
+
if (cpk_quick)
- cpk_quick->add_key_name(str, &first);
- str->append(')');
+ {
+ if ((child_explain= cpk_quick->get_explain(alloc)))
+ res->children.push_back(child_explain);
+ else
+ return NULL;
+ }
+ return res;
}
-void QUICK_ROR_UNION_SELECT::add_info_string(String *str)
+Explain_quick_select* QUICK_ROR_UNION_SELECT::get_explain(MEM_ROOT *alloc)
{
+ Explain_quick_select *res;
+ Explain_quick_select *child_explain;
+
+ if (!(res= new (alloc) Explain_quick_select(get_type())))
+ return NULL;
+
QUICK_SELECT_I *quick;
- bool first= TRUE;
List_iterator_fast<QUICK_SELECT_I> it(quick_selects);
-
- str->append(STRING_WITH_LEN("union("));
while ((quick= it++))
{
- if (first)
- first= FALSE;
+ if ((child_explain= quick->get_explain(alloc)))
+ res->children.push_back(child_explain);
else
- str->append(',');
- quick->add_info_string(str);
+ return NULL;
}
- str->append(')');
+
+ return res;
}
@@ -12035,7 +12681,7 @@ cost_group_min_max(TABLE* table, KEY *index_info, uint used_key_parts,
TODO
- What happens if the query groups by the MIN/MAX field, and there is no
- other field as in: "select min(a) from t1 group by a" ?
+ other field as in: "select MY_MIN(a) from t1 group by a" ?
- We assume that the general correctness of the GROUP-BY query was checked
before this point. Is this correct, or do we have to check it completely?
- Lift the limitation in condition (B3), that is, make this access method
@@ -12195,7 +12841,11 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree, double read_time)
uchar cur_key_infix[MAX_KEY_LENGTH];
uint cur_used_key_parts;
- /* Check (B1) - if current index is covering. */
+ /*
+ Check (B1) - if current index is covering.
+ (was also: "Exclude UNIQUE indexes ..." but this was removed because
+ there are cases Loose Scan over a multi-part index is useful).
+ */
if (!table->covering_keys.is_set(cur_index))
goto next_index;
@@ -12209,7 +12859,7 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree, double read_time)
does not qualify as covering in our case. If this is the case, below
we check that all query fields are indeed covered by 'cur_index'.
*/
- if (cur_index_info->key_parts == table->actual_n_key_parts(cur_index_info)
+ if (cur_index_info->user_defined_key_parts == table->actual_n_key_parts(cur_index_info)
&& pk < MAX_KEY && cur_index != pk &&
(table->file->ha_table_flags() & HA_PRIMARY_KEY_IN_READ_INDEX))
{
@@ -12310,7 +12960,7 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree, double read_time)
cur_group_prefix_len+= cur_part->store_length;
used_key_parts_map.set_bit(key_part_nr);
++cur_group_key_parts;
- max_key_part= max(max_key_part,key_part_nr);
+ max_key_part= MY_MAX(max_key_part,key_part_nr);
}
/*
Check that used key parts forms a prefix of the index.
@@ -12335,6 +12985,16 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree, double read_time)
}
/*
+ Aplly a heuristic: there is no point to use loose index scan when we're
+ using the whole unique index.
+ */
+ if (cur_index_info->flags & HA_NOSAME &&
+ cur_group_key_parts == cur_index_info->user_defined_key_parts)
+ {
+ goto next_index;
+ }
+
+ /*
Check (NGA1, NGA2) and extract a sequence of constants to be used as part
of all search keys.
*/
@@ -12454,7 +13114,7 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree, double read_time)
cur_index_tree= get_index_range_tree(cur_index, tree, param,
&cur_param_idx);
/* Check if this range tree can be used for prefix retrieval. */
- COST_VECT dummy_cost;
+ Cost_estimate dummy_cost;
uint mrr_flags= HA_MRR_USE_DEFAULT_IMPL;
uint mrr_bufsize=0;
cur_quick_prefix_records= check_quick_select(param, cur_param_idx,
@@ -12721,16 +13381,31 @@ check_group_min_max_predicates(Item *cond, Item_field *min_max_arg_item,
DBUG_RETURN(FALSE);
/* Check for compatible string comparisons - similar to get_mm_leaf. */
- if (args[0] && args[1] && !args[2] && // this is a binary function
- min_max_arg_item->result_type() == STRING_RESULT &&
- /*
- Don't use an index when comparing strings of different collations.
- */
- ((args[1]->result_type() == STRING_RESULT &&
- image_type == Field::itRAW &&
- ((Field_str*) min_max_arg_item->field)->charset() !=
- pred->compare_collation())
- ||
+ if (args[0] && args[1] && !args[2]) // this is a binary function
+ {
+ if (args[1]->cmp_type() == TIME_RESULT &&
+ min_max_arg_item->field->cmp_type() != TIME_RESULT)
+ DBUG_RETURN(FALSE);
+
+ /*
+ Can't use GROUP_MIN_MAX optimization for ENUM and SET,
+ because the values are stored as numbers in index,
+ while MIN() and MAX() work as strings.
+ It would return the records with min and max enum numeric indexes.
+ "Bug#45300 MAX() and ENUM type" should be fixed first.
+ */
+ if (min_max_arg_item->field->real_type() == MYSQL_TYPE_ENUM ||
+ min_max_arg_item->field->real_type() == MYSQL_TYPE_SET)
+ DBUG_RETURN(FALSE);
+
+ if (min_max_arg_item->result_type() == STRING_RESULT &&
+ /*
+ Don't use an index when comparing strings of different collations.
+ */
+ ((args[1]->result_type() == STRING_RESULT &&
+ image_type == Field::itRAW &&
+ min_max_arg_item->field->charset() !=
+ pred->compare_collation()) ||
/*
We can't always use indexes when comparing a string index to a
number.
@@ -12738,6 +13413,7 @@ check_group_min_max_predicates(Item *cond, Item_field *min_max_arg_item,
(args[1]->result_type() != STRING_RESULT &&
min_max_arg_item->field->cmp_type() != args[1]->result_type())))
DBUG_RETURN(FALSE);
+ }
}
else
has_other= true;
@@ -13044,7 +13720,7 @@ SEL_ARG * get_index_range_tree(uint index, SEL_TREE* range_tree, PARAM *param,
DESCRIPTION
This method computes the access cost of a TRP_GROUP_MIN_MAX instance and
- the number of rows returned. It updates this->read_cost and this->records.
+ the number of rows returned.
NOTES
The cost computation distinguishes several cases:
@@ -13091,37 +13767,36 @@ void cost_group_min_max(TABLE* table, KEY *index_info, uint used_key_parts,
double *read_cost, ha_rows *records)
{
ha_rows table_records;
- uint num_groups;
- uint num_blocks;
- uint keys_per_block;
- uint keys_per_group;
- uint keys_per_subgroup; /* Average number of keys in sub-groups */
+ ha_rows num_groups;
+ ha_rows num_blocks;
+ uint keys_per_block;
+ ha_rows keys_per_group;
+ ha_rows keys_per_subgroup; /* Average number of keys in sub-groups */
/* formed by a key infix. */
double p_overlap; /* Probability that a sub-group overlaps two blocks. */
double quick_prefix_selectivity;
double io_cost;
- double cpu_cost= 0; /* TODO: CPU cost of index_read calls? */
DBUG_ENTER("cost_group_min_max");
- table_records= table->file->stats.records;
- keys_per_block= (table->file->stats.block_size / 2 /
- (index_info->key_length + table->file->ref_length)
- + 1);
- num_blocks= (uint)(table_records / keys_per_block) + 1;
+ table_records= table->stat_records();
+ keys_per_block= (uint) (table->file->stats.block_size / 2 /
+ (index_info->key_length + table->file->ref_length)
+ + 1);
+ num_blocks= (ha_rows)(table_records / keys_per_block) + 1;
/* Compute the number of keys in a group. */
- keys_per_group= index_info->rec_per_key[group_key_parts - 1];
+ keys_per_group= (ha_rows) index_info->actual_rec_per_key(group_key_parts - 1);
if (keys_per_group == 0) /* If there is no statistics try to guess */
/* each group contains 10% of all records */
- keys_per_group= (uint)(table_records / 10) + 1;
- num_groups= (uint)(table_records / keys_per_group) + 1;
+ keys_per_group= (table_records / 10) + 1;
+ num_groups= (table_records / keys_per_group) + 1;
/* Apply the selectivity of the quick select for group prefixes. */
if (range_tree && (quick_prefix_records != HA_POS_ERROR))
{
quick_prefix_selectivity= (double) quick_prefix_records /
(double) table_records;
- num_groups= (uint) rint(num_groups * quick_prefix_selectivity);
+ num_groups= (ha_rows) rint(num_groups * quick_prefix_selectivity);
set_if_bigger(num_groups, 1);
}
@@ -13130,16 +13805,16 @@ void cost_group_min_max(TABLE* table, KEY *index_info, uint used_key_parts,
Compute the probability that two ends of a subgroup are inside
different blocks.
*/
- keys_per_subgroup= index_info->rec_per_key[used_key_parts - 1];
+ keys_per_subgroup= (ha_rows) index_info->actual_rec_per_key(used_key_parts - 1);
if (keys_per_subgroup >= keys_per_block) /* If a subgroup is bigger than */
p_overlap= 1.0; /* a block, it will overlap at least two blocks. */
else
{
double blocks_per_group= (double) num_blocks / (double) num_groups;
p_overlap= (blocks_per_group * (keys_per_subgroup - 1)) / keys_per_group;
- p_overlap= min(p_overlap, 1.0);
+ p_overlap= MY_MIN(p_overlap, 1.0);
}
- io_cost= (double) min(num_groups * (1 + p_overlap), num_blocks);
+ io_cost= (double) MY_MIN(num_groups * (1 + p_overlap), num_blocks);
}
else
io_cost= (keys_per_group > keys_per_block) ?
@@ -13148,19 +13823,33 @@ void cost_group_min_max(TABLE* table, KEY *index_info, uint used_key_parts,
(double) num_blocks;
/*
- TODO: If there is no WHERE clause and no other expressions, there should be
- no CPU cost. We leave it here to make this cost comparable to that of index
- scan as computed in SQL_SELECT::test_quick_select().
+ CPU cost must be comparable to that of an index scan as computed
+ in SQL_SELECT::test_quick_select(). When the groups are small,
+ e.g. for a unique index, using index scan will be cheaper since it
+ reads the next record without having to re-position to it on every
+ group. To make the CPU cost reflect this, we estimate the CPU cost
+ as the sum of:
+ 1. Cost for evaluating the condition (similarly as for index scan).
+ 2. Cost for navigating the index structure (assuming a b-tree).
+ Note: We only add the cost for one comparision per block. For a
+ b-tree the number of comparisons will be larger.
+ TODO: This cost should be provided by the storage engine.
*/
- cpu_cost= (double) num_groups / TIME_FOR_COMPARE;
+ const double tree_traversal_cost=
+ ceil(log(static_cast<double>(table_records))/
+ log(static_cast<double>(keys_per_block))) *
+ 1/double(2*TIME_FOR_COMPARE);
+
+ const double cpu_cost= num_groups *
+ (tree_traversal_cost + 1/double(TIME_FOR_COMPARE));
*read_cost= io_cost + cpu_cost;
*records= num_groups;
DBUG_PRINT("info",
- ("table rows: %lu keys/block: %u keys/group: %u result rows: %lu blocks: %u",
- (ulong)table_records, keys_per_block, keys_per_group,
- (ulong) *records, num_blocks));
+ ("table rows: %lu keys/block: %u keys/group: %lu result rows: %lu blocks: %lu",
+ (ulong)table_records, keys_per_block, (ulong) keys_per_group,
+ (ulong) *records, (ulong) num_blocks));
DBUG_VOID_RETURN;
}
@@ -13327,7 +14016,8 @@ QUICK_GROUP_MIN_MAX_SELECT(TABLE *table, JOIN *join_arg, bool have_min_arg,
DBUG_ASSERT(!parent_alloc);
if (!parent_alloc)
{
- init_sql_alloc(&alloc, join->thd->variables.range_alloc_block_size, 0);
+ init_sql_alloc(&alloc, join->thd->variables.range_alloc_block_size, 0,
+ MYF(MY_THREAD_SPECIFIC));
join->thd->mem_root= &alloc;
}
else
@@ -13356,15 +14046,21 @@ int QUICK_GROUP_MIN_MAX_SELECT::init()
{
if (group_prefix) /* Already initialized. */
return 0;
-
- if (!(last_prefix= (uchar*) alloc_root(&alloc, group_prefix_len)))
+
+ /*
+ We allocate one byte more to serve the case when the last field in
+ the buffer is compared using uint3korr (e.g. a Field_newdate field)
+ */
+ if (!(last_prefix= (uchar*) alloc_root(&alloc, group_prefix_len+1)))
return 1;
/*
We may use group_prefix to store keys with all select fields, so allocate
enough space for it.
+ We allocate one byte more to serve the case when the last field in
+ the buffer is compared using uint3korr (e.g. a Field_newdate field)
*/
if (!(group_prefix= (uchar*) alloc_root(&alloc,
- real_prefix_len + min_max_arg_len)))
+ real_prefix_len+min_max_arg_len+1)))
return 1;
if (key_infix_len > 0)
@@ -13382,7 +14078,8 @@ int QUICK_GROUP_MIN_MAX_SELECT::init()
if (min_max_arg_part)
{
- if (my_init_dynamic_array(&min_max_ranges, sizeof(QUICK_RANGE*), 16, 16))
+ if (my_init_dynamic_array(&min_max_ranges, sizeof(QUICK_RANGE*), 16, 16,
+ MYF(MY_THREAD_SPECIFIC)))
return 1;
if (have_min)
@@ -14590,11 +15287,3 @@ void QUICK_GROUP_MIN_MAX_SELECT::dbug_dump(int indent, bool verbose)
#endif /* !DBUG_OFF */
-/*****************************************************************************
-** Instantiate templates
-*****************************************************************************/
-
-#ifdef HAVE_EXPLICIT_TEMPLATE_INSTANTIATION
-template class List<QUICK_RANGE>;
-template class List_iterator<QUICK_RANGE>;
-#endif
diff --git a/sql/opt_range.h b/sql/opt_range.h
index b8b46ae5ab1..54b15826d1b 100644
--- a/sql/opt_range.h
+++ b/sql/opt_range.h
@@ -52,7 +52,7 @@ typedef struct st_key_part {
Field::imagetype image_type;
} KEY_PART;
-
+class Explain_quick_select;
/*
A "MIN_TUPLE < tbl.key_tuple < MAX_TUPLE" interval.
@@ -104,7 +104,7 @@ class QUICK_RANGE :public Sql_alloc {
void make_min_endpoint(key_range *kr, uint prefix_length,
key_part_map keypart_map) {
make_min_endpoint(kr);
- kr->length= min(kr->length, prefix_length);
+ kr->length= MY_MIN(kr->length, prefix_length);
kr->keypart_map&= keypart_map;
}
@@ -142,7 +142,7 @@ class QUICK_RANGE :public Sql_alloc {
void make_max_endpoint(key_range *kr, uint prefix_length,
key_part_map keypart_map) {
make_max_endpoint(kr);
- kr->length= min(kr->length, prefix_length);
+ kr->length= MY_MIN(kr->length, prefix_length);
kr->keypart_map&= keypart_map;
}
@@ -345,13 +345,8 @@ public:
void add_key_name(String *str, bool *first);
- /*
- Append text representation of quick select structure (what and how is
- merged) to str. The result is added to "Extra" field in EXPLAIN output.
- This function is implemented only by quick selects that merge other quick
- selects output and/or can produce output suitable for merging.
- */
- virtual void add_info_string(String *str) {}
+ /* Save information about quick select's query plan */
+ virtual Explain_quick_select* get_explain(MEM_ROOT *alloc)= 0;
/*
Return 1 if any index used by this quick select
@@ -485,7 +480,7 @@ public:
{ file->position(record); }
int get_type() { return QS_TYPE_RANGE; }
void add_keys_and_lengths(String *key_names, String *used_lengths);
- void add_info_string(String *str);
+ Explain_quick_select *get_explain(MEM_ROOT *alloc);
#ifndef DBUG_OFF
void dbug_dump(int indent, bool verbose);
#endif
@@ -625,6 +620,7 @@ public:
#ifndef DBUG_OFF
void dbug_dump(int indent, bool verbose);
#endif
+ Explain_quick_select *get_explain(MEM_ROOT *alloc);
bool push_quick_back(QUICK_RANGE_SELECT *quick_sel_range);
@@ -675,7 +671,6 @@ public:
int get_next();
int get_type() { return QS_TYPE_INDEX_MERGE; }
void add_keys_and_lengths(String *key_names, String *used_lengths);
- void add_info_string(String *str);
};
class QUICK_INDEX_INTERSECT_SELECT : public QUICK_INDEX_SORT_SELECT
@@ -691,7 +686,7 @@ public:
int get_next();
int get_type() { return QS_TYPE_INDEX_INTERSECT; }
void add_keys_and_lengths(String *key_names, String *used_lengths);
- void add_info_string(String *str);
+ Explain_quick_select *get_explain(MEM_ROOT *alloc);
};
@@ -729,7 +724,7 @@ public:
bool unique_key_range() { return false; }
int get_type() { return QS_TYPE_ROR_INTERSECT; }
void add_keys_and_lengths(String *key_names, String *used_lengths);
- void add_info_string(String *str);
+ Explain_quick_select *get_explain(MEM_ROOT *alloc);
bool is_keys_used(const MY_BITMAP *fields);
void add_used_key_part_to_set(MY_BITMAP *col_set);
#ifndef DBUG_OFF
@@ -809,7 +804,7 @@ public:
bool unique_key_range() { return false; }
int get_type() { return QS_TYPE_ROR_UNION; }
void add_keys_and_lengths(String *key_names, String *used_lengths);
- void add_info_string(String *str);
+ Explain_quick_select *get_explain(MEM_ROOT *alloc);
bool is_keys_used(const MY_BITMAP *fields);
void add_used_key_part_to_set(MY_BITMAP *col_set);
#ifndef DBUG_OFF
@@ -959,11 +954,8 @@ public:
void dbug_dump(int indent, bool verbose);
#endif
bool is_agg_distinct() { return have_agg_distinct; }
- virtual void append_loose_scan_type(String *str)
- {
- if (is_index_scan)
- str->append(STRING_WITH_LEN(" (scanning)"));
- }
+ bool loose_scan_is_scanning() { return is_index_scan; }
+ Explain_quick_select *get_explain(MEM_ROOT *alloc);
};
@@ -1005,6 +997,8 @@ class SQL_SELECT :public Sql_alloc {
key_map quick_keys; // Possible quick keys
key_map needed_reg; // Possible quick keys after prev tables.
table_map const_tables,read_tables;
+ /* See PARAM::possible_keys */
+ key_map possible_keys;
bool free_cond; /* Currently not used and always FALSE */
SQL_SELECT();
@@ -1025,7 +1019,7 @@ class SQL_SELECT :public Sql_alloc {
*/
inline int skip_record(THD *thd)
{
- int rc= test(!cond || cond->val_int());
+ int rc= MY_TEST(!cond || cond->val_int());
if (thd->is_error())
rc= -1;
return rc;
@@ -1057,11 +1051,20 @@ SQL_SELECT *make_select(TABLE *head, table_map const_tables,
table_map read_tables, COND *conds,
bool allow_null_cond, int *error);
+bool calculate_cond_selectivity_for_table(THD *thd, TABLE *table, Item *cond);
+
#ifdef WITH_PARTITION_STORAGE_ENGINE
bool prune_partitions(THD *thd, TABLE *table, Item *pprune_cond);
-void store_key_image_to_rec(Field *field, uchar *ptr, uint len);
#endif
+void store_key_image_to_rec(Field *field, uchar *ptr, uint len);
extern String null_string;
+/* check this number of rows (default value) */
+#define SELECTIVITY_SAMPLING_LIMIT 100
+/* but no more then this part of table (10%) */
+#define SELECTIVITY_SAMPLING_SHARE 0.10
+/* do not check if we are going check less then this number of records */
+#define SELECTIVITY_SAMPLING_THRESHOLD 10
+
#endif
diff --git a/sql/opt_range_mrr.cc b/sql/opt_range_mrr.cc
index 1f4e36178db..bff96c7d4cb 100644
--- a/sql/opt_range_mrr.cc
+++ b/sql/opt_range_mrr.cc
@@ -268,8 +268,10 @@ walk_up_n_right:
range->end_key.keypart_map= make_prev_keypart_map(cur->max_key_parts);
if (!(cur->min_key_flag & ~NULL_RANGE) && !cur->max_key_flag &&
- (uint)key_tree->part+1 == seq->param->table->key_info[seq->real_keyno].key_parts &&
- (seq->param->table->key_info[seq->real_keyno].flags & HA_NOSAME) &&
+ (seq->real_keyno == MAX_KEY ||
+ ((uint)key_tree->part+1 ==
+ seq->param->table->key_info[seq->real_keyno].user_defined_key_parts &&
+ (seq->param->table->key_info[seq->real_keyno].flags & HA_NOSAME))) &&
range->start_key.length == range->end_key.length &&
!memcmp(seq->param->min_key,seq->param->max_key,range->start_key.length))
range->range_flag= UNIQUE_RANGE | (cur->min_key_flag & NULL_RANGE);
@@ -293,7 +295,7 @@ walk_up_n_right:
}
}
seq->param->range_count++;
- seq->param->max_key_part=max(seq->param->max_key_part,key_tree->part);
+ seq->param->max_key_part=MY_MAX(seq->param->max_key_part,key_tree->part);
return 0;
}
diff --git a/sql/opt_subselect.cc b/sql/opt_subselect.cc
index 5137d8a0986..de57143e61d 100644
--- a/sql/opt_subselect.cc
+++ b/sql/opt_subselect.cc
@@ -1,5 +1,5 @@
/*
- Copyright (c) 2010, 2012, Monty Program Ab
+ Copyright (c) 2010, 2015, MariaDB
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
@@ -26,6 +26,7 @@
#pragma implementation // gcc: Class implementation
#endif
+#include <my_global.h>
#include "sql_base.h"
#include "sql_select.h"
#include "filesort.h"
@@ -512,8 +513,6 @@ bool is_materialization_applicable(THD *thd, Item_in_subselect *in_subs,
Subquery !contains {GROUP BY, ORDER BY [LIMIT],
aggregate functions}) && subquery predicate is not under "NOT IN"))
- (*) The subquery must be part of a SELECT or CREATE TABLE ... SELECT statement.
- The current condition also excludes multi-table update statements.
A note about prepared statements: we want the if-branch to be taken on
PREPARE and each EXECUTE. The rewrites are only done once, but we need
select_lex->sj_subselects list to be populated for every EXECUTE.
@@ -522,9 +521,7 @@ bool is_materialization_applicable(THD *thd, Item_in_subselect *in_subs,
if (optimizer_flag(thd, OPTIMIZER_SWITCH_MATERIALIZATION) && // 0
!child_select->is_part_of_union() && // 1
parent_unit->first_select()->leaf_tables.elements && // 2
- (thd->lex->sql_command == SQLCOM_SELECT || // *
- thd->lex->sql_command == SQLCOM_CREATE_TABLE) && // *
- child_select->outer_select()->leaf_tables.elements && // 2A
+ child_select->outer_select()->leaf_tables.elements && // 2A
subquery_types_allow_materialization(in_subs) &&
(in_subs->is_top_level_item() || //3
optimizer_flag(thd,
@@ -664,6 +661,9 @@ int check_and_do_in_subquery_rewrites(JOIN *join)
8. No execution method was already chosen (by a prepared statement)
9. Parent select is not a table-less select
10. Neither parent nor child select have STRAIGHT_JOIN option.
+ 11. It is first optimisation (the subquery could be moved from ON
+ clause during first optimisation and then be considered for SJ
+ on the second when it is too late)
*/
if (optimizer_flag(thd, OPTIMIZER_SWITCH_SEMIJOIN) &&
in_subs && // 1
@@ -677,7 +677,8 @@ int check_and_do_in_subquery_rewrites(JOIN *join)
select_lex->outer_select()->leaf_tables.elements && // 9
!((join->select_options | // 10
select_lex->outer_select()->join->select_options) // 10
- & SELECT_STRAIGHT_JOIN)) // 10
+ & SELECT_STRAIGHT_JOIN) && // 10
+ select_lex->first_cond_optimization) // 11
{
DBUG_PRINT("info", ("Subquery is semi-join conversion candidate"));
@@ -1089,7 +1090,7 @@ bool convert_join_subqueries_to_semijoins(JOIN *join)
if (convert_join_subqueries_to_semijoins(child_join))
DBUG_RETURN(TRUE);
in_subq->sj_convert_priority=
- test(in_subq->emb_on_expr_nest != NO_JOIN_NEST) * MAX_TABLES * 2 +
+ MY_TEST(in_subq->emb_on_expr_nest != NO_JOIN_NEST) * MAX_TABLES * 2 +
in_subq->is_correlated * MAX_TABLES + child_join->outer_tables;
}
@@ -1703,6 +1704,7 @@ static bool convert_subq_to_sj(JOIN *parent_join, Item_in_subselect *subq_pred)
parent_lex->ftfunc_list->push_front(ifm);
}
+ parent_lex->have_merged_subqueries= TRUE;
DBUG_RETURN(FALSE);
}
@@ -1816,6 +1818,8 @@ static bool convert_subq_to_jtbm(JOIN *parent_join,
create_subquery_temptable_name(tbl_alias, hash_sj_engine->materialize_join->
select_lex->select_number);
jtbm->alias= tbl_alias;
+
+ parent_lex->have_merged_subqueries= TRUE;
#if 0
/* Inject sj_on_expr into the parent's WHERE or ON */
if (emb_tbl_nest)
@@ -2237,7 +2241,7 @@ bool optimize_semijoin_nests(JOIN *join, table_map all_table_map)
double rows= 1.0;
while ((tableno = tm_it.next_bit()) != Table_map_iterator::BITMAP_END)
rows *= join->map2table[tableno]->table->quick_condition_rows;
- sjm->rows= min(sjm->rows, rows);
+ sjm->rows= MY_MIN(sjm->rows, rows);
}
memcpy(sjm->positions, join->best_positions + join->const_tables,
sizeof(POSITION) * n_tables);
@@ -2262,7 +2266,7 @@ bool optimize_semijoin_nests(JOIN *join, table_map all_table_map)
Set the cost to do a full scan of the temptable (will need this to
consider doing sjm-scan):
*/
- sjm->scan_cost.zero();
+ sjm->scan_cost.reset();
sjm->scan_cost.add_io(sjm->rows, lookup_cost);
sjm->lookup_cost.convert_from_cost(lookup_cost);
@@ -2417,7 +2421,7 @@ bool find_eq_ref_candidate(TABLE *table, table_map sj_inner_tables)
if (!is_excluded_key)
{
keyinfo= table->key_info + key;
- is_excluded_key= !test(keyinfo->flags & HA_NOSAME);
+ is_excluded_key= !MY_TEST(keyinfo->flags & HA_NOSAME);
}
if (!is_excluded_key)
{
@@ -2432,7 +2436,7 @@ bool find_eq_ref_candidate(TABLE *table, table_map sj_inner_tables)
keyuse++;
} while (keyuse->key == key && keyuse->table == table);
- if (bound_parts == PREV_BITS(uint, keyinfo->key_parts))
+ if (bound_parts == PREV_BITS(uint, keyinfo->user_defined_key_parts))
return TRUE;
}
else
@@ -2504,8 +2508,8 @@ bool is_multiple_semi_joins(JOIN *join, POSITION *prefix, uint idx, table_map in
if ((emb_sj_nest= prefix[i].table->emb_sj_nest))
{
if (inner_tables & emb_sj_nest->sj_inner_tables)
- return !test(inner_tables == (emb_sj_nest->sj_inner_tables &
- ~join->const_table_map));
+ return !MY_TEST(inner_tables == (emb_sj_nest->sj_inner_tables &
+ ~join->const_table_map));
}
}
return FALSE;
@@ -2708,12 +2712,12 @@ bool Sj_materialization_picker::check_qep(JOIN *join,
else
{
/* This is SJ-Materialization with lookups */
- COST_VECT prefix_cost;
+ Cost_estimate prefix_cost;
signed int first_tab= (int)idx - mat_info->tables;
double prefix_rec_count;
if (first_tab < (int)join->const_tables)
{
- prefix_cost.zero();
+ prefix_cost.reset();
prefix_rec_count= 1.0;
}
else
@@ -3244,9 +3248,9 @@ at_sjmat_pos(const JOIN *join, table_map remaining_tables, const JOIN_TAB *tab,
if (join->positions[idx - i].table->emb_sj_nest != tab->emb_sj_nest)
return NULL;
}
- *loose_scan= test(remaining_tables & ~tab->table->map &
- (emb_sj_nest->sj_inner_tables |
- emb_sj_nest->nested_join->sj_depends_on));
+ *loose_scan= MY_TEST(remaining_tables & ~tab->table->map &
+ (emb_sj_nest->sj_inner_tables |
+ emb_sj_nest->nested_join->sj_depends_on));
if (*loose_scan && !emb_sj_nest->sj_subq_pred->sjm_scan_allowed)
return NULL;
else
@@ -3601,7 +3605,7 @@ bool setup_sj_materialization_part2(JOIN_TAB *sjm_tab)
KEY *tmp_key; /* The only index on the temporary table. */
uint tmp_key_parts; /* Number of keyparts in tmp_key. */
tmp_key= sjm->table->key_info;
- tmp_key_parts= tmp_key->key_parts;
+ tmp_key_parts= tmp_key->user_defined_key_parts;
/*
Create/initialize everything we will need to index lookups into the
@@ -3632,12 +3636,12 @@ bool setup_sj_materialization_part2(JOIN_TAB *sjm_tab)
for (i= 0; i < tmp_key_parts; i++, cur_key_part++, ref_key++)
{
tab_ref->items[i]= emb_sj_nest->sj_subq_pred->left_expr->element_index(i);
- int null_count= test(cur_key_part->field->real_maybe_null());
+ int null_count= MY_TEST(cur_key_part->field->real_maybe_null());
*ref_key= new store_key_item(thd, cur_key_part->field,
/* TODO:
the NULL byte is taken into account in
cur_key_part->store_length, so instead of
- cur_ref_buff + test(maybe_null), we could
+ cur_ref_buff + MY_TEST(maybe_null), we could
use that information instead.
*/
cur_ref_buff + null_count,
@@ -3866,7 +3870,7 @@ static bool is_cond_sj_in_equality(Item *item)
((Item_func*)item)->functype()== Item_func::EQ_FUNC)
{
Item_func_eq *item_eq= (Item_func_eq*)item;
- return test(item_eq->in_equality_no != UINT_MAX);
+ return MY_TEST(item_eq->in_equality_no != UINT_MAX);
}
return FALSE;
}
@@ -3931,7 +3935,6 @@ SJ_TMP_TABLE::create_sj_weedout_tmp_table(THD *thd)
/*
STEP 1: Get temporary table name
*/
- statistic_increment(thd->status_var.created_tmp_tables, &LOCK_status);
if (use_temp_pool && !(test_flags & TEST_KEEP_TMP_TABLES))
temp_pool_slot = bitmap_lock_set_next(&temp_pool);
@@ -3952,7 +3955,7 @@ SJ_TMP_TABLE::create_sj_weedout_tmp_table(THD *thd)
using_unique_constraint= TRUE;
/* STEP 3: Allocate memory for temptable description */
- init_sql_alloc(&own_root, TABLE_ALLOC_BLOCK_SIZE, 0);
+ init_sql_alloc(&own_root, TABLE_ALLOC_BLOCK_SIZE, 0, MYF(MY_THREAD_SPECIFIC));
if (!multi_alloc_root(&own_root,
&table, sizeof(*table),
&share, sizeof(*share),
@@ -3965,7 +3968,7 @@ SJ_TMP_TABLE::create_sj_weedout_tmp_table(THD *thd)
&tmpname, (uint) strlen(path)+1,
&group_buff, (!using_unique_constraint ?
uniq_tuple_length_arg : 0),
- &bitmaps, bitmap_buffer_size(1)*3,
+ &bitmaps, bitmap_buffer_size(1)*5,
NullS))
{
if (temp_pool_slot != MY_BIT_NONE)
@@ -3999,7 +4002,6 @@ SJ_TMP_TABLE::create_sj_weedout_tmp_table(THD *thd)
table->s= share;
init_tmp_table_share(thd, share, "", 0, tmpname, tmpname);
share->blob_field= blob_field;
- share->blob_ptr_size= portable_sizeof_char_ptr;
share->table_charset= NULL;
share->primary_key= MAX_KEY; // Indicate no primary key
share->keys_for_keyread.init();
@@ -4052,6 +4054,12 @@ SJ_TMP_TABLE::create_sj_weedout_tmp_table(THD *thd)
if (!table->file)
goto err;
+ if (table->file->set_ha_share_ref(&share->ha_share))
+ {
+ delete table->file;
+ goto err;
+ }
+
null_count=1;
null_pack_length= 1;
@@ -4121,7 +4129,7 @@ SJ_TMP_TABLE::create_sj_weedout_tmp_table(THD *thd)
share->max_rows= ~(ha_rows) 0;
else
share->max_rows= (ha_rows) (((share->db_type() == heap_hton) ?
- min(thd->variables.tmp_table_size,
+ MY_MIN(thd->variables.tmp_table_size,
thd->variables.max_heap_table_size) :
thd->variables.tmp_table_size) /
share->reclength);
@@ -4133,11 +4141,11 @@ SJ_TMP_TABLE::create_sj_weedout_tmp_table(THD *thd)
{
DBUG_PRINT("info",("Creating group key in temporary table"));
share->keys=1;
- share->uniques= test(using_unique_constraint);
+ share->uniques= MY_TEST(using_unique_constraint);
table->key_info=keyinfo;
keyinfo->key_part=key_part_info;
keyinfo->flags=HA_NOSAME;
- keyinfo->usable_key_parts= keyinfo->key_parts= 1;
+ keyinfo->usable_key_parts= keyinfo->user_defined_key_parts= 1;
keyinfo->key_length=0;
keyinfo->rec_per_key=0;
keyinfo->algorithm= HA_KEY_ALG_UNDEF;
@@ -4153,6 +4161,7 @@ SJ_TMP_TABLE::create_sj_weedout_tmp_table(THD *thd)
{
if (!(key_field= field->new_key_field(thd->mem_root, table,
group_buff,
+ key_part_info->length,
field->null_ptr,
field->null_bit)))
goto err;
@@ -4375,6 +4384,74 @@ int init_dups_weedout(JOIN *join, uint first_table, int first_fanout_table, uint
/*
+ @brief
+ Set up semi-join Loose Scan strategy for execution
+
+ @detail
+ Other strategies are done in setup_semijoin_dups_elimination(),
+ however, we need to set up Loose Scan earlier, before make_join_select is
+ called. This is to prevent make_join_select() from switching full index
+ scans into quick selects (which will break Loose Scan access).
+
+ @return
+ 0 OK
+ 1 Error
+*/
+
+int setup_semijoin_loosescan(JOIN *join)
+{
+ uint i;
+ DBUG_ENTER("setup_semijoin_loosescan");
+
+ POSITION *pos= join->best_positions + join->const_tables;
+ for (i= join->const_tables ; i < join->top_join_tab_count; )
+ {
+ JOIN_TAB *tab=join->join_tab + i;
+ switch (pos->sj_strategy) {
+ case SJ_OPT_MATERIALIZE:
+ case SJ_OPT_MATERIALIZE_SCAN:
+ i+= 1; /* join tabs are embedded in the nest */
+ pos += pos->n_sj_tables;
+ break;
+ case SJ_OPT_LOOSE_SCAN:
+ {
+ /* We jump from the last table to the first one */
+ tab->loosescan_match_tab= tab + pos->n_sj_tables - 1;
+
+ /* LooseScan requires records to be produced in order */
+ if (tab->select && tab->select->quick)
+ tab->select->quick->need_sorted_output();
+
+ for (uint j= i; j < i + pos->n_sj_tables; j++)
+ join->join_tab[j].inside_loosescan_range= TRUE;
+
+ /* Calculate key length */
+ uint keylen= 0;
+ uint keyno= pos->loosescan_picker.loosescan_key;
+ for (uint kp=0; kp < pos->loosescan_picker.loosescan_parts; kp++)
+ keylen += tab->table->key_info[keyno].key_part[kp].store_length;
+
+ tab->loosescan_key= keyno;
+ tab->loosescan_key_len= keylen;
+ if (pos->n_sj_tables > 1)
+ tab[pos->n_sj_tables - 1].do_firstmatch= tab;
+ i+= pos->n_sj_tables;
+ pos+= pos->n_sj_tables;
+ break;
+ }
+ default:
+ {
+ i++;
+ pos++;
+ break;
+ }
+ }
+ }
+ DBUG_RETURN(FALSE);
+}
+
+
+/*
Setup the strategies to eliminate semi-join duplicates.
SYNOPSIS
@@ -4482,8 +4559,6 @@ int setup_semijoin_dups_elimination(JOIN *join, ulonglong options,
for (i= join->const_tables ; i < join->top_join_tab_count; )
{
JOIN_TAB *tab=join->join_tab + i;
- //POSITION *pos= join->best_positions + i;
- uint keylen, keyno;
switch (pos->sj_strategy) {
case SJ_OPT_MATERIALIZE:
case SJ_OPT_MATERIALIZE_SCAN:
@@ -4493,26 +4568,7 @@ int setup_semijoin_dups_elimination(JOIN *join, ulonglong options,
break;
case SJ_OPT_LOOSE_SCAN:
{
- /* We jump from the last table to the first one */
- tab->loosescan_match_tab= tab + pos->n_sj_tables - 1;
-
- /* LooseScan requires records to be produced in order */
- if (tab->select && tab->select->quick)
- tab->select->quick->need_sorted_output();
-
- for (uint j= i; j < i + pos->n_sj_tables; j++)
- join->join_tab[j].inside_loosescan_range= TRUE;
-
- /* Calculate key length */
- keylen= 0;
- keyno= pos->loosescan_picker.loosescan_key;
- for (uint kp=0; kp < pos->loosescan_picker.loosescan_parts; kp++)
- keylen += tab->table->key_info[keyno].key_part[kp].store_length;
-
- tab->loosescan_key= keyno;
- tab->loosescan_key_len= keylen;
- if (pos->n_sj_tables > 1)
- tab[pos->n_sj_tables - 1].do_firstmatch= tab;
+ /* Setup already handled by setup_semijoin_loosescan */
i+= pos->n_sj_tables;
pos+= pos->n_sj_tables;
break;
@@ -5237,7 +5293,7 @@ bool setup_jtbm_semi_joins(JOIN *join, List<TABLE_LIST> *join_list,
0 or 1 record. Examples of both cases:
select * from ot where col in (select ... from it where 2>3)
- select * from ot where col in (select min(it.key) from it)
+ select * from ot where col in (select MY_MIN(it.key) from it)
in this case, the subquery predicate has not been setup for
materialization. In particular, there is no materialized temp.table.
@@ -5321,7 +5377,7 @@ bool setup_jtbm_semi_joins(JOIN *join, List<TABLE_LIST> *join_list,
if (!(*join_where)->fixed)
(*join_where)->fix_fields(join->thd, join_where);
}
- table->table->maybe_null= test(join->mixed_implicit_grouping);
+ table->table->maybe_null= MY_TEST(join->mixed_implicit_grouping);
}
if ((nested_join= table->nested_join))
diff --git a/sql/opt_subselect.h b/sql/opt_subselect.h
index e4d679fa377..0fb1a931e36 100644
--- a/sql/opt_subselect.h
+++ b/sql/opt_subselect.h
@@ -1,5 +1,5 @@
/*
- Copyright (c) 2010, 2012, Monty Program Ab
+ Copyright (c) 2010, 2015, MariaDB
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
@@ -168,7 +168,7 @@ public:
}
}
- bool have_a_case() { return test(handled_sj_equalities); }
+ bool have_a_case() { return MY_TEST(handled_sj_equalities); }
void check_ref_access_part1(JOIN_TAB *s, uint key, KEYUSE *start_key,
table_map found_part)
@@ -194,8 +194,6 @@ public:
PREV_BITS(key_part_map, max_loose_keypart+1) && // (3)
!key_uses_partial_cols(s->table->s, key))
{
- /* Ok, can use the strategy */
- part1_conds_met= TRUE;
if (s->quick && s->quick->index == key &&
s->quick->get_type() == QUICK_SELECT_I::QS_TYPE_RANGE)
{
@@ -204,6 +202,12 @@ public:
}
DBUG_PRINT("info", ("Can use LooseScan scan"));
+ if (found_part & 1)
+ {
+ /* Can use LooseScan on ref access if the first key part is bound */
+ part1_conds_met= TRUE;
+ }
+
/*
Check if this is a special case where there are no usable bound
IN-equalities, i.e. we have
@@ -211,11 +215,13 @@ public:
outer_expr IN (SELECT innertbl.key FROM ...)
and outer_expr cannot be evaluated yet, so it's actually full
- index scan and not a ref access
+ index scan and not a ref access.
+ We can do full index scan if it uses index-only.
*/
if (!(found_part & 1 ) && /* no usable ref access for 1st key part */
s->table->covering_keys.is_set(key))
{
+ part1_conds_met= TRUE;
DBUG_PRINT("info", ("Can use full index scan for LooseScan"));
/* Calculate the cost of complete loose index scan. */
@@ -284,6 +290,7 @@ public:
{
pos->records_read= best_loose_scan_records;
pos->key= best_loose_scan_start_key;
+ pos->cond_selectivity= 1.0;
pos->loosescan_picker.loosescan_key= best_loose_scan_key;
pos->loosescan_picker.loosescan_parts= best_max_loose_keypart + 1;
pos->use_join_buffer= FALSE;
@@ -372,8 +379,8 @@ public:
These are the members we got from temptable creation code. We'll need
them if we'll need to convert table from HEAP to MyISAM/Maria.
*/
- ENGINE_COLUMNDEF *start_recinfo;
- ENGINE_COLUMNDEF *recinfo;
+ TMP_ENGINE_COLUMNDEF *start_recinfo;
+ TMP_ENGINE_COLUMNDEF *recinfo;
SJ_TMP_TABLE *next_flush_table;
@@ -382,6 +389,7 @@ public:
bool create_sj_weedout_tmp_table(THD *thd);
};
+int setup_semijoin_loosescan(JOIN *join);
int setup_semijoin_dups_elimination(JOIN *join, ulonglong options,
uint no_jbuf_after);
void destroy_sj_tmp_tables(JOIN *join);
diff --git a/sql/opt_sum.cc b/sql/opt_sum.cc
index aa8b17a2c85..1ff1f4a6449 100644
--- a/sql/opt_sum.cc
+++ b/sql/opt_sum.cc
@@ -48,6 +48,7 @@
(assuming a index for column d of table t2 is defined)
*/
+#include <my_global.h>
#include "sql_priv.h"
#include "key.h" // key_cmp_if_same
#include "sql_select.h"
@@ -295,9 +296,9 @@ int opt_sum_query(THD *thd,
if (!(tl->table->file->ha_table_flags() & HA_STATS_RECORDS_IS_EXACT) ||
tl->schema_table)
{
- maybe_exact_count&= test(!tl->schema_table &&
- (tl->table->file->ha_table_flags() &
- HA_HAS_RECORDS));
+ maybe_exact_count&= MY_TEST(!tl->schema_table &&
+ (tl->table->file->ha_table_flags() &
+ HA_HAS_RECORDS));
is_exact_count= FALSE;
count= 1; // ensure count != 0
}
@@ -340,7 +341,8 @@ int opt_sum_query(THD *thd,
there are no outer joins.
*/
if (!conds && !((Item_sum_count*) item)->get_arg(0)->maybe_null &&
- !outer_tables && maybe_exact_count)
+ !outer_tables && maybe_exact_count &&
+ ((item->used_tables() & OUTER_REF_TABLE_BIT) == 0))
{
if (!is_exact_count)
{
@@ -361,14 +363,15 @@ int opt_sum_query(THD *thd,
case Item_sum::MIN_FUNC:
case Item_sum::MAX_FUNC:
{
- int is_max= test(item_sum->sum_func() == Item_sum::MAX_FUNC);
+ int is_max= MY_TEST(item_sum->sum_func() == Item_sum::MAX_FUNC);
/*
If MIN/MAX(expr) is the first part of a key or if all previous
parts of the key is found in the COND, then we can use
indexes to find the key.
*/
Item *expr=item_sum->get_arg(0);
- if (expr->real_item()->type() == Item::FIELD_ITEM)
+ if (((expr->used_tables() & OUTER_REF_TABLE_BIT) == 0) &&
+ expr->real_item()->type() == Item::FIELD_ITEM)
{
uchar key_buff[MAX_KEY_LENGTH];
TABLE_REF ref;
@@ -464,7 +467,7 @@ int opt_sum_query(THD *thd,
}
if (thd->is_error())
- DBUG_RETURN(thd->stmt_da->sql_errno());
+ DBUG_RETURN(thd->get_stmt_da()->sql_errno());
/*
If we have a where clause, we can only ignore searching in the
@@ -658,7 +661,7 @@ static bool matching_cond(bool max_fl, TABLE_REF *ref, KEY *keyinfo,
DBUG_RETURN(FALSE);
}
if (!(cond->used_tables() & field->table->map) &&
- test(cond->used_tables() & ~PSEUDO_TABLE_BITS))
+ MY_TEST(cond->used_tables() & ~PSEUDO_TABLE_BITS))
{
/* Condition doesn't restrict the used table */
DBUG_RETURN(!cond->const_item());
@@ -811,7 +814,7 @@ static bool matching_cond(bool max_fl, TABLE_REF *ref, KEY *keyinfo,
Item *value= args[between && max_fl ? 2 : 1];
value->save_in_field_no_warnings(part->field, 1);
if (part->null_bit)
- *key_ptr++= (uchar) test(part->field->is_null());
+ *key_ptr++= (uchar) MY_TEST(part->field->is_null());
part->field->get_key_image(key_ptr, part->length, Field::itRAW);
}
if (is_field_part)
@@ -831,7 +834,7 @@ static bool matching_cond(bool max_fl, TABLE_REF *ref, KEY *keyinfo,
else if (eq_type)
{
if ((!is_null && !cond->val_int()) ||
- (is_null && !test(part->field->is_null())))
+ (is_null && !MY_TEST(part->field->is_null())))
DBUG_RETURN(FALSE); // Impossible test
}
else if (is_field_part)
diff --git a/sql/opt_table_elimination.cc b/sql/opt_table_elimination.cc
index 9f304ad043b..6434c36aaf2 100644
--- a/sql/opt_table_elimination.cc
+++ b/sql/opt_table_elimination.cc
@@ -328,7 +328,7 @@ const size_t Dep_value_table::iterator_size=
ALIGN_SIZE(sizeof(Dep_value_table::Module_iter));
const size_t Dep_value::iterator_size=
- max(Dep_value_table::iterator_size, Dep_value_field::iterator_size);
+ MY_MAX(Dep_value_table::iterator_size, Dep_value_field::iterator_size);
/*
@@ -356,7 +356,7 @@ public:
bound.
*/
void touch() { unbound_args--; }
- bool is_applicable() { return !test(unbound_args); }
+ bool is_applicable() { return !MY_TEST(unbound_args); }
/* Iteration over values that */
typedef char *Iterator;
@@ -441,7 +441,7 @@ const size_t Dep_module_key::iterator_size=
ALIGN_SIZE(sizeof(Dep_module_key::Value_iter));
const size_t Dep_module::iterator_size=
- max(Dep_module_expr::iterator_size, Dep_module_key::iterator_size);
+ MY_MAX(Dep_module_expr::iterator_size, Dep_module_key::iterator_size);
/*
@@ -1042,7 +1042,7 @@ bool Dep_analysis_context::setup_equality_modules_deps(List<Dep_module>
void *buf;
if (!(buf= current_thd->alloc(bitmap_buffer_size(offset))) ||
- bitmap_init(&expr_deps, (my_bitmap_map*)buf, offset, FALSE))
+ my_bitmap_init(&expr_deps, (my_bitmap_map*)buf, offset, FALSE))
{
DBUG_RETURN(TRUE); /* purecov: inspected */
}
@@ -1072,7 +1072,7 @@ bool Dep_analysis_context::setup_equality_modules_deps(List<Dep_module>
else
{
/* It's a multi-equality */
- eq_mod->unbound_args= !test(eq_mod->expr);
+ eq_mod->unbound_args= !MY_TEST(eq_mod->expr);
List_iterator<Dep_value_field> it(*eq_mod->mult_equal_fields);
Dep_value_field* field_val;
while ((field_val= it++))
@@ -1398,7 +1398,7 @@ Dep_module_expr *merge_eq_mods(Dep_module_expr *start,
}
}
- if (fv->elements + test(old->expr) > 1)
+ if (fv->elements + MY_TEST(old->expr) > 1)
{
old->mult_equal_fields= fv;
old->level= and_level;
@@ -1486,6 +1486,8 @@ void check_equality(Dep_analysis_context *ctx, Dep_module_expr **eq_mod,
left->real_item()->type() == Item::FIELD_ITEM)
{
Field *field= ((Item_field*)left->real_item())->field;
+ if (right->cmp_type() == TIME_RESULT && field->cmp_type() != TIME_RESULT)
+ return;
if (field->result_type() == STRING_RESULT)
{
if (right->result_type() != STRING_RESULT)
@@ -1499,8 +1501,10 @@ void check_equality(Dep_analysis_context *ctx, Dep_module_expr **eq_mod,
We can't assume there's a functional dependency if the effective
collation of the operation differ from the field collation.
*/
- if (field->cmp_type() == STRING_RESULT &&
- ((Field_str*)field)->charset() != cond->compare_collation())
+ if ((field->cmp_type() == STRING_RESULT ||
+ field->real_type() == MYSQL_TYPE_ENUM ||
+ field->real_type() == MYSQL_TYPE_SET) &&
+ field->charset() != cond->compare_collation())
return;
}
}
@@ -1581,7 +1585,7 @@ Dep_value_table *Dep_analysis_context::create_table_value(TABLE *table)
if (key->flags & HA_NOSAME)
{
Dep_module_key *key_dep;
- if (!(key_dep= new Dep_module_key(tbl_dep, i, key->key_parts)))
+ if (!(key_dep= new Dep_module_key(tbl_dep, i, key->user_defined_key_parts)))
return NULL;
*key_list= key_dep;
key_list= &(key_dep->next_table_key);
diff --git a/sql/parse_file.cc b/sql/parse_file.cc
index a6e3aa7ed66..197f7c97fda 100644
--- a/sql/parse_file.cc
+++ b/sql/parse_file.cc
@@ -20,11 +20,11 @@
Text .frm files management routines
*/
+#include <my_global.h>
#include "sql_priv.h"
#include "parse_file.h"
#include "unireg.h" // CREATE_MODE
#include "sql_table.h" // build_table_filename
-#include <errno.h>
#include <m_ctype.h>
#include <my_sys.h>
#include <my_dir.h>
@@ -282,7 +282,7 @@ sql_create_definition_file(const LEX_STRING *dir, const LEX_STRING *file_name,
path[path_end+1]= '\0';
if ((handler= mysql_file_create(key_file_fileparser,
path, CREATE_MODE, O_RDWR | O_TRUNC,
- MYF(MY_WME))) <= 0)
+ MYF(MY_WME))) < 0)
{
DBUG_RETURN(TRUE);
}
diff --git a/sql/partition_element.h b/sql/partition_element.h
index 75033d3552f..308a4d6ddd2 100644
--- a/sql/partition_element.h
+++ b/sql/partition_element.h
@@ -115,12 +115,10 @@ public:
partition_name(NULL), tablespace_name(NULL),
log_entry(NULL), part_comment(NULL),
data_file_name(NULL), index_file_name(NULL),
- engine_type(NULL), part_state(PART_NORMAL),
+ engine_type(NULL), connect_string(null_lex_str), part_state(PART_NORMAL),
nodegroup_id(UNDEF_NODEGROUP), has_null_value(FALSE),
signed_flag(FALSE), max_value(FALSE)
{
- connect_string.str= 0;
- connect_string.length= 0;
}
partition_element(partition_element *part_elem)
: part_max_rows(part_elem->part_max_rows),
@@ -131,13 +129,11 @@ public:
data_file_name(part_elem->data_file_name),
index_file_name(part_elem->index_file_name),
engine_type(part_elem->engine_type),
- connect_string(part_elem->connect_string),
+ connect_string(null_lex_str),
part_state(part_elem->part_state),
nodegroup_id(part_elem->nodegroup_id),
has_null_value(FALSE)
{
- connect_string.str= 0;
- connect_string.length= 0;
}
~partition_element() {}
};
diff --git a/sql/partition_info.cc b/sql/partition_info.cc
index d8b901701cb..1607b1937df 100644
--- a/sql/partition_info.cc
+++ b/sql/partition_info.cc
@@ -20,13 +20,16 @@
#pragma implementation
#endif
+#include <my_global.h>
#include "sql_priv.h"
// Required to get server definitions for mysql/plugin.h right
#include "sql_plugin.h"
-#include "sql_partition.h" /* partition_info.h: LIST_PART_ENTRY */
+#include "sql_partition.h" // partition_info.h: LIST_PART_ENTRY
+ // NOT_A_PARTITION_ID
#include "partition_info.h"
#include "sql_parse.h" // test_if_data_home_dir
#include "sql_acl.h" // *_ACL
+#include "sql_base.h" // fill_record
#ifdef WITH_PARTITION_STORAGE_ENGINE
#include "ha_partition.h"
@@ -34,17 +37,21 @@
partition_info *partition_info::get_clone()
{
+ DBUG_ENTER("partition_info::get_clone");
if (!this)
- return 0;
+ DBUG_RETURN(NULL);
List_iterator<partition_element> part_it(partitions);
partition_element *part;
partition_info *clone= new partition_info();
if (!clone)
{
mem_alloc_error(sizeof(partition_info));
- return NULL;
+ DBUG_RETURN(NULL);
}
memcpy(clone, this, sizeof(partition_info));
+ memset(&(clone->read_partitions), 0, sizeof(clone->read_partitions));
+ memset(&(clone->lock_partitions), 0, sizeof(clone->lock_partitions));
+ clone->bitmaps_are_initialized= FALSE;
clone->partitions.empty();
while ((part= (part_it++)))
@@ -55,7 +62,7 @@ partition_info *partition_info::get_clone()
if (!part_clone)
{
mem_alloc_error(sizeof(partition_element));
- return NULL;
+ DBUG_RETURN(NULL);
}
memcpy(part_clone, part, sizeof(partition_element));
part_clone->subpartitions.empty();
@@ -65,16 +72,462 @@ partition_info *partition_info::get_clone()
if (!subpart_clone)
{
mem_alloc_error(sizeof(partition_element));
- return NULL;
+ DBUG_RETURN(NULL);
}
memcpy(subpart_clone, subpart, sizeof(partition_element));
part_clone->subpartitions.push_back(subpart_clone);
}
clone->partitions.push_back(part_clone);
+ part_clone->list_val_list.empty();
+ List_iterator<part_elem_value> list_val_it(part->list_val_list);
+ part_elem_value *new_val_arr=
+ (part_elem_value *)sql_alloc(sizeof(part_elem_value) *
+ part->list_val_list.elements);
+ if (!new_val_arr)
+ {
+ mem_alloc_error(sizeof(part_elem_value) * part->list_val_list.elements);
+ DBUG_RETURN(NULL);
+ }
+ p_column_list_val *new_colval_arr=
+ (p_column_list_val*)sql_alloc(sizeof(p_column_list_val) *
+ num_columns *
+ part->list_val_list.elements);
+ if (!new_colval_arr)
+ {
+ mem_alloc_error(sizeof(p_column_list_val) * num_columns *
+ part->list_val_list.elements);
+ DBUG_RETURN(NULL);
+ }
+ part_elem_value *val;
+ while ((val= list_val_it++))
+ {
+ part_elem_value *new_val= new_val_arr++;
+ memcpy(new_val, val, sizeof(part_elem_value));
+ if (!val->null_value)
+ {
+ p_column_list_val *new_colval= new_colval_arr;
+ new_colval_arr+= num_columns;
+ memcpy(new_colval, val->col_val_array,
+ sizeof(p_column_list_val) * num_columns);
+ new_val->col_val_array= new_colval;
+ }
+ part_clone->list_val_list.push_back(new_val);
+ }
+ }
+ DBUG_RETURN(clone);
+}
+
+/**
+ Mark named [sub]partition to be used/locked.
+
+ @param part_name Partition name to match.
+ @param length Partition name length.
+
+ @return Success if partition found
+ @retval true Partition found
+ @retval false Partition not found
+*/
+
+bool partition_info::add_named_partition(const char *part_name,
+ uint length)
+{
+ HASH *part_name_hash;
+ PART_NAME_DEF *part_def;
+ Partition_share *part_share;
+ DBUG_ENTER("partition_info::add_named_partition");
+ DBUG_ASSERT(table && table->s && table->s->ha_share);
+ part_share= static_cast<Partition_share*>((table->s->ha_share));
+ DBUG_ASSERT(part_share->partition_name_hash_initialized);
+ part_name_hash= &part_share->partition_name_hash;
+ DBUG_ASSERT(part_name_hash->records);
+
+ part_def= (PART_NAME_DEF*) my_hash_search(part_name_hash,
+ (const uchar*) part_name,
+ length);
+ if (!part_def)
+ {
+ my_error(ER_UNKNOWN_PARTITION, MYF(0), part_name, table->alias.c_ptr());
+ DBUG_RETURN(true);
+ }
+
+ if (part_def->is_subpart)
+ {
+ bitmap_set_bit(&read_partitions, part_def->part_id);
+ }
+ else
+ {
+ if (is_sub_partitioned())
+ {
+ /* Mark all subpartitions in the partition */
+ uint j, start= part_def->part_id;
+ uint end= start + num_subparts;
+ for (j= start; j < end; j++)
+ bitmap_set_bit(&read_partitions, j);
+ }
+ else
+ bitmap_set_bit(&read_partitions, part_def->part_id);
+ }
+ DBUG_PRINT("info", ("Found partition %u is_subpart %d for name %s",
+ part_def->part_id, part_def->is_subpart,
+ part_name));
+ DBUG_RETURN(false);
+}
+
+
+/**
+ Mark named [sub]partition to be used/locked.
+
+ @param part_elem Partition element that matched.
+*/
+
+bool partition_info::set_named_partition_bitmap(const char *part_name,
+ uint length)
+{
+ DBUG_ENTER("partition_info::set_named_partition_bitmap");
+ bitmap_clear_all(&read_partitions);
+ if (add_named_partition(part_name, length))
+ DBUG_RETURN(true);
+ bitmap_copy(&lock_partitions, &read_partitions);
+ DBUG_RETURN(false);
+}
+
+
+
+/**
+ Prune away partitions not mentioned in the PARTITION () clause,
+ if used.
+
+ @param table_list Table list pointing to table to prune.
+
+ @return Operation status
+ @retval true Failure
+ @retval false Success
+*/
+bool partition_info::prune_partition_bitmaps(TABLE_LIST *table_list)
+{
+ List_iterator<String> partition_names_it(*(table_list->partition_names));
+ uint num_names= table_list->partition_names->elements;
+ uint i= 0;
+ DBUG_ENTER("partition_info::prune_partition_bitmaps");
+
+ if (num_names < 1)
+ DBUG_RETURN(true);
+
+ /*
+ TODO: When adding support for FK in partitioned tables, the referenced
+ table must probably lock all partitions for read, and also write depending
+ of ON DELETE/UPDATE.
+ */
+ bitmap_clear_all(&read_partitions);
+
+ /* No check for duplicate names or overlapping partitions/subpartitions. */
+
+ DBUG_PRINT("info", ("Searching through partition_name_hash"));
+ do
+ {
+ String *part_name_str= partition_names_it++;
+ if (add_named_partition(part_name_str->c_ptr(), part_name_str->length()))
+ DBUG_RETURN(true);
+ } while (++i < num_names);
+ DBUG_RETURN(false);
+}
+
+
+/**
+ Set read/lock_partitions bitmap over non pruned partitions
+
+ @param table_list Possible TABLE_LIST which can contain
+ list of partition names to query
+
+ @return Operation status
+ @retval FALSE OK
+ @retval TRUE Failed to allocate memory for bitmap or list of partitions
+ did not match
+
+ @note OK to call multiple times without the need for free_bitmaps.
+*/
+
+bool partition_info::set_partition_bitmaps(TABLE_LIST *table_list)
+{
+ DBUG_ENTER("partition_info::set_partition_bitmaps");
+
+ DBUG_ASSERT(bitmaps_are_initialized);
+ DBUG_ASSERT(table);
+ is_pruning_completed= false;
+ if (!bitmaps_are_initialized)
+ DBUG_RETURN(TRUE);
+
+ if (table_list &&
+ table_list->partition_names &&
+ table_list->partition_names->elements)
+ {
+ if (table->s->db_type()->partition_flags() & HA_USE_AUTO_PARTITION)
+ {
+ /*
+ Don't allow PARTITION () clause on a NDB tables yet.
+ TODO: Add partition name handling to NDB/partition_info.
+ which is currently ha_partition specific.
+ */
+ my_error(ER_PARTITION_CLAUSE_ON_NONPARTITIONED, MYF(0));
+ DBUG_RETURN(true);
+ }
+ if (prune_partition_bitmaps(table_list))
+ DBUG_RETURN(TRUE);
+ }
+ else
+ {
+ bitmap_set_all(&read_partitions);
+ DBUG_PRINT("info", ("Set all partitions"));
+ }
+ bitmap_copy(&lock_partitions, &read_partitions);
+ DBUG_ASSERT(bitmap_get_first_set(&lock_partitions) != MY_BIT_NONE);
+ DBUG_RETURN(FALSE);
+}
+
+
+/**
+ Checks if possible to do prune partitions on insert.
+
+ @param thd Thread context
+ @param duplic How to handle duplicates
+ @param update In case of ON DUPLICATE UPDATE, default function fields
+ @param update_fields In case of ON DUPLICATE UPDATE, which fields to update
+ @param fields Listed fields
+ @param empty_values True if values is empty (only defaults)
+ @param[out] prune_needs_default_values Set on return if copying of default
+ values is needed
+ @param[out] can_prune_partitions Enum showing if possible to prune
+ @param[inout] used_partitions If possible to prune the bitmap
+ is initialized and cleared
+
+ @return Operation status
+ @retval false Success
+ @retval true Failure
+*/
+
+bool partition_info::can_prune_insert(THD* thd,
+ enum_duplicates duplic,
+ COPY_INFO &update,
+ List<Item> &update_fields,
+ List<Item> &fields,
+ bool empty_values,
+ enum_can_prune *can_prune_partitions,
+ bool *prune_needs_default_values,
+ MY_BITMAP *used_partitions)
+{
+ uint32 *bitmap_buf;
+ uint bitmap_bytes;
+ uint num_partitions= 0;
+ *can_prune_partitions= PRUNE_NO;
+ DBUG_ASSERT(bitmaps_are_initialized);
+ DBUG_ENTER("partition_info::can_prune_insert");
+
+ if (table->s->db_type()->partition_flags() & HA_USE_AUTO_PARTITION)
+ DBUG_RETURN(false); /* Should not insert prune NDB tables */
+
+ /*
+ If under LOCK TABLES pruning will skip start_stmt instead of external_lock
+ for unused partitions.
+
+ Cannot prune if there are BEFORE INSERT triggers that changes any
+ partitioning column, since they may change the row to be in another
+ partition.
+ */
+ if (table->triggers &&
+ table->triggers->has_triggers(TRG_EVENT_INSERT, TRG_ACTION_BEFORE) &&
+ table->triggers->is_fields_updated_in_trigger(&full_part_field_set,
+ TRG_EVENT_INSERT,
+ TRG_ACTION_BEFORE))
+ DBUG_RETURN(false);
+
+ if (table->found_next_number_field)
+ {
+ /*
+ If the field is used in the partitioning expression, we cannot prune.
+ TODO: If all rows have not null values and
+ is not 0 (with NO_AUTO_VALUE_ON_ZERO sql_mode), then pruning is possible!
+ */
+ if (bitmap_is_set(&full_part_field_set,
+ table->found_next_number_field->field_index))
+ DBUG_RETURN(false);
+ }
+
+ /*
+ If updating a field in the partitioning expression, we cannot prune.
+
+ Note: TIMESTAMP_AUTO_SET_ON_INSERT is handled by converting Item_null
+ to the start time of the statement. Which will be the same as in
+ write_row(). So pruning of TIMESTAMP DEFAULT CURRENT_TIME will work.
+ But TIMESTAMP_AUTO_SET_ON_UPDATE cannot be pruned if the timestamp
+ column is a part of any part/subpart expression.
+ */
+ if (duplic == DUP_UPDATE)
+ {
+ /*
+ TODO: add check for static update values, which can be pruned.
+ */
+ if (is_field_in_part_expr(update_fields))
+ DBUG_RETURN(false);
+
+ /*
+ Cannot prune if there are BEFORE UPDATE triggers that changes any
+ partitioning column, since they may change the row to be in another
+ partition.
+ */
+ if (table->triggers &&
+ table->triggers->has_triggers(TRG_EVENT_UPDATE,
+ TRG_ACTION_BEFORE) &&
+ table->triggers->is_fields_updated_in_trigger(&full_part_field_set,
+ TRG_EVENT_UPDATE,
+ TRG_ACTION_BEFORE))
+ {
+ DBUG_RETURN(false);
+ }
+ }
+
+ /*
+ If not all partitioning fields are given,
+ we also must set all non given partitioning fields
+ to get correct defaults.
+ TODO: If any gain, we could enhance this by only copy the needed default
+ fields by
+ 1) check which fields needs to be set.
+ 2) only copy those fields from the default record.
+ */
+ *prune_needs_default_values= false;
+ if (fields.elements)
+ {
+ if (!is_full_part_expr_in_fields(fields))
+ *prune_needs_default_values= true;
+ }
+ else if (empty_values)
+ {
+ *prune_needs_default_values= true; // like 'INSERT INTO t () VALUES ()'
+ }
+ else
+ {
+ /*
+ In case of INSERT INTO t VALUES (...) we must get values for
+ all fields in table from VALUES (...) part, so no defaults
+ are needed.
+ */
+ }
+
+ /* Pruning possible, have to initialize the used_partitions bitmap. */
+ num_partitions= lock_partitions.n_bits;
+ bitmap_bytes= bitmap_buffer_size(num_partitions);
+ if (!(bitmap_buf= (uint32*) thd->alloc(bitmap_bytes)))
+ {
+ mem_alloc_error(bitmap_bytes);
+ DBUG_RETURN(true);
+ }
+ /* Also clears all bits. */
+ if (my_bitmap_init(used_partitions, bitmap_buf, num_partitions, false))
+ {
+ /* purecov: begin deadcode */
+ /* Cannot happen, due to pre-alloc. */
+ mem_alloc_error(bitmap_bytes);
+ DBUG_RETURN(true);
+ /* purecov: end */
+ }
+ /*
+ If no partitioning field in set (e.g. defaults) check pruning only once.
+ */
+ if (fields.elements &&
+ !is_field_in_part_expr(fields))
+ *can_prune_partitions= PRUNE_DEFAULTS;
+ else
+ *can_prune_partitions= PRUNE_YES;
+
+ DBUG_RETURN(false);
+}
+
+
+/**
+ Mark the partition, the record belongs to, as used.
+
+ @param fields Fields to set
+ @param values Values to use
+ @param info COPY_INFO used for default values handling
+ @param copy_default_values True if we should copy default values
+ @param used_partitions Bitmap to set
+
+ @returns Operational status
+ @retval false Success
+ @retval true Failure
+*/
+
+bool partition_info::set_used_partition(List<Item> &fields,
+ List<Item> &values,
+ COPY_INFO &info,
+ bool copy_default_values,
+ MY_BITMAP *used_partitions)
+{
+ THD *thd= table->in_use;
+ uint32 part_id;
+ longlong func_value;
+ Dummy_error_handler error_handler;
+ bool ret= true;
+ DBUG_ENTER("set_partition");
+ DBUG_ASSERT(thd);
+
+ /* Only allow checking of constant values */
+ List_iterator_fast<Item> v(values);
+ Item *item;
+ thd->push_internal_handler(&error_handler);
+ while ((item= v++))
+ {
+ if (!item->const_item())
+ goto err;
+ }
+
+ if (copy_default_values)
+ restore_record(table,s->default_values);
+
+ if (fields.elements || !values.elements)
+ {
+ if (fill_record(thd, table, fields, values, false))
+ goto err;
+ }
+ else
+ {
+ if (fill_record(thd, table, table->field, values, false, false))
+ goto err;
+ }
+ DBUG_ASSERT(!table->auto_increment_field_not_null);
+
+ /*
+ Evaluate DEFAULT functions like CURRENT_TIMESTAMP.
+ TODO: avoid setting non partitioning fields default value, to avoid
+ overhead. Not yet done, since mostly only one DEFAULT function per
+ table, or at least very few such columns.
+ */
+// if (info.function_defaults_apply_on_columns(&full_part_field_set))
+// info.set_function_defaults(table);
+
+ {
+ /*
+ This function is used in INSERT; 'values' are supplied by user,
+ or are default values, not values read from a table, so read_set is
+ irrelevant.
+ */
+ my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->read_set);
+ const int rc= get_partition_id(this, &part_id, &func_value);
+ dbug_tmp_restore_column_map(table->read_set, old_map);
+ if (rc)
+ goto err;
}
- return clone;
+
+ DBUG_PRINT("info", ("Insert into partition %u", part_id));
+ bitmap_set_bit(used_partitions, part_id);
+ ret= false;
+
+err:
+ thd->pop_internal_handler();
+ DBUG_RETURN(ret);
}
+
/*
Create a memory area where default partition names are stored and fill it
up with the names.
@@ -160,8 +613,9 @@ void partition_info::set_show_version_string(String *packet)
/*
Create a unique name for the subpartition as part_name'sp''subpart_no'
+
SYNOPSIS
- create_subpartition_name()
+ create_default_subpartition_name()
subpart_no Number of subpartition
part_name Name of partition
RETURN VALUES
@@ -169,12 +623,12 @@ void partition_info::set_show_version_string(String *packet)
0 Memory allocation error
*/
-char *partition_info::create_subpartition_name(uint subpart_no,
+char *partition_info::create_default_subpartition_name(uint subpart_no,
const char *part_name)
{
uint size_alloc= strlen(part_name) + MAX_PART_NAME_SIZE;
char *ptr= (char*) sql_calloc(size_alloc);
- DBUG_ENTER("create_subpartition_name");
+ DBUG_ENTER("create_default_subpartition_name");
if (likely(ptr != NULL))
{
@@ -320,7 +774,8 @@ bool partition_info::set_up_default_subpartitions(handler *file,
if (likely(subpart_elem != 0 &&
(!part_elem->subpartitions.push_back(subpart_elem))))
{
- char *ptr= create_subpartition_name(j, part_elem->partition_name);
+ char *ptr= create_default_subpartition_name(j,
+ part_elem->partition_name);
if (!ptr)
goto end;
subpart_elem->engine_type= default_engine_type;
@@ -380,7 +835,7 @@ bool partition_info::set_up_defaults_for_partitioning(handler *file,
Support routine for check_partition_info
SYNOPSIS
- has_unique_fields
+ find_duplicate_field
no parameters
RETURN VALUE
@@ -391,13 +846,13 @@ bool partition_info::set_up_defaults_for_partitioning(handler *file,
Check that the user haven't defined the same field twice in
key or column list partitioning.
*/
-char* partition_info::has_unique_fields()
+char* partition_info::find_duplicate_field()
{
char *field_name_outer, *field_name_inner;
List_iterator<char> it_outer(part_field_list);
uint num_fields= part_field_list.elements;
uint i,j;
- DBUG_ENTER("partition_info::has_unique_fields");
+ DBUG_ENTER("partition_info::find_duplicate_field");
for (i= 0; i < num_fields; i++)
{
@@ -419,6 +874,152 @@ char* partition_info::has_unique_fields()
DBUG_RETURN(NULL);
}
+
+/**
+ @brief Get part_elem and part_id from partition name
+
+ @param partition_name Name of partition to search for.
+ @param file_name[out] Partition file name (part after table name,
+ #P#<part>[#SP#<subpart>]), skipped if NULL.
+ @param part_id[out] Id of found partition or NOT_A_PARTITION_ID.
+
+ @retval Pointer to part_elem of [sub]partition, if not found NULL
+
+ @note Since names of partitions AND subpartitions must be unique,
+ this function searches both partitions and subpartitions and if name of
+ a partition is given for a subpartitioned table, part_elem will be
+ the partition, but part_id will be NOT_A_PARTITION_ID and file_name not set.
+*/
+partition_element *partition_info::get_part_elem(const char *partition_name,
+ char *file_name,
+ uint32 *part_id)
+{
+ List_iterator<partition_element> part_it(partitions);
+ uint i= 0;
+ DBUG_ENTER("partition_info::get_part_elem");
+ DBUG_ASSERT(part_id);
+ *part_id= NOT_A_PARTITION_ID;
+ do
+ {
+ partition_element *part_elem= part_it++;
+ if (is_sub_partitioned())
+ {
+ List_iterator<partition_element> sub_part_it(part_elem->subpartitions);
+ uint j= 0;
+ do
+ {
+ partition_element *sub_part_elem= sub_part_it++;
+ if (!my_strcasecmp(system_charset_info,
+ sub_part_elem->partition_name, partition_name))
+ {
+ if (file_name)
+ create_subpartition_name(file_name, "",
+ part_elem->partition_name,
+ partition_name,
+ NORMAL_PART_NAME);
+ *part_id= j + (i * num_subparts);
+ DBUG_RETURN(sub_part_elem);
+ }
+ } while (++j < num_subparts);
+
+ /* Naming a partition (first level) on a subpartitioned table. */
+ if (!my_strcasecmp(system_charset_info,
+ part_elem->partition_name, partition_name))
+ DBUG_RETURN(part_elem);
+ }
+ else if (!my_strcasecmp(system_charset_info,
+ part_elem->partition_name, partition_name))
+ {
+ if (file_name)
+ create_partition_name(file_name, "", partition_name,
+ NORMAL_PART_NAME, TRUE);
+ *part_id= i;
+ DBUG_RETURN(part_elem);
+ }
+ } while (++i < num_parts);
+ DBUG_RETURN(NULL);
+}
+
+
+/**
+ Helper function to find_duplicate_name.
+*/
+
+static const char *get_part_name_from_elem(const char *name, size_t *length,
+ my_bool not_used __attribute__((unused)))
+{
+ *length= strlen(name);
+ return name;
+}
+
+/*
+ A support function to check partition names for duplication in a
+ partitioned table
+
+ SYNOPSIS
+ find_duplicate_name()
+
+ RETURN VALUES
+ NULL Has unique part and subpart names
+ !NULL Pointer to duplicated name
+
+ DESCRIPTION
+ Checks that the list of names in the partitions doesn't contain any
+ duplicated names.
+*/
+
+char *partition_info::find_duplicate_name()
+{
+ HASH partition_names;
+ uint max_names;
+ const uchar *curr_name= NULL;
+ List_iterator<partition_element> parts_it(partitions);
+ partition_element *p_elem;
+
+ DBUG_ENTER("partition_info::find_duplicate_name");
+
+ /*
+ TODO: If table->s->ha_part_data->partition_name_hash.elements is > 0,
+ then we could just return NULL, but that has not been verified.
+ And this only happens when in ALTER TABLE with full table copy.
+ */
+
+ max_names= num_parts;
+ if (is_sub_partitioned())
+ max_names+= num_parts * num_subparts;
+ if (my_hash_init(&partition_names, system_charset_info, max_names, 0, 0,
+ (my_hash_get_key) get_part_name_from_elem, 0, HASH_UNIQUE))
+ {
+ DBUG_ASSERT(0);
+ curr_name= (const uchar*) "Internal failure";
+ goto error;
+ }
+ while ((p_elem= (parts_it++)))
+ {
+ curr_name= (const uchar*) p_elem->partition_name;
+ if (my_hash_insert(&partition_names, curr_name))
+ goto error;
+
+ if (!p_elem->subpartitions.is_empty())
+ {
+ List_iterator<partition_element> subparts_it(p_elem->subpartitions);
+ partition_element *subp_elem;
+ while ((subp_elem= (subparts_it++)))
+ {
+ curr_name= (const uchar*) subp_elem->partition_name;
+ if (my_hash_insert(&partition_names, curr_name))
+ goto error;
+ }
+ }
+ }
+ my_hash_free(&partition_names);
+ DBUG_RETURN(NULL);
+error:
+ my_hash_free(&partition_names);
+ DBUG_RETURN((char*) curr_name);
+}
+
+
/*
A support function to check if a partition element's name is unique
@@ -462,49 +1063,6 @@ bool partition_info::has_unique_name(partition_element *element)
/*
- A support function to check partition names for duplication in a
- partitioned table
-
- SYNOPSIS
- has_unique_names()
-
- RETURN VALUES
- TRUE Has unique part and subpart names
- FALSE Doesn't
-
- DESCRIPTION
- Checks that the list of names in the partitions doesn't contain any
- duplicated names.
-*/
-
-char *partition_info::has_unique_names()
-{
- DBUG_ENTER("partition_info::has_unique_names");
-
- List_iterator<partition_element> parts_it(partitions);
-
- partition_element *el;
- while ((el= (parts_it++)))
- {
- if (! has_unique_name(el))
- DBUG_RETURN(el->partition_name);
-
- if (!el->subpartitions.is_empty())
- {
- List_iterator<partition_element> subparts_it(el->subpartitions);
- partition_element *subel;
- while ((subel= (subparts_it++)))
- {
- if (! has_unique_name(subel))
- DBUG_RETURN(subel->partition_name);
- }
- }
- }
- DBUG_RETURN(NULL);
-}
-
-
-/*
Check that the partition/subpartition is setup to use the correct
storage engine
SYNOPSIS
@@ -1053,16 +1611,14 @@ end:
*/
static void warn_if_dir_in_part_elem(THD *thd, partition_element *part_elem)
{
-#ifdef HAVE_READLINK
- if (!my_use_symdir || (thd->variables.sql_mode & MODE_NO_DIR_IN_CREATE))
-#endif
+ if (thd->variables.sql_mode & MODE_NO_DIR_IN_CREATE)
{
if (part_elem->data_file_name)
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
WARN_OPTION_IGNORED, ER(WARN_OPTION_IGNORED),
"DATA DIRECTORY");
if (part_elem->index_file_name)
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
WARN_OPTION_IGNORED, ER(WARN_OPTION_IGNORED),
"INDEX DIRECTORY");
part_elem->data_file_name= part_elem->index_file_name= NULL;
@@ -1195,12 +1751,12 @@ bool partition_info::check_partition_info(THD *thd, handlerton **eng_type,
}
if (part_field_list.elements > 0 &&
- (same_name= has_unique_fields()))
+ (same_name= find_duplicate_field()))
{
my_error(ER_SAME_NAME_PARTITION_FIELD, MYF(0), same_name);
goto end;
}
- if ((same_name= has_unique_names()))
+ if ((same_name= find_duplicate_name()))
{
my_error(ER_SAME_NAME_PARTITION, MYF(0), same_name);
goto end;
@@ -1589,29 +2145,21 @@ bool check_partition_dirs(partition_info *part_info)
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;
+ if (error_if_data_home_dir(subpart_elem->data_file_name,
+ "DATA DIRECTORY") ||
+ error_if_data_home_dir(subpart_elem->index_file_name,
+ "INDEX DIRECTORY"))
+ return 1;
}
}
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;
+ if (error_if_data_home_dir(part_elem->data_file_name, "DATA DIRECTORY") ||
+ error_if_data_home_dir(part_elem->index_file_name, "INDEX DIRECTORY"))
+ return 1;
}
}
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;
}
@@ -1660,6 +2208,71 @@ void partition_info::report_part_expr_error(bool use_subpart_expr)
}
+/**
+ Check if fields are in the partitioning expression.
+
+ @param fields List of Items (fields)
+
+ @return True if any field in the fields list is used by a partitioning expr.
+ @retval true At least one field in the field list is found.
+ @retval false No field is within any partitioning expression.
+*/
+
+bool partition_info::is_field_in_part_expr(List<Item> &fields)
+{
+ List_iterator<Item> it(fields);
+ Item *item;
+ Item_field *field;
+ DBUG_ENTER("is_fields_in_part_expr");
+ while ((item= it++))
+ {
+ field= item->field_for_view_update();
+ DBUG_ASSERT(field->field->table == table);
+ if (bitmap_is_set(&full_part_field_set, field->field->field_index))
+ DBUG_RETURN(true);
+ }
+ DBUG_RETURN(false);
+}
+
+
+/**
+ Check if all partitioning fields are included.
+*/
+
+bool partition_info::is_full_part_expr_in_fields(List<Item> &fields)
+{
+ Field **part_field= full_part_field_array;
+ DBUG_ASSERT(*part_field);
+ DBUG_ENTER("is_full_part_expr_in_fields");
+ /*
+ It is very seldom many fields in full_part_field_array, so it is OK
+ to loop over all of them instead of creating a bitmap fields argument
+ to compare with.
+ */
+ do
+ {
+ List_iterator<Item> it(fields);
+ Item *item;
+ Item_field *field;
+ bool found= false;
+
+ while ((item= it++))
+ {
+ field= item->field_for_view_update();
+ DBUG_ASSERT(field->field->table == table);
+ if (*part_field == field->field)
+ {
+ found= true;
+ break;
+ }
+ }
+ if (!found)
+ DBUG_RETURN(false);
+ } while (*(++part_field));
+ DBUG_RETURN(true);
+}
+
+
/*
Create a new column value in current list with maxvalue
Called from parser
@@ -2134,9 +2747,11 @@ end:
DBUG_RETURN(result);
}
-/*
- The parser generates generic data structures, we need to set them up
- as the rest of the code expects to find them. This is in reality part
+/**
+ Fix partition data from parser.
+
+ @details The parser generates generic data structures, we need to set them
+ up as the rest of the code expects to find them. This is in reality part
of the syntax check of the parser code.
It is necessary to call this function in the case of a CREATE TABLE
@@ -2168,16 +2783,14 @@ end:
and number of elements are in synch with each other. So only partitioning
using functions need to be set-up to their data structures.
- SYNOPSIS
- fix_parser_data()
- thd Thread object
+ @param thd Thread object
- RETURN VALUES
- TRUE Failure
- FALSE Success
+ @return Operation status
+ @retval TRUE Failure
+ @retval FALSE Success
*/
-int partition_info::fix_parser_data(THD *thd)
+bool partition_info::fix_parser_data(THD *thd)
{
List_iterator<partition_element> it(partitions);
partition_element *part_elem;
@@ -2576,4 +3189,9 @@ void partition_info::print_debug(const char *str, uint *value)
{
}
+bool check_partition_dirs(partition_info *part_info)
+{
+ return 0;
+}
+
#endif /* WITH_PARTITION_STORAGE_ENGINE */
diff --git a/sql/partition_info.h b/sql/partition_info.h
index cff941a858a..8ad7b1fd1fd 100644
--- a/sql/partition_info.h
+++ b/sql/partition_info.h
@@ -20,10 +20,11 @@
#pragma interface /* gcc class implementation */
#endif
+#include "sql_class.h"
#include "partition_element.h"
class partition_info;
-
+struct TABLE_LIST;
/* Some function typedefs */
typedef int (*get_part_id_func)(partition_info *part_info,
uint32 *part_id,
@@ -111,14 +112,30 @@ public:
struct st_ddl_log_memory_entry *frm_log_entry;
/*
- A bitmap of partitions used by the current query.
+ Bitmaps of partitions used by the current query.
+ * read_partitions - partitions to be used for reading.
+ * lock_partitions - partitions that must be locked (read or write).
+ Usually read_partitions is the same set as lock_partitions, but
+ in case of UPDATE the WHERE clause can limit the read_partitions set,
+ but not neccesarily the lock_partitions set.
Usage pattern:
- * The handler->extra(HA_EXTRA_RESET) call at query start/end sets all
- partitions to be unused.
- * Before index/rnd_init(), partition pruning code sets the bits for used
- partitions.
+ * Initialized in ha_partition::open().
+ * read+lock_partitions is set according to explicit PARTITION,
+ WL#5217, in open_and_lock_tables().
+ * Bits in read_partitions can be cleared in prune_partitions()
+ in the optimizing step.
+ (WL#4443 is about allowing prune_partitions() to affect lock_partitions
+ and be done before locking too).
+ * When the partition enabled handler get an external_lock call it locks
+ all partitions in lock_partitions (and remembers which partitions it
+ locked, so that it can unlock them later). In case of LOCK TABLES it will
+ lock all partitions, and keep them locked while lock_partitions can
+ change for each statement under LOCK TABLES.
+ * Freed at the same time item_free_list is freed.
*/
- MY_BITMAP used_partitions;
+ MY_BITMAP read_partitions;
+ MY_BITMAP lock_partitions;
+ bool bitmaps_are_initialized;
union {
longlong *range_int_array;
@@ -151,12 +168,13 @@ public:
char *part_func_string;
char *subpart_func_string;
- partition_element *curr_part_elem;
- partition_element *current_partition;
+ partition_element *curr_part_elem; // part or sub part
+ partition_element *current_partition; // partition
part_elem_value *curr_list_val;
uint curr_list_object;
uint num_columns;
+ TABLE *table;
/*
These key_map's are used for Partitioning to enable quick decisions
on whether we can derive more information about which partition to
@@ -175,9 +193,7 @@ public:
uint num_parts;
uint num_subparts;
- uint count_curr_subparts;
-
- uint part_error_code;
+ uint count_curr_subparts; // used during parsing
uint num_list_values;
@@ -206,20 +222,30 @@ public:
};
enum_key_algorithm key_algorithm;
+ /* Only the number of partitions defined (uses default names and options). */
bool use_default_partitions;
bool use_default_num_partitions;
+ /* Only the number of subpartitions defined (uses default names etc.). */
bool use_default_subpartitions;
bool use_default_num_subpartitions;
bool default_partitions_setup;
bool defined_max_value;
- bool list_of_part_fields;
- bool list_of_subpart_fields;
- bool linear_hash_ind;
+ bool list_of_part_fields; // KEY or COLUMNS PARTITIONING
+ bool list_of_subpart_fields; // KEY SUBPARTITIONING
+ bool linear_hash_ind; // LINEAR HASH/KEY
bool fixed;
bool is_auto_partitioned;
- bool from_openfrm;
bool has_null_value;
- bool column_list;
+ bool column_list; // COLUMNS PARTITIONING, 5.5+
+ /**
+ True if pruning has been completed and can not be pruned any further,
+ even if there are subqueries or stored programs in the condition.
+
+ Some times it is needed to run prune_partitions() a second time to prune
+ read partitions after tables are locked, when subquery and
+ stored functions might have been evaluated.
+ */
+ bool is_pruning_completed;
partition_info()
: get_partition_id(NULL), get_part_partition_id(NULL),
@@ -232,17 +258,18 @@ public:
restore_part_field_ptrs(NULL), restore_subpart_field_ptrs(NULL),
part_expr(NULL), subpart_expr(NULL), item_free_list(NULL),
first_log_entry(NULL), exec_log_entry(NULL), frm_log_entry(NULL),
+ bitmaps_are_initialized(FALSE),
list_array(NULL), err_value(0),
part_info_string(NULL),
part_func_string(NULL), subpart_func_string(NULL),
curr_part_elem(NULL), current_partition(NULL),
- curr_list_object(0), num_columns(0),
+ curr_list_object(0), num_columns(0), table(NULL),
default_engine_type(NULL),
part_type(NOT_A_PARTITION), subpart_type(NOT_A_PARTITION),
part_info_len(0),
part_func_len(0), subpart_func_len(0),
num_parts(0), num_subparts(0),
- count_curr_subparts(0), part_error_code(0),
+ count_curr_subparts(0),
num_list_values(0), num_part_fields(0), num_subpart_fields(0),
num_full_part_fields(0), has_null_part_id(0), linear_hash_mask(0),
key_algorithm(KEY_ALGORITHM_NONE),
@@ -251,8 +278,8 @@ public:
default_partitions_setup(FALSE), defined_max_value(FALSE),
list_of_part_fields(FALSE), list_of_subpart_fields(FALSE),
linear_hash_ind(FALSE), fixed(FALSE),
- is_auto_partitioned(FALSE), from_openfrm(FALSE),
- has_null_value(FALSE), column_list(FALSE)
+ is_auto_partitioned(FALSE),
+ has_null_value(FALSE), column_list(FALSE), is_pruning_completed(false)
{
all_fields_in_PF.clear_all();
all_fields_in_PPF.clear_all();
@@ -266,6 +293,8 @@ public:
~partition_info() {}
partition_info *get_clone();
+ bool set_named_partition_bitmap(const char *part_name, uint length);
+ bool set_partition_bitmaps(TABLE_LIST *table_list);
/* Answers the question if subpartitioning is used for a certain table */
bool is_sub_partitioned()
{
@@ -280,8 +309,8 @@ public:
bool set_up_defaults_for_partitioning(handler *file, HA_CREATE_INFO *info,
uint start_no);
- char *has_unique_fields();
- char *has_unique_names();
+ char *find_duplicate_field();
+ char *find_duplicate_name();
bool check_engine_mix(handlerton *engine_type, bool default_engine);
bool check_range_constants(THD *thd);
bool check_list_constants(THD *thd);
@@ -298,7 +327,7 @@ public:
bool fix_column_value_functions(THD *thd,
part_elem_value *val,
uint part_id);
- int fix_parser_data(THD *thd);
+ bool fix_parser_data(THD *thd);
int add_max_value();
void init_col_val(part_column_list_val *col_val, Item *item);
int reorganize_into_single_field_col_val();
@@ -311,7 +340,34 @@ public:
bool init_column_part();
bool add_column_list_value(THD *thd, Item *item);
void set_show_version_string(String *packet);
+ partition_element *get_part_elem(const char *partition_name,
+ char *file_name,
+ uint32 *part_id);
void report_part_expr_error(bool use_subpart_expr);
+ bool set_used_partition(List<Item> &fields,
+ List<Item> &values,
+ COPY_INFO &info,
+ bool copy_default_values,
+ MY_BITMAP *used_partitions);
+ /**
+ PRUNE_NO - Unable to prune.
+ PRUNE_DEFAULTS - Partitioning field is only set to
+ DEFAULT values, only need to check
+ pruning for one row where the DEFAULTS
+ values are set.
+ PRUNE_YES - Pruning is possible, calculate the used partition set
+ by evaluate the partition_id on row by row basis.
+ */
+ enum enum_can_prune {PRUNE_NO=0, PRUNE_DEFAULTS, PRUNE_YES};
+ bool can_prune_insert(THD *thd,
+ enum_duplicates duplic,
+ COPY_INFO &update,
+ List<Item> &update_fields,
+ List<Item> &fields,
+ bool empty_values,
+ enum_can_prune *can_prune_partitions,
+ bool *prune_needs_default_values,
+ MY_BITMAP *used_partitions);
bool has_same_partitioning(partition_info *new_part_info);
private:
static int list_part_cmp(const void* a, const void* b);
@@ -320,7 +376,13 @@ private:
bool set_up_default_subpartitions(handler *file, HA_CREATE_INFO *info);
char *create_default_partition_names(uint part_no, uint num_parts,
uint start_no);
- char *create_subpartition_name(uint subpart_no, const char *part_name);
+ char *create_default_subpartition_name(uint subpart_no,
+ const char *part_name);
+ bool prune_partition_bitmaps(TABLE_LIST *table_list);
+ bool add_named_partition(const char *part_name, uint length);
+ bool is_field_in_part_expr(List<Item> &fields);
+ bool is_full_part_expr_in_fields(List<Item> &fields);
+public:
bool has_unique_name(partition_element *element);
};
diff --git a/sql/password.c b/sql/password.c
index 947620ddf7a..37d06136d80 100644
--- a/sql/password.c
+++ b/sql/password.c
@@ -60,12 +60,13 @@
*****************************************************************************/
-#include <password.h>
#include <my_global.h>
#include <my_sys.h>
#include <m_string.h>
+#include <password.h>
+#include <mysql.h>
+#include <my_rnd.h>
#include <sha1.h>
-#include "mysql.h"
/************ MySQL 3.23-4.0 authentication routines: untouched ***********/
@@ -278,14 +279,13 @@ void make_password_from_salt_323(char *to, const ulong *salt)
**************** MySQL 4.1.1 authentication routines *************
*/
-/*
- Generate string of printable random characters of requested length
- SYNOPSIS
- create_random_string()
- to OUT buffer for generation; must be at least length+1 bytes
- long; result string is always null-terminated
- length IN how many random characters to put in buffer
- rand_st INOUT structure used for number generation
+/**
+ Generate string of printable random characters of requested length.
+
+ @param to[out] Buffer for generation; must be at least length+1 bytes
+ long; result string is always null-terminated
+ length[in] How many random characters to put in buffer
+ rand_st Structure used for number generation
*/
void create_random_string(char *to, uint length,
@@ -372,6 +372,30 @@ my_crypt(char *to, const uchar *s1, const uchar *s2, uint len)
}
+/**
+ Compute two stage SHA1 hash of the password :
+
+ hash_stage1=sha1("password")
+ hash_stage2=sha1(hash_stage1)
+
+ @param password [IN] Password string.
+ @param pass_len [IN] Length of the password.
+ @param hash_stage1 [OUT] sha1(password)
+ @param hash_stage2 [OUT] sha1(hash_stage1)
+*/
+
+inline static
+void compute_two_stage_sha1_hash(const char *password, size_t pass_len,
+ uint8 *hash_stage1, uint8 *hash_stage2)
+{
+ /* Stage 1: hash password */
+ compute_sha1_hash(hash_stage1, password, pass_len);
+
+ /* Stage 2 : hash first stage's output. */
+ compute_sha1_hash(hash_stage2, (const char *) hash_stage1, SHA1_HASH_SIZE);
+}
+
+
/*
MySQL 4.1.1 password hashing: SHA conversion (see RFC 2289, 3174) twice
applied to the password string, and then produced octet sequence is
@@ -388,18 +412,11 @@ my_crypt(char *to, const uchar *s1, const uchar *s2, uint len)
void my_make_scrambled_password(char *to, const char *password,
size_t pass_len)
{
- SHA1_CONTEXT sha1_context;
uint8 hash_stage2[SHA1_HASH_SIZE];
- mysql_sha1_reset(&sha1_context);
- /* stage 1: hash password */
- mysql_sha1_input(&sha1_context, (uint8 *) password, (uint) pass_len);
- mysql_sha1_result(&sha1_context, (uint8 *) to);
- /* stage 2: hash stage1 output */
- mysql_sha1_reset(&sha1_context);
- mysql_sha1_input(&sha1_context, (uint8 *) to, SHA1_HASH_SIZE);
- /* separate buffer is used to pass 'to' in octet2hex */
- mysql_sha1_result(&sha1_context, hash_stage2);
+ /* Two stage SHA1 hash of the password. */
+ compute_two_stage_sha1_hash(password, pass_len, (uint8 *) to, hash_stage2);
+
/* convert hash_stage2 to hex string */
*to++= PVERSION41_CHAR;
octet2hex(to, (const char*) hash_stage2, SHA1_HASH_SIZE);
@@ -425,7 +442,7 @@ void make_scrambled_password(char *to, const char *password)
/*
Produce an obscure octet sequence from password and random
- string, recieved from the server. This sequence corresponds to the
+ string, received from the server. This sequence corresponds to the
password, but password can not be easily restored from it. The sequence
is then sent to the server for validation. Trailing zero is not stored
in the buf as it is not needed.
@@ -443,31 +460,23 @@ void make_scrambled_password(char *to, const char *password)
void
scramble(char *to, const char *message, const char *password)
{
- SHA1_CONTEXT sha1_context;
uint8 hash_stage1[SHA1_HASH_SIZE];
uint8 hash_stage2[SHA1_HASH_SIZE];
- mysql_sha1_reset(&sha1_context);
- /* stage 1: hash password */
- mysql_sha1_input(&sha1_context, (uint8 *) password, (uint) strlen(password));
- mysql_sha1_result(&sha1_context, hash_stage1);
- /* stage 2: hash stage 1; note that hash_stage2 is stored in the database */
- mysql_sha1_reset(&sha1_context);
- mysql_sha1_input(&sha1_context, hash_stage1, SHA1_HASH_SIZE);
- mysql_sha1_result(&sha1_context, hash_stage2);
+ /* Two stage SHA1 hash of the password. */
+ compute_two_stage_sha1_hash(password, strlen(password), hash_stage1,
+ hash_stage2);
+
/* create crypt string as sha1(message, hash_stage2) */;
- mysql_sha1_reset(&sha1_context);
- mysql_sha1_input(&sha1_context, (const uint8 *) message, SCRAMBLE_LENGTH);
- mysql_sha1_input(&sha1_context, hash_stage2, SHA1_HASH_SIZE);
- /* xor allows 'from' and 'to' overlap: lets take advantage of it */
- mysql_sha1_result(&sha1_context, (uint8 *) to);
+ compute_sha1_hash_multi((uint8 *) to, message, SCRAMBLE_LENGTH,
+ (const char *) hash_stage2, SHA1_HASH_SIZE);
my_crypt(to, (const uchar *) to, hash_stage1, SCRAMBLE_LENGTH);
}
/*
Check that scrambled message corresponds to the password; the function
- is used by server to check that recieved reply is authentic.
+ is used by server to check that received reply is authentic.
This function does not check lengths of given strings: message must be
null-terminated, reply and hash_stage2 must be at least SHA1_HASH_SIZE
long (if not, something fishy is going on).
@@ -489,24 +498,20 @@ my_bool
check_scramble(const uchar *scramble_arg, const char *message,
const uint8 *hash_stage2)
{
- SHA1_CONTEXT sha1_context;
uint8 buf[SHA1_HASH_SIZE];
uint8 hash_stage2_reassured[SHA1_HASH_SIZE];
- mysql_sha1_reset(&sha1_context);
/* create key to encrypt scramble */
- mysql_sha1_input(&sha1_context, (const uint8 *) message, SCRAMBLE_LENGTH);
- mysql_sha1_input(&sha1_context, hash_stage2, SHA1_HASH_SIZE);
- mysql_sha1_result(&sha1_context, buf);
+ compute_sha1_hash_multi(buf, message, SCRAMBLE_LENGTH,
+ (const char *) hash_stage2, SHA1_HASH_SIZE);
/* encrypt scramble */
- my_crypt((char *) buf, buf, scramble_arg, SCRAMBLE_LENGTH);
+ my_crypt((char *) buf, buf, scramble_arg, SCRAMBLE_LENGTH);
+
/* now buf supposedly contains hash_stage1: so we can get hash_stage2 */
- mysql_sha1_reset(&sha1_context);
- mysql_sha1_input(&sha1_context, buf, SHA1_HASH_SIZE);
- mysql_sha1_result(&sha1_context, hash_stage2_reassured);
- return test(memcmp(hash_stage2, hash_stage2_reassured, SHA1_HASH_SIZE));
-}
+ compute_sha1_hash(hash_stage2_reassured, (const char *) buf, SHA1_HASH_SIZE);
+ return MY_TEST(memcmp(hash_stage2, hash_stage2_reassured, SHA1_HASH_SIZE));
+}
/*
Convert scrambled password from asciiz hex string to binary form.
@@ -536,3 +541,4 @@ void make_password_from_salt(char *to, const uint8 *hash_stage2)
*to++= PVERSION41_CHAR;
octet2hex(to, (const char*) hash_stage2, SHA1_HASH_SIZE);
}
+
diff --git a/sql/procedure.cc b/sql/procedure.cc
index bdaced20586..8f9d6c0a7f3 100644
--- a/sql/procedure.cc
+++ b/sql/procedure.cc
@@ -20,6 +20,7 @@
#pragma implementation // gcc: Class implementation
#endif
+#include <my_global.h>
#include "sql_priv.h"
#include "procedure.h"
#include "sql_analyse.h" // Includes procedure
diff --git a/sql/protocol.cc b/sql/protocol.cc
index 5afddb277e4..eeca35359f6 100644
--- a/sql/protocol.cc
+++ b/sql/protocol.cc
@@ -25,8 +25,8 @@
#pragma implementation // gcc: Class implementation
#endif
+#include <my_global.h>
#include "sql_priv.h"
-#include "unireg.h" // REQUIRED: for other includes
#include "protocol.h"
#include "sql_class.h" // THD
#include <stdarg.h>
@@ -164,14 +164,14 @@ bool net_send_error(THD *thd, uint sql_errno, const char *err,
It's one case when we can push an error even though there
is an OK or EOF already.
*/
- thd->stmt_da->can_overwrite_status= TRUE;
+ thd->get_stmt_da()->set_overwrite_status(true);
/* Abort multi-result sets */
thd->server_status&= ~SERVER_MORE_RESULTS_EXISTS;
error= net_send_error_packet(thd, sql_errno, err, sqlstate);
- thd->stmt_da->can_overwrite_status= FALSE;
+ thd->get_stmt_da()->set_overwrite_status(false);
DBUG_RETURN(error);
}
@@ -236,7 +236,7 @@ net_send_ok(THD *thd,
pos+=2;
/* We can only return up to 65535 warnings in two bytes */
- uint tmp= min(statement_warn_count, 65535);
+ uint tmp= MY_MIN(statement_warn_count, 65535);
int2store(pos, tmp);
pos+= 2;
}
@@ -245,7 +245,7 @@ net_send_ok(THD *thd,
int2store(pos, server_status);
pos+=2;
}
- thd->stmt_da->can_overwrite_status= TRUE;
+ thd->get_stmt_da()->set_overwrite_status(true);
if (message && message[0])
pos= net_store_data(pos, (uchar*) message, strlen(message));
@@ -254,7 +254,7 @@ net_send_ok(THD *thd,
error= net_flush(net);
- thd->stmt_da->can_overwrite_status= FALSE;
+ thd->get_stmt_da()->set_overwrite_status(false);
DBUG_PRINT("info", ("OK sent, so no more error sending allowed"));
DBUG_RETURN(error);
@@ -294,11 +294,11 @@ net_send_eof(THD *thd, uint server_status, uint statement_warn_count)
/* Set to TRUE if no active vio, to work well in case of --init-file */
if (net->vio != 0)
{
- thd->stmt_da->can_overwrite_status= TRUE;
+ thd->get_stmt_da()->set_overwrite_status(true);
error= write_eof_packet(thd, net, server_status, statement_warn_count);
if (!error)
error= net_flush(net);
- thd->stmt_da->can_overwrite_status= FALSE;
+ thd->get_stmt_da()->set_overwrite_status(false);
DBUG_PRINT("info", ("EOF sent, so no more error sending allowed"));
}
DBUG_RETURN(error);
@@ -332,7 +332,7 @@ static bool write_eof_packet(THD *thd, NET *net,
Don't send warn count during SP execution, as the warn_list
is cleared between substatements, and mysqltest gets confused
*/
- uint tmp= min(statement_warn_count, 65535);
+ uint tmp= MY_MIN(statement_warn_count, 65535);
buff[0]= 254;
int2store(buff+1, tmp);
/*
@@ -497,30 +497,30 @@ void Protocol::end_statement()
}
#endif
DBUG_ENTER("Protocol::end_statement");
- DBUG_ASSERT(! thd->stmt_da->is_sent);
+ DBUG_ASSERT(! thd->get_stmt_da()->is_sent());
bool error= FALSE;
/* Can not be true, but do not take chances in production. */
- if (thd->stmt_da->is_sent)
+ if (thd->get_stmt_da()->is_sent())
DBUG_VOID_RETURN;
- switch (thd->stmt_da->status()) {
+ switch (thd->get_stmt_da()->status()) {
case Diagnostics_area::DA_ERROR:
/* The query failed, send error to log and abort bootstrap. */
- error= send_error(thd->stmt_da->sql_errno(),
- thd->stmt_da->message(),
- thd->stmt_da->get_sqlstate());
+ error= send_error(thd->get_stmt_da()->sql_errno(),
+ thd->get_stmt_da()->message(),
+ thd->get_stmt_da()->get_sqlstate());
break;
case Diagnostics_area::DA_EOF:
error= send_eof(thd->server_status,
- thd->stmt_da->statement_warn_count());
+ thd->get_stmt_da()->statement_warn_count());
break;
case Diagnostics_area::DA_OK:
error= send_ok(thd->server_status,
- thd->stmt_da->statement_warn_count(),
- thd->stmt_da->affected_rows(),
- thd->stmt_da->last_insert_id(),
- thd->stmt_da->message());
+ thd->get_stmt_da()->statement_warn_count(),
+ thd->get_stmt_da()->affected_rows(),
+ thd->get_stmt_da()->last_insert_id(),
+ thd->get_stmt_da()->message());
break;
case Diagnostics_area::DA_DISABLED:
break;
@@ -531,7 +531,7 @@ void Protocol::end_statement()
break;
}
if (!error)
- thd->stmt_da->is_sent= TRUE;
+ thd->get_stmt_da()->set_is_sent(true);
DBUG_VOID_RETURN;
}
@@ -617,17 +617,17 @@ void net_send_progress_packet(THD *thd)
*pos++= (uchar) 1; // Number of strings
*pos++= (uchar) thd->progress.stage + 1;
/*
- We have the max() here to avoid problems if max_stage is not set,
+ We have the MY_MAX() here to avoid problems if max_stage is not set,
which may happen during automatic repair of table
*/
- *pos++= (uchar) max(thd->progress.max_stage, thd->progress.stage + 1);
+ *pos++= (uchar) MY_MAX(thd->progress.max_stage, thd->progress.stage + 1);
progress= 0;
if (thd->progress.max_counter)
progress= 100000ULL * thd->progress.counter / thd->progress.max_counter;
int3store(pos, progress); // Between 0 & 100000
pos+= 3;
pos= net_store_data(pos, (const uchar*) proc_info,
- min(length, sizeof(buff)-7));
+ MY_MIN(length, sizeof(buff)-7));
net_write_command(&thd->net, (uchar) 255, progress_header,
sizeof(progress_header), (uchar*) buff,
(uint) (pos - buff));
@@ -699,9 +699,9 @@ bool Protocol::flush()
{
#ifndef EMBEDDED_LIBRARY
bool error;
- thd->stmt_da->can_overwrite_status= TRUE;
+ thd->get_stmt_da()->set_overwrite_status(true);
error= net_flush(&thd->net);
- thd->stmt_da->can_overwrite_status= FALSE;
+ thd->get_stmt_da()->set_overwrite_status(false);
return error;
#else
return 0;
@@ -787,7 +787,7 @@ bool Protocol::send_result_set_metadata(List<Item> *list, uint flags)
pos= (char*) local_packet->ptr()+local_packet->length();
*pos++= 12; // Length of packed fields
/* inject a NULL to test the client */
- DBUG_EXECUTE_IF("poison_rs_fields", pos[-1]= 0xfb;);
+ DBUG_EXECUTE_IF("poison_rs_fields", pos[-1]= (char) 0xfb;);
if (item->charset_for_protocol() == &my_charset_bin || thd_charset == NULL)
{
/* No conversion */
@@ -867,7 +867,7 @@ bool Protocol::send_result_set_metadata(List<Item> *list, uint flags)
Send no warning information, as it will be sent at statement end.
*/
if (write_eof_packet(thd, &thd->net, thd->server_status,
- thd->warning_info->statement_warn_count()))
+ thd->get_stmt_da()->current_statement_warn_count()))
DBUG_RETURN(1);
}
DBUG_RETURN(prepare_for_send(list->elements));
@@ -1438,7 +1438,7 @@ bool Protocol_binary::store(MYSQL_TIME *tm, int decimals)
DBUG_ASSERT(decimals == AUTO_SEC_PART_DIGITS ||
(decimals >= 0 && decimals <= TIME_SECOND_PART_DIGITS));
if (decimals != AUTO_SEC_PART_DIGITS)
- tm->second_part= sec_part_truncate(tm->second_part, decimals);
+ my_time_trunc(tm, decimals);
int4store(pos+7, tm->second_part);
if (tm->second_part)
length=11;
@@ -1480,7 +1480,7 @@ bool Protocol_binary::store_time(MYSQL_TIME *tm, int decimals)
DBUG_ASSERT(decimals == AUTO_SEC_PART_DIGITS ||
(decimals >= 0 && decimals <= TIME_SECOND_PART_DIGITS));
if (decimals != AUTO_SEC_PART_DIGITS)
- tm->second_part= sec_part_truncate(tm->second_part, decimals);
+ my_time_trunc(tm, decimals);
int4store(pos+8, tm->second_part);
if (tm->second_part)
length=12;
diff --git a/sql/protocol.h b/sql/protocol.h
index 871d6018458..c58de68289f 100644
--- a/sql/protocol.h
+++ b/sql/protocol.h
@@ -35,6 +35,7 @@ class Protocol
protected:
THD *thd;
String *packet;
+ /* Used by net_store_data() for charset conversions */
String *convert;
uint field_pos;
#ifndef DBUG_OFF
@@ -53,6 +54,10 @@ protected:
MYSQL_FIELD *next_mysql_field;
MEM_ROOT *alloc;
#endif
+ /*
+ The following two are low-level functions that are invoked from
+ higher-level store_xxx() funcs. The data is stored into this->packet.
+ */
bool store_string_aux(const char *from, size_t length,
CHARSET_INFO *fromcs, CHARSET_INFO *tocs);
diff --git a/sql/records.cc b/sql/records.cc
index aca950d7435..a37f7a18c11 100644
--- a/sql/records.cc
+++ b/sql/records.cc
@@ -25,6 +25,7 @@
Functions for easy reading of records, possible through a cache
*/
+#include <my_global.h>
#include "records.h"
#include "sql_priv.h"
#include "records.h"
@@ -65,10 +66,12 @@ static int rr_index_desc(READ_RECORD *info);
@param reverse Scan in the reverse direction
*/
-void init_read_record_idx(READ_RECORD *info, THD *thd, TABLE *table,
+bool init_read_record_idx(READ_RECORD *info, THD *thd, TABLE *table,
bool print_error, uint idx, bool reverse)
{
int error;
+ DBUG_ENTER("init_read_record_idx");
+
empty_record(table);
bzero((char*) info,sizeof(*info));
info->thd= thd;
@@ -87,6 +90,7 @@ void init_read_record_idx(READ_RECORD *info, THD *thd, TABLE *table,
/* read_record will be changed to rr_index in rr_index_first */
info->read_record= reverse ? rr_index_last : rr_index_first;
+ DBUG_RETURN(error != 0);
}
@@ -287,9 +291,7 @@ bool init_read_record(READ_RECORD *info,THD *thd, TABLE *table,
thd->variables.read_buff_size);
}
/* Condition pushdown to storage engine */
- if ((thd->variables.optimizer_switch &
- OPTIMIZER_SWITCH_ENGINE_CONDITION_PUSHDOWN) &&
- select && select->cond &&
+ if (thd->use_cond_push(table->file) && select && select->cond &&
(select->cond->used_tables() & table->map) &&
!table->file->pushed_cond)
table->file->cond_push(select->cond);
@@ -603,7 +605,7 @@ static int init_rr_cache(THD *thd, READ_RECORD *info)
if (info->cache_records <= 2 ||
!(info->cache=(uchar*) my_malloc_lock(rec_cache_size+info->cache_records*
info->struct_length+1,
- MYF(0))))
+ MYF(MY_THREAD_SPECIFIC))))
DBUG_RETURN(1);
#ifdef HAVE_valgrind
// Avoid warnings in qsort
diff --git a/sql/records.h b/sql/records.h
index 57467d665d4..a3f0b5eb084 100644
--- a/sql/records.h
+++ b/sql/records.h
@@ -18,7 +18,6 @@
#ifdef USE_PRAGMA_INTERFACE
#pragma interface /* gcc class implementation */
#endif
-#include <my_global.h> /* for uint typedefs */
struct st_join_table;
class handler;
@@ -77,7 +76,7 @@ public:
bool init_read_record(READ_RECORD *info, THD *thd, TABLE *reg_form,
SQL_SELECT *select, int use_record_cache,
bool print_errors, bool disable_rr_cache);
-void init_read_record_idx(READ_RECORD *info, THD *thd, TABLE *table,
+bool init_read_record_idx(READ_RECORD *info, THD *thd, TABLE *table,
bool print_error, uint idx, bool reverse);
void end_read_record(READ_RECORD *info);
diff --git a/sql/repl_failsafe.cc b/sql/repl_failsafe.cc
index 89fb1bb27de..3c99becf304 100644
--- a/sql/repl_failsafe.cc
+++ b/sql/repl_failsafe.cc
@@ -24,6 +24,7 @@
functions like register_slave()) are working.
*/
+#include <my_global.h>
#include "sql_priv.h"
#include "sql_parse.h" // check_access
#ifdef HAVE_REPLICATION
@@ -43,7 +44,6 @@
ulong rpl_status=RPL_NULL;
mysql_mutex_t LOCK_rpl_status;
-mysql_cond_t COND_rpl_status;
HASH slave_list;
const char *rpl_role_type[] = {"MASTER","SLAVE",NullS};
@@ -69,7 +69,6 @@ void change_rpl_status(ulong from_status, ulong to_status)
mysql_mutex_lock(&LOCK_rpl_status);
if (rpl_status == from_status || rpl_status == RPL_ANY)
rpl_status = to_status;
- mysql_cond_signal(&COND_rpl_status);
mysql_mutex_unlock(&LOCK_rpl_status);
}
@@ -89,14 +88,15 @@ void change_rpl_status(ulong from_status, ulong to_status)
void unregister_slave(THD* thd, bool only_mine, bool need_mutex)
{
- if (thd->server_id)
+ uint32 thd_server_id= thd->variables.server_id;
+ if (thd_server_id)
{
if (need_mutex)
mysql_mutex_lock(&LOCK_slave_list);
SLAVE_INFO* old_si;
if ((old_si = (SLAVE_INFO*)my_hash_search(&slave_list,
- (uchar*)&thd->server_id, 4)) &&
+ (uchar*)&thd_server_id, 4)) &&
(!only_mine || old_si->thd == thd))
my_hash_delete(&slave_list, (uchar*)old_si);
@@ -127,7 +127,7 @@ int register_slave(THD* thd, uchar* packet, uint packet_length)
if (!(si = (SLAVE_INFO*)my_malloc(sizeof(SLAVE_INFO), MYF(MY_WME))))
goto err2;
- thd->server_id= si->server_id= uint4korr(p);
+ thd->variables.server_id= si->server_id= uint4korr(p);
p+= 4;
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'");
@@ -145,7 +145,7 @@ int register_slave(THD* thd, uchar* packet, uint packet_length)
// si->rpl_recovery_rank= uint4korr(p);
p += 4;
if (!(si->master_id= uint4korr(p)))
- si->master_id= server_id;
+ si->master_id= global_system_variables.server_id;
si->thd= thd;
mysql_mutex_lock(&LOCK_slave_list);
diff --git a/sql/replication.h b/sql/replication.h
index 9492c54fabd..fc48ecd9ffc 100644
--- a/sql/replication.h
+++ b/sql/replication.h
@@ -10,8 +10,8 @@
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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+ along with this program; if not, write to the Free Software Foundation,
+ 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */
#ifndef REPLICATION_H
#define REPLICATION_H
@@ -474,34 +474,6 @@ int unregister_binlog_relay_io_observer(Binlog_relay_IO_observer *observer, void
MYSQL *rpl_connect_master(MYSQL *mysql);
/**
- Set thread entering a condition
-
- This function should be called before putting a thread to wait for
- a condition. @a mutex should be held before calling this
- function. After being waken up, @f thd_exit_cond should be called.
-
- @param thd The thread entering the condition, NULL means current thread
- @param cond The condition the thread is going to wait for
- @param mutex The mutex associated with the condition, this must be
- held before call this function
- @param msg The new process message for the thread
-*/
-const char* thd_enter_cond(MYSQL_THD thd, mysql_cond_t *cond,
- mysql_mutex_t *mutex, const char *msg);
-
-/**
- Set thread leaving a condition
-
- This function should be called after a thread being waken up for a
- condition.
-
- @param thd The thread entering the condition, NULL means current thread
- @param old_msg The process message, ususally this should be the old process
- message before calling @f thd_enter_cond
-*/
-void thd_exit_cond(MYSQL_THD thd, const char *old_msg);
-
-/**
Get the value of user variable as an integer.
This function will return the value of variable @a name as an
diff --git a/sql/rpl_constants.h b/sql/rpl_constants.h
index 3c605d24563..f83588ce321 100644
--- a/sql/rpl_constants.h
+++ b/sql/rpl_constants.h
@@ -31,4 +31,44 @@ enum Incident {
INCIDENT_COUNT
};
+
+/**
+ Enumeration of the reserved formats of Binlog extra row information
+*/
+enum ExtraRowInfoFormat {
+ /** Ndb format */
+ ERIF_NDB = 0,
+
+ /** Reserved formats 0 -> 63 inclusive */
+ ERIF_LASTRESERVED = 63,
+
+ /**
+ Available / uncontrolled formats
+ 64 -> 254 inclusive
+ */
+ ERIF_OPEN1 = 64,
+ ERIF_OPEN2 = 65,
+
+ ERIF_LASTOPEN = 254,
+
+ /**
+ Multi-payload format 255
+
+ Length is total length, payload is sequence of
+ sub-payloads with their own headers containing
+ length + format.
+ */
+ ERIF_MULTI = 255
+};
+
+/*
+ 1 byte length, 1 byte format
+ Length is total length in bytes, including 2 byte header
+ Length values 0 and 1 are currently invalid and reserved.
+*/
+#define EXTRA_ROW_INFO_LEN_OFFSET 0
+#define EXTRA_ROW_INFO_FORMAT_OFFSET 1
+#define EXTRA_ROW_INFO_HDR_BYTES 2
+#define EXTRA_ROW_INFO_MAX_PAYLOAD (255 - EXTRA_ROW_INFO_HDR_BYTES)
+
#endif /* RPL_CONSTANTS_H */
diff --git a/sql/rpl_filter.cc b/sql/rpl_filter.cc
index 9103cad337d..28859c2eb85 100644
--- a/sql/rpl_filter.cc
+++ b/sql/rpl_filter.cc
@@ -13,6 +13,7 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+#include <my_global.h>
#include "sql_priv.h"
#include "mysqld.h" // system_charset_info
#include "rpl_filter.h"
@@ -574,7 +575,7 @@ void
Rpl_filter::init_table_rule_array(DYNAMIC_ARRAY* a, bool* a_inited)
{
my_init_dynamic_array(a, sizeof(TABLE_RULE_ENT*), TABLE_RULE_ARR_SIZE,
- TABLE_RULE_ARR_SIZE);
+ TABLE_RULE_ARR_SIZE, MYF(0));
*a_inited = 1;
}
@@ -735,6 +736,18 @@ Rpl_filter::get_rewrite_db(const char* db, size_t *new_len)
}
+void
+Rpl_filter::copy_rewrite_db(Rpl_filter *from)
+{
+ I_List_iterator<i_string_pair> it(from->rewrite_db);
+ i_string_pair* tmp;
+ DBUG_ASSERT(rewrite_db.is_empty());
+
+ /* TODO: Add memory checking here and in all add_xxxx functions ! */
+ while ((tmp=it++))
+ add_db_rewrite(tmp->key, tmp->val);
+}
+
I_List<i_string>*
Rpl_filter::get_do_db()
{
diff --git a/sql/rpl_filter.h b/sql/rpl_filter.h
index 2eb0340b714..65d11cfb6e6 100644
--- a/sql/rpl_filter.h
+++ b/sql/rpl_filter.h
@@ -88,6 +88,7 @@ public:
bool rewrite_db_is_empty();
const char* get_rewrite_db(const char* db, size_t *new_len);
+ void copy_rewrite_db(Rpl_filter *from);
I_List<i_string>* get_do_db();
I_List<i_string>* get_ignore_db();
@@ -139,7 +140,7 @@ private:
I_List<i_string_pair> rewrite_db;
};
-extern Rpl_filter *rpl_filter;
+extern Rpl_filter *global_rpl_filter;
extern Rpl_filter *binlog_filter;
#endif // RPL_FILTER_H
diff --git a/sql/rpl_gtid.cc b/sql/rpl_gtid.cc
new file mode 100644
index 00000000000..52cec9f0a85
--- /dev/null
+++ b/sql/rpl_gtid.cc
@@ -0,0 +1,2362 @@
+/* Copyright (c) 2013, Kristian Nielsen and MariaDB Services 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+
+/* Definitions for MariaDB global transaction ID (GTID). */
+
+#include <my_global.h>
+#include "sql_priv.h"
+#include "my_sys.h"
+#include "unireg.h"
+#include "my_global.h"
+#include "sql_base.h"
+#include "sql_parse.h"
+#include "key.h"
+#include "rpl_gtid.h"
+#include "rpl_rli.h"
+
+
+const LEX_STRING rpl_gtid_slave_state_table_name=
+ { C_STRING_WITH_LEN("gtid_slave_pos") };
+
+
+void
+rpl_slave_state::update_state_hash(uint64 sub_id, rpl_gtid *gtid,
+ rpl_group_info *rgi)
+{
+ int err;
+ /*
+ Add the gtid to the HASH in the replication slave state.
+
+ We must do this only _after_ commit, so that for parallel replication,
+ there will not be an attempt to delete the corresponding table row before
+ it is even committed.
+ */
+ mysql_mutex_lock(&LOCK_slave_state);
+ err= update(gtid->domain_id, gtid->server_id, sub_id, gtid->seq_no, rgi);
+ mysql_mutex_unlock(&LOCK_slave_state);
+ if (err)
+ {
+ sql_print_warning("Slave: Out of memory during slave state maintenance. "
+ "Some no longer necessary rows in table "
+ "mysql.%s may be left undeleted.",
+ rpl_gtid_slave_state_table_name.str);
+ /*
+ Such failure is not fatal. We will fail to delete the row for this
+ GTID, but it will do no harm and will be removed automatically on next
+ server restart.
+ */
+ }
+}
+
+
+int
+rpl_slave_state::record_and_update_gtid(THD *thd, rpl_group_info *rgi)
+{
+ DBUG_ENTER("rpl_slave_state::record_and_update_gtid");
+
+ /*
+ Update the GTID position, if we have it and did not already update
+ it in a GTID transaction.
+ */
+ if (rgi->gtid_pending)
+ {
+ uint64 sub_id= rgi->gtid_sub_id;
+ rgi->gtid_pending= false;
+ if (rgi->gtid_ignore_duplicate_state!=rpl_group_info::GTID_DUPLICATE_IGNORE)
+ {
+ if (record_gtid(thd, &rgi->current_gtid, sub_id, false, false))
+ DBUG_RETURN(1);
+ update_state_hash(sub_id, &rgi->current_gtid, rgi);
+ }
+ rgi->gtid_ignore_duplicate_state= rpl_group_info::GTID_DUPLICATE_NULL;
+ }
+ DBUG_RETURN(0);
+}
+
+
+/*
+ Check GTID event execution when --gtid-ignore-duplicates.
+
+ The idea with --gtid-ignore-duplicates is that we allow multiple master
+ connections (in multi-source replication) to all receive the same GTIDs and
+ event groups. Only one instance of each is applied; we use the sequence
+ number in the GTID to decide whether a GTID has already been applied.
+
+ So if the seq_no of a GTID (or a higher sequence number) has already been
+ applied, then the event should be skipped. If not then the event should be
+ applied.
+
+ To avoid two master connections tring to apply the same event
+ simultaneously, only one is allowed to work in any given domain at any point
+ in time. The associated Relay_log_info object is called the owner of the
+ domain (and there can be multiple parallel worker threads working in that
+ domain for that Relay_log_info). Any other Relay_log_info/master connection
+ must wait for the domain to become free, or for their GTID to have been
+ applied, before being allowed to proceed.
+
+ Returns:
+ 0 This GTID is already applied, it should be skipped.
+ 1 The GTID is not yet applied; this rli is now the owner, and must apply
+ the event and release the domain afterwards.
+ -1 Error (out of memory to allocate a new element for the domain).
+*/
+int
+rpl_slave_state::check_duplicate_gtid(rpl_gtid *gtid, rpl_group_info *rgi)
+{
+ uint32 domain_id= gtid->domain_id;
+ uint64 seq_no= gtid->seq_no;
+ rpl_slave_state::element *elem;
+ int res;
+ bool did_enter_cond= false;
+ PSI_stage_info old_stage;
+ THD *UNINIT_VAR(thd);
+ Relay_log_info *rli= rgi->rli;
+
+ mysql_mutex_lock(&LOCK_slave_state);
+ if (!(elem= get_element(domain_id)))
+ {
+ my_error(ER_OUT_OF_RESOURCES, MYF(0));
+ res= -1;
+ goto err;
+ }
+ /*
+ Note that the elem pointer does not change once inserted in the hash. So
+ we can re-use the pointer without looking it up again in the hash after
+ each lock release and re-take.
+ */
+
+ for (;;)
+ {
+ if (elem->highest_seq_no >= seq_no)
+ {
+ /* This sequence number is already applied, ignore it. */
+ res= 0;
+ rgi->gtid_ignore_duplicate_state= rpl_group_info::GTID_DUPLICATE_IGNORE;
+ break;
+ }
+ if (!elem->owner_rli)
+ {
+ /* The domain became free, grab it and apply the event. */
+ elem->owner_rli= rli;
+ elem->owner_count= 1;
+ rgi->gtid_ignore_duplicate_state= rpl_group_info::GTID_DUPLICATE_OWNER;
+ res= 1;
+ break;
+ }
+ if (elem->owner_rli == rli)
+ {
+ /* Already own this domain, increment reference count and apply event. */
+ ++elem->owner_count;
+ rgi->gtid_ignore_duplicate_state= rpl_group_info::GTID_DUPLICATE_OWNER;
+ res= 1;
+ break;
+ }
+ thd= rgi->thd;
+ if (thd->check_killed())
+ {
+ thd->send_kill_message();
+ res= -1;
+ break;
+ }
+ /*
+ Someone else is currently processing this GTID (or an earlier one).
+ Wait for them to complete (or fail), and then check again.
+ */
+ if (!did_enter_cond)
+ {
+ thd->ENTER_COND(&elem->COND_gtid_ignore_duplicates, &LOCK_slave_state,
+ &stage_gtid_wait_other_connection, &old_stage);
+ did_enter_cond= true;
+ }
+ mysql_cond_wait(&elem->COND_gtid_ignore_duplicates,
+ &LOCK_slave_state);
+ }
+
+err:
+ if (did_enter_cond)
+ thd->EXIT_COND(&old_stage);
+ else
+ mysql_mutex_unlock(&LOCK_slave_state);
+ return res;
+}
+
+
+void
+rpl_slave_state::release_domain_owner(rpl_group_info *rgi)
+{
+ element *elem= NULL;
+
+ mysql_mutex_lock(&LOCK_slave_state);
+ if (!(elem= get_element(rgi->current_gtid.domain_id)))
+ {
+ /*
+ We cannot really deal with error here, as we are already called in an
+ error handling case (transaction failure and rollback).
+
+ However, get_element() only fails if the element did not exist already
+ and could not be allocated due to out-of-memory - and if it did not
+ exist, then we would not get here in the first place.
+ */
+ mysql_mutex_unlock(&LOCK_slave_state);
+ return;
+ }
+
+ if (rgi->gtid_ignore_duplicate_state == rpl_group_info::GTID_DUPLICATE_OWNER)
+ {
+ uint32 count= elem->owner_count;
+ DBUG_ASSERT(count > 0);
+ DBUG_ASSERT(elem->owner_rli == rgi->rli);
+ --count;
+ elem->owner_count= count;
+ if (count == 0)
+ {
+ elem->owner_rli= NULL;
+ mysql_cond_broadcast(&elem->COND_gtid_ignore_duplicates);
+ }
+ }
+ rgi->gtid_ignore_duplicate_state= rpl_group_info::GTID_DUPLICATE_NULL;
+ mysql_mutex_unlock(&LOCK_slave_state);
+}
+
+
+static void
+rpl_slave_state_free_element(void *arg)
+{
+ struct rpl_slave_state::element *elem= (struct rpl_slave_state::element *)arg;
+ mysql_cond_destroy(&elem->COND_wait_gtid);
+ mysql_cond_destroy(&elem->COND_gtid_ignore_duplicates);
+ my_free(elem);
+}
+
+
+rpl_slave_state::rpl_slave_state()
+ : last_sub_id(0), loaded(false)
+{
+ mysql_mutex_init(key_LOCK_slave_state, &LOCK_slave_state,
+ MY_MUTEX_INIT_SLOW);
+ my_hash_init(&hash, &my_charset_bin, 32, offsetof(element, domain_id),
+ sizeof(uint32), NULL, rpl_slave_state_free_element, HASH_UNIQUE);
+}
+
+
+rpl_slave_state::~rpl_slave_state()
+{
+ truncate_hash();
+ my_hash_free(&hash);
+ mysql_mutex_destroy(&LOCK_slave_state);
+}
+
+
+void
+rpl_slave_state::truncate_hash()
+{
+ uint32 i;
+
+ for (i= 0; i < hash.records; ++i)
+ {
+ element *e= (element *)my_hash_element(&hash, i);
+ list_element *l= e->list;
+ list_element *next;
+ while (l)
+ {
+ next= l->next;
+ my_free(l);
+ l= next;
+ }
+ /* The element itself is freed by the hash element free function. */
+ }
+ my_hash_reset(&hash);
+}
+
+
+int
+rpl_slave_state::update(uint32 domain_id, uint32 server_id, uint64 sub_id,
+ uint64 seq_no, rpl_group_info *rgi)
+{
+ element *elem= NULL;
+ list_element *list_elem= NULL;
+
+ if (!(elem= get_element(domain_id)))
+ return 1;
+
+ if (seq_no > elem->highest_seq_no)
+ elem->highest_seq_no= seq_no;
+ if (elem->gtid_waiter && elem->min_wait_seq_no <= seq_no)
+ {
+ /*
+ Someone was waiting in MASTER_GTID_WAIT() for this GTID to appear.
+ Signal (and remove) them. The waiter will handle all the processing
+ of all pending MASTER_GTID_WAIT(), so we do not slow down the
+ replication SQL thread.
+ */
+ mysql_mutex_assert_owner(&LOCK_slave_state);
+ elem->gtid_waiter= NULL;
+ mysql_cond_broadcast(&elem->COND_wait_gtid);
+ }
+
+ if (rgi)
+ {
+ if (rgi->gtid_ignore_duplicate_state==rpl_group_info::GTID_DUPLICATE_OWNER)
+ {
+#ifndef DBUG_OFF
+ Relay_log_info *rli= rgi->rli;
+#endif
+ uint32 count= elem->owner_count;
+ DBUG_ASSERT(count > 0);
+ DBUG_ASSERT(elem->owner_rli == rli);
+ --count;
+ elem->owner_count= count;
+ if (count == 0)
+ {
+ elem->owner_rli= NULL;
+ mysql_cond_broadcast(&elem->COND_gtid_ignore_duplicates);
+ }
+ }
+ rgi->gtid_ignore_duplicate_state= rpl_group_info::GTID_DUPLICATE_NULL;
+ }
+
+ if (!(list_elem= (list_element *)my_malloc(sizeof(*list_elem), MYF(MY_WME))))
+ return 1;
+ list_elem->server_id= server_id;
+ list_elem->sub_id= sub_id;
+ list_elem->seq_no= seq_no;
+
+ elem->add(list_elem);
+ if (last_sub_id < sub_id)
+ last_sub_id= sub_id;
+
+ return 0;
+}
+
+
+struct rpl_slave_state::element *
+rpl_slave_state::get_element(uint32 domain_id)
+{
+ struct element *elem;
+
+ elem= (element *)my_hash_search(&hash, (const uchar *)&domain_id, 0);
+ if (elem)
+ return elem;
+
+ if (!(elem= (element *)my_malloc(sizeof(*elem), MYF(MY_WME))))
+ return NULL;
+ elem->list= NULL;
+ elem->domain_id= domain_id;
+ elem->highest_seq_no= 0;
+ elem->gtid_waiter= NULL;
+ elem->owner_rli= NULL;
+ elem->owner_count= 0;
+ mysql_cond_init(key_COND_wait_gtid, &elem->COND_wait_gtid, 0);
+ mysql_cond_init(key_COND_gtid_ignore_duplicates,
+ &elem->COND_gtid_ignore_duplicates, 0);
+ if (my_hash_insert(&hash, (uchar *)elem))
+ {
+ my_free(elem);
+ return NULL;
+ }
+ return elem;
+}
+
+
+int
+rpl_slave_state::put_back_list(uint32 domain_id, list_element *list)
+{
+ element *e;
+ if (!(e= (element *)my_hash_search(&hash, (const uchar *)&domain_id, 0)))
+ return 1;
+ while (list)
+ {
+ list_element *next= list->next;
+ e->add(list);
+ list= next;
+ }
+ return 0;
+}
+
+
+int
+rpl_slave_state::truncate_state_table(THD *thd)
+{
+ TABLE_LIST tlist;
+ int err= 0;
+
+ tmp_disable_binlog(thd);
+ tlist.init_one_table(STRING_WITH_LEN("mysql"),
+ rpl_gtid_slave_state_table_name.str,
+ rpl_gtid_slave_state_table_name.length,
+ NULL, TL_WRITE);
+ if (!(err= open_and_lock_tables(thd, &tlist, FALSE, 0)))
+ {
+ err= tlist.table->file->ha_truncate();
+
+ if (err)
+ {
+ ha_rollback_trans(thd, FALSE);
+ close_thread_tables(thd);
+ ha_rollback_trans(thd, TRUE);
+ }
+ else
+ {
+ ha_commit_trans(thd, FALSE);
+ close_thread_tables(thd);
+ ha_commit_trans(thd, TRUE);
+ }
+ thd->mdl_context.release_transactional_locks();
+ }
+
+ reenable_binlog(thd);
+ return err;
+}
+
+
+static const TABLE_FIELD_TYPE mysql_rpl_slave_state_coltypes[4]= {
+ { { C_STRING_WITH_LEN("domain_id") },
+ { C_STRING_WITH_LEN("int(10) unsigned") },
+ {NULL, 0} },
+ { { C_STRING_WITH_LEN("sub_id") },
+ { C_STRING_WITH_LEN("bigint(20) unsigned") },
+ {NULL, 0} },
+ { { C_STRING_WITH_LEN("server_id") },
+ { C_STRING_WITH_LEN("int(10) unsigned") },
+ {NULL, 0} },
+ { { C_STRING_WITH_LEN("seq_no") },
+ { C_STRING_WITH_LEN("bigint(20) unsigned") },
+ {NULL, 0} },
+};
+
+static const uint mysql_rpl_slave_state_pk_parts[]= {0, 1};
+
+static const TABLE_FIELD_DEF mysql_gtid_slave_pos_tabledef= {
+ array_elements(mysql_rpl_slave_state_coltypes),
+ mysql_rpl_slave_state_coltypes,
+ array_elements(mysql_rpl_slave_state_pk_parts),
+ mysql_rpl_slave_state_pk_parts
+};
+
+class Gtid_db_intact : public Table_check_intact
+{
+protected:
+ void report_error(uint, const char *fmt, ...)
+ {
+ va_list args;
+ va_start(args, fmt);
+ error_log_print(ERROR_LEVEL, fmt, args);
+ va_end(args);
+ }
+};
+
+static Gtid_db_intact gtid_table_intact;
+
+/*
+ Check that the mysql.gtid_slave_pos table has the correct definition.
+*/
+int
+gtid_check_rpl_slave_state_table(TABLE *table)
+{
+ int err;
+
+ if ((err= gtid_table_intact.check(table, &mysql_gtid_slave_pos_tabledef)))
+ my_error(ER_GTID_OPEN_TABLE_FAILED, MYF(0), "mysql",
+ rpl_gtid_slave_state_table_name.str);
+ return err;
+}
+
+
+/*
+ Write a gtid to the replication slave state table.
+
+ Do it as part of the transaction, to get slave crash safety, or as a separate
+ transaction if !in_transaction (eg. MyISAM or DDL).
+
+ gtid The global transaction id for this event group.
+ sub_id Value allocated within the sub_id when the event group was
+ read (sub_id must be consistent with commit order in master binlog).
+
+ Note that caller must later ensure that the new gtid and sub_id is inserted
+ into the appropriate HASH element with rpl_slave_state.add(), so that it can
+ be deleted later. But this must only be done after COMMIT if in transaction.
+*/
+int
+rpl_slave_state::record_gtid(THD *thd, const rpl_gtid *gtid, uint64 sub_id,
+ bool in_transaction, bool in_statement)
+{
+ TABLE_LIST tlist;
+ int err= 0;
+ bool table_opened= false;
+ TABLE *table;
+ list_element *elist= 0, *next;
+ element *elem;
+ ulonglong thd_saved_option= thd->variables.option_bits;
+ Query_tables_list lex_backup;
+ wait_for_commit* suspended_wfc;
+ DBUG_ENTER("record_gtid");
+
+ if (unlikely(!loaded))
+ {
+ /*
+ Probably the mysql.gtid_slave_pos table is missing (eg. upgrade) or
+ corrupt.
+
+ We already complained loudly about this, but we can try to continue
+ until the DBA fixes it.
+ */
+ DBUG_RETURN(0);
+ }
+
+ if (!in_statement)
+ mysql_reset_thd_for_next_command(thd);
+
+ DBUG_EXECUTE_IF("gtid_inject_record_gtid",
+ {
+ my_error(ER_CANNOT_UPDATE_GTID_STATE, MYF(0));
+ DBUG_RETURN(1);
+ } );
+
+ /*
+ If we are applying a non-transactional event group, we will be committing
+ here a transaction, but that does not imply that the event group has
+ completed or has been binlogged. So we should not trigger
+ wakeup_subsequent_commits() here.
+
+ Note: An alternative here could be to put a call to mark_start_commit() in
+ stmt_done() before the call to record_and_update_gtid(). This would
+ prevent later calling mark_start_commit() after we have run
+ wakeup_subsequent_commits() from committing the GTID update transaction
+ (which must be avoided to avoid accessing freed group_commit_orderer
+ object). It would also allow following event groups to start slightly
+ earlier. And in the cases where record_gtid() is called without an active
+ transaction, the current statement should have been binlogged already, so
+ binlog order is preserved.
+
+ But this is rather subtle, and potentially fragile. And it does not really
+ seem worth it; non-transactional loads are unlikely to benefit much from
+ parallel replication in any case. So for now, we go with the simple
+ suspend/resume of wakeup_subsequent_commits() here in record_gtid().
+ */
+ suspended_wfc= thd->suspend_subsequent_commits();
+ thd->lex->reset_n_backup_query_tables_list(&lex_backup);
+ tlist.init_one_table(STRING_WITH_LEN("mysql"),
+ rpl_gtid_slave_state_table_name.str,
+ rpl_gtid_slave_state_table_name.length,
+ NULL, TL_WRITE);
+ if ((err= open_and_lock_tables(thd, &tlist, FALSE, 0)))
+ goto end;
+ table_opened= true;
+ table= tlist.table;
+
+ if ((err= gtid_check_rpl_slave_state_table(table)))
+ goto end;
+
+#ifdef WITH_WSREP
+ /*
+ Updates in slave state table should not be appended to galera transaction
+ writeset.
+ */
+ thd->wsrep_skip_append_keys= true;
+#endif
+
+ if (!in_transaction)
+ {
+ DBUG_PRINT("info", ("resetting OPTION_BEGIN"));
+ thd->variables.option_bits&=
+ ~(ulonglong)(OPTION_NOT_AUTOCOMMIT |OPTION_BEGIN |OPTION_BIN_LOG |
+ OPTION_GTID_BEGIN);
+ }
+ else
+ thd->variables.option_bits&= ~(ulonglong)OPTION_BIN_LOG;
+
+ bitmap_set_all(table->write_set);
+
+ table->field[0]->store((ulonglong)gtid->domain_id, true);
+ table->field[1]->store(sub_id, true);
+ table->field[2]->store((ulonglong)gtid->server_id, true);
+ table->field[3]->store(gtid->seq_no, true);
+ DBUG_EXECUTE_IF("inject_crash_before_write_rpl_slave_state", DBUG_SUICIDE(););
+ if ((err= table->file->ha_write_row(table->record[0])))
+ {
+ table->file->print_error(err, MYF(0));
+ goto end;
+ }
+
+ if(opt_bin_log &&
+ (err= mysql_bin_log.bump_seq_no_counter_if_needed(gtid->domain_id,
+ gtid->seq_no)))
+ {
+ my_error(ER_OUT_OF_RESOURCES, MYF(0));
+ goto end;
+ }
+
+ mysql_mutex_lock(&LOCK_slave_state);
+ if ((elem= get_element(gtid->domain_id)) == NULL)
+ {
+ mysql_mutex_unlock(&LOCK_slave_state);
+ my_error(ER_OUT_OF_RESOURCES, MYF(0));
+ err= 1;
+ goto end;
+ }
+ if ((elist= elem->grab_list()) != NULL)
+ {
+ /* Delete any old stuff, but keep around the most recent one. */
+ list_element *cur= elist;
+ uint64 best_sub_id= cur->sub_id;
+ list_element **best_ptr_ptr= &elist;
+ while ((next= cur->next))
+ {
+ if (next->sub_id > best_sub_id)
+ {
+ best_sub_id= next->sub_id;
+ best_ptr_ptr= &cur->next;
+ }
+ cur= next;
+ }
+ /*
+ Delete the highest sub_id element from the old list, and put it back as
+ the single-element new list.
+ */
+ cur= *best_ptr_ptr;
+ *best_ptr_ptr= cur->next;
+ cur->next= NULL;
+ elem->list= cur;
+ }
+ mysql_mutex_unlock(&LOCK_slave_state);
+
+ if (!elist)
+ goto end;
+
+ /* Now delete any already committed rows. */
+ bitmap_set_bit(table->read_set, table->field[0]->field_index);
+ bitmap_set_bit(table->read_set, table->field[1]->field_index);
+
+ if ((err= table->file->ha_index_init(0, 0)))
+ {
+ table->file->print_error(err, MYF(0));
+ goto end;
+ }
+ while (elist)
+ {
+ uchar key_buffer[4+8];
+
+ DBUG_EXECUTE_IF("gtid_slave_pos_simulate_failed_delete",
+ { err= ENOENT;
+ table->file->print_error(err, MYF(0));
+ /* `break' does not work inside DBUG_EXECUTE_IF */
+ goto dbug_break; });
+
+ next= elist->next;
+
+ table->field[1]->store(elist->sub_id, true);
+ /* domain_id is already set in table->record[0] from write_row() above. */
+ key_copy(key_buffer, table->record[0], &table->key_info[0], 0, false);
+ if (table->file->ha_index_read_map(table->record[1], key_buffer,
+ HA_WHOLE_KEY, HA_READ_KEY_EXACT))
+ /* We cannot find the row, assume it is already deleted. */
+ ;
+ else if ((err= table->file->ha_delete_row(table->record[1])))
+ table->file->print_error(err, MYF(0));
+ /*
+ In case of error, we still discard the element from the list. We do
+ not want to endlessly error on the same element in case of table
+ corruption or such.
+ */
+ my_free(elist);
+ elist= next;
+ if (err)
+ break;
+ }
+IF_DBUG(dbug_break:, )
+ table->file->ha_index_end();
+
+end:
+
+#ifdef WITH_WSREP
+ thd->wsrep_skip_append_keys= false;
+#endif
+
+ if (table_opened)
+ {
+ if (err || (err= ha_commit_trans(thd, FALSE)))
+ {
+ /*
+ If error, we need to put any remaining elist back into the HASH so we
+ can do another delete attempt later.
+ */
+ if (elist)
+ {
+ mysql_mutex_lock(&LOCK_slave_state);
+ put_back_list(gtid->domain_id, elist);
+ mysql_mutex_unlock(&LOCK_slave_state);
+ }
+
+ ha_rollback_trans(thd, FALSE);
+ }
+ close_thread_tables(thd);
+ if (in_transaction)
+ thd->mdl_context.release_statement_locks();
+ else
+ thd->mdl_context.release_transactional_locks();
+ }
+ thd->lex->restore_backup_query_tables_list(&lex_backup);
+ thd->variables.option_bits= thd_saved_option;
+ thd->resume_subsequent_commits(suspended_wfc);
+ DBUG_EXECUTE_IF("inject_record_gtid_serverid_100_sleep",
+ {
+ if (gtid->server_id == 100)
+ my_sleep(500000);
+ });
+ DBUG_RETURN(err);
+}
+
+
+uint64
+rpl_slave_state::next_sub_id(uint32 domain_id)
+{
+ uint64 sub_id= 0;
+
+ mysql_mutex_lock(&LOCK_slave_state);
+ sub_id= ++last_sub_id;
+ mysql_mutex_unlock(&LOCK_slave_state);
+
+ return sub_id;
+}
+
+
+bool
+rpl_slave_state_tostring_helper(String *dest, const rpl_gtid *gtid, bool *first)
+{
+ if (*first)
+ *first= false;
+ else
+ if (dest->append(",",1))
+ return true;
+ return
+ dest->append_ulonglong(gtid->domain_id) ||
+ dest->append("-",1) ||
+ dest->append_ulonglong(gtid->server_id) ||
+ dest->append("-",1) ||
+ dest->append_ulonglong(gtid->seq_no);
+}
+
+
+int
+rpl_slave_state::iterate(int (*cb)(rpl_gtid *, void *), void *data,
+ rpl_gtid *extra_gtids, uint32 num_extra)
+{
+ uint32 i;
+ HASH gtid_hash;
+ uchar *rec;
+ rpl_gtid *gtid;
+ int res= 1;
+
+ my_hash_init(&gtid_hash, &my_charset_bin, 32, offsetof(rpl_gtid, domain_id),
+ sizeof(uint32), NULL, NULL, HASH_UNIQUE);
+ for (i= 0; i < num_extra; ++i)
+ if (extra_gtids[i].server_id == global_system_variables.server_id &&
+ my_hash_insert(&gtid_hash, (uchar *)(&extra_gtids[i])))
+ goto err;
+
+ mysql_mutex_lock(&LOCK_slave_state);
+
+ for (i= 0; i < hash.records; ++i)
+ {
+ uint64 best_sub_id;
+ rpl_gtid best_gtid;
+ element *e= (element *)my_hash_element(&hash, i);
+ list_element *l= e->list;
+
+ if (!l)
+ continue; /* Nothing here */
+
+ best_gtid.domain_id= e->domain_id;
+ best_gtid.server_id= l->server_id;
+ best_gtid.seq_no= l->seq_no;
+ best_sub_id= l->sub_id;
+ while ((l= l->next))
+ {
+ if (l->sub_id > best_sub_id)
+ {
+ best_sub_id= l->sub_id;
+ best_gtid.server_id= l->server_id;
+ best_gtid.seq_no= l->seq_no;
+ }
+ }
+
+ /* Check if we have something newer in the extra list. */
+ rec= my_hash_search(&gtid_hash, (const uchar *)&best_gtid.domain_id, 0);
+ if (rec)
+ {
+ gtid= (rpl_gtid *)rec;
+ if (gtid->seq_no > best_gtid.seq_no)
+ memcpy(&best_gtid, gtid, sizeof(best_gtid));
+ if (my_hash_delete(&gtid_hash, rec))
+ {
+ mysql_mutex_unlock(&LOCK_slave_state);
+ goto err;
+ }
+ }
+
+ if ((res= (*cb)(&best_gtid, data)))
+ {
+ mysql_mutex_unlock(&LOCK_slave_state);
+ goto err;
+ }
+ }
+
+ mysql_mutex_unlock(&LOCK_slave_state);
+
+ /* Also add any remaining extra domain_ids. */
+ for (i= 0; i < gtid_hash.records; ++i)
+ {
+ gtid= (rpl_gtid *)my_hash_element(&gtid_hash, i);
+ if ((res= (*cb)(gtid, data)))
+ goto err;
+ }
+
+ res= 0;
+
+err:
+ my_hash_free(&gtid_hash);
+
+ return res;
+}
+
+
+struct rpl_slave_state_tostring_data {
+ String *dest;
+ bool first;
+};
+static int
+rpl_slave_state_tostring_cb(rpl_gtid *gtid, void *data)
+{
+ rpl_slave_state_tostring_data *p= (rpl_slave_state_tostring_data *)data;
+ return rpl_slave_state_tostring_helper(p->dest, gtid, &p->first);
+}
+
+
+/*
+ Prepare the current slave state as a string, suitable for sending to the
+ master to request to receive binlog events starting from that GTID state.
+
+ The state consists of the most recently applied GTID for each domain_id,
+ ie. the one with the highest sub_id within each domain_id.
+
+ Optinally, extra_gtids is a list of GTIDs from the binlog. This is used when
+ a server was previously a master and now needs to connect to a new master as
+ a slave. For each domain_id, if the GTID in the binlog was logged with our
+ own server_id _and_ has a higher seq_no than what is in the slave state,
+ then this should be used as the position to start replicating at. This
+ allows to promote a slave as new master, and connect the old master as a
+ slave with MASTER_GTID_POS=AUTO.
+*/
+int
+rpl_slave_state::tostring(String *dest, rpl_gtid *extra_gtids, uint32 num_extra)
+{
+ struct rpl_slave_state_tostring_data data;
+ data.first= true;
+ data.dest= dest;
+
+ return iterate(rpl_slave_state_tostring_cb, &data, extra_gtids, num_extra);
+}
+
+
+/*
+ Lookup a domain_id in the current replication slave state.
+
+ Returns false if the domain_id has no entries in the slave state.
+ Otherwise returns true, and fills in out_gtid with the corresponding
+ GTID.
+*/
+bool
+rpl_slave_state::domain_to_gtid(uint32 domain_id, rpl_gtid *out_gtid)
+{
+ element *elem;
+ list_element *list;
+ uint64 best_sub_id;
+
+ mysql_mutex_lock(&LOCK_slave_state);
+ elem= (element *)my_hash_search(&hash, (const uchar *)&domain_id, 0);
+ if (!elem || !(list= elem->list))
+ {
+ mysql_mutex_unlock(&LOCK_slave_state);
+ return false;
+ }
+
+ out_gtid->domain_id= domain_id;
+ out_gtid->server_id= list->server_id;
+ out_gtid->seq_no= list->seq_no;
+ best_sub_id= list->sub_id;
+
+ while ((list= list->next))
+ {
+ if (best_sub_id > list->sub_id)
+ continue;
+ best_sub_id= list->sub_id;
+ out_gtid->server_id= list->server_id;
+ out_gtid->seq_no= list->seq_no;
+ }
+
+ mysql_mutex_unlock(&LOCK_slave_state);
+ return true;
+}
+
+
+/*
+ Parse a GTID at the start of a string, and update the pointer to point
+ at the first character after the parsed GTID.
+
+ Returns 0 on ok, non-zero on parse error.
+*/
+static int
+gtid_parser_helper(char **ptr, char *end, rpl_gtid *out_gtid)
+{
+ char *q;
+ char *p= *ptr;
+ uint64 v1, v2, v3;
+ int err= 0;
+
+ q= end;
+ v1= (uint64)my_strtoll10(p, &q, &err);
+ if (err != 0 || v1 > (uint32)0xffffffff || q == end || *q != '-')
+ return 1;
+ p= q+1;
+ q= end;
+ v2= (uint64)my_strtoll10(p, &q, &err);
+ if (err != 0 || v2 > (uint32)0xffffffff || q == end || *q != '-')
+ return 1;
+ p= q+1;
+ q= end;
+ v3= (uint64)my_strtoll10(p, &q, &err);
+ if (err != 0)
+ return 1;
+
+ out_gtid->domain_id= v1;
+ out_gtid->server_id= v2;
+ out_gtid->seq_no= v3;
+ *ptr= q;
+ return 0;
+}
+
+
+rpl_gtid *
+gtid_parse_string_to_list(const char *str, size_t str_len, uint32 *out_len)
+{
+ char *p= const_cast<char *>(str);
+ char *end= p + str_len;
+ uint32 len= 0, alloc_len= 5;
+ rpl_gtid *list= NULL;
+
+ for (;;)
+ {
+ rpl_gtid gtid;
+
+ if (len >= (((uint32)1 << 28)-1) || gtid_parser_helper(&p, end, &gtid))
+ {
+ my_free(list);
+ return NULL;
+ }
+ if ((!list || len >= alloc_len) &&
+ !(list=
+ (rpl_gtid *)my_realloc(list,
+ (alloc_len= alloc_len*2) * sizeof(rpl_gtid),
+ MYF(MY_FREE_ON_ERROR|MY_ALLOW_ZERO_PTR))))
+ return NULL;
+ list[len++]= gtid;
+
+ if (p == end)
+ break;
+ if (*p != ',')
+ {
+ my_free(list);
+ return NULL;
+ }
+ ++p;
+ }
+ *out_len= len;
+ return list;
+}
+
+
+/*
+ Update the slave replication state with the GTID position obtained from
+ master when connecting with old-style (filename,offset) position.
+
+ If RESET is true then all existing entries are removed. Otherwise only
+ domain_ids mentioned in the STATE_FROM_MASTER are changed.
+
+ Returns 0 if ok, non-zero if error.
+*/
+int
+rpl_slave_state::load(THD *thd, char *state_from_master, size_t len,
+ bool reset, bool in_statement)
+{
+ char *end= state_from_master + len;
+
+ if (reset)
+ {
+ if (truncate_state_table(thd))
+ return 1;
+ truncate_hash();
+ }
+ if (state_from_master == end)
+ return 0;
+ for (;;)
+ {
+ rpl_gtid gtid;
+ uint64 sub_id;
+
+ if (gtid_parser_helper(&state_from_master, end, &gtid) ||
+ !(sub_id= next_sub_id(gtid.domain_id)) ||
+ record_gtid(thd, &gtid, sub_id, false, in_statement) ||
+ update(gtid.domain_id, gtid.server_id, sub_id, gtid.seq_no, NULL))
+ return 1;
+ if (state_from_master == end)
+ break;
+ if (*state_from_master != ',')
+ return 1;
+ ++state_from_master;
+ }
+ return 0;
+}
+
+
+bool
+rpl_slave_state::is_empty()
+{
+ uint32 i;
+ bool result= true;
+
+ mysql_mutex_lock(&LOCK_slave_state);
+ for (i= 0; i < hash.records; ++i)
+ {
+ element *e= (element *)my_hash_element(&hash, i);
+ if (e->list)
+ {
+ result= false;
+ break;
+ }
+ }
+ mysql_mutex_unlock(&LOCK_slave_state);
+
+ return result;
+}
+
+
+rpl_binlog_state::rpl_binlog_state()
+{
+ my_hash_init(&hash, &my_charset_bin, 32, offsetof(element, domain_id),
+ sizeof(uint32), NULL, my_free, HASH_UNIQUE);
+ mysql_mutex_init(key_LOCK_binlog_state, &LOCK_binlog_state,
+ MY_MUTEX_INIT_SLOW);
+ initialized= 1;
+}
+
+
+void
+rpl_binlog_state::reset_nolock()
+{
+ uint32 i;
+
+ for (i= 0; i < hash.records; ++i)
+ my_hash_free(&((element *)my_hash_element(&hash, i))->hash);
+ my_hash_reset(&hash);
+}
+
+
+void
+rpl_binlog_state::reset()
+{
+ mysql_mutex_lock(&LOCK_binlog_state);
+ reset_nolock();
+ mysql_mutex_unlock(&LOCK_binlog_state);
+}
+
+
+void rpl_binlog_state::free()
+{
+ if (initialized)
+ {
+ initialized= 0;
+ reset_nolock();
+ my_hash_free(&hash);
+ mysql_mutex_destroy(&LOCK_binlog_state);
+ }
+}
+
+
+bool
+rpl_binlog_state::load(struct rpl_gtid *list, uint32 count)
+{
+ uint32 i;
+ bool res= false;
+
+ mysql_mutex_lock(&LOCK_binlog_state);
+ reset_nolock();
+ for (i= 0; i < count; ++i)
+ {
+ if (update_nolock(&(list[i]), false))
+ {
+ res= true;
+ break;
+ }
+ }
+ mysql_mutex_unlock(&LOCK_binlog_state);
+ return res;
+}
+
+
+static int rpl_binlog_state_load_cb(rpl_gtid *gtid, void *data)
+{
+ rpl_binlog_state *self= (rpl_binlog_state *)data;
+ return self->update_nolock(gtid, false);
+}
+
+
+bool
+rpl_binlog_state::load(rpl_slave_state *slave_pos)
+{
+ bool res= false;
+
+ mysql_mutex_lock(&LOCK_binlog_state);
+ reset_nolock();
+ if (slave_pos->iterate(rpl_binlog_state_load_cb, this, NULL, 0))
+ res= true;
+ mysql_mutex_unlock(&LOCK_binlog_state);
+ return res;
+}
+
+
+rpl_binlog_state::~rpl_binlog_state()
+{
+ free();
+}
+
+
+/*
+ Update replication state with a new GTID.
+
+ If the (domain_id, server_id) pair already exists, then the new GTID replaces
+ the old one for that domain id. Else a new entry is inserted.
+
+ Returns 0 for ok, 1 for error.
+*/
+int
+rpl_binlog_state::update_nolock(const struct rpl_gtid *gtid, bool strict)
+{
+ element *elem;
+
+ if ((elem= (element *)my_hash_search(&hash,
+ (const uchar *)(&gtid->domain_id), 0)))
+ {
+ if (strict && elem->last_gtid && elem->last_gtid->seq_no >= gtid->seq_no)
+ {
+ my_error(ER_GTID_STRICT_OUT_OF_ORDER, MYF(0), gtid->domain_id,
+ gtid->server_id, gtid->seq_no, elem->last_gtid->domain_id,
+ elem->last_gtid->server_id, elem->last_gtid->seq_no);
+ return 1;
+ }
+ if (elem->seq_no_counter < gtid->seq_no)
+ elem->seq_no_counter= gtid->seq_no;
+ if (!elem->update_element(gtid))
+ return 0;
+ }
+ else if (!alloc_element_nolock(gtid))
+ return 0;
+
+ my_error(ER_OUT_OF_RESOURCES, MYF(0));
+ return 1;
+}
+
+
+int
+rpl_binlog_state::update(const struct rpl_gtid *gtid, bool strict)
+{
+ int res;
+ mysql_mutex_lock(&LOCK_binlog_state);
+ res= update_nolock(gtid, strict);
+ mysql_mutex_unlock(&LOCK_binlog_state);
+ return res;
+}
+
+
+/*
+ Fill in a new GTID, allocating next sequence number, and update state
+ accordingly.
+*/
+int
+rpl_binlog_state::update_with_next_gtid(uint32 domain_id, uint32 server_id,
+ rpl_gtid *gtid)
+{
+ element *elem;
+ int res= 0;
+
+ gtid->domain_id= domain_id;
+ gtid->server_id= server_id;
+
+ mysql_mutex_lock(&LOCK_binlog_state);
+ if ((elem= (element *)my_hash_search(&hash, (const uchar *)(&domain_id), 0)))
+ {
+ gtid->seq_no= ++elem->seq_no_counter;
+ if (!elem->update_element(gtid))
+ goto end;
+ }
+ else
+ {
+ gtid->seq_no= 1;
+ if (!alloc_element_nolock(gtid))
+ goto end;
+ }
+
+ my_error(ER_OUT_OF_RESOURCES, MYF(0));
+ res= 1;
+end:
+ mysql_mutex_unlock(&LOCK_binlog_state);
+ return res;
+}
+
+
+/* Helper functions for update. */
+int
+rpl_binlog_state::element::update_element(const rpl_gtid *gtid)
+{
+ rpl_gtid *lookup_gtid;
+
+ /*
+ By far the most common case is that successive events within same
+ replication domain have the same server id (it changes only when
+ switching to a new master). So save a hash lookup in this case.
+ */
+ if (likely(last_gtid && last_gtid->server_id == gtid->server_id))
+ {
+ last_gtid->seq_no= gtid->seq_no;
+ return 0;
+ }
+
+ lookup_gtid= (rpl_gtid *)
+ my_hash_search(&hash, (const uchar *)&gtid->server_id, 0);
+ if (lookup_gtid)
+ {
+ lookup_gtid->seq_no= gtid->seq_no;
+ last_gtid= lookup_gtid;
+ return 0;
+ }
+
+ /* Allocate a new GTID and insert it. */
+ lookup_gtid= (rpl_gtid *)my_malloc(sizeof(*lookup_gtid), MYF(MY_WME));
+ if (!lookup_gtid)
+ return 1;
+ memcpy(lookup_gtid, gtid, sizeof(*lookup_gtid));
+ if (my_hash_insert(&hash, (const uchar *)lookup_gtid))
+ {
+ my_free(lookup_gtid);
+ return 1;
+ }
+ last_gtid= lookup_gtid;
+ return 0;
+}
+
+
+int
+rpl_binlog_state::alloc_element_nolock(const rpl_gtid *gtid)
+{
+ element *elem;
+ rpl_gtid *lookup_gtid;
+
+ /* First time we see this domain_id; allocate a new element. */
+ elem= (element *)my_malloc(sizeof(*elem), MYF(MY_WME));
+ lookup_gtid= (rpl_gtid *)my_malloc(sizeof(*lookup_gtid), MYF(MY_WME));
+ if (elem && lookup_gtid)
+ {
+ elem->domain_id= gtid->domain_id;
+ my_hash_init(&elem->hash, &my_charset_bin, 32,
+ offsetof(rpl_gtid, server_id), sizeof(uint32), NULL, my_free,
+ HASH_UNIQUE);
+ elem->last_gtid= lookup_gtid;
+ elem->seq_no_counter= gtid->seq_no;
+ memcpy(lookup_gtid, gtid, sizeof(*lookup_gtid));
+ if (0 == my_hash_insert(&elem->hash, (const uchar *)lookup_gtid))
+ {
+ lookup_gtid= NULL; /* Do not free. */
+ if (0 == my_hash_insert(&hash, (const uchar *)elem))
+ return 0;
+ }
+ my_hash_free(&elem->hash);
+ }
+
+ /* An error. */
+ if (elem)
+ my_free(elem);
+ if (lookup_gtid)
+ my_free(lookup_gtid);
+ return 1;
+}
+
+
+/*
+ Check that a new GTID can be logged without creating an out-of-order
+ sequence number with existing GTIDs.
+*/
+bool
+rpl_binlog_state::check_strict_sequence(uint32 domain_id, uint32 server_id,
+ uint64 seq_no)
+{
+ element *elem;
+ bool res= 0;
+
+ mysql_mutex_lock(&LOCK_binlog_state);
+ if ((elem= (element *)my_hash_search(&hash,
+ (const uchar *)(&domain_id), 0)) &&
+ elem->last_gtid && elem->last_gtid->seq_no >= seq_no)
+ {
+ my_error(ER_GTID_STRICT_OUT_OF_ORDER, MYF(0), domain_id, server_id, seq_no,
+ elem->last_gtid->domain_id, elem->last_gtid->server_id,
+ elem->last_gtid->seq_no);
+ res= 1;
+ }
+ mysql_mutex_unlock(&LOCK_binlog_state);
+ return res;
+}
+
+
+/*
+ When we see a new GTID that will not be binlogged (eg. slave thread
+ with --log-slave-updates=0), then we need to remember to allocate any
+ GTID seq_no of our own within that domain starting from there.
+
+ Returns 0 if ok, non-zero if out-of-memory.
+*/
+int
+rpl_binlog_state::bump_seq_no_if_needed(uint32 domain_id, uint64 seq_no)
+{
+ element *elem;
+ int res;
+
+ mysql_mutex_lock(&LOCK_binlog_state);
+ if ((elem= (element *)my_hash_search(&hash, (const uchar *)(&domain_id), 0)))
+ {
+ if (elem->seq_no_counter < seq_no)
+ elem->seq_no_counter= seq_no;
+ res= 0;
+ goto end;
+ }
+
+ /* We need to allocate a new, empty element to remember the next seq_no. */
+ if (!(elem= (element *)my_malloc(sizeof(*elem), MYF(MY_WME))))
+ {
+ res= 1;
+ goto end;
+ }
+
+ elem->domain_id= domain_id;
+ my_hash_init(&elem->hash, &my_charset_bin, 32,
+ offsetof(rpl_gtid, server_id), sizeof(uint32), NULL, my_free,
+ HASH_UNIQUE);
+ elem->last_gtid= NULL;
+ elem->seq_no_counter= seq_no;
+ if (0 == my_hash_insert(&hash, (const uchar *)elem))
+ {
+ res= 0;
+ goto end;
+ }
+
+ my_hash_free(&elem->hash);
+ my_free(elem);
+ res= 1;
+
+end:
+ mysql_mutex_unlock(&LOCK_binlog_state);
+ return res;
+}
+
+
+/*
+ Write binlog state to text file, so we can read it in again without having
+ to scan last binlog file (normal shutdown/startup, not crash recovery).
+
+ The most recent GTID within each domain_id is written after any other GTID
+ within this domain.
+*/
+int
+rpl_binlog_state::write_to_iocache(IO_CACHE *dest)
+{
+ ulong i, j;
+ char buf[21];
+ int res= 0;
+
+ mysql_mutex_lock(&LOCK_binlog_state);
+ for (i= 0; i < hash.records; ++i)
+ {
+ size_t res;
+ element *e= (element *)my_hash_element(&hash, i);
+ if (!e->last_gtid)
+ {
+ DBUG_ASSERT(e->hash.records == 0);
+ continue;
+ }
+ for (j= 0; j <= e->hash.records; ++j)
+ {
+ const rpl_gtid *gtid;
+ if (j < e->hash.records)
+ {
+ gtid= (const rpl_gtid *)my_hash_element(&e->hash, j);
+ if (gtid == e->last_gtid)
+ continue;
+ }
+ else
+ gtid= e->last_gtid;
+
+ longlong10_to_str(gtid->seq_no, buf, 10);
+ res= my_b_printf(dest, "%u-%u-%s\n", gtid->domain_id, gtid->server_id, buf);
+ if (res == (size_t) -1)
+ {
+ res= 1;
+ goto end;
+ }
+ }
+ }
+
+end:
+ mysql_mutex_unlock(&LOCK_binlog_state);
+ return res;
+}
+
+
+int
+rpl_binlog_state::read_from_iocache(IO_CACHE *src)
+{
+ /* 10-digit - 10-digit - 20-digit \n \0 */
+ char buf[10+1+10+1+20+1+1];
+ char *p, *end;
+ rpl_gtid gtid;
+ int res= 0;
+
+ mysql_mutex_lock(&LOCK_binlog_state);
+ reset_nolock();
+ for (;;)
+ {
+ size_t len= my_b_gets(src, buf, sizeof(buf));
+ if (!len)
+ break;
+ p= buf;
+ end= buf + len;
+ if (gtid_parser_helper(&p, end, &gtid) ||
+ update_nolock(&gtid, false))
+ {
+ res= 1;
+ break;
+ }
+ }
+ mysql_mutex_unlock(&LOCK_binlog_state);
+ return res;
+}
+
+
+rpl_gtid *
+rpl_binlog_state::find_nolock(uint32 domain_id, uint32 server_id)
+{
+ element *elem;
+ if (!(elem= (element *)my_hash_search(&hash, (const uchar *)&domain_id, 0)))
+ return NULL;
+ return (rpl_gtid *)my_hash_search(&elem->hash, (const uchar *)&server_id, 0);
+}
+
+rpl_gtid *
+rpl_binlog_state::find(uint32 domain_id, uint32 server_id)
+{
+ rpl_gtid *p;
+ mysql_mutex_lock(&LOCK_binlog_state);
+ p= find_nolock(domain_id, server_id);
+ mysql_mutex_unlock(&LOCK_binlog_state);
+ return p;
+}
+
+rpl_gtid *
+rpl_binlog_state::find_most_recent(uint32 domain_id)
+{
+ element *elem;
+ rpl_gtid *gtid= NULL;
+
+ mysql_mutex_lock(&LOCK_binlog_state);
+ elem= (element *)my_hash_search(&hash, (const uchar *)&domain_id, 0);
+ if (elem && elem->last_gtid)
+ gtid= elem->last_gtid;
+ mysql_mutex_unlock(&LOCK_binlog_state);
+
+ return gtid;
+}
+
+
+uint32
+rpl_binlog_state::count()
+{
+ uint32 c= 0;
+ uint32 i;
+
+ mysql_mutex_lock(&LOCK_binlog_state);
+ for (i= 0; i < hash.records; ++i)
+ c+= ((element *)my_hash_element(&hash, i))->hash.records;
+ mysql_mutex_unlock(&LOCK_binlog_state);
+
+ return c;
+}
+
+
+int
+rpl_binlog_state::get_gtid_list(rpl_gtid *gtid_list, uint32 list_size)
+{
+ uint32 i, j, pos;
+ int res= 0;
+
+ mysql_mutex_lock(&LOCK_binlog_state);
+ pos= 0;
+ for (i= 0; i < hash.records; ++i)
+ {
+ element *e= (element *)my_hash_element(&hash, i);
+ if (!e->last_gtid)
+ {
+ DBUG_ASSERT(e->hash.records==0);
+ continue;
+ }
+ for (j= 0; j <= e->hash.records; ++j)
+ {
+ const rpl_gtid *gtid;
+ if (j < e->hash.records)
+ {
+ gtid= (rpl_gtid *)my_hash_element(&e->hash, j);
+ if (gtid == e->last_gtid)
+ continue;
+ }
+ else
+ gtid= e->last_gtid;
+
+ if (pos >= list_size)
+ {
+ res= 1;
+ goto end;
+ }
+ memcpy(&gtid_list[pos++], gtid, sizeof(*gtid));
+ }
+ }
+
+end:
+ mysql_mutex_unlock(&LOCK_binlog_state);
+ return res;
+}
+
+
+/*
+ Get a list of the most recently binlogged GTID, for each domain_id.
+
+ This can be used when switching from being a master to being a slave,
+ to know where to start replicating from the new master.
+
+ The returned list must be de-allocated with my_free().
+
+ Returns 0 for ok, non-zero for out-of-memory.
+*/
+int
+rpl_binlog_state::get_most_recent_gtid_list(rpl_gtid **list, uint32 *size)
+{
+ uint32 i;
+ uint32 alloc_size, out_size;
+ int res= 0;
+
+ out_size= 0;
+ mysql_mutex_lock(&LOCK_binlog_state);
+ alloc_size= hash.records;
+ if (!(*list= (rpl_gtid *)my_malloc(alloc_size * sizeof(rpl_gtid),
+ MYF(MY_WME))))
+ {
+ res= 1;
+ goto end;
+ }
+ for (i= 0; i < alloc_size; ++i)
+ {
+ element *e= (element *)my_hash_element(&hash, i);
+ if (!e->last_gtid)
+ continue;
+ memcpy(&((*list)[out_size++]), e->last_gtid, sizeof(rpl_gtid));
+ }
+
+end:
+ mysql_mutex_unlock(&LOCK_binlog_state);
+ *size= out_size;
+ return res;
+}
+
+
+bool
+rpl_binlog_state::append_pos(String *str)
+{
+ uint32 i;
+ bool first= true;
+
+ mysql_mutex_lock(&LOCK_binlog_state);
+ for (i= 0; i < hash.records; ++i)
+ {
+ element *e= (element *)my_hash_element(&hash, i);
+ if (e->last_gtid &&
+ rpl_slave_state_tostring_helper(str, e->last_gtid, &first))
+ return true;
+ }
+ mysql_mutex_unlock(&LOCK_binlog_state);
+
+ return false;
+}
+
+
+bool
+rpl_binlog_state::append_state(String *str)
+{
+ uint32 i, j;
+ bool first= true;
+ bool res= false;
+
+ mysql_mutex_lock(&LOCK_binlog_state);
+ for (i= 0; i < hash.records; ++i)
+ {
+ element *e= (element *)my_hash_element(&hash, i);
+ if (!e->last_gtid)
+ {
+ DBUG_ASSERT(e->hash.records==0);
+ continue;
+ }
+ for (j= 0; j <= e->hash.records; ++j)
+ {
+ const rpl_gtid *gtid;
+ if (j < e->hash.records)
+ {
+ gtid= (rpl_gtid *)my_hash_element(&e->hash, j);
+ if (gtid == e->last_gtid)
+ continue;
+ }
+ else
+ gtid= e->last_gtid;
+
+ if (rpl_slave_state_tostring_helper(str, gtid, &first))
+ {
+ res= true;
+ goto end;
+ }
+ }
+ }
+
+end:
+ mysql_mutex_unlock(&LOCK_binlog_state);
+ return res;
+}
+
+
+slave_connection_state::slave_connection_state()
+{
+ my_hash_init(&hash, &my_charset_bin, 32,
+ offsetof(entry, gtid) + offsetof(rpl_gtid, domain_id),
+ sizeof(uint32), NULL, my_free, HASH_UNIQUE);
+}
+
+
+slave_connection_state::~slave_connection_state()
+{
+ my_hash_free(&hash);
+}
+
+
+/*
+ Create a hash from the slave GTID state that is sent to master when slave
+ connects to start replication.
+
+ The state is sent as <GTID>,<GTID>,...,<GTID>, for example:
+
+ 0-2-112,1-4-1022
+
+ The state gives for each domain_id the GTID to start replication from for
+ the corresponding replication stream. So domain_id must be unique.
+
+ Returns 0 if ok, non-zero if error due to malformed input.
+
+ Note that input string is built by slave server, so it will not be incorrect
+ unless bug/corruption/malicious server. So we just need basic sanity check,
+ not fancy user-friendly error message.
+*/
+
+int
+slave_connection_state::load(char *slave_request, size_t len)
+{
+ char *p, *end;
+ uchar *rec;
+ rpl_gtid *gtid;
+ const entry *e;
+
+ reset();
+ p= slave_request;
+ end= slave_request + len;
+ if (p == end)
+ return 0;
+ for (;;)
+ {
+ if (!(rec= (uchar *)my_malloc(sizeof(entry), MYF(MY_WME))))
+ {
+ my_error(ER_OUTOFMEMORY, MYF(0), sizeof(*gtid));
+ return 1;
+ }
+ gtid= &((entry *)rec)->gtid;
+ if (gtid_parser_helper(&p, end, gtid))
+ {
+ my_free(rec);
+ my_error(ER_INCORRECT_GTID_STATE, MYF(0));
+ return 1;
+ }
+ if ((e= (const entry *)
+ my_hash_search(&hash, (const uchar *)(&gtid->domain_id), 0)))
+ {
+ my_error(ER_DUPLICATE_GTID_DOMAIN, MYF(0), gtid->domain_id,
+ gtid->server_id, (ulonglong)gtid->seq_no, e->gtid.domain_id,
+ e->gtid.server_id, (ulonglong)e->gtid.seq_no, gtid->domain_id);
+ my_free(rec);
+ return 1;
+ }
+ ((entry *)rec)->flags= 0;
+ if (my_hash_insert(&hash, rec))
+ {
+ my_free(rec);
+ my_error(ER_OUT_OF_RESOURCES, MYF(0));
+ return 1;
+ }
+ if (p == end)
+ break; /* Finished. */
+ if (*p != ',')
+ {
+ my_error(ER_INCORRECT_GTID_STATE, MYF(0));
+ return 1;
+ }
+ ++p;
+ }
+
+ return 0;
+}
+
+
+int
+slave_connection_state::load(const rpl_gtid *gtid_list, uint32 count)
+{
+ uint32 i;
+
+ reset();
+ for (i= 0; i < count; ++i)
+ if (update(&gtid_list[i]))
+ return 1;
+ return 0;
+}
+
+
+static int
+slave_connection_state_load_cb(rpl_gtid *gtid, void *data)
+{
+ slave_connection_state *state= (slave_connection_state *)data;
+ return state->update(gtid);
+}
+
+
+/*
+ Same as rpl_slave_state::tostring(), but populates a slave_connection_state
+ instead.
+*/
+int
+slave_connection_state::load(rpl_slave_state *state,
+ rpl_gtid *extra_gtids, uint32 num_extra)
+{
+ reset();
+ return state->iterate(slave_connection_state_load_cb, this,
+ extra_gtids, num_extra);
+}
+
+
+slave_connection_state::entry *
+slave_connection_state::find_entry(uint32 domain_id)
+{
+ return (entry *) my_hash_search(&hash, (const uchar *)(&domain_id), 0);
+}
+
+
+rpl_gtid *
+slave_connection_state::find(uint32 domain_id)
+{
+ entry *e= find_entry(domain_id);
+ if (!e)
+ return NULL;
+ return &e->gtid;
+}
+
+
+int
+slave_connection_state::update(const rpl_gtid *in_gtid)
+{
+ entry *e;
+ uchar *rec= my_hash_search(&hash, (const uchar *)(&in_gtid->domain_id), 0);
+ if (rec)
+ {
+ e= (entry *)rec;
+ e->gtid= *in_gtid;
+ return 0;
+ }
+
+ if (!(e= (entry *)my_malloc(sizeof(*e), MYF(MY_WME))))
+ return 1;
+ e->gtid= *in_gtid;
+ e->flags= 0;
+ if (my_hash_insert(&hash, (uchar *)e))
+ {
+ my_free(e);
+ return 1;
+ }
+
+ return 0;
+}
+
+
+void
+slave_connection_state::remove(const rpl_gtid *in_gtid)
+{
+ uchar *rec= my_hash_search(&hash, (const uchar *)(&in_gtid->domain_id), 0);
+#ifndef DBUG_OFF
+ bool err;
+ rpl_gtid *slave_gtid= &((entry *)rec)->gtid;
+ DBUG_ASSERT(rec /* We should never try to remove not present domain_id. */);
+ DBUG_ASSERT(slave_gtid->server_id == in_gtid->server_id);
+ DBUG_ASSERT(slave_gtid->seq_no == in_gtid->seq_no);
+#endif
+
+ IF_DBUG(err=, )
+ my_hash_delete(&hash, rec);
+ DBUG_ASSERT(!err);
+}
+
+
+void
+slave_connection_state::remove_if_present(const rpl_gtid *in_gtid)
+{
+ uchar *rec= my_hash_search(&hash, (const uchar *)(&in_gtid->domain_id), 0);
+ if (rec)
+ my_hash_delete(&hash, rec);
+}
+
+
+int
+slave_connection_state::to_string(String *out_str)
+{
+ out_str->length(0);
+ return append_to_string(out_str);
+}
+
+
+int
+slave_connection_state::append_to_string(String *out_str)
+{
+ uint32 i;
+ bool first;
+
+ first= true;
+ for (i= 0; i < hash.records; ++i)
+ {
+ const entry *e= (const entry *)my_hash_element(&hash, i);
+ if (rpl_slave_state_tostring_helper(out_str, &e->gtid, &first))
+ return 1;
+ }
+ return 0;
+}
+
+
+int
+slave_connection_state::get_gtid_list(rpl_gtid *gtid_list, uint32 list_size)
+{
+ uint32 i, pos;
+
+ pos= 0;
+ for (i= 0; i < hash.records; ++i)
+ {
+ entry *e;
+ if (pos >= list_size)
+ return 1;
+ e= (entry *)my_hash_element(&hash, i);
+ memcpy(&gtid_list[pos++], &e->gtid, sizeof(e->gtid));
+ }
+
+ return 0;
+}
+
+
+/*
+ Check if the GTID position has been reached, for mysql_binlog_send().
+
+ The position has not been reached if we have anything in the state, unless
+ it has either the START_ON_EMPTY_DOMAIN flag set (which means it does not
+ belong to this master at all), or the START_OWN_SLAVE_POS (which means that
+ we start on an old position from when the server was a slave with
+ --log-slave-updates=0).
+*/
+bool
+slave_connection_state::is_pos_reached()
+{
+ uint32 i;
+
+ for (i= 0; i < hash.records; ++i)
+ {
+ entry *e= (entry *)my_hash_element(&hash, i);
+ if (!(e->flags & (START_OWN_SLAVE_POS|START_ON_EMPTY_DOMAIN)))
+ return false;
+ }
+
+ return true;
+}
+
+
+/*
+ Execute a MASTER_GTID_WAIT().
+ The position to wait for is in gtid_str in string form.
+ The timeout in microseconds is in timeout_us, zero means no timeout.
+
+ Returns:
+ 1 for error.
+ 0 for wait completed.
+ -1 for wait timed out.
+*/
+int
+gtid_waiting::wait_for_pos(THD *thd, String *gtid_str, longlong timeout_us)
+{
+ int err;
+ rpl_gtid *wait_pos;
+ uint32 count, i;
+ struct timespec wait_until, *wait_until_ptr;
+
+ /* Wait for the empty position returns immediately. */
+ if (gtid_str->length() == 0)
+ return 0;
+
+ if (!(wait_pos= gtid_parse_string_to_list(gtid_str->ptr(), gtid_str->length(),
+ &count)))
+ {
+ my_error(ER_INCORRECT_GTID_STATE, MYF(0));
+ return 1;
+ }
+
+ if (timeout_us >= 0)
+ {
+ set_timespec_nsec(wait_until, (ulonglong)1000*timeout_us);
+ wait_until_ptr= &wait_until;
+ }
+ else
+ wait_until_ptr= NULL;
+ err= 0;
+ for (i= 0; i < count; ++i)
+ {
+ if ((err= wait_for_gtid(thd, &wait_pos[i], wait_until_ptr)))
+ break;
+ }
+ my_free(wait_pos);
+ return err;
+}
+
+
+void
+gtid_waiting::promote_new_waiter(gtid_waiting::hash_element *he)
+{
+ queue_element *qe;
+
+ mysql_mutex_assert_owner(&LOCK_gtid_waiting);
+ if (queue_empty(&he->queue))
+ return;
+ qe= (queue_element *)queue_top(&he->queue);
+ qe->do_small_wait= true;
+ mysql_cond_signal(&qe->thd->COND_wakeup_ready);
+}
+
+void
+gtid_waiting::process_wait_hash(uint64 wakeup_seq_no,
+ gtid_waiting::hash_element *he)
+{
+ mysql_mutex_assert_owner(&LOCK_gtid_waiting);
+
+ for (;;)
+ {
+ queue_element *qe;
+
+ if (queue_empty(&he->queue))
+ break;
+ qe= (queue_element *)queue_top(&he->queue);
+ if (qe->wait_seq_no > wakeup_seq_no)
+ break;
+ DBUG_ASSERT(!qe->done);
+ queue_remove_top(&he->queue);
+ qe->done= true;;
+ mysql_cond_signal(&qe->thd->COND_wakeup_ready);
+ }
+}
+
+
+/*
+ Execute a MASTER_GTID_WAIT() for one specific domain.
+
+ The implementation is optimised primarily for (1) minimal performance impact
+ on the slave replication threads, and secondarily for (2) quick performance
+ of MASTER_GTID_WAIT() on a single GTID, which can be useful for consistent
+ read to clients in an async replication read-scaleout scenario.
+
+ To achieve (1), we have a "small" wait and a "large" wait. The small wait
+ contends with the replication threads on the lock on the gtid_slave_pos, so
+ only minimal processing is done under that lock, and only a single waiter at
+ a time does the small wait.
+
+ If there is already a small waiter, a new thread will either replace the
+ small waiter (if it needs to wait for an earlier sequence number), or
+ instead do a "large" wait.
+
+ Once awoken on the small wait, the waiting thread releases the lock shared
+ with the SQL threads quickly, and then processes all waiters currently doing
+ the large wait using a different lock that does not impact replication.
+
+ This way, the SQL threads only need to do a single check + possibly a
+ pthread_cond_signal() when updating the gtid_slave_state, and the time that
+ non-SQL threads contend for the lock on gtid_slave_state is minimized.
+
+ There is always at least one thread that has the responsibility to ensure
+ that there is a small waiter; this thread has queue_element::do_small_wait
+ set to true. This thread will do the small wait until it is done, at which
+ point it will make sure to pass on the responsibility to another thread.
+ Normally only one thread has do_small_wait==true, but it can occasionally
+ happen that there is more than one, when threads race one another for the
+ lock on the small wait (this results in slightly increased activity on the
+ small lock but is otherwise harmless).
+
+ Returns:
+ 0 Wait completed normally
+ -1 Wait completed due to timeout
+ 1 An error (my_error() will have been called to set the error in the da)
+*/
+int
+gtid_waiting::wait_for_gtid(THD *thd, rpl_gtid *wait_gtid,
+ struct timespec *wait_until)
+{
+ bool timed_out= false;
+#ifdef HAVE_REPLICATION
+ queue_element elem;
+ uint32 domain_id= wait_gtid->domain_id;
+ uint64 seq_no= wait_gtid->seq_no;
+ hash_element *he;
+ rpl_slave_state::element *slave_state_elem= NULL;
+ PSI_stage_info old_stage;
+ bool did_enter_cond= false;
+
+ elem.wait_seq_no= seq_no;
+ elem.thd= thd;
+ elem.done= false;
+
+ mysql_mutex_lock(&LOCK_gtid_waiting);
+ if (!(he= get_entry(wait_gtid->domain_id)))
+ {
+ mysql_mutex_unlock(&LOCK_gtid_waiting);
+ return 1;
+ }
+ /*
+ If there is already another waiter with seq_no no larger than our own,
+ we are sure that there is already a small waiter that will wake us up
+ (or later pass the small wait responsibility to us). So in this case, we
+ do not need to touch the small wait lock at all.
+ */
+ elem.do_small_wait=
+ (queue_empty(&he->queue) ||
+ ((queue_element *)queue_top(&he->queue))->wait_seq_no > seq_no);
+
+ if (register_in_wait_queue(thd, wait_gtid, he, &elem))
+ {
+ mysql_mutex_unlock(&LOCK_gtid_waiting);
+ return 1;
+ }
+ /*
+ Loop, doing either the small or large wait as appropriate, until either
+ the position waited for is reached, or we get a kill or timeout.
+ */
+ for (;;)
+ {
+ mysql_mutex_assert_owner(&LOCK_gtid_waiting);
+
+ if (elem.do_small_wait)
+ {
+ uint64 wakeup_seq_no;
+ queue_element *cur_waiter;
+
+ mysql_mutex_lock(&rpl_global_gtid_slave_state->LOCK_slave_state);
+ /*
+ The elements in the gtid_slave_state_hash are never re-allocated once
+ they enter the hash, so we do not need to re-do the lookup after releasing
+ and re-aquiring the lock.
+ */
+ if (!slave_state_elem &&
+ !(slave_state_elem= rpl_global_gtid_slave_state->get_element(domain_id)))
+ {
+ mysql_mutex_unlock(&rpl_global_gtid_slave_state->LOCK_slave_state);
+ remove_from_wait_queue(he, &elem);
+ promote_new_waiter(he);
+ if (did_enter_cond)
+ thd->EXIT_COND(&old_stage);
+ else
+ mysql_mutex_unlock(&LOCK_gtid_waiting);
+ my_error(ER_OUT_OF_RESOURCES, MYF(0));
+ return 1;
+ }
+
+ if ((wakeup_seq_no= slave_state_elem->highest_seq_no) >= seq_no)
+ {
+ /*
+ We do not have to wait. (We will be removed from the wait queue when
+ we call process_wait_hash() below.
+ */
+ mysql_mutex_unlock(&rpl_global_gtid_slave_state->LOCK_slave_state);
+ }
+ else if ((cur_waiter= slave_state_elem->gtid_waiter) &&
+ slave_state_elem->min_wait_seq_no <= seq_no)
+ {
+ /*
+ There is already a suitable small waiter, go do the large wait.
+ (Normally we would not have needed to check the small wait in this
+ case, but it can happen if we race with another thread for the small
+ lock).
+ */
+ elem.do_small_wait= false;
+ mysql_mutex_unlock(&rpl_global_gtid_slave_state->LOCK_slave_state);
+ }
+ else
+ {
+ /*
+ We have to do the small wait ourselves (stealing it from any thread
+ that might already be waiting for a later seq_no).
+ */
+ slave_state_elem->gtid_waiter= &elem;
+ slave_state_elem->min_wait_seq_no= seq_no;
+ if (cur_waiter)
+ {
+ /* We stole the wait, so wake up the old waiting thread. */
+ mysql_cond_signal(&slave_state_elem->COND_wait_gtid);
+ }
+
+ /* Release the large lock, and do the small wait. */
+ if (did_enter_cond)
+ {
+ thd->EXIT_COND(&old_stage);
+ did_enter_cond= false;
+ }
+ else
+ mysql_mutex_unlock(&LOCK_gtid_waiting);
+ thd->ENTER_COND(&slave_state_elem->COND_wait_gtid,
+ &rpl_global_gtid_slave_state->LOCK_slave_state,
+ &stage_master_gtid_wait_primary, &old_stage);
+ do
+ {
+ if (thd->check_killed())
+ break;
+ else if (wait_until)
+ {
+ int err=
+ mysql_cond_timedwait(&slave_state_elem->COND_wait_gtid,
+ &rpl_global_gtid_slave_state->LOCK_slave_state,
+ wait_until);
+ if (err == ETIMEDOUT || err == ETIME)
+ {
+ timed_out= true;
+ break;
+ }
+ }
+ else
+ mysql_cond_wait(&slave_state_elem->COND_wait_gtid,
+ &rpl_global_gtid_slave_state->LOCK_slave_state);
+ } while (slave_state_elem->gtid_waiter == &elem);
+ wakeup_seq_no= slave_state_elem->highest_seq_no;
+ /*
+ If we aborted due to timeout or kill, remove us as waiter.
+
+ If we were replaced by another waiter with a smaller seq_no, then we
+ no longer have responsibility for the small wait.
+ */
+ if ((cur_waiter= slave_state_elem->gtid_waiter))
+ {
+ if (cur_waiter == &elem)
+ slave_state_elem->gtid_waiter= NULL;
+ else if (slave_state_elem->min_wait_seq_no <= seq_no)
+ elem.do_small_wait= false;
+ }
+ thd->EXIT_COND(&old_stage);
+
+ mysql_mutex_lock(&LOCK_gtid_waiting);
+ }
+
+ /*
+ Note that hash_entry pointers do not change once allocated, so we do
+ not need to lookup `he' again after re-aquiring LOCK_gtid_waiting.
+ */
+ process_wait_hash(wakeup_seq_no, he);
+ }
+ else
+ {
+ /* Do the large wait. */
+ if (!did_enter_cond)
+ {
+ thd->ENTER_COND(&thd->COND_wakeup_ready, &LOCK_gtid_waiting,
+ &stage_master_gtid_wait, &old_stage);
+ did_enter_cond= true;
+ }
+ while (!elem.done && !thd->check_killed())
+ {
+ thd_wait_begin(thd, THD_WAIT_BINLOG);
+ if (wait_until)
+ {
+ int err= mysql_cond_timedwait(&thd->COND_wakeup_ready,
+ &LOCK_gtid_waiting, wait_until);
+ if (err == ETIMEDOUT || err == ETIME)
+ timed_out= true;
+ }
+ else
+ mysql_cond_wait(&thd->COND_wakeup_ready, &LOCK_gtid_waiting);
+ thd_wait_end(thd);
+ if (elem.do_small_wait || timed_out)
+ break;
+ }
+ }
+
+ if ((thd->killed || timed_out) && !elem.done)
+ {
+ /* Aborted, so remove ourselves from the hash. */
+ remove_from_wait_queue(he, &elem);
+ elem.done= true;
+ }
+ if (elem.done)
+ {
+ /*
+ If our wait is done, but we have (or were passed) responsibility for
+ the small wait, then we need to pass on that task to someone else.
+ */
+ if (elem.do_small_wait)
+ promote_new_waiter(he);
+ break;
+ }
+ }
+
+ if (did_enter_cond)
+ thd->EXIT_COND(&old_stage);
+ else
+ mysql_mutex_unlock(&LOCK_gtid_waiting);
+ if (thd->killed)
+ thd->send_kill_message();
+#endif /* HAVE_REPLICATION */
+ return timed_out ? -1 : 0;
+}
+
+
+static void
+free_hash_element(void *p)
+{
+ gtid_waiting::hash_element *e= (gtid_waiting::hash_element *)p;
+ delete_queue(&e->queue);
+ my_free(e);
+}
+
+
+void
+gtid_waiting::init()
+{
+ my_hash_init(&hash, &my_charset_bin, 32,
+ offsetof(hash_element, domain_id), sizeof(uint32), NULL,
+ free_hash_element, HASH_UNIQUE);
+ mysql_mutex_init(key_LOCK_gtid_waiting, &LOCK_gtid_waiting, 0);
+}
+
+
+void
+gtid_waiting::destroy()
+{
+ mysql_mutex_destroy(&LOCK_gtid_waiting);
+ my_hash_free(&hash);
+}
+
+
+static int
+cmp_queue_elem(void *, uchar *a, uchar *b)
+{
+ uint64 seq_no_a= *(uint64 *)a;
+ uint64 seq_no_b= *(uint64 *)b;
+ if (seq_no_a < seq_no_b)
+ return -1;
+ else if (seq_no_a == seq_no_b)
+ return 0;
+ else
+ return 1;
+}
+
+
+gtid_waiting::hash_element *
+gtid_waiting::get_entry(uint32 domain_id)
+{
+ hash_element *e;
+
+ if ((e= (hash_element *)my_hash_search(&hash, (const uchar *)&domain_id, 0)))
+ return e;
+
+ if (!(e= (hash_element *)my_malloc(sizeof(*e), MYF(MY_WME))))
+ {
+ my_error(ER_OUTOFMEMORY, MYF(0), sizeof(*e));
+ return NULL;
+ }
+
+ if (init_queue(&e->queue, 8, offsetof(queue_element, wait_seq_no), 0,
+ cmp_queue_elem, NULL, 1+offsetof(queue_element, queue_idx), 1))
+ {
+ my_error(ER_OUT_OF_RESOURCES, MYF(0));
+ my_free(e);
+ return NULL;
+ }
+ e->domain_id= domain_id;
+ if (my_hash_insert(&hash, (uchar *)e))
+ {
+ my_error(ER_OUT_OF_RESOURCES, MYF(0));
+ delete_queue(&e->queue);
+ my_free(e);
+ return NULL;
+ }
+ return e;
+}
+
+
+int
+gtid_waiting::register_in_wait_queue(THD *thd, rpl_gtid *wait_gtid,
+ gtid_waiting::hash_element *he,
+ gtid_waiting::queue_element *elem)
+{
+ mysql_mutex_assert_owner(&LOCK_gtid_waiting);
+
+ if (queue_insert_safe(&he->queue, (uchar *)elem))
+ {
+ my_error(ER_OUT_OF_RESOURCES, MYF(0));
+ return 1;
+ }
+
+ return 0;
+}
+
+
+void
+gtid_waiting::remove_from_wait_queue(gtid_waiting::hash_element *he,
+ gtid_waiting::queue_element *elem)
+{
+ mysql_mutex_assert_owner(&LOCK_gtid_waiting);
+
+ queue_remove(&he->queue, elem->queue_idx);
+}
diff --git a/sql/rpl_gtid.h b/sql/rpl_gtid.h
new file mode 100644
index 00000000000..d17ddf3451a
--- /dev/null
+++ b/sql/rpl_gtid.h
@@ -0,0 +1,298 @@
+/* Copyright (c) 2013, Kristian Nielsen and MariaDB Services 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#ifndef RPL_GTID_H
+#define RPL_GTID_H
+
+#include "hash.h"
+#include "queues.h"
+
+
+/* Definitions for MariaDB global transaction ID (GTID). */
+
+
+extern const LEX_STRING rpl_gtid_slave_state_table_name;
+
+class String;
+
+struct rpl_gtid
+{
+ uint32 domain_id;
+ uint32 server_id;
+ uint64 seq_no;
+};
+
+
+enum enum_gtid_skip_type {
+ GTID_SKIP_NOT, GTID_SKIP_STANDALONE, GTID_SKIP_TRANSACTION
+};
+
+
+/*
+ Structure to keep track of threads waiting in MASTER_GTID_WAIT().
+
+ Since replication is (mostly) single-threaded, we want to minimise the
+ performance impact on that from MASTER_GTID_WAIT(). To achieve this, we
+ are careful to keep the common lock between replication threads and
+ MASTER_GTID_WAIT threads held for as short as possible. We keep only
+ a single thread waiting to be notified by the replication threads; this
+ thread then handles all the (potentially heavy) lifting of dealing with
+ all current waiting threads.
+*/
+struct gtid_waiting {
+ /* Elements in the hash, basically a priority queue for each domain. */
+ struct hash_element {
+ QUEUE queue;
+ uint32 domain_id;
+ };
+ /* A priority queue to handle waiters in one domain in seq_no order. */
+ struct queue_element {
+ uint64 wait_seq_no;
+ THD *thd;
+ int queue_idx;
+ /*
+ do_small_wait is true if we have responsibility for ensuring that there
+ is a small waiter.
+ */
+ bool do_small_wait;
+ /*
+ The flag `done' is set when the wait is completed (either due to reaching
+ the position waited for, or due to timeout or kill). The queue_element
+ is in the queue if and only if `done' is true.
+ */
+ bool done;
+ };
+
+ mysql_mutex_t LOCK_gtid_waiting;
+ HASH hash;
+
+ void init();
+ void destroy();
+ hash_element *get_entry(uint32 domain_id);
+ int wait_for_pos(THD *thd, String *gtid_str, longlong timeout_us);
+ void promote_new_waiter(gtid_waiting::hash_element *he);
+ int wait_for_gtid(THD *thd, rpl_gtid *wait_gtid, struct timespec *wait_until);
+ void process_wait_hash(uint64 wakeup_seq_no, gtid_waiting::hash_element *he);
+ int register_in_wait_queue(THD *thd, rpl_gtid *wait_gtid, hash_element *he,
+ queue_element *elem);
+ void remove_from_wait_queue(hash_element *he, queue_element *elem);
+};
+
+
+class Relay_log_info;
+struct rpl_group_info;
+
+/*
+ Replication slave state.
+
+ For every independent replication stream (identified by domain_id), this
+ remembers the last gtid applied on the slave within this domain.
+
+ Since events are always committed in-order within a single domain, this is
+ sufficient to maintain the state of the replication slave.
+*/
+struct rpl_slave_state
+{
+ /* Elements in the list of GTIDs kept for each domain_id. */
+ struct list_element
+ {
+ struct list_element *next;
+ uint64 sub_id;
+ uint64 seq_no;
+ uint32 server_id;
+ };
+
+ /* Elements in the HASH that hold the state for one domain_id. */
+ struct element
+ {
+ struct list_element *list;
+ uint32 domain_id;
+ /* Highest seq_no seen so far in this domain. */
+ uint64 highest_seq_no;
+ /*
+ If this is non-NULL, then it is the waiter responsible for the small
+ wait in MASTER_GTID_WAIT().
+ */
+ gtid_waiting::queue_element *gtid_waiter;
+ /*
+ If gtid_waiter is non-NULL, then this is the seq_no that its
+ MASTER_GTID_WAIT() is waiting on. When we reach this seq_no, we need to
+ signal the waiter on COND_wait_gtid.
+ */
+ uint64 min_wait_seq_no;
+ mysql_cond_t COND_wait_gtid;
+
+ /*
+ For --gtid-ignore-duplicates. The Relay_log_info that currently owns
+ this domain, and the number of worker threads that are active in it.
+
+ The idea is that only one of multiple master connections is allowed to
+ actively apply events for a given domain. Other connections must either
+ discard the events (if the seq_no in GTID shows they have already been
+ applied), or wait to see if the current owner will apply it.
+ */
+ const Relay_log_info *owner_rli;
+ uint32 owner_count;
+ mysql_cond_t COND_gtid_ignore_duplicates;
+
+ list_element *grab_list() { list_element *l= list; list= NULL; return l; }
+ void add(list_element *l)
+ {
+ l->next= list;
+ list= l;
+ }
+ };
+
+ /* Mapping from domain_id to its element. */
+ HASH hash;
+ /* Mutex protecting access to the state. */
+ mysql_mutex_t LOCK_slave_state;
+
+ uint64 last_sub_id;
+ bool loaded;
+
+ rpl_slave_state();
+ ~rpl_slave_state();
+
+ void truncate_hash();
+ ulong count() const { return hash.records; }
+ int update(uint32 domain_id, uint32 server_id, uint64 sub_id,
+ uint64 seq_no, rpl_group_info *rgi);
+ int truncate_state_table(THD *thd);
+ int record_gtid(THD *thd, const rpl_gtid *gtid, uint64 sub_id,
+ bool in_transaction, bool in_statement);
+ uint64 next_sub_id(uint32 domain_id);
+ int iterate(int (*cb)(rpl_gtid *, void *), void *data,
+ rpl_gtid *extra_gtids, uint32 num_extra);
+ int tostring(String *dest, rpl_gtid *extra_gtids, uint32 num_extra);
+ bool domain_to_gtid(uint32 domain_id, rpl_gtid *out_gtid);
+ int load(THD *thd, char *state_from_master, size_t len, bool reset,
+ bool in_statement);
+ bool is_empty();
+
+ element *get_element(uint32 domain_id);
+ int put_back_list(uint32 domain_id, list_element *list);
+
+ void update_state_hash(uint64 sub_id, rpl_gtid *gtid, rpl_group_info *rgi);
+ int record_and_update_gtid(THD *thd, struct rpl_group_info *rgi);
+ int check_duplicate_gtid(rpl_gtid *gtid, rpl_group_info *rgi);
+ void release_domain_owner(rpl_group_info *rgi);
+};
+
+
+/*
+ Binlog state.
+ This keeps the last GTID written to the binlog for every distinct
+ (domain_id, server_id) pair.
+ This will be logged at the start of the next binlog file as a
+ Gtid_list_log_event; this way, it is easy to find the binlog file
+ containing a gigen GTID, by simply scanning backwards from the newest
+ one until a lower seq_no is found in the Gtid_list_log_event at the
+ start of a binlog for the given domain_id and server_id.
+
+ We also remember the last logged GTID for every domain_id. This is used
+ to know where to start when a master is changed to a slave. As a side
+ effect, it also allows to skip a hash lookup in the very common case of
+ logging a new GTID with same server id as last GTID.
+*/
+struct rpl_binlog_state
+{
+ struct element {
+ uint32 domain_id;
+ HASH hash; /* Containing all server_id for one domain_id */
+ /* The most recent entry in the hash. */
+ rpl_gtid *last_gtid;
+ /* Counter to allocate next seq_no for this domain. */
+ uint64 seq_no_counter;
+
+ int update_element(const rpl_gtid *gtid);
+ };
+ /* Mapping from domain_id to collection of elements. */
+ HASH hash;
+ /* Mutex protecting access to the state. */
+ mysql_mutex_t LOCK_binlog_state;
+ my_bool initialized;
+
+ rpl_binlog_state();
+ ~rpl_binlog_state();
+
+ void reset_nolock();
+ void reset();
+ void free();
+ bool load(struct rpl_gtid *list, uint32 count);
+ bool load(rpl_slave_state *slave_pos);
+ int update_nolock(const struct rpl_gtid *gtid, bool strict);
+ int update(const struct rpl_gtid *gtid, bool strict);
+ int update_with_next_gtid(uint32 domain_id, uint32 server_id,
+ rpl_gtid *gtid);
+ int alloc_element_nolock(const rpl_gtid *gtid);
+ bool check_strict_sequence(uint32 domain_id, uint32 server_id, uint64 seq_no);
+ int bump_seq_no_if_needed(uint32 domain_id, uint64 seq_no);
+ int write_to_iocache(IO_CACHE *dest);
+ int read_from_iocache(IO_CACHE *src);
+ uint32 count();
+ int get_gtid_list(rpl_gtid *gtid_list, uint32 list_size);
+ int get_most_recent_gtid_list(rpl_gtid **list, uint32 *size);
+ bool append_pos(String *str);
+ bool append_state(String *str);
+ rpl_gtid *find_nolock(uint32 domain_id, uint32 server_id);
+ rpl_gtid *find(uint32 domain_id, uint32 server_id);
+ rpl_gtid *find_most_recent(uint32 domain_id);
+};
+
+
+/*
+ Represent the GTID state that a slave connection to a master requests
+ the master to start sending binlog events from.
+*/
+struct slave_connection_state
+{
+ struct entry {
+ rpl_gtid gtid;
+ uint32 flags;
+ };
+ static const uint32 START_OWN_SLAVE_POS= 0x1;
+ static const uint32 START_ON_EMPTY_DOMAIN= 0x2;
+
+ /* Mapping from domain_id to the entry with GTID requested for that domain. */
+ HASH hash;
+
+ slave_connection_state();
+ ~slave_connection_state();
+
+ void reset() { my_hash_reset(&hash); }
+ int load(char *slave_request, size_t len);
+ int load(const rpl_gtid *gtid_list, uint32 count);
+ int load(rpl_slave_state *state, rpl_gtid *extra_gtids, uint32 num_extra);
+ rpl_gtid *find(uint32 domain_id);
+ entry *find_entry(uint32 domain_id);
+ int update(const rpl_gtid *in_gtid);
+ void remove(const rpl_gtid *gtid);
+ void remove_if_present(const rpl_gtid *in_gtid);
+ ulong count() const { return hash.records; }
+ int to_string(String *out_str);
+ int append_to_string(String *out_str);
+ int get_gtid_list(rpl_gtid *gtid_list, uint32 list_size);
+ bool is_pos_reached();
+};
+
+
+extern bool rpl_slave_state_tostring_helper(String *dest, const rpl_gtid *gtid,
+ bool *first);
+extern int gtid_check_rpl_slave_state_table(TABLE *table);
+extern rpl_gtid *gtid_parse_string_to_list(const char *p, size_t len,
+ uint32 *out_len);
+
+#endif /* RPL_GTID_H */
diff --git a/sql/rpl_handler.cc b/sql/rpl_handler.cc
index 5b75d6c30ea..917a9d11b5a 100644
--- a/sql/rpl_handler.cc
+++ b/sql/rpl_handler.cc
@@ -13,6 +13,7 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+#include <my_global.h>
#include "sql_priv.h"
#include "unireg.h"
@@ -164,7 +165,7 @@ void delegates_destroy()
delegate.
*/
#define FOREACH_OBSERVER(r, f, do_lock, args) \
- param.server_id= thd->server_id; \
+ param.server_id= thd->variables.server_id; \
read_lock(); \
Observer_info_iterator iter= observer_info_iter(); \
Observer_info *info= iter++; \
@@ -304,7 +305,7 @@ int Binlog_transmit_delegate::reserve_header(THD *thd, ushort flags,
ulong hlen;
Binlog_transmit_param param;
param.flags= flags;
- param.server_id= thd->server_id;
+ param.server_id= thd->variables.server_id;
int ret= 0;
read_lock();
@@ -502,4 +503,24 @@ int unregister_binlog_relay_io_observer(Binlog_relay_IO_observer *observer, void
{
return binlog_relay_io_delegate->remove_observer(observer, (st_plugin_int *)p);
}
+#else
+int register_binlog_transmit_observer(Binlog_transmit_observer *observer, void *p)
+{
+ return 0;
+}
+
+int unregister_binlog_transmit_observer(Binlog_transmit_observer *observer, void *p)
+{
+ return 0;
+}
+
+int register_binlog_relay_io_observer(Binlog_relay_IO_observer *observer, void *p)
+{
+ return 0;
+}
+
+int unregister_binlog_relay_io_observer(Binlog_relay_IO_observer *observer, void *p)
+{
+ return 0;
+}
#endif /* HAVE_REPLICATION */
diff --git a/sql/rpl_handler.h b/sql/rpl_handler.h
index 362f3c74a4b..e262ebdbd6b 100644
--- a/sql/rpl_handler.h
+++ b/sql/rpl_handler.h
@@ -121,7 +121,7 @@ public:
inited= FALSE;
if (my_rwlock_init(&lock, NULL))
return;
- init_sql_alloc(&memroot, 1024, 0);
+ init_sql_alloc(&memroot, 1024, 0, MYF(0));
inited= TRUE;
}
~Delegate()
diff --git a/sql/rpl_injector.cc b/sql/rpl_injector.cc
index ec1a96e8a2b..94b081bf4f4 100644
--- a/sql/rpl_injector.cc
+++ b/sql/rpl_injector.cc
@@ -13,8 +13,8 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+#include <my_global.h>
#include "sql_priv.h"
-#include "unireg.h" // REQUIRED by later includes
#include "rpl_injector.h"
#include "transaction.h"
#include "sql_parse.h" // begin_trans, end_trans, COMMIT
@@ -108,7 +108,7 @@ int injector::transaction::use_table(server_id_type sid, table tbl)
if ((error= check_state(TABLE_STATE)))
DBUG_RETURN(error);
- server_id_type save_id= m_thd->server_id;
+ server_id_type save_id= m_thd->variables.server_id;
m_thd->set_server_id(sid);
error= m_thd->binlog_write_table_map(tbl.get_table(),
tbl.is_transactional());
@@ -127,7 +127,7 @@ int injector::transaction::write_row (server_id_type sid, table tbl,
if (error)
DBUG_RETURN(error);
- server_id_type save_id= m_thd->server_id;
+ server_id_type save_id= m_thd->variables.server_id;
m_thd->set_server_id(sid);
error= m_thd->binlog_write_row(tbl.get_table(), tbl.is_transactional(),
cols, colcnt, record);
@@ -146,7 +146,7 @@ int injector::transaction::delete_row(server_id_type sid, table tbl,
if (error)
DBUG_RETURN(error);
- server_id_type save_id= m_thd->server_id;
+ server_id_type save_id= m_thd->variables.server_id;
m_thd->set_server_id(sid);
error= m_thd->binlog_delete_row(tbl.get_table(), tbl.is_transactional(),
cols, colcnt, record);
@@ -165,7 +165,7 @@ int injector::transaction::update_row(server_id_type sid, table tbl,
if (error)
DBUG_RETURN(error);
- server_id_type save_id= m_thd->server_id;
+ server_id_type save_id= m_thd->variables.server_id;
m_thd->set_server_id(sid);
error= m_thd->binlog_update_row(tbl.get_table(), tbl.is_transactional(),
cols, colcnt, before, after);
diff --git a/sql/rpl_injector.h b/sql/rpl_injector.h
index f4790cc963a..98788955e24 100644
--- a/sql/rpl_injector.h
+++ b/sql/rpl_injector.h
@@ -94,13 +94,13 @@ public:
injector::transaction::table tbl(share->table, true);
MY_BITMAP cols;
- bitmap_init(&cols, NULL, (i + 7) / 8, false);
+ my_bitmap_init(&cols, NULL, (i + 7) / 8, false);
inj->write_row(::server_id, tbl, &cols, row_data);
or
MY_BITMAP cols;
- bitmap_init(&cols, NULL, (i + 7) / 8, false);
+ my_bitmap_init(&cols, NULL, (i + 7) / 8, false);
inj->write_row(::server_id,
injector::transaction::table(share->table, true),
&cols, row_data);
diff --git a/sql/rpl_mi.cc b/sql/rpl_mi.cc
index 3c5a99121fa..007f4c2828b 100644
--- a/sql/rpl_mi.cc
+++ b/sql/rpl_mi.cc
@@ -17,9 +17,10 @@
#include <my_global.h> // For HAVE_REPLICATION
#include "sql_priv.h"
#include <my_dir.h>
-#include "unireg.h" // REQUIRED by other includes
#include "rpl_mi.h"
#include "slave.h" // SLAVE_MAX_HEARTBEAT_PERIOD
+#include "strfunc.h"
+#include "sql_repl.h"
#ifdef HAVE_REPLICATION
@@ -27,20 +28,53 @@
static void init_master_log_pos(Master_info* mi);
-Master_info::Master_info(bool is_slave_recovery)
+Master_info::Master_info(LEX_STRING *connection_name_arg,
+ bool is_slave_recovery)
:Slave_reporting_capability("I/O"),
ssl(0), ssl_verify_server_cert(1), fd(-1), io_thd(0),
rli(is_slave_recovery), port(MYSQL_PORT),
checksum_alg_before_fd(BINLOG_CHECKSUM_ALG_UNDEF),
connect_retry(DEFAULT_CONNECT_RETRY), inited(0), abort_slave(0),
- slave_running(0), slave_run_id(0), sync_counter(0),
- heartbeat_period(0), received_heartbeats(0), master_id(0)
+ slave_running(MYSQL_SLAVE_NOT_RUN), slave_run_id(0),
+ clock_diff_with_master(0),
+ sync_counter(0), heartbeat_period(0), received_heartbeats(0),
+ master_id(0), prev_master_id(0),
+ using_gtid(USE_GTID_NO), events_queued_since_last_gtid(0),
+ gtid_reconnect_event_skip_count(0), gtid_event_seen(false)
{
host[0] = 0; user[0] = 0; password[0] = 0;
ssl_ca[0]= 0; ssl_capath[0]= 0; ssl_cert[0]= 0;
ssl_cipher[0]= 0; ssl_key[0]= 0;
+ ssl_crl[0]= 0; ssl_crlpath[0]= 0;
- my_init_dynamic_array(&ignore_server_ids, sizeof(::server_id), 16, 16);
+ /*
+ Store connection name and lower case connection name
+ It's safe to ignore any OMM errors as this is checked by error()
+ */
+ connection_name.length= cmp_connection_name.length=
+ connection_name_arg->length;
+ if ((connection_name.str= (char*) my_malloc(connection_name_arg->length*2+2,
+ MYF(MY_WME))))
+ {
+ cmp_connection_name.str= (connection_name.str +
+ connection_name_arg->length+1);
+ strmake(connection_name.str, connection_name_arg->str,
+ connection_name.length);
+ memcpy(cmp_connection_name.str, connection_name_arg->str,
+ connection_name.length+1);
+ my_casedn_str(system_charset_info, cmp_connection_name.str);
+ }
+ /* When MySQL restarted, all Rpl_filter settings which aren't in the my.cnf
+ * will lose. So if you want a setting will not lose after restarting, you
+ * should add them into my.cnf
+ * */
+ rpl_filter= get_or_create_rpl_filter(connection_name.str,
+ connection_name.length);
+ copy_filter_setting(rpl_filter, global_rpl_filter);
+
+ my_init_dynamic_array(&ignore_server_ids,
+ sizeof(global_system_variables.server_id), 16, 16,
+ MYF(0));
bzero((char*) &file, sizeof(file));
mysql_mutex_init(key_master_info_run_lock, &run_lock, MY_MUTEX_INIT_FAST);
mysql_mutex_init(key_master_info_data_lock, &data_lock, MY_MUTEX_INIT_FAST);
@@ -55,6 +89,16 @@ Master_info::Master_info(bool is_slave_recovery)
Master_info::~Master_info()
{
+#ifdef WITH_WSREP
+ /*
+ Do not free "wsrep" rpl_filter. It will eventually be freed by
+ free_all_rpl_filters() when server terminates.
+ */
+ if (strncmp(connection_name.str, STRING_WITH_LEN("wsrep")))
+#endif
+ rpl_filters.delete_element(connection_name.str, connection_name.length,
+ (void (*)(const char*, uchar*)) free_rpl_filter);
+ my_free(connection_name.str);
delete_dynamic(&ignore_server_ids);
mysql_mutex_destroy(&run_lock);
mysql_mutex_destroy(&data_lock);
@@ -113,12 +157,34 @@ void Master_info::clear_in_memory_info(bool all)
}
}
+
+const char *
+Master_info::using_gtid_astext(enum enum_using_gtid arg)
+{
+ switch (arg)
+ {
+ case USE_GTID_NO:
+ return "No";
+ case USE_GTID_SLAVE_POS:
+ return "Slave_Pos";
+ default:
+ DBUG_ASSERT(arg == USE_GTID_CURRENT_POS);
+ return "Current_Pos";
+ }
+}
+
+
void init_master_log_pos(Master_info* mi)
{
DBUG_ENTER("init_master_log_pos");
mi->master_log_name[0] = 0;
mi->master_log_pos = BIN_LOG_HEADER_SIZE; // skip magic number
+ mi->using_gtid= Master_info::USE_GTID_NO;
+ mi->gtid_current_pos.reset();
+ mi->events_queued_since_last_gtid= 0;
+ mi->gtid_reconnect_event_skip_count= 0;
+ mi->gtid_event_seen= false;
/* Intentionally init ssl_verify_server_cert to 0, no option available */
mi->ssl_verify_server_cert= 0;
@@ -128,7 +194,7 @@ void init_master_log_pos(Master_info* mi)
if CHANGE MASTER did not specify it. (no data loss in conversion
as hb period has a max)
*/
- mi->heartbeat_period= (float) min(SLAVE_MAX_HEARTBEAT_PERIOD,
+ mi->heartbeat_period= (float) MY_MIN(SLAVE_MAX_HEARTBEAT_PERIOD,
(slave_net_timeout/2.0));
DBUG_ASSERT(mi->heartbeat_period > (float) 0.001
|| mi->heartbeat_period == 0);
@@ -142,14 +208,35 @@ enum {
/* 5.1.16 added value of master_ssl_verify_server_cert */
LINE_FOR_MASTER_SSL_VERIFY_SERVER_CERT= 15,
- /* 6.0 added value of master_heartbeat_period */
+
+ /* 5.5 added value of master_heartbeat_period */
LINE_FOR_MASTER_HEARTBEAT_PERIOD= 16,
+
/* MySQL Cluster 6.3 added master_bind */
LINE_FOR_MASTER_BIND = 17,
+
/* 6.0 added value of master_ignore_server_id */
LINE_FOR_REPLICATE_IGNORE_SERVER_IDS= 18,
- /* Number of lines currently used when saving master info file */
- LINES_IN_MASTER_INFO= LINE_FOR_REPLICATE_IGNORE_SERVER_IDS
+
+ /* 6.0 added value of master_uuid */
+ LINE_FOR_MASTER_UUID= 19,
+
+ /* line for master_retry_count */
+ LINE_FOR_MASTER_RETRY_COUNT= 20,
+
+ /* line for ssl_crl */
+ LINE_FOR_SSL_CRL= 21,
+
+ /* line for ssl_crl */
+ LINE_FOR_SSL_CRLPATH= 22,
+
+ /* MySQL 5.6 fixed-position lines. */
+ LINE_FOR_FIRST_MYSQL_5_6=23,
+ LINE_FOR_LAST_MYSQL_5_6=23,
+ /* Reserved lines for MySQL future versions. */
+ LINE_FOR_LAST_MYSQL_FUTURE=33,
+ /* Number of (fixed-position) lines used when saving master info file */
+ LINES_IN_MASTER_INFO= LINE_FOR_LAST_MYSQL_FUTURE
};
int init_master_info(Master_info* mi, const char* master_info_fname,
@@ -277,7 +364,7 @@ file '%s')", fname);
int ssl= 0, ssl_verify_server_cert= 0;
float master_heartbeat_period= 0.0;
char *first_non_digit;
- char dummy_buf[HOSTNAME_LENGTH+1];
+ char buf[HOSTNAME_LENGTH+1];
/*
Starting from 4.1.x master.info has new format. Now its
@@ -371,7 +458,7 @@ file '%s')", fname);
(this is just a reservation to avoid future upgrade problems)
*/
if (lines >= LINE_FOR_MASTER_BIND &&
- init_strvar_from_file(dummy_buf, sizeof(dummy_buf), &mi->file, ""))
+ init_strvar_from_file(buf, sizeof(buf), &mi->file, ""))
goto errwithmsg;
/*
Starting from 6.0 list of server_id of ignorable servers might be
@@ -383,6 +470,59 @@ file '%s')", fname);
sql_print_error("Failed to initialize master info ignore_server_ids");
goto errwithmsg;
}
+
+ /* reserved */
+ if (lines >= LINE_FOR_MASTER_UUID &&
+ init_strvar_from_file(buf, sizeof(buf), &mi->file, ""))
+ goto errwithmsg;
+
+ /* Starting from 5.5 the master_retry_count may be in the repository. */
+ if (lines >= LINE_FOR_MASTER_RETRY_COUNT &&
+ init_strvar_from_file(buf, sizeof(buf), &mi->file, ""))
+ goto errwithmsg;
+
+ if (lines >= LINE_FOR_SSL_CRLPATH &&
+ (init_strvar_from_file(mi->ssl_crl, sizeof(mi->ssl_crl),
+ &mi->file, "") ||
+ init_strvar_from_file(mi->ssl_crlpath, sizeof(mi->ssl_crlpath),
+ &mi->file, "")))
+ goto errwithmsg;
+
+ /*
+ Starting with MariaDB 10.0, we use a key=value syntax, which is nicer
+ in several ways. But we leave a bunch of empty lines to accomodate
+ any future old-style additions in MySQL (this will make it easier for
+ users moving from MariaDB to MySQL, to not have MySQL try to
+ interpret a MariaDB key=value line.)
+ */
+ if (lines >= LINE_FOR_LAST_MYSQL_FUTURE)
+ {
+ uint i;
+ /* Skip lines used by / reserved for MySQL >= 5.6. */
+ for (i= LINE_FOR_FIRST_MYSQL_5_6; i <= LINE_FOR_LAST_MYSQL_FUTURE; ++i)
+ {
+ if (init_strvar_from_file(buf, sizeof(buf), &mi->file, ""))
+ goto errwithmsg;
+ }
+
+ /*
+ Parse any extra key=value lines.
+ Ignore unknown lines, to facilitate downgrades.
+ */
+ while (!init_strvar_from_file(buf, sizeof(buf), &mi->file, 0))
+ {
+ if (0 == strncmp(buf, STRING_WITH_LEN("using_gtid=")))
+ {
+ int val= atoi(buf + sizeof("using_gtid"));
+ if (val == Master_info::USE_GTID_CURRENT_POS)
+ mi->using_gtid= Master_info::USE_GTID_CURRENT_POS;
+ else if (val == Master_info::USE_GTID_SLAVE_POS)
+ mi->using_gtid= Master_info::USE_GTID_SLAVE_POS;
+ else
+ mi->using_gtid= Master_info::USE_GTID_NO;
+ }
+ }
+ }
}
#ifndef HAVE_OPENSSL
@@ -407,7 +547,7 @@ file '%s')", fname);
mi->master_log_name,
(ulong) mi->master_log_pos));
- mi->rli.mi = mi;
+ mi->rli.mi= mi;
if (init_relay_log_info(&mi->rli, slave_info_fname))
goto err;
@@ -415,7 +555,7 @@ file '%s')", fname);
mi->rli.is_relay_log_recovery= FALSE;
// now change cache READ -> WRITE - must do this before flush_master_info
reinit_io_cache(&mi->file, WRITE_CACHE, 0L, 0, 1);
- if ((error=test(flush_master_info(mi, TRUE, TRUE))))
+ if ((error= MY_TEST(flush_master_info(mi, TRUE, TRUE))))
sql_print_error("Failed to flush master info file");
mysql_mutex_unlock(&mi->data_lock);
DBUG_RETURN(error);
@@ -488,7 +628,7 @@ int flush_master_info(Master_info* mi,
char* ignore_server_ids_buf;
{
ignore_server_ids_buf=
- (char *) my_malloc((sizeof(::server_id) * 3 + 1) *
+ (char *) my_malloc((sizeof(global_system_variables.server_id) * 3 + 1) *
(1 + mi->ignore_server_ids.elements), MYF(MY_WME));
if (!ignore_server_ids_buf)
DBUG_RETURN(1);
@@ -522,14 +662,18 @@ int flush_master_info(Master_info* mi,
sprintf(heartbeat_buf, "%.3f", mi->heartbeat_period);
my_b_seek(file, 0L);
my_b_printf(file,
- "%u\n%s\n%s\n%s\n%s\n%s\n%d\n%d\n%d\n%s\n%s\n%s\n%s\n%s\n%d\n%s\n%s\n%s\n",
+ "%u\n%s\n%s\n%s\n%s\n%s\n%d\n%d\n%d\n%s\n%s\n%s\n%s\n%s\n%d\n%s\n%s\n%s\n%s\n%d\n%s\n%s\n"
+ "\n\n\n\n\n\n\n\n\n\n\n"
+ "using_gtid=%d\n",
LINES_IN_MASTER_INFO,
mi->master_log_name, llstr(mi->master_log_pos, lbuf),
mi->host, mi->user,
mi->password, mi->port, mi->connect_retry,
(int)(mi->ssl), mi->ssl_ca, mi->ssl_capath, mi->ssl_cert,
mi->ssl_cipher, mi->ssl_key, mi->ssl_verify_server_cert,
- heartbeat_buf, "", ignore_server_ids_buf);
+ heartbeat_buf, "", ignore_server_ids_buf,
+ "", 0,
+ mi->ssl_crl, mi->ssl_crlpath, mi->using_gtid);
my_free(ignore_server_ids_buf);
err= flush_io_cache(file);
if (sync_masterinfo_period && !err &&
@@ -560,5 +704,696 @@ void end_master_info(Master_info* mi)
DBUG_VOID_RETURN;
}
+/* Multi-Master By P.Linux */
+uchar *get_key_master_info(Master_info *mi, size_t *length,
+ my_bool not_used __attribute__((unused)))
+{
+ /* Return lower case name */
+ *length= mi->cmp_connection_name.length;
+ return (uchar*) mi->cmp_connection_name.str;
+}
+
+void free_key_master_info(Master_info *mi)
+{
+ DBUG_ENTER("free_key_master_info");
+ terminate_slave_threads(mi,SLAVE_FORCE_ALL);
+ end_master_info(mi);
+ delete mi;
+ DBUG_VOID_RETURN;
+}
+
+/**
+ Check if connection name for master_info is valid.
+
+ It's valid if it's a valid system name of length less than
+ MAX_CONNECTION_NAME.
+
+ @return
+ 0 ok
+ 1 error
+*/
+
+bool check_master_connection_name(LEX_STRING *name)
+{
+ if (name->length >= MAX_CONNECTION_NAME)
+ return 1;
+ return 0;
+}
+
+
+/**
+ Create a log file with a given suffix.
+
+ @param
+ res_file_name Store result here
+ length Length of res_file_name buffer
+ info_file Original file name (prefix)
+ append 1 if we should add suffix last (not before ext)
+ suffix Suffix
+
+ @note
+ The suffix is added before the extension of the file name prefixed with '-'.
+ The suffix is also converted to lower case and we transform
+ all not safe character, as we do with MySQL table names.
+
+ If suffix is an empty string, then we don't add any suffix.
+ This is to allow one to use this function also to generate old
+ file names without a prefix.
+*/
+
+void create_logfile_name_with_suffix(char *res_file_name, size_t length,
+ const char *info_file, bool append,
+ LEX_STRING *suffix)
+{
+ char buff[MAX_CONNECTION_NAME+1],
+ res[MAX_CONNECTION_NAME * MAX_FILENAME_MBWIDTH+1], *p;
+
+ p= strmake(res_file_name, info_file, length);
+ /* If not empty suffix and there is place left for some part of the suffix */
+ if (suffix->length != 0 && p <= res_file_name + length -1)
+ {
+ const char *info_file_end= info_file + (p - res_file_name);
+ const char *ext= append ? info_file_end : fn_ext2(info_file);
+ size_t res_length, ext_pos, from_length;
+ uint errors;
+
+ /* Create null terminated string */
+ from_length= strmake(buff, suffix->str, suffix->length) - buff;
+ /* Convert to characters usable in a file name */
+ res_length= strconvert(system_charset_info, buff, from_length,
+ &my_charset_filename, res, sizeof(res), &errors);
+
+ ext_pos= (size_t) (ext - info_file);
+ length-= (suffix->length - ext_pos); /* Leave place for extension */
+ p= res_file_name + ext_pos;
+ *p++= '-'; /* Add separator */
+ p= strmake(p, res, MY_MIN((size_t) (length - (p - res_file_name)),
+ res_length));
+ /* Add back extension. We have checked above that there is space for it */
+ strmov(p, ext);
+ }
+}
+
+void copy_filter_setting(Rpl_filter* dst_filter, Rpl_filter* src_filter)
+{
+ char buf[256];
+ String tmp(buf, sizeof(buf), &my_charset_bin);
+
+ dst_filter->get_do_db(&tmp);
+ if (tmp.is_empty())
+ {
+ src_filter->get_do_db(&tmp);
+ if (!tmp.is_empty())
+ dst_filter->set_do_db(tmp.ptr());
+ }
+
+ dst_filter->get_do_table(&tmp);
+ if (tmp.is_empty())
+ {
+ src_filter->get_do_table(&tmp);
+ if (!tmp.is_empty())
+ dst_filter->set_do_table(tmp.ptr());
+ }
+
+ dst_filter->get_ignore_db(&tmp);
+ if (tmp.is_empty())
+ {
+ src_filter->get_ignore_db(&tmp);
+ if (!tmp.is_empty())
+ dst_filter->set_ignore_db(tmp.ptr());
+ }
+
+ dst_filter->get_ignore_table(&tmp);
+ if (tmp.is_empty())
+ {
+ src_filter->get_ignore_table(&tmp);
+ if (!tmp.is_empty())
+ dst_filter->set_ignore_table(tmp.ptr());
+ }
+
+ dst_filter->get_wild_do_table(&tmp);
+ if (tmp.is_empty())
+ {
+ src_filter->get_wild_do_table(&tmp);
+ if (!tmp.is_empty())
+ dst_filter->set_wild_do_table(tmp.ptr());
+ }
+
+ dst_filter->get_wild_ignore_table(&tmp);
+ if (tmp.is_empty())
+ {
+ src_filter->get_wild_ignore_table(&tmp);
+ if (!tmp.is_empty())
+ dst_filter->set_wild_ignore_table(tmp.ptr());
+ }
+
+ if (dst_filter->rewrite_db_is_empty())
+ {
+ if (!src_filter->rewrite_db_is_empty())
+ dst_filter->copy_rewrite_db(src_filter);
+ }
+}
+
+Master_info_index::Master_info_index()
+{
+ size_t filename_length, dir_length;
+ /*
+ Create the Master_info index file by prepending 'multi-' before
+ the master_info_file file name.
+ */
+ fn_format(index_file_name, master_info_file, mysql_data_home,
+ "", MY_UNPACK_FILENAME);
+ filename_length= strlen(index_file_name) + 1; /* Count 0 byte */
+ dir_length= dirname_length(index_file_name);
+ bmove_upp((uchar*) index_file_name + filename_length + 6,
+ (uchar*) index_file_name + filename_length,
+ filename_length - dir_length);
+ memcpy(index_file_name + dir_length, "multi-", 6);
+
+ bzero((char*) &index_file, sizeof(index_file));
+ index_file.file= -1;
+}
+
+Master_info_index::~Master_info_index()
+{
+ /* This will close connection for all objects in the cache */
+ my_hash_free(&master_info_hash);
+ end_io_cache(&index_file);
+ if (index_file.file >= 0)
+ my_close(index_file.file, MYF(MY_WME));
+}
+
+
+/* Load All Master_info from master.info.index File
+ * RETURN:
+ * 0 - All Success
+ * 1 - All Fail
+ * 2 - Some Success, Some Fail
+ */
+
+bool Master_info_index::init_all_master_info()
+{
+ int thread_mask;
+ int err_num= 0, succ_num= 0; // The number of success read Master_info
+ char sign[MAX_CONNECTION_NAME+1];
+ File index_file_nr;
+ DBUG_ENTER("init_all_master_info");
+
+ mysql_mutex_assert_owner(&LOCK_active_mi);
+ DBUG_ASSERT(master_info_index);
+
+ if ((index_file_nr= my_open(index_file_name,
+ O_RDWR | O_CREAT | O_BINARY ,
+ MYF(MY_WME | ME_NOREFRESH))) < 0 ||
+ my_sync(index_file_nr, MYF(MY_WME)) ||
+ init_io_cache(&index_file, index_file_nr,
+ IO_SIZE, READ_CACHE,
+ my_seek(index_file_nr,0L,MY_SEEK_END,MYF(0)),
+ 0, MYF(MY_WME | MY_WAIT_IF_FULL)))
+ {
+ if (index_file_nr >= 0)
+ my_close(index_file_nr,MYF(0));
+
+ sql_print_error("Creation of Master_info index file '%s' failed",
+ index_file_name);
+ DBUG_RETURN(1);
+ }
+
+ /* Initialize Master_info Hash Table */
+ if (my_hash_init(&master_info_hash, system_charset_info,
+ MAX_REPLICATION_THREAD, 0, 0,
+ (my_hash_get_key) get_key_master_info,
+ (my_hash_free_key)free_key_master_info, HASH_UNIQUE))
+ {
+ sql_print_error("Initializing Master_info hash table failed");
+ DBUG_RETURN(1);
+ }
+
+ reinit_io_cache(&index_file, READ_CACHE, 0L,0,0);
+ while (!init_strvar_from_file(sign, sizeof(sign),
+ &index_file, NULL))
+ {
+ LEX_STRING connection_name;
+ Master_info *mi;
+ char buf_master_info_file[FN_REFLEN];
+ char buf_relay_log_info_file[FN_REFLEN];
+
+ connection_name.str= sign;
+ connection_name.length= strlen(sign);
+ if (!(mi= new Master_info(&connection_name, relay_log_recovery)) ||
+ mi->error())
+ {
+ delete mi;
+ DBUG_RETURN(1);
+ }
+
+ lock_slave_threads(mi);
+ init_thread_mask(&thread_mask,mi,0 /*not inverse*/);
+
+ create_logfile_name_with_suffix(buf_master_info_file,
+ sizeof(buf_master_info_file),
+ master_info_file, 0,
+ &mi->cmp_connection_name);
+ create_logfile_name_with_suffix(buf_relay_log_info_file,
+ sizeof(buf_relay_log_info_file),
+ relay_log_info_file, 0,
+ &mi->cmp_connection_name);
+ if (global_system_variables.log_warnings > 1)
+ sql_print_information("Reading Master_info: '%s' Relay_info:'%s'",
+ buf_master_info_file, buf_relay_log_info_file);
+
+ if (init_master_info(mi, buf_master_info_file, buf_relay_log_info_file,
+ 0, thread_mask))
+ {
+ err_num++;
+ sql_print_error("Initialized Master_info from '%s' failed",
+ buf_master_info_file);
+ if (!master_info_index->get_master_info(&connection_name,
+ Sql_condition::WARN_LEVEL_NOTE))
+ {
+ /* Master_info is not in HASH; Add it */
+ if (master_info_index->add_master_info(mi, FALSE))
+ DBUG_RETURN(1);
+ succ_num++;
+ unlock_slave_threads(mi);
+ }
+ else
+ {
+ /* Master_info already in HASH */
+ sql_print_error(ER(ER_CONNECTION_ALREADY_EXISTS),
+ (int) connection_name.length, connection_name.str);
+ unlock_slave_threads(mi);
+ delete mi;
+ }
+ continue;
+ }
+ else
+ {
+ /* Initialization of Master_info succeded. Add it to HASH */
+ if (global_system_variables.log_warnings > 1)
+ sql_print_information("Initialized Master_info from '%s'",
+ buf_master_info_file);
+ if (master_info_index->get_master_info(&connection_name,
+ Sql_condition::WARN_LEVEL_NOTE))
+ {
+ /* Master_info was already registered */
+ sql_print_error(ER(ER_CONNECTION_ALREADY_EXISTS),
+ (int) connection_name.length, connection_name.str);
+ unlock_slave_threads(mi);
+ delete mi;
+ continue;
+ }
+
+ /* Master_info was not registered; add it */
+ if (master_info_index->add_master_info(mi, FALSE))
+ DBUG_RETURN(1);
+ succ_num++;
+ unlock_slave_threads(mi);
+
+ if (!opt_skip_slave_start)
+ {
+ if (start_slave_threads(1 /* need mutex */,
+ 0 /* no wait for start*/,
+ mi,
+ buf_master_info_file,
+ buf_relay_log_info_file,
+ SLAVE_IO | SLAVE_SQL))
+ {
+ sql_print_error("Failed to create slave threads for connection '%.*s'",
+ (int) connection_name.length,
+ connection_name.str);
+ continue;
+ }
+ if (global_system_variables.log_warnings)
+ sql_print_information("Started replication for '%.*s'",
+ (int) connection_name.length,
+ connection_name.str);
+ }
+ }
+ }
+
+ if (!err_num) // No Error on read Master_info
+ {
+ if (global_system_variables.log_warnings > 1)
+ sql_print_information("Reading of all Master_info entries succeded");
+ DBUG_RETURN(0);
+ }
+ else if (succ_num) // Have some Error and some Success
+ {
+ sql_print_warning("Reading of some Master_info entries failed");
+ DBUG_RETURN(2);
+ }
+ else // All failed
+ {
+ sql_print_error("Reading of all Master_info entries failed!");
+ DBUG_RETURN(1);
+ }
+}
+
+
+/* Write new master.info to master.info.index File */
+bool Master_info_index::write_master_name_to_index_file(LEX_STRING *name,
+ bool do_sync)
+{
+ DBUG_ASSERT(my_b_inited(&index_file) != 0);
+ DBUG_ENTER("write_master_name_to_index_file");
+
+ /* Don't write default slave to master_info.index */
+ if (name->length == 0)
+ DBUG_RETURN(0);
+
+ reinit_io_cache(&index_file, WRITE_CACHE,
+ my_b_filelength(&index_file), 0, 0);
+
+ if (my_b_write(&index_file, (uchar*) name->str, name->length) ||
+ my_b_write(&index_file, (uchar*) "\n", 1) ||
+ flush_io_cache(&index_file) ||
+ (do_sync && my_sync(index_file.file, MYF(MY_WME))))
+ {
+ sql_print_error("Write of new Master_info for '%.*s' to index file failed",
+ (int) name->length, name->str);
+ DBUG_RETURN(1);
+ }
+
+ DBUG_RETURN(0);
+}
+
+
+/**
+ Get Master_info for a connection
+
+ @param
+ connection_name Connection name
+ warning WARN_LEVEL_NOTE -> Don't print anything
+ WARN_LEVEL_WARN -> Issue warning if not exists
+ WARN_LEVEL_ERROR-> Issue error if not exists
+*/
+
+Master_info *
+Master_info_index::get_master_info(LEX_STRING *connection_name,
+ Sql_condition::enum_warning_level warning)
+{
+ Master_info *mi;
+ char buff[MAX_CONNECTION_NAME+1], *res;
+ uint buff_length;
+ DBUG_ENTER("get_master_info");
+ DBUG_PRINT("enter",
+ ("connection_name: '%.*s'", (int) connection_name->length,
+ connection_name->str));
+
+ mysql_mutex_assert_owner(&LOCK_active_mi);
+ if (!this) // master_info_index is set to NULL on server shutdown
+ DBUG_RETURN(NULL);
+
+ /* Make name lower case for comparison */
+ res= strmake(buff, connection_name->str, connection_name->length);
+ my_casedn_str(system_charset_info, buff);
+ buff_length= (size_t) (res-buff);
+
+ mi= (Master_info*) my_hash_search(&master_info_hash,
+ (uchar*) buff, buff_length);
+ if (!mi && warning != Sql_condition::WARN_LEVEL_NOTE)
+ {
+ my_error(WARN_NO_MASTER_INFO,
+ MYF(warning == Sql_condition::WARN_LEVEL_WARN ? ME_JUST_WARNING :
+ 0),
+ (int) connection_name->length,
+ connection_name->str);
+ }
+ DBUG_RETURN(mi);
+}
+
+
+/* Check Master_host & Master_port is duplicated or not */
+bool Master_info_index::check_duplicate_master_info(LEX_STRING *name_arg,
+ const char *host,
+ uint port)
+{
+ Master_info *mi;
+ DBUG_ENTER("check_duplicate_master_info");
+
+ mysql_mutex_assert_owner(&LOCK_active_mi);
+ DBUG_ASSERT(master_info_index);
+
+ /* Get full host and port name */
+ if ((mi= master_info_index->get_master_info(name_arg,
+ Sql_condition::WARN_LEVEL_NOTE)))
+ {
+ if (!host)
+ host= mi->host;
+ if (!port)
+ port= mi->port;
+ }
+ if (!host || !port)
+ DBUG_RETURN(FALSE); // Not comparable yet
+
+ for (uint i= 0; i < master_info_hash.records; ++i)
+ {
+ Master_info *tmp_mi;
+ tmp_mi= (Master_info *) my_hash_element(&master_info_hash, i);
+ if (tmp_mi == mi)
+ continue; // Current connection
+ if (!strcasecmp(host, tmp_mi->host) && port == tmp_mi->port)
+ {
+ my_error(ER_CONNECTION_ALREADY_EXISTS, MYF(0),
+ (int) name_arg->length,
+ name_arg->str,
+ (int) tmp_mi->connection_name.length,
+ tmp_mi->connection_name.str);
+ DBUG_RETURN(TRUE);
+ }
+ }
+ DBUG_RETURN(FALSE);
+}
+
+
+/* Add a Master_info class to Hash Table */
+bool Master_info_index::add_master_info(Master_info *mi, bool write_to_file)
+{
+ if (!my_hash_insert(&master_info_hash, (uchar*) mi))
+ {
+ if (global_system_variables.log_warnings > 1)
+ sql_print_information("Added new Master_info '%.*s' to hash table",
+ (int) mi->connection_name.length,
+ mi->connection_name.str);
+ if (write_to_file)
+ return write_master_name_to_index_file(&mi->connection_name, 1);
+ return FALSE;
+ }
+
+ /* Impossible error (EOM) ? */
+ sql_print_error("Adding new entry '%.*s' to master_info failed",
+ (int) mi->connection_name.length,
+ mi->connection_name.str);
+ return TRUE;
+}
+
+
+/**
+ Remove a Master_info class From Hash Table
+
+ TODO: Change this to use my_rename() to make the file name creation
+ atomic
+*/
+
+bool Master_info_index::remove_master_info(LEX_STRING *name)
+{
+ Master_info* mi;
+ DBUG_ENTER("remove_master_info");
+
+ if ((mi= get_master_info(name, Sql_condition::WARN_LEVEL_WARN)))
+ {
+ // Delete Master_info and rewrite others to file
+ if (!my_hash_delete(&master_info_hash, (uchar*) mi))
+ {
+ File index_file_nr;
+
+ // Close IO_CACHE and FILE handler fisrt
+ end_io_cache(&index_file);
+ my_close(index_file.file, MYF(MY_WME));
+
+ // Reopen File and truncate it
+ if ((index_file_nr= my_open(index_file_name,
+ O_RDWR | O_CREAT | O_TRUNC | O_BINARY ,
+ MYF(MY_WME))) < 0 ||
+ init_io_cache(&index_file, index_file_nr,
+ IO_SIZE, WRITE_CACHE,
+ my_seek(index_file_nr,0L,MY_SEEK_END,MYF(0)),
+ 0, MYF(MY_WME | MY_WAIT_IF_FULL)))
+ {
+ int error= my_errno;
+ if (index_file_nr >= 0)
+ my_close(index_file_nr,MYF(0));
+
+ sql_print_error("Create of Master Info Index file '%s' failed with "
+ "error: %M",
+ index_file_name, error);
+ DBUG_RETURN(TRUE);
+ }
+
+ // Rewrite Master_info.index
+ for (uint i= 0; i< master_info_hash.records; ++i)
+ {
+ Master_info *tmp_mi;
+ tmp_mi= (Master_info *) my_hash_element(&master_info_hash, i);
+ write_master_name_to_index_file(&tmp_mi->connection_name, 0);
+ }
+ my_sync(index_file_nr, MYF(MY_WME));
+ }
+ }
+ DBUG_RETURN(FALSE);
+}
+
+
+/**
+ Master_info_index::give_error_if_slave_running()
+
+ @return
+ TRUE If some slave is running. An error is printed
+ FALSE No slave is running
+*/
+
+bool Master_info_index::give_error_if_slave_running()
+{
+ DBUG_ENTER("give_error_if_slave_running");
+ mysql_mutex_assert_owner(&LOCK_active_mi);
+ if (!this) // master_info_index is set to NULL on server shutdown
+ DBUG_RETURN(TRUE);
+
+ for (uint i= 0; i< master_info_hash.records; ++i)
+ {
+ Master_info *mi;
+ mi= (Master_info *) my_hash_element(&master_info_hash, i);
+ if (mi->rli.slave_running != MYSQL_SLAVE_NOT_RUN)
+ {
+ my_error(ER_SLAVE_MUST_STOP, MYF(0), (int) mi->connection_name.length,
+ mi->connection_name.str);
+ DBUG_RETURN(TRUE);
+ }
+ }
+ DBUG_RETURN(FALSE);
+}
+
+
+/**
+ Master_info_index::any_slave_sql_running()
+
+ The LOCK_active_mi must be held while calling this function.
+
+ @return
+ 0 No Slave SQL thread is running
+ # Number of slave SQL thread running
+*/
+
+uint Master_info_index::any_slave_sql_running()
+{
+ uint count= 0;
+ DBUG_ENTER("any_slave_sql_running");
+ if (!this) // master_info_index is set to NULL on server shutdown
+ DBUG_RETURN(count);
+
+ for (uint i= 0; i< master_info_hash.records; ++i)
+ {
+ Master_info *mi= (Master_info *)my_hash_element(&master_info_hash, i);
+ if (mi->rli.slave_running != MYSQL_SLAVE_NOT_RUN)
+ count++;
+ }
+ DBUG_RETURN(count);
+}
+
+
+/**
+ Master_info_index::start_all_slaves()
+
+ Start all slaves that was not running.
+
+ @return
+ TRUE Error
+ FALSE Everything ok.
+*/
+
+bool Master_info_index::start_all_slaves(THD *thd)
+{
+ bool result= FALSE;
+ DBUG_ENTER("warn_if_slave_running");
+ mysql_mutex_assert_owner(&LOCK_active_mi);
+
+ for (uint i= 0; i< master_info_hash.records; ++i)
+ {
+ int error;
+ Master_info *mi;
+ mi= (Master_info *) my_hash_element(&master_info_hash, i);
+
+ /*
+ Try to start all slaves that are configured (host is defined)
+ and are not already running
+ */
+ if ((mi->slave_running == MYSQL_SLAVE_NOT_RUN ||
+ !mi->rli.slave_running) && *mi->host)
+ {
+ if ((error= start_slave(thd, mi, 1)))
+ {
+ my_error(ER_CANT_START_STOP_SLAVE, MYF(0),
+ "START",
+ (int) mi->connection_name.length,
+ mi->connection_name.str);
+ result= 1;
+ if (error < 0) // fatal error
+ break;
+ }
+ else
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
+ ER_SLAVE_STARTED, ER(ER_SLAVE_STARTED),
+ (int) mi->connection_name.length,
+ mi->connection_name.str);
+ }
+ }
+ DBUG_RETURN(result);
+}
+
+
+/**
+ Master_info_index::stop_all_slaves()
+
+ Start all slaves that was not running.
+
+ @return
+ TRUE Error
+ FALSE Everything ok.
+*/
+
+bool Master_info_index::stop_all_slaves(THD *thd)
+{
+ bool result= FALSE;
+ DBUG_ENTER("warn_if_slave_running");
+ mysql_mutex_assert_owner(&LOCK_active_mi);
+
+ for (uint i= 0; i< master_info_hash.records; ++i)
+ {
+ int error;
+ Master_info *mi;
+ mi= (Master_info *) my_hash_element(&master_info_hash, i);
+ if ((mi->slave_running != MYSQL_SLAVE_NOT_RUN ||
+ mi->rli.slave_running))
+ {
+ if ((error= stop_slave(thd, mi, 1)))
+ {
+ my_error(ER_CANT_START_STOP_SLAVE, MYF(0),
+ "STOP",
+ (int) mi->connection_name.length,
+ mi->connection_name.str);
+ result= 1;
+ if (error < 0) // Fatal error
+ break;
+ }
+ else
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
+ ER_SLAVE_STOPPED, ER(ER_SLAVE_STOPPED),
+ (int) mi->connection_name.length,
+ mi->connection_name.str);
+ }
+ }
+ DBUG_RETURN(result);
+}
#endif /* HAVE_REPLICATION */
diff --git a/sql/rpl_mi.h b/sql/rpl_mi.h
index f9c8c8ea5b2..a27672e4c90 100644
--- a/sql/rpl_mi.h
+++ b/sql/rpl_mi.h
@@ -21,6 +21,8 @@
#include "rpl_rli.h"
#include "rpl_reporting.h"
#include "my_sys.h"
+#include "rpl_filter.h"
+#include "keycaches.h"
typedef struct st_mysql MYSQL;
@@ -59,19 +61,32 @@ typedef struct st_mysql MYSQL;
class Master_info : public Slave_reporting_capability
{
public:
- Master_info(bool is_slave_recovery);
+ enum enum_using_gtid {
+ USE_GTID_NO= 0, USE_GTID_CURRENT_POS= 1, USE_GTID_SLAVE_POS= 2
+ };
+
+ Master_info(LEX_STRING *connection_name, bool is_slave_recovery);
~Master_info();
bool shall_ignore_server_id(ulong s_id);
void clear_in_memory_info(bool all);
+ bool error()
+ {
+ /* If malloc() in initialization failed */
+ return connection_name.str == 0;
+ }
+ static const char *using_gtid_astext(enum enum_using_gtid arg);
/* the variables below are needed because we can change masters on the fly */
- char master_log_name[FN_REFLEN];
+ char master_log_name[FN_REFLEN+6]; /* Room for multi-*/
char host[HOSTNAME_LENGTH*SYSTEM_CHARSET_MBMAXLEN+1];
char user[USERNAME_LENGTH+1];
char password[MAX_PASSWORD_LENGTH*SYSTEM_CHARSET_MBMAXLEN+1];
+ LEX_STRING connection_name; /* User supplied connection name */
+ LEX_STRING cmp_connection_name; /* Connection name in lower case */
bool ssl; // enables use of SSL connection if true
char ssl_ca[FN_REFLEN], ssl_capath[FN_REFLEN], ssl_cert[FN_REFLEN];
char ssl_cipher[FN_REFLEN], ssl_key[FN_REFLEN];
+ char ssl_crl[FN_REFLEN], ssl_crlpath[FN_REFLEN];
bool ssl_verify_server_cert;
my_off_t master_log_pos;
@@ -85,6 +100,7 @@ class Master_info : public Slave_reporting_capability
uint32 file_id; /* for 3.23 load data infile */
Relay_log_info rli;
uint port;
+ Rpl_filter* rpl_filter; /* Each replication can set its filter rule*/
/*
to hold checksum alg in use until IO thread has received FD.
Initialized to novalue, then set to the queried from master
@@ -119,6 +135,49 @@ class Master_info : public Slave_reporting_capability
ulonglong received_heartbeats; // counter of received heartbeat events
DYNAMIC_ARRAY ignore_server_ids;
ulong master_id;
+ /*
+ At reconnect and until the first rotate event is seen, prev_master_id is
+ the value of master_id during the previous connection, used to detect
+ silent change of master server during reconnects.
+ */
+ ulong prev_master_id;
+ /*
+ Which kind of GTID position (if any) is used when connecting to master.
+
+ Note that you can not change the numeric values of these, they are used
+ in master.info.
+ */
+ enum enum_using_gtid using_gtid;
+
+ /*
+ This GTID position records how far we have fetched into the relay logs.
+ This is used to continue fetching when the IO thread reconnects to the
+ master.
+
+ (Full slave stop/start does not use it, as it resets the relay logs).
+ */
+ slave_connection_state gtid_current_pos;
+ /*
+ If events_queued_since_last_gtid is non-zero, it is the number of events
+ queued so far in the relaylog of a GTID-prefixed event group.
+ It is zero when no partial event group has been queued at the moment.
+ */
+ uint64 events_queued_since_last_gtid;
+ /*
+ The GTID of the partially-queued event group, when
+ events_queued_since_last_gtid is non-zero.
+ */
+ rpl_gtid last_queued_gtid;
+ /* Whether last_queued_gtid had the FL_STANDALONE flag set. */
+ bool last_queued_gtid_standalone;
+ /*
+ When slave IO thread needs to reconnect, gtid_reconnect_event_skip_count
+ counts number of events to skip from the first GTID-prefixed event group,
+ to avoid duplicating events in the relay log.
+ */
+ uint64 gtid_reconnect_event_skip_count;
+ /* gtid_event_seen is false until we receive first GTID event from master. */
+ bool gtid_event_seen;
};
int init_master_info(Master_info* mi, const char* master_info_fname,
const char* slave_info_fname,
@@ -129,6 +188,61 @@ int flush_master_info(Master_info* mi,
bool flush_relay_log_cache,
bool need_lock_relay_log);
int change_master_server_id_cmp(ulong *id1, ulong *id2);
+void copy_filter_setting(Rpl_filter* dst_filter, Rpl_filter* src_filter);
+
+/*
+ Multi master are handled trough this struct.
+ Changes to this needs to be protected by LOCK_active_mi;
+*/
+
+class Master_info_index
+{
+private:
+ IO_CACHE index_file;
+ char index_file_name[FN_REFLEN];
+
+public:
+ Master_info_index();
+ ~Master_info_index();
+
+ HASH master_info_hash;
+
+ bool init_all_master_info();
+ bool write_master_name_to_index_file(LEX_STRING *connection_name,
+ bool do_sync);
+
+ bool check_duplicate_master_info(LEX_STRING *connection_name,
+ const char *host, uint port);
+ bool add_master_info(Master_info *mi, bool write_to_file);
+ bool remove_master_info(LEX_STRING *connection_name);
+ Master_info *get_master_info(LEX_STRING *connection_name,
+ Sql_condition::enum_warning_level warning);
+ bool give_error_if_slave_running();
+ uint any_slave_sql_running();
+ bool start_all_slaves(THD *thd);
+ bool stop_all_slaves(THD *thd);
+};
+
+
+/*
+ The class rpl_io_thread_info is the THD::system_thread_info for the IO thread.
+*/
+class rpl_io_thread_info
+{
+public:
+};
+
+
+bool check_master_connection_name(LEX_STRING *name);
+void create_logfile_name_with_suffix(char *res_file_name, size_t length,
+ const char *info_file,
+ bool append,
+ LEX_STRING *suffix);
+
+uchar *get_key_master_info(Master_info *mi, size_t *length,
+ my_bool not_used __attribute__((unused)));
+void free_key_master_info(Master_info *mi);
+
#endif /* HAVE_REPLICATION */
#endif /* RPL_MI_H */
diff --git a/sql/rpl_parallel.cc b/sql/rpl_parallel.cc
new file mode 100644
index 00000000000..b2e957a3e6e
--- /dev/null
+++ b/sql/rpl_parallel.cc
@@ -0,0 +1,2626 @@
+#include "my_global.h"
+#include "rpl_parallel.h"
+#include "slave.h"
+#include "rpl_mi.h"
+#include "sql_parse.h"
+#include "debug_sync.h"
+
+/*
+ Code for optional parallel execution of replicated events on the slave.
+*/
+
+
+/*
+ Maximum number of queued events to accumulate in a local free list, before
+ moving them to the global free list. There is additional a limit of how much
+ to accumulate based on opt_slave_parallel_max_queued.
+*/
+#define QEV_BATCH_FREE 200
+
+
+struct rpl_parallel_thread_pool global_rpl_thread_pool;
+
+static void signal_error_to_sql_driver_thread(THD *thd, rpl_group_info *rgi,
+ int err);
+
+static int
+rpt_handle_event(rpl_parallel_thread::queued_event *qev,
+ struct rpl_parallel_thread *rpt)
+{
+ int err;
+ rpl_group_info *rgi= qev->rgi;
+ Relay_log_info *rli= rgi->rli;
+ THD *thd= rgi->thd;
+ Log_event *ev;
+
+ DBUG_ASSERT(qev->typ == rpl_parallel_thread::queued_event::QUEUED_EVENT);
+ ev= qev->ev;
+
+ thd->system_thread_info.rpl_sql_info->rpl_filter = rli->mi->rpl_filter;
+ ev->thd= thd;
+
+ strcpy(rgi->event_relay_log_name_buf, qev->event_relay_log_name);
+ rgi->event_relay_log_name= rgi->event_relay_log_name_buf;
+ rgi->event_relay_log_pos= qev->event_relay_log_pos;
+ rgi->future_event_relay_log_pos= qev->future_event_relay_log_pos;
+ strcpy(rgi->future_event_master_log_name, qev->future_event_master_log_name);
+ if (!(ev->is_artificial_event() || ev->is_relay_log_event() ||
+ (ev->when == 0)))
+ rgi->last_master_timestamp= ev->when + (time_t)ev->exec_time;
+ mysql_mutex_lock(&rli->data_lock);
+ /* Mutex will be released in apply_event_and_update_pos(). */
+ err= apply_event_and_update_pos(ev, thd, rgi, rpt);
+
+ thread_safe_increment64(&rli->executed_entries,
+ &slave_executed_entries_lock);
+ /* ToDo: error handling. */
+ return err;
+}
+
+
+static void
+handle_queued_pos_update(THD *thd, rpl_parallel_thread::queued_event *qev)
+{
+ int cmp;
+ Relay_log_info *rli;
+ rpl_parallel_entry *e;
+
+ /*
+ Events that are not part of an event group, such as Format Description,
+ Stop, GTID List and such, are executed directly in the driver SQL thread,
+ to keep the relay log state up-to-date. But the associated position update
+ is done here, in sync with other normal events as they are queued to
+ worker threads.
+ */
+ if ((thd->variables.option_bits & OPTION_BEGIN) &&
+ opt_using_transactions)
+ return;
+
+ /* Do not update position if an earlier event group caused an error abort. */
+ DBUG_ASSERT(qev->typ == rpl_parallel_thread::queued_event::QUEUED_POS_UPDATE);
+ e= qev->entry_for_queued;
+ if (e->stop_on_error_sub_id < (uint64)ULONGLONG_MAX || e->force_abort)
+ return;
+
+ rli= qev->rgi->rli;
+ mysql_mutex_lock(&rli->data_lock);
+ cmp= strcmp(rli->group_relay_log_name, qev->event_relay_log_name);
+ if (cmp < 0)
+ {
+ rli->group_relay_log_pos= qev->future_event_relay_log_pos;
+ strmake_buf(rli->group_relay_log_name, qev->event_relay_log_name);
+ rli->notify_group_relay_log_name_update();
+ } else if (cmp == 0 &&
+ rli->group_relay_log_pos < qev->future_event_relay_log_pos)
+ rli->group_relay_log_pos= qev->future_event_relay_log_pos;
+
+ cmp= strcmp(rli->group_master_log_name, qev->future_event_master_log_name);
+ if (cmp < 0)
+ {
+ strcpy(rli->group_master_log_name, qev->future_event_master_log_name);
+ rli->group_master_log_pos= qev->future_event_master_log_pos;
+ }
+ else if (cmp == 0
+ && rli->group_master_log_pos < qev->future_event_master_log_pos)
+ rli->group_master_log_pos= qev->future_event_master_log_pos;
+ mysql_mutex_unlock(&rli->data_lock);
+ mysql_cond_broadcast(&rli->data_cond);
+}
+
+
+static void
+finish_event_group(rpl_parallel_thread *rpt, uint64 sub_id,
+ rpl_parallel_entry *entry, rpl_group_info *rgi)
+{
+ THD *thd= rpt->thd;
+ wait_for_commit *wfc= &rgi->commit_orderer;
+ int err;
+
+ thd->get_stmt_da()->set_overwrite_status(true);
+ /*
+ Remove any left-over registration to wait for a prior commit to
+ complete. Normally, such wait would already have been removed at
+ this point by wait_for_prior_commit() called from within COMMIT
+ processing. However, in case of MyISAM and no binlog, we might not
+ have any commit processing, and so we need to do the wait here,
+ before waking up any subsequent commits, to preserve correct
+ order of event execution. Also, in the error case we might have
+ skipped waiting and thus need to remove it explicitly.
+
+ It is important in the non-error case to do a wait, not just an
+ unregister. Because we might be last in a group-commit that is
+ replicated in parallel, and the following event will then wait
+ for us to complete and rely on this also ensuring that any other
+ event in the group has completed.
+
+ And in the error case, correct GCO lifetime relies on the fact that once
+ the last event group in the GCO has executed wait_for_prior_commit(),
+ all earlier event groups have also committed; this way no more
+ mark_start_commit() calls can be made and it is safe to de-allocate
+ the GCO.
+ */
+ err= wfc->wait_for_prior_commit(thd);
+ if (unlikely(err) && !rgi->worker_error)
+ signal_error_to_sql_driver_thread(thd, rgi, err);
+ thd->wait_for_commit_ptr= NULL;
+
+ mysql_mutex_lock(&entry->LOCK_parallel_entry);
+ /*
+ We need to mark that this event group started its commit phase, in case we
+ missed it before (otherwise we would deadlock the next event group that is
+ waiting for this). In most cases (normal DML), it will be a no-op.
+ */
+ rgi->mark_start_commit_no_lock();
+
+ if (entry->last_committed_sub_id < sub_id)
+ {
+ /*
+ Record that this event group has finished (eg. transaction is
+ committed, if transactional), so other event groups will no longer
+ attempt to wait for us to commit. Once we have increased
+ entry->last_committed_sub_id, no other threads will execute
+ register_wait_for_prior_commit() against us. Thus, by doing one
+ extra (usually redundant) wakeup_subsequent_commits() we can ensure
+ that no register_wait_for_prior_commit() can ever happen without a
+ subsequent wakeup_subsequent_commits() to wake it up.
+
+ We can race here with the next transactions, but that is fine, as
+ long as we check that we do not decrease last_committed_sub_id. If
+ this commit is done, then any prior commits will also have been
+ done and also no longer need waiting for.
+ */
+ entry->last_committed_sub_id= sub_id;
+ if (entry->need_sub_id_signal)
+ mysql_cond_broadcast(&entry->COND_parallel_entry);
+
+ /* Now free any GCOs in which all transactions have committed. */
+ group_commit_orderer *tmp_gco= rgi->gco;
+ while (tmp_gco &&
+ (!tmp_gco->next_gco || tmp_gco->last_sub_id > sub_id ||
+ tmp_gco->next_gco->wait_count > entry->count_committing_event_groups))
+ {
+ /*
+ We must not free a GCO before the wait_count of the following GCO has
+ been reached and wakeup has been sent. Otherwise we will lose the
+ wakeup and hang (there were several such bugs in the past).
+
+ The intention is that this is ensured already since we only free when
+ the last event group in the GCO has committed
+ (tmp_gco->last_sub_id <= sub_id). However, if we have a bug, we have
+ extra check on next_gco->wait_count to hopefully avoid hanging; we
+ have here an assertion in debug builds that this check does not in
+ fact trigger.
+ */
+ DBUG_ASSERT(!tmp_gco->next_gco || tmp_gco->last_sub_id > sub_id);
+ tmp_gco= tmp_gco->prev_gco;
+ }
+ while (tmp_gco)
+ {
+ group_commit_orderer *prev_gco= tmp_gco->prev_gco;
+ tmp_gco->next_gco->prev_gco= NULL;
+ rpt->loc_free_gco(tmp_gco);
+ tmp_gco= prev_gco;
+ }
+ }
+
+ /*
+ If this event group got error, then any following event groups that have
+ not yet started should just skip their group, preparing for stop of the
+ SQL driver thread.
+ */
+ if (unlikely(rgi->worker_error) &&
+ entry->stop_on_error_sub_id == (uint64)ULONGLONG_MAX)
+ entry->stop_on_error_sub_id= sub_id;
+ mysql_mutex_unlock(&entry->LOCK_parallel_entry);
+
+ thd->clear_error();
+ thd->reset_killed();
+ /*
+ Would do thd->get_stmt_da()->set_overwrite_status(false) here, but
+ reset_diagnostics_area() already does that.
+ */
+ thd->get_stmt_da()->reset_diagnostics_area();
+ wfc->wakeup_subsequent_commits(rgi->worker_error);
+}
+
+
+static void
+signal_error_to_sql_driver_thread(THD *thd, rpl_group_info *rgi, int err)
+{
+ rgi->worker_error= err;
+ /*
+ In case we get an error during commit, inform following transactions that
+ we aborted our commit.
+ */
+ rgi->unmark_start_commit();
+ rgi->cleanup_context(thd, true);
+ rgi->rli->abort_slave= true;
+ rgi->rli->stop_for_until= false;
+ mysql_mutex_lock(rgi->rli->relay_log.get_log_lock());
+ mysql_mutex_unlock(rgi->rli->relay_log.get_log_lock());
+ rgi->rli->relay_log.signal_update();
+}
+
+
+static void
+unlock_or_exit_cond(THD *thd, mysql_mutex_t *lock, bool *did_enter_cond,
+ PSI_stage_info *old_stage)
+{
+ if (*did_enter_cond)
+ {
+ thd->EXIT_COND(old_stage);
+ *did_enter_cond= false;
+ }
+ else
+ mysql_mutex_unlock(lock);
+}
+
+
+static void
+register_wait_for_prior_event_group_commit(rpl_group_info *rgi,
+ rpl_parallel_entry *entry)
+{
+ mysql_mutex_assert_owner(&entry->LOCK_parallel_entry);
+ if (rgi->wait_commit_sub_id > entry->last_committed_sub_id)
+ {
+ /*
+ Register that the commit of this event group must wait for the
+ commit of the previous event group to complete before it may
+ complete itself, so that we preserve commit order.
+ */
+ wait_for_commit *waitee=
+ &rgi->wait_commit_group_info->commit_orderer;
+ rgi->commit_orderer.register_wait_for_prior_commit(waitee);
+ }
+}
+
+
+/*
+ Do not start parallel execution of this event group until all prior groups
+ have reached the commit phase that are not safe to run in parallel with.
+*/
+static bool
+do_gco_wait(rpl_group_info *rgi, group_commit_orderer *gco,
+ bool *did_enter_cond, PSI_stage_info *old_stage)
+{
+ THD *thd= rgi->thd;
+ rpl_parallel_entry *entry= rgi->parallel_entry;
+ uint64 wait_count;
+
+ mysql_mutex_assert_owner(&entry->LOCK_parallel_entry);
+
+ if (!gco->installed)
+ {
+ group_commit_orderer *prev_gco= gco->prev_gco;
+ if (prev_gco)
+ {
+ prev_gco->last_sub_id= gco->prior_sub_id;
+ prev_gco->next_gco= gco;
+ }
+ gco->installed= true;
+ }
+ wait_count= gco->wait_count;
+ if (wait_count > entry->count_committing_event_groups)
+ {
+ DEBUG_SYNC(thd, "rpl_parallel_start_waiting_for_prior");
+ thd->ENTER_COND(&gco->COND_group_commit_orderer,
+ &entry->LOCK_parallel_entry,
+ &stage_waiting_for_prior_transaction_to_start_commit,
+ old_stage);
+ *did_enter_cond= true;
+ do
+ {
+ if (thd->check_killed() && !rgi->worker_error)
+ {
+ DEBUG_SYNC(thd, "rpl_parallel_start_waiting_for_prior_killed");
+ thd->clear_error();
+ thd->get_stmt_da()->reset_diagnostics_area();
+ thd->send_kill_message();
+ slave_output_error_info(rgi, thd);
+ signal_error_to_sql_driver_thread(thd, rgi, 1);
+ /*
+ Even though we were killed, we need to continue waiting for the
+ prior event groups to signal that we can continue. Otherwise we
+ mess up the accounting for ordering. However, now that we have
+ marked the error, events will just be skipped rather than
+ executed, and things will progress quickly towards stop.
+ */
+ }
+ mysql_cond_wait(&gco->COND_group_commit_orderer,
+ &entry->LOCK_parallel_entry);
+ } while (wait_count > entry->count_committing_event_groups);
+ }
+
+ if (entry->force_abort && wait_count > entry->stop_count)
+ {
+ /*
+ We are stopping (STOP SLAVE), and this event group is beyond the point
+ where we can safely stop. So return a flag that will cause us to skip,
+ rather than execute, the following events.
+ */
+ return true;
+ }
+ else
+ return false;
+}
+
+
+static void
+do_ftwrl_wait(rpl_group_info *rgi,
+ bool *did_enter_cond, PSI_stage_info *old_stage)
+{
+ THD *thd= rgi->thd;
+ rpl_parallel_entry *entry= rgi->parallel_entry;
+ uint64 sub_id= rgi->gtid_sub_id;
+ DBUG_ENTER("do_ftwrl_wait");
+
+ mysql_mutex_assert_owner(&entry->LOCK_parallel_entry);
+
+ /*
+ If a FLUSH TABLES WITH READ LOCK (FTWRL) is pending, check if this
+ transaction is later than transactions that have priority to complete
+ before FTWRL. If so, wait here so that FTWRL can proceed and complete
+ first.
+
+ (entry->pause_sub_id is ULONGLONG_MAX if no FTWRL is pending, which makes
+ this test false as required).
+ */
+ if (unlikely(sub_id > entry->pause_sub_id))
+ {
+ thd->ENTER_COND(&entry->COND_parallel_entry, &entry->LOCK_parallel_entry,
+ &stage_waiting_for_ftwrl, old_stage);
+ *did_enter_cond= true;
+ do
+ {
+ if (entry->force_abort || rgi->worker_error)
+ break;
+ if (thd->check_killed())
+ {
+ thd->send_kill_message();
+ slave_output_error_info(rgi, thd);
+ signal_error_to_sql_driver_thread(thd, rgi, 1);
+ break;
+ }
+ mysql_cond_wait(&entry->COND_parallel_entry, &entry->LOCK_parallel_entry);
+ } while (sub_id > entry->pause_sub_id);
+
+ /*
+ We do not call EXIT_COND() here, as this will be done later by our
+ caller (since we set *did_enter_cond to true).
+ */
+ }
+
+ if (sub_id > entry->largest_started_sub_id)
+ entry->largest_started_sub_id= sub_id;
+
+ DBUG_VOID_RETURN;
+}
+
+
+static int
+pool_mark_busy(rpl_parallel_thread_pool *pool, THD *thd)
+{
+ PSI_stage_info old_stage;
+ int res= 0;
+
+ /*
+ Wait here while the queue is busy. This is done to make FLUSH TABLES WITH
+ READ LOCK work correctly, without incuring extra locking penalties in
+ normal operation. FLUSH TABLES WITH READ LOCK needs to lock threads in the
+ thread pool, and for this we need to make sure the pool will not go away
+ during the operation. The LOCK_rpl_thread_pool is not suitable for
+ this. It is taken by release_thread() while holding LOCK_rpl_thread; so it
+ must be released before locking any LOCK_rpl_thread lock, or a deadlock
+ can occur.
+
+ So we protect the infrequent operations of FLUSH TABLES WITH READ LOCK and
+ pool size changes with this condition wait.
+ */
+ mysql_mutex_lock(&pool->LOCK_rpl_thread_pool);
+ if (thd)
+ thd->ENTER_COND(&pool->COND_rpl_thread_pool, &pool->LOCK_rpl_thread_pool,
+ &stage_waiting_for_rpl_thread_pool, &old_stage);
+ while (pool->busy)
+ {
+ if (thd && thd->check_killed())
+ {
+ thd->send_kill_message();
+ res= 1;
+ break;
+ }
+ mysql_cond_wait(&pool->COND_rpl_thread_pool, &pool->LOCK_rpl_thread_pool);
+ }
+ if (!res)
+ pool->busy= true;
+ if (thd)
+ thd->EXIT_COND(&old_stage);
+ else
+ mysql_mutex_unlock(&pool->LOCK_rpl_thread_pool);
+
+ return res;
+}
+
+
+static void
+pool_mark_not_busy(rpl_parallel_thread_pool *pool)
+{
+ mysql_mutex_lock(&pool->LOCK_rpl_thread_pool);
+ DBUG_ASSERT(pool->busy);
+ pool->busy= false;
+ mysql_cond_broadcast(&pool->COND_rpl_thread_pool);
+ mysql_mutex_unlock(&pool->LOCK_rpl_thread_pool);
+}
+
+
+void
+rpl_unpause_after_ftwrl(THD *thd)
+{
+ uint32 i;
+ rpl_parallel_thread_pool *pool= &global_rpl_thread_pool;
+ DBUG_ENTER("rpl_unpause_after_ftwrl");
+
+ DBUG_ASSERT(pool->busy);
+
+ for (i= 0; i < pool->count; ++i)
+ {
+ rpl_parallel_entry *e;
+ rpl_parallel_thread *rpt= pool->threads[i];
+
+ mysql_mutex_lock(&rpt->LOCK_rpl_thread);
+ if (!rpt->current_owner)
+ {
+ mysql_mutex_unlock(&rpt->LOCK_rpl_thread);
+ continue;
+ }
+ e= rpt->current_entry;
+ mysql_mutex_lock(&e->LOCK_parallel_entry);
+ rpt->pause_for_ftwrl = false;
+ mysql_mutex_unlock(&rpt->LOCK_rpl_thread);
+ e->pause_sub_id= (uint64)ULONGLONG_MAX;
+ mysql_cond_broadcast(&e->COND_parallel_entry);
+ mysql_mutex_unlock(&e->LOCK_parallel_entry);
+ }
+
+ pool_mark_not_busy(pool);
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ .
+
+ Note: in case of error return, rpl_unpause_after_ftwrl() must _not_ be called.
+*/
+int
+rpl_pause_for_ftwrl(THD *thd)
+{
+ uint32 i;
+ rpl_parallel_thread_pool *pool= &global_rpl_thread_pool;
+ int err;
+ DBUG_ENTER("rpl_pause_for_ftwrl");
+
+ /*
+ While the count_pending_pause_for_ftwrl counter is non-zero, the pool
+ cannot be shutdown/resized, so threads are guaranteed to not disappear.
+
+ This is required to safely be able to access the individual threads below.
+ (We cannot lock an individual thread while holding LOCK_rpl_thread_pool,
+ as this can deadlock against release_thread()).
+ */
+ if ((err= pool_mark_busy(pool, thd)))
+ DBUG_RETURN(err);
+
+ for (i= 0; i < pool->count; ++i)
+ {
+ PSI_stage_info old_stage;
+ rpl_parallel_entry *e;
+ rpl_parallel_thread *rpt= pool->threads[i];
+
+ mysql_mutex_lock(&rpt->LOCK_rpl_thread);
+ if (!rpt->current_owner)
+ {
+ mysql_mutex_unlock(&rpt->LOCK_rpl_thread);
+ continue;
+ }
+ e= rpt->current_entry;
+ mysql_mutex_lock(&e->LOCK_parallel_entry);
+ /*
+ Setting the rpt->pause_for_ftwrl flag makes sure that the thread will not
+ de-allocate itself until signalled to do so by rpl_unpause_after_ftwrl().
+ */
+ rpt->pause_for_ftwrl = true;
+ mysql_mutex_unlock(&rpt->LOCK_rpl_thread);
+ ++e->need_sub_id_signal;
+ if (e->pause_sub_id == (uint64)ULONGLONG_MAX)
+ e->pause_sub_id= e->largest_started_sub_id;
+ thd->ENTER_COND(&e->COND_parallel_entry, &e->LOCK_parallel_entry,
+ &stage_waiting_for_ftwrl_threads_to_pause, &old_stage);
+ while (e->pause_sub_id < (uint64)ULONGLONG_MAX &&
+ e->last_committed_sub_id < e->pause_sub_id &&
+ !err)
+ {
+ if (thd->check_killed())
+ {
+ thd->send_kill_message();
+ err= 1;
+ break;
+ }
+ mysql_cond_wait(&e->COND_parallel_entry, &e->LOCK_parallel_entry);
+ };
+ --e->need_sub_id_signal;
+ thd->EXIT_COND(&old_stage);
+ if (err)
+ break;
+ }
+
+ if (err)
+ rpl_unpause_after_ftwrl(thd);
+ DBUG_RETURN(err);
+}
+
+
+#ifndef DBUG_OFF
+static int
+dbug_simulate_tmp_error(rpl_group_info *rgi, THD *thd)
+{
+ if (rgi->current_gtid.domain_id == 0 && rgi->current_gtid.seq_no == 100 &&
+ rgi->retry_event_count == 4)
+ {
+ thd->clear_error();
+ thd->get_stmt_da()->reset_diagnostics_area();
+ my_error(ER_LOCK_DEADLOCK, MYF(0));
+ return 1;
+ }
+ return 0;
+}
+#endif
+
+
+/*
+ If we detect a deadlock due to eg. storage engine locks that conflict with
+ the fixed commit order, then the later transaction will be killed
+ asynchroneously to allow the former to complete its commit.
+
+ In this case, we convert the 'killed' error into a deadlock error, and retry
+ the later transaction. */
+static void
+convert_kill_to_deadlock_error(rpl_group_info *rgi)
+{
+ THD *thd= rgi->thd;
+ int err_code;
+
+ if (!thd->get_stmt_da()->is_error())
+ return;
+ err_code= thd->get_stmt_da()->sql_errno();
+ if ((err_code == ER_QUERY_INTERRUPTED || err_code == ER_CONNECTION_KILLED) &&
+ rgi->killed_for_retry)
+ {
+ thd->clear_error();
+ my_error(ER_LOCK_DEADLOCK, MYF(0));
+ rgi->killed_for_retry= false;
+ thd->reset_killed();
+ }
+}
+
+
+/*
+ Check if an event marks the end of an event group. Returns non-zero if so,
+ zero otherwise.
+
+ In addition, returns 1 if the group is committing, 2 if it is rolling back.
+*/
+static int
+is_group_ending(Log_event *ev, Log_event_type event_type)
+{
+ if (event_type == XID_EVENT)
+ return 1;
+ if (event_type == QUERY_EVENT)
+ {
+ Query_log_event *qev = (Query_log_event *)ev;
+ if (qev->is_commit())
+ return 1;
+ if (qev->is_rollback())
+ return 2;
+ }
+ return 0;
+}
+
+
+static int
+retry_event_group(rpl_group_info *rgi, rpl_parallel_thread *rpt,
+ rpl_parallel_thread::queued_event *orig_qev)
+{
+ IO_CACHE rlog;
+ LOG_INFO linfo;
+ File fd= (File)-1;
+ const char *errmsg;
+ inuse_relaylog *ir= rgi->relay_log;
+ uint64 event_count;
+ uint64 events_to_execute= rgi->retry_event_count;
+ Relay_log_info *rli= rgi->rli;
+ int err;
+ ulonglong cur_offset, old_offset;
+ char log_name[FN_REFLEN];
+ THD *thd= rgi->thd;
+ rpl_parallel_entry *entry= rgi->parallel_entry;
+ ulong retries= 0;
+ Format_description_log_event *description_event= NULL;
+
+do_retry:
+ event_count= 0;
+ err= 0;
+ errmsg= NULL;
+
+ /*
+ If we already started committing before getting the deadlock (or other
+ error) that caused us to need to retry, we have already signalled
+ subsequent transactions that we have started committing. This is
+ potentially a problem, as now we will rollback, and if subsequent
+ transactions would start to execute now, they could see an unexpected
+ state of the database and get eg. key not found or duplicate key error.
+
+ However, to get a deadlock in the first place, there must have been
+ another earlier transaction that is waiting for us. Thus that other
+ transaction has _not_ yet started to commit, and any subsequent
+ transactions will still be waiting at this point.
+
+ So here, we decrement back the count of transactions that started
+ committing (if we already incremented it), undoing the effect of an
+ earlier mark_start_commit(). Then later, when the retry succeeds and we
+ commit again, we can do a new mark_start_commit() and eventually wake up
+ subsequent transactions at the proper time.
+
+ We need to do the unmark before the rollback, to be sure that the
+ transaction we deadlocked with will not signal that it started to commit
+ until after the unmark.
+ */
+ DBUG_EXECUTE_IF("inject_mdev8302", { my_sleep(20000);});
+ rgi->unmark_start_commit();
+ DEBUG_SYNC(thd, "rpl_parallel_retry_after_unmark");
+
+ /*
+ We might get the deadlock error that causes the retry during commit, while
+ sitting in wait_for_prior_commit(). If this happens, we will have a
+ pending error in the wait_for_commit object. So clear this by
+ unregistering (and later re-registering) the wait.
+ */
+ if(thd->wait_for_commit_ptr)
+ thd->wait_for_commit_ptr->unregister_wait_for_prior_commit();
+ DBUG_EXECUTE_IF("inject_mdev8031", {
+ /* Simulate that we get deadlock killed at this exact point. */
+ rgi->killed_for_retry= true;
+ mysql_mutex_lock(&thd->LOCK_thd_data);
+ thd->killed= KILL_CONNECTION;
+ mysql_mutex_unlock(&thd->LOCK_thd_data);
+ });
+ rgi->cleanup_context(thd, 1);
+ thd->reset_killed();
+ thd->clear_error();
+
+ /*
+ If we retry due to a deadlock kill that occured during the commit step, we
+ might have already updated (but not committed) an update of table
+ mysql.gtid_slave_pos, and cleared the gtid_pending flag. Now we have
+ rolled back any such update, so we must set the gtid_pending flag back to
+ true so that we will do a new update when/if we succeed with the retry.
+ */
+ rgi->gtid_pending= true;
+
+ mysql_mutex_lock(&rli->data_lock);
+ ++rli->retried_trans;
+ statistic_increment(slave_retried_transactions, LOCK_status);
+ mysql_mutex_unlock(&rli->data_lock);
+
+ for (;;)
+ {
+ mysql_mutex_lock(&entry->LOCK_parallel_entry);
+ register_wait_for_prior_event_group_commit(rgi, entry);
+ mysql_mutex_unlock(&entry->LOCK_parallel_entry);
+
+ /*
+ Let us wait for all prior transactions to complete before trying again.
+ This way, we avoid repeatedly conflicting with and getting deadlock
+ killed by the same earlier transaction.
+ */
+ if (!(err= thd->wait_for_prior_commit()))
+ break;
+
+ convert_kill_to_deadlock_error(rgi);
+ if (!has_temporary_error(thd))
+ goto err;
+ /*
+ If we get a temporary error such as a deadlock kill, we can safely
+ ignore it, as we already rolled back.
+
+ But we still want to retry the wait for the prior transaction to
+ complete its commit.
+ */
+ thd->clear_error();
+ thd->reset_killed();
+ if(thd->wait_for_commit_ptr)
+ thd->wait_for_commit_ptr->unregister_wait_for_prior_commit();
+ DBUG_EXECUTE_IF("inject_mdev8031", {
+ /* Inject a small sleep to give prior transaction a chance to commit. */
+ my_sleep(100000);
+ });
+ }
+
+ /*
+ Let us clear any lingering deadlock kill one more time, here after
+ wait_for_prior_commit() has completed. This should rule out any
+ possibility of an old deadlock kill lingering on beyond this point.
+ */
+ thd->reset_killed();
+
+ strmake_buf(log_name, ir->name);
+ if ((fd= open_binlog(&rlog, log_name, &errmsg)) <0)
+ {
+ err= 1;
+ goto err;
+ }
+ cur_offset= rgi->retry_start_offset;
+ delete description_event;
+ description_event=
+ read_relay_log_description_event(&rlog, cur_offset, &errmsg);
+ if (!description_event)
+ {
+ err= 1;
+ goto err;
+ }
+ DBUG_EXECUTE_IF("inject_mdev8031", {
+ /* Simulate pending KILL caught in read_relay_log_description_event(). */
+ if (thd->check_killed()) {
+ thd->send_kill_message();
+ err= 1;
+ goto err;
+ }
+ });
+ my_b_seek(&rlog, cur_offset);
+
+ do
+ {
+ Log_event_type event_type;
+ Log_event *ev;
+ rpl_parallel_thread::queued_event *qev;
+
+ /* The loop is here so we can try again the next relay log file on EOF. */
+ for (;;)
+ {
+ old_offset= cur_offset;
+ ev= Log_event::read_log_event(&rlog, 0, description_event,
+ opt_slave_sql_verify_checksum);
+ cur_offset= my_b_tell(&rlog);
+
+ if (ev)
+ break;
+ if (rlog.error < 0)
+ {
+ errmsg= "slave SQL thread aborted because of I/O error";
+ err= 1;
+ goto check_retry;
+ }
+ if (rlog.error > 0)
+ {
+ sql_print_error("Slave SQL thread: I/O error reading "
+ "event(errno: %d cur_log->error: %d)",
+ my_errno, rlog.error);
+ errmsg= "Aborting slave SQL thread because of partial event read";
+ err= 1;
+ goto err;
+ }
+ /* EOF. Move to the next relay log. */
+ end_io_cache(&rlog);
+ mysql_file_close(fd, MYF(MY_WME));
+ fd= (File)-1;
+
+ /* Find the next relay log file. */
+ if((err= rli->relay_log.find_log_pos(&linfo, log_name, 1)) ||
+ (err= rli->relay_log.find_next_log(&linfo, 1)))
+ {
+ char buff[22];
+ sql_print_error("next log error: %d offset: %s log: %s",
+ err,
+ llstr(linfo.index_file_offset, buff),
+ log_name);
+ goto err;
+ }
+ strmake_buf(log_name ,linfo.log_file_name);
+
+ DBUG_EXECUTE_IF("inject_retry_event_group_open_binlog_kill", {
+ if (retries < 2)
+ {
+ /* Simulate that we get deadlock killed during open_binlog(). */
+ mysql_reset_thd_for_next_command(thd);
+ rgi->killed_for_retry= true;
+ mysql_mutex_lock(&thd->LOCK_thd_data);
+ thd->killed= KILL_CONNECTION;
+ mysql_mutex_unlock(&thd->LOCK_thd_data);
+ thd->send_kill_message();
+ fd= (File)-1;
+ err= 1;
+ goto check_retry;
+ }
+ });
+ if ((fd= open_binlog(&rlog, log_name, &errmsg)) <0)
+ {
+ err= 1;
+ goto check_retry;
+ }
+ /* Loop to try again on the new log file. */
+ }
+
+ event_type= ev->get_type_code();
+ if (event_type == FORMAT_DESCRIPTION_EVENT)
+ {
+ delete description_event;
+ description_event= (Format_description_log_event *)ev;
+ continue;
+ } else if (!Log_event::is_group_event(event_type))
+ {
+ delete ev;
+ continue;
+ }
+ ev->thd= thd;
+
+ mysql_mutex_lock(&rpt->LOCK_rpl_thread);
+ qev= rpt->retry_get_qev(ev, orig_qev, log_name, old_offset,
+ cur_offset - old_offset);
+ mysql_mutex_unlock(&rpt->LOCK_rpl_thread);
+ if (!qev)
+ {
+ delete ev;
+ my_error(ER_OUT_OF_RESOURCES, MYF(0));
+ err= 1;
+ goto err;
+ }
+ if (is_group_ending(ev, event_type) == 1)
+ rgi->mark_start_commit();
+
+ err= rpt_handle_event(qev, rpt);
+ ++event_count;
+ mysql_mutex_lock(&rpt->LOCK_rpl_thread);
+ rpt->free_qev(qev);
+ mysql_mutex_unlock(&rpt->LOCK_rpl_thread);
+
+ delete_or_keep_event_post_apply(rgi, event_type, ev);
+ DBUG_EXECUTE_IF("rpl_parallel_simulate_double_temp_err_gtid_0_x_100",
+ if (retries == 0) err= dbug_simulate_tmp_error(rgi, thd););
+ DBUG_EXECUTE_IF("rpl_parallel_simulate_infinite_temp_err_gtid_0_x_100",
+ err= dbug_simulate_tmp_error(rgi, thd););
+ if (!err)
+ continue;
+
+check_retry:
+ convert_kill_to_deadlock_error(rgi);
+ if (has_temporary_error(thd))
+ {
+ ++retries;
+ if (retries < slave_trans_retries)
+ {
+ if (fd >= 0)
+ {
+ end_io_cache(&rlog);
+ mysql_file_close(fd, MYF(MY_WME));
+ fd= (File)-1;
+ }
+ goto do_retry;
+ }
+ sql_print_error("Slave worker thread retried transaction %lu time(s) "
+ "in vain, giving up. Consider raising the value of "
+ "the slave_transaction_retries variable.",
+ slave_trans_retries);
+ }
+ goto err;
+
+ } while (event_count < events_to_execute);
+
+err:
+
+ if (description_event)
+ delete description_event;
+ if (fd >= 0)
+ {
+ end_io_cache(&rlog);
+ mysql_file_close(fd, MYF(MY_WME));
+ }
+ if (errmsg)
+ sql_print_error("Error reading relay log event: %s", errmsg);
+ return err;
+}
+
+
+pthread_handler_t
+handle_rpl_parallel_thread(void *arg)
+{
+ THD *thd;
+ PSI_stage_info old_stage;
+ struct rpl_parallel_thread::queued_event *events;
+ bool group_standalone= true;
+ bool in_event_group= false;
+ bool skip_event_group= false;
+ rpl_group_info *group_rgi= NULL;
+ group_commit_orderer *gco;
+ uint64 event_gtid_sub_id= 0;
+ rpl_sql_thread_info sql_info(NULL);
+ int err;
+
+ struct rpl_parallel_thread *rpt= (struct rpl_parallel_thread *)arg;
+
+ my_thread_init();
+ thd = new THD;
+ thd->thread_stack = (char*)&thd;
+ mysql_mutex_lock(&LOCK_thread_count);
+ thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;
+ threads.append(thd);
+ mysql_mutex_unlock(&LOCK_thread_count);
+ set_current_thd(thd);
+ pthread_detach_this_thread();
+ thd->init_for_queries();
+ thd->variables.binlog_annotate_row_events= 0;
+ init_thr_lock();
+ thd->store_globals();
+ thd->system_thread= SYSTEM_THREAD_SLAVE_SQL;
+ thd->security_ctx->skip_grants();
+ thd->variables.max_allowed_packet= slave_max_allowed_packet;
+ thd->slave_thread= 1;
+ thd->enable_slow_log= opt_log_slow_slave_statements;
+ thd->variables.log_slow_filter= global_system_variables.log_slow_filter;
+ set_slave_thread_options(thd);
+ thd->client_capabilities = CLIENT_LOCAL_FILES;
+ thd->net.reading_or_writing= 0;
+ thd_proc_info(thd, "Waiting for work from main SQL threads");
+ thd->set_time();
+ thd->variables.lock_wait_timeout= LONG_TIMEOUT;
+ thd->system_thread_info.rpl_sql_info= &sql_info;
+
+ mysql_mutex_lock(&rpt->LOCK_rpl_thread);
+ rpt->thd= thd;
+
+ while (rpt->delay_start)
+ mysql_cond_wait(&rpt->COND_rpl_thread, &rpt->LOCK_rpl_thread);
+
+ rpt->running= true;
+ mysql_cond_signal(&rpt->COND_rpl_thread);
+
+ while (!rpt->stop)
+ {
+ rpl_parallel_thread::queued_event *qev, *next_qev;
+
+ thd->ENTER_COND(&rpt->COND_rpl_thread, &rpt->LOCK_rpl_thread,
+ &stage_waiting_for_work_from_sql_thread, &old_stage);
+ /*
+ There are 4 cases that should cause us to wake up:
+ - Events have been queued for us to handle.
+ - We have an owner, but no events and not inside event group -> we need
+ to release ourself to the thread pool
+ - SQL thread is stopping, and we have an owner but no events, and we are
+ inside an event group; no more events will be queued to us, so we need
+ to abort the group (force_abort==1).
+ - Thread pool shutdown (rpt->stop==1).
+ */
+ while (!( (events= rpt->event_queue) ||
+ (rpt->current_owner && !in_event_group) ||
+ (rpt->current_owner && group_rgi->parallel_entry->force_abort) ||
+ rpt->stop))
+ mysql_cond_wait(&rpt->COND_rpl_thread, &rpt->LOCK_rpl_thread);
+ rpt->dequeue1(events);
+ thd->EXIT_COND(&old_stage);
+
+ more_events:
+ for (qev= events; qev; qev= next_qev)
+ {
+ Log_event_type event_type;
+ rpl_group_info *rgi= qev->rgi;
+ rpl_parallel_entry *entry= rgi->parallel_entry;
+ bool end_of_group;
+ int group_ending;
+
+ next_qev= qev->next;
+ if (qev->typ == rpl_parallel_thread::queued_event::QUEUED_POS_UPDATE)
+ {
+ handle_queued_pos_update(thd, qev);
+ rpt->loc_free_qev(qev);
+ continue;
+ }
+ else if (qev->typ ==
+ rpl_parallel_thread::queued_event::QUEUED_MASTER_RESTART)
+ {
+ if (in_event_group)
+ {
+ /*
+ Master restarted (crashed) in the middle of an event group.
+ So we need to roll back and discard that event group.
+ */
+ group_rgi->cleanup_context(thd, 1);
+ in_event_group= false;
+ finish_event_group(rpt, group_rgi->gtid_sub_id,
+ qev->entry_for_queued, group_rgi);
+
+ rpt->loc_free_rgi(group_rgi);
+ thd->rgi_slave= group_rgi= NULL;
+ }
+
+ rpt->loc_free_qev(qev);
+ continue;
+ }
+ DBUG_ASSERT(qev->typ==rpl_parallel_thread::queued_event::QUEUED_EVENT);
+
+ thd->rgi_slave= rgi;
+ gco= rgi->gco;
+ /* Handle a new event group, which will be initiated by a GTID event. */
+ if ((event_type= qev->ev->get_type_code()) == GTID_EVENT)
+ {
+ bool did_enter_cond= false;
+ PSI_stage_info old_stage;
+
+ DBUG_EXECUTE_IF("rpl_parallel_scheduled_gtid_0_x_100", {
+ if (rgi->current_gtid.domain_id == 0 &&
+ rgi->current_gtid.seq_no == 100) {
+ debug_sync_set_action(thd,
+ STRING_WITH_LEN("now SIGNAL scheduled_gtid_0_x_100"));
+ }
+ });
+
+ if(unlikely(thd->wait_for_commit_ptr) && group_rgi != NULL)
+ {
+ /*
+ This indicates that we get a new GTID event in the middle of
+ a not completed event group. This is corrupt binlog (the master
+ will never write such binlog), so it does not happen unless
+ someone tries to inject wrong crafted binlog, but let us still
+ try to handle it somewhat nicely.
+ */
+ group_rgi->cleanup_context(thd, true);
+ finish_event_group(rpt, group_rgi->gtid_sub_id,
+ group_rgi->parallel_entry, group_rgi);
+ rpt->loc_free_rgi(group_rgi);
+ }
+
+ in_event_group= true;
+ /*
+ If the standalone flag is set, then this event group consists of a
+ single statement (possibly preceeded by some Intvar_log_event and
+ similar), without any terminating COMMIT/ROLLBACK/XID.
+ */
+ group_standalone=
+ (0 != (static_cast<Gtid_log_event *>(qev->ev)->flags2 &
+ Gtid_log_event::FL_STANDALONE));
+
+ event_gtid_sub_id= rgi->gtid_sub_id;
+ rgi->thd= thd;
+
+ mysql_mutex_lock(&entry->LOCK_parallel_entry);
+ skip_event_group= do_gco_wait(rgi, gco, &did_enter_cond, &old_stage);
+
+ if (unlikely(entry->stop_on_error_sub_id <= rgi->wait_commit_sub_id))
+ skip_event_group= true;
+ if (likely(!skip_event_group))
+ do_ftwrl_wait(rgi, &did_enter_cond, &old_stage);
+
+ /*
+ Register ourself to wait for the previous commit, if we need to do
+ such registration _and_ that previous commit has not already
+ occured.
+ */
+ register_wait_for_prior_event_group_commit(rgi, entry);
+
+ unlock_or_exit_cond(thd, &entry->LOCK_parallel_entry,
+ &did_enter_cond, &old_stage);
+
+ thd->wait_for_commit_ptr= &rgi->commit_orderer;
+
+ if (opt_gtid_ignore_duplicates)
+ {
+ int res=
+ rpl_global_gtid_slave_state->check_duplicate_gtid(&rgi->current_gtid,
+ rgi);
+ if (res < 0)
+ {
+ /* Error. */
+ slave_output_error_info(rgi, thd);
+ signal_error_to_sql_driver_thread(thd, rgi, 1);
+ }
+ else if (!res)
+ {
+ /* GTID already applied by another master connection, skip. */
+ skip_event_group= true;
+ }
+ else
+ {
+ /* We have to apply the event. */
+ }
+ }
+ }
+
+ group_rgi= rgi;
+ group_ending= is_group_ending(qev->ev, event_type);
+ /*
+ We do not unmark_start_commit() here in case of an explicit ROLLBACK
+ statement. Such events should be very rare, there is no real reason
+ to try to group commit them - on the contrary, it seems best to avoid
+ running them in parallel with following group commits, as with
+ ROLLBACK events we are already deep in dangerous corner cases with
+ mix of transactional and non-transactional tables or the like. And
+ avoiding the mark_start_commit() here allows us to keep an assertion
+ in ha_rollback_trans() that we do not rollback after doing
+ mark_start_commit().
+ */
+ if (group_ending == 1 && likely(!rgi->worker_error))
+ {
+ /*
+ Do an extra check for (deadlock) kill here. This helps prevent a
+ lingering deadlock kill that occured during normal DML processing to
+ propagate past the mark_start_commit(). If we detect a deadlock only
+ after mark_start_commit(), we have to unmark, which has at least a
+ theoretical possibility of leaving a window where it looks like all
+ transactions in a GCO have started committing, while in fact one
+ will need to rollback and retry. This is not supposed to be possible
+ (since there is a deadlock, at least one transaction should be
+ blocked from reaching commit), but this seems a fragile ensurance,
+ and there were historically a number of subtle bugs in this area.
+ */
+ if (!thd->killed)
+ {
+ DEBUG_SYNC(thd, "rpl_parallel_before_mark_start_commit");
+ rgi->mark_start_commit();
+ DEBUG_SYNC(thd, "rpl_parallel_after_mark_start_commit");
+ }
+ }
+
+ /*
+ If the SQL thread is stopping, we just skip execution of all the
+ following event groups. We still do all the normal waiting and wakeup
+ processing between the event groups as a simple way to ensure that
+ everything is stopped and cleaned up correctly.
+ */
+ if (likely(!rgi->worker_error) && !skip_event_group)
+ {
+ ++rgi->retry_event_count;
+#ifndef DBUG_OFF
+ err= 0;
+ DBUG_EXECUTE_IF("rpl_parallel_simulate_temp_err_xid",
+ if (event_type == XID_EVENT)
+ {
+ thd->clear_error();
+ thd->get_stmt_da()->reset_diagnostics_area();
+ my_error(ER_LOCK_DEADLOCK, MYF(0));
+ err= 1;
+ DEBUG_SYNC(thd, "rpl_parallel_simulate_temp_err_xid");
+ });
+ if (!err)
+#endif
+ {
+ if (thd->check_killed())
+ {
+ thd->clear_error();
+ thd->get_stmt_da()->reset_diagnostics_area();
+ thd->send_kill_message();
+ err= 1;
+ }
+ else
+ err= rpt_handle_event(qev, rpt);
+ }
+ delete_or_keep_event_post_apply(rgi, event_type, qev->ev);
+ DBUG_EXECUTE_IF("rpl_parallel_simulate_temp_err_gtid_0_x_100",
+ err= dbug_simulate_tmp_error(rgi, thd););
+ if (err)
+ {
+ convert_kill_to_deadlock_error(rgi);
+ if (has_temporary_error(thd) && slave_trans_retries > 0)
+ err= retry_event_group(rgi, rpt, qev);
+ }
+ }
+ else
+ {
+ delete qev->ev;
+ thd->get_stmt_da()->set_overwrite_status(true);
+ err= thd->wait_for_prior_commit();
+ thd->get_stmt_da()->set_overwrite_status(false);
+ }
+
+ end_of_group=
+ in_event_group &&
+ ((group_standalone && !Log_event::is_part_of_group(event_type)) ||
+ group_ending);
+
+ rpt->loc_free_qev(qev);
+
+ if (unlikely(err))
+ {
+ if (!rgi->worker_error)
+ {
+ slave_output_error_info(rgi, thd);
+ signal_error_to_sql_driver_thread(thd, rgi, err);
+ }
+ thd->reset_killed();
+ }
+ if (end_of_group)
+ {
+ in_event_group= false;
+ finish_event_group(rpt, event_gtid_sub_id, entry, rgi);
+ rpt->loc_free_rgi(rgi);
+ thd->rgi_slave= group_rgi= rgi= NULL;
+ skip_event_group= false;
+ DEBUG_SYNC(thd, "rpl_parallel_end_of_group");
+ }
+ }
+
+ mysql_mutex_lock(&rpt->LOCK_rpl_thread);
+ /*
+ Now that we have the lock, we can move everything from our local free
+ lists to the real free lists that are also accessible from the SQL
+ driver thread.
+ */
+ rpt->batch_free();
+
+ for (;;)
+ {
+ if ((events= rpt->event_queue) != NULL)
+ {
+ /*
+ Take next group of events from the replication pool.
+ This is faster than having to wakeup the pool manager thread to give
+ us a new event.
+ */
+ rpt->dequeue1(events);
+ mysql_mutex_unlock(&rpt->LOCK_rpl_thread);
+ goto more_events;
+ }
+ if (!rpt->pause_for_ftwrl ||
+ (in_event_group && !group_rgi->parallel_entry->force_abort))
+ break;
+ /*
+ We are currently in the delicate process of pausing parallel
+ replication while FLUSH TABLES WITH READ LOCK is starting. We must
+ not de-allocate the thread (setting rpt->current_owner= NULL) until
+ rpl_unpause_after_ftwrl() has woken us up.
+ */
+ mysql_mutex_lock(&rpt->current_entry->LOCK_parallel_entry);
+ mysql_mutex_unlock(&rpt->LOCK_rpl_thread);
+ if (rpt->pause_for_ftwrl)
+ mysql_cond_wait(&rpt->current_entry->COND_parallel_entry,
+ &rpt->current_entry->LOCK_parallel_entry);
+ mysql_mutex_unlock(&rpt->current_entry->LOCK_parallel_entry);
+ mysql_mutex_lock(&rpt->LOCK_rpl_thread);
+ /*
+ Now loop to check again for more events available, since we released
+ and re-aquired the LOCK_rpl_thread mutex.
+ */
+ }
+
+ rpt->inuse_relaylog_refcount_update();
+
+ if (in_event_group && group_rgi->parallel_entry->force_abort)
+ {
+ /*
+ We are asked to abort, without getting the remaining events in the
+ current event group.
+
+ We have to rollback the current transaction and update the last
+ sub_id value so that SQL thread will know we are done with the
+ half-processed event group.
+ */
+ mysql_mutex_unlock(&rpt->LOCK_rpl_thread);
+ signal_error_to_sql_driver_thread(thd, group_rgi, 1);
+ finish_event_group(rpt, group_rgi->gtid_sub_id,
+ group_rgi->parallel_entry, group_rgi);
+ in_event_group= false;
+ mysql_mutex_lock(&rpt->LOCK_rpl_thread);
+ rpt->free_rgi(group_rgi);
+ thd->rgi_slave= group_rgi= NULL;
+ skip_event_group= false;
+ }
+ if (!in_event_group)
+ {
+ rpt->current_owner= NULL;
+ /* Tell wait_for_done() that we are done, if it is waiting. */
+ if (likely(rpt->current_entry) &&
+ unlikely(rpt->current_entry->force_abort))
+ mysql_cond_broadcast(&rpt->COND_rpl_thread_stop);
+ rpt->current_entry= NULL;
+ if (!rpt->stop)
+ rpt->pool->release_thread(rpt);
+ }
+ }
+
+ rpt->thd= NULL;
+ mysql_mutex_unlock(&rpt->LOCK_rpl_thread);
+
+ thd->clear_error();
+ thd->catalog= 0;
+ thd->reset_query();
+ thd->reset_db(NULL, 0);
+ thd_proc_info(thd, "Slave worker thread exiting");
+ thd->temporary_tables= 0;
+ mysql_mutex_lock(&LOCK_thread_count);
+ THD_CHECK_SENTRY(thd);
+ delete thd;
+ mysql_mutex_unlock(&LOCK_thread_count);
+
+ mysql_mutex_lock(&rpt->LOCK_rpl_thread);
+ rpt->running= false;
+ mysql_cond_signal(&rpt->COND_rpl_thread);
+ mysql_mutex_unlock(&rpt->LOCK_rpl_thread);
+
+ my_thread_end();
+
+ return NULL;
+}
+
+
+static void
+dealloc_gco(group_commit_orderer *gco)
+{
+ mysql_cond_destroy(&gco->COND_group_commit_orderer);
+ my_free(gco);
+}
+
+
+static int
+rpl_parallel_change_thread_count(rpl_parallel_thread_pool *pool,
+ uint32 new_count)
+{
+ uint32 i;
+ rpl_parallel_thread **new_list= NULL;
+ rpl_parallel_thread *new_free_list= NULL;
+ rpl_parallel_thread *rpt_array= NULL;
+ int res;
+
+ if ((res= pool_mark_busy(pool, current_thd)))
+ return res;
+
+ /*
+ Allocate the new list of threads up-front.
+ That way, if we fail half-way, we only need to free whatever we managed
+ to allocate, and will not be left with a half-functional thread pool.
+ */
+ if (new_count &&
+ !my_multi_malloc(MYF(MY_WME|MY_ZEROFILL),
+ &new_list, new_count*sizeof(*new_list),
+ &rpt_array, new_count*sizeof(*rpt_array),
+ NULL))
+ {
+ my_error(ER_OUTOFMEMORY, MYF(0), (int(new_count*sizeof(*new_list) +
+ new_count*sizeof(*rpt_array))));
+ goto err;;
+ }
+
+ for (i= 0; i < new_count; ++i)
+ {
+ pthread_t th;
+
+ new_list[i]= &rpt_array[i];
+ new_list[i]->delay_start= true;
+ mysql_mutex_init(key_LOCK_rpl_thread, &new_list[i]->LOCK_rpl_thread,
+ MY_MUTEX_INIT_SLOW);
+ mysql_cond_init(key_COND_rpl_thread, &new_list[i]->COND_rpl_thread, NULL);
+ mysql_cond_init(key_COND_rpl_thread_queue,
+ &new_list[i]->COND_rpl_thread_queue, NULL);
+ mysql_cond_init(key_COND_rpl_thread_stop,
+ &new_list[i]->COND_rpl_thread_stop, NULL);
+ new_list[i]->pool= pool;
+ if (mysql_thread_create(key_rpl_parallel_thread, &th, &connection_attrib,
+ handle_rpl_parallel_thread, new_list[i]))
+ {
+ my_error(ER_OUT_OF_RESOURCES, MYF(0));
+ goto err;
+ }
+ new_list[i]->next= new_free_list;
+ new_free_list= new_list[i];
+ }
+
+ /*
+ Grab each old thread in turn, and signal it to stop.
+
+ Note that since we require all replication threads to be stopped before
+ changing the parallel replication worker thread pool, all the threads will
+ be already idle and will terminate immediately.
+ */
+ for (i= 0; i < pool->count; ++i)
+ {
+ rpl_parallel_thread *rpt;
+
+ mysql_mutex_lock(&pool->LOCK_rpl_thread_pool);
+ while ((rpt= pool->free_list) == NULL)
+ mysql_cond_wait(&pool->COND_rpl_thread_pool, &pool->LOCK_rpl_thread_pool);
+ pool->free_list= rpt->next;
+ mysql_mutex_unlock(&pool->LOCK_rpl_thread_pool);
+ mysql_mutex_lock(&rpt->LOCK_rpl_thread);
+ rpt->stop= true;
+ mysql_cond_signal(&rpt->COND_rpl_thread);
+ mysql_mutex_unlock(&rpt->LOCK_rpl_thread);
+ }
+
+ for (i= 0; i < pool->count; ++i)
+ {
+ rpl_parallel_thread *rpt= pool->threads[i];
+ mysql_mutex_lock(&rpt->LOCK_rpl_thread);
+ while (rpt->running)
+ mysql_cond_wait(&rpt->COND_rpl_thread, &rpt->LOCK_rpl_thread);
+ mysql_mutex_unlock(&rpt->LOCK_rpl_thread);
+ mysql_mutex_destroy(&rpt->LOCK_rpl_thread);
+ mysql_cond_destroy(&rpt->COND_rpl_thread);
+ while (rpt->qev_free_list)
+ {
+ rpl_parallel_thread::queued_event *next= rpt->qev_free_list->next;
+ my_free(rpt->qev_free_list);
+ rpt->qev_free_list= next;
+ }
+ while (rpt->rgi_free_list)
+ {
+ rpl_group_info *next= rpt->rgi_free_list->next;
+ delete rpt->rgi_free_list;
+ rpt->rgi_free_list= next;
+ }
+ while (rpt->gco_free_list)
+ {
+ group_commit_orderer *next= rpt->gco_free_list->next_gco;
+ dealloc_gco(rpt->gco_free_list);
+ rpt->gco_free_list= next;
+ }
+ }
+
+ my_free(pool->threads);
+ pool->threads= new_list;
+ pool->free_list= new_free_list;
+ pool->count= new_count;
+ for (i= 0; i < pool->count; ++i)
+ {
+ mysql_mutex_lock(&pool->threads[i]->LOCK_rpl_thread);
+ pool->threads[i]->delay_start= false;
+ mysql_cond_signal(&pool->threads[i]->COND_rpl_thread);
+ while (!pool->threads[i]->running)
+ mysql_cond_wait(&pool->threads[i]->COND_rpl_thread,
+ &pool->threads[i]->LOCK_rpl_thread);
+ mysql_mutex_unlock(&pool->threads[i]->LOCK_rpl_thread);
+ }
+
+ pool_mark_not_busy(pool);
+
+ return 0;
+
+err:
+ if (new_list)
+ {
+ while (new_free_list)
+ {
+ mysql_mutex_lock(&new_free_list->LOCK_rpl_thread);
+ new_free_list->delay_start= false;
+ new_free_list->stop= true;
+ mysql_cond_signal(&new_free_list->COND_rpl_thread);
+ while (!new_free_list->running)
+ mysql_cond_wait(&new_free_list->COND_rpl_thread,
+ &new_free_list->LOCK_rpl_thread);
+ while (new_free_list->running)
+ mysql_cond_wait(&new_free_list->COND_rpl_thread,
+ &new_free_list->LOCK_rpl_thread);
+ mysql_mutex_unlock(&new_free_list->LOCK_rpl_thread);
+ new_free_list= new_free_list->next;
+ }
+ my_free(new_list);
+ }
+ pool_mark_not_busy(pool);
+ return 1;
+}
+
+
+int
+rpl_parallel_activate_pool(rpl_parallel_thread_pool *pool)
+{
+ if (!pool->count)
+ return rpl_parallel_change_thread_count(pool, opt_slave_parallel_threads);
+ return 0;
+}
+
+
+int
+rpl_parallel_inactivate_pool(rpl_parallel_thread_pool *pool)
+{
+ return rpl_parallel_change_thread_count(pool, 0);
+}
+
+
+void
+rpl_parallel_thread::batch_free()
+{
+ mysql_mutex_assert_owner(&LOCK_rpl_thread);
+ if (loc_qev_list)
+ {
+ *loc_qev_last_ptr_ptr= qev_free_list;
+ qev_free_list= loc_qev_list;
+ loc_qev_list= NULL;
+ dequeue2(loc_qev_size);
+ /* Signal that our queue can now accept more events. */
+ mysql_cond_signal(&COND_rpl_thread_queue);
+ loc_qev_size= 0;
+ qev_free_pending= 0;
+ }
+ if (loc_rgi_list)
+ {
+ *loc_rgi_last_ptr_ptr= rgi_free_list;
+ rgi_free_list= loc_rgi_list;
+ loc_rgi_list= NULL;
+ }
+ if (loc_gco_list)
+ {
+ *loc_gco_last_ptr_ptr= gco_free_list;
+ gco_free_list= loc_gco_list;
+ loc_gco_list= NULL;
+ }
+}
+
+
+void
+rpl_parallel_thread::inuse_relaylog_refcount_update()
+{
+ inuse_relaylog *ir= accumulated_ir_last;
+ if (ir)
+ {
+ my_atomic_rwlock_wrlock(&ir->inuse_relaylog_atomic_lock);
+ my_atomic_add64(&ir->dequeued_count, accumulated_ir_count);
+ my_atomic_rwlock_wrunlock(&ir->inuse_relaylog_atomic_lock);
+ accumulated_ir_count= 0;
+ accumulated_ir_last= NULL;
+ }
+}
+
+
+rpl_parallel_thread::queued_event *
+rpl_parallel_thread::get_qev_common(Log_event *ev, ulonglong event_size)
+{
+ queued_event *qev;
+ mysql_mutex_assert_owner(&LOCK_rpl_thread);
+ if ((qev= qev_free_list))
+ qev_free_list= qev->next;
+ else if(!(qev= (queued_event *)my_malloc(sizeof(*qev), MYF(0))))
+ {
+ my_error(ER_OUTOFMEMORY, MYF(0), (int)sizeof(*qev));
+ return NULL;
+ }
+ qev->typ= rpl_parallel_thread::queued_event::QUEUED_EVENT;
+ qev->ev= ev;
+ qev->event_size= event_size;
+ qev->next= NULL;
+ return qev;
+}
+
+
+rpl_parallel_thread::queued_event *
+rpl_parallel_thread::get_qev(Log_event *ev, ulonglong event_size,
+ Relay_log_info *rli)
+{
+ queued_event *qev= get_qev_common(ev, event_size);
+ if (!qev)
+ return NULL;
+ strcpy(qev->event_relay_log_name, rli->event_relay_log_name);
+ qev->event_relay_log_pos= rli->event_relay_log_pos;
+ qev->future_event_relay_log_pos= rli->future_event_relay_log_pos;
+ strcpy(qev->future_event_master_log_name, rli->future_event_master_log_name);
+ return qev;
+}
+
+
+rpl_parallel_thread::queued_event *
+rpl_parallel_thread::retry_get_qev(Log_event *ev, queued_event *orig_qev,
+ const char *relay_log_name,
+ ulonglong event_pos, ulonglong event_size)
+{
+ queued_event *qev= get_qev_common(ev, event_size);
+ if (!qev)
+ return NULL;
+ qev->rgi= orig_qev->rgi;
+ strcpy(qev->event_relay_log_name, relay_log_name);
+ qev->event_relay_log_pos= event_pos;
+ qev->future_event_relay_log_pos= event_pos+event_size;
+ strcpy(qev->future_event_master_log_name,
+ orig_qev->future_event_master_log_name);
+ return qev;
+}
+
+
+void
+rpl_parallel_thread::loc_free_qev(rpl_parallel_thread::queued_event *qev)
+{
+ inuse_relaylog *ir= qev->ir;
+ inuse_relaylog *last_ir= accumulated_ir_last;
+ if (ir != last_ir)
+ {
+ if (last_ir)
+ inuse_relaylog_refcount_update();
+ accumulated_ir_last= ir;
+ }
+ ++accumulated_ir_count;
+ if (!loc_qev_list)
+ loc_qev_last_ptr_ptr= &qev->next;
+ else
+ qev->next= loc_qev_list;
+ loc_qev_list= qev;
+ loc_qev_size+= qev->event_size;
+ /*
+ We want to release to the global free list only occasionally, to avoid
+ having to take the LOCK_rpl_thread muted too many times.
+
+ However, we do need to release regularly. If we let the unreleased part
+ grow too large, then the SQL driver thread may go to sleep waiting for
+ the queue to drop below opt_slave_parallel_max_queued, and this in turn
+ can stall all other worker threads for more stuff to do.
+ */
+ if (++qev_free_pending >= QEV_BATCH_FREE ||
+ loc_qev_size >= opt_slave_parallel_max_queued/3)
+ {
+ mysql_mutex_lock(&LOCK_rpl_thread);
+ batch_free();
+ mysql_mutex_unlock(&LOCK_rpl_thread);
+ }
+}
+
+
+void
+rpl_parallel_thread::free_qev(rpl_parallel_thread::queued_event *qev)
+{
+ mysql_mutex_assert_owner(&LOCK_rpl_thread);
+ qev->next= qev_free_list;
+ qev_free_list= qev;
+}
+
+
+rpl_group_info*
+rpl_parallel_thread::get_rgi(Relay_log_info *rli, Gtid_log_event *gtid_ev,
+ rpl_parallel_entry *e, ulonglong event_size)
+{
+ rpl_group_info *rgi;
+ mysql_mutex_assert_owner(&LOCK_rpl_thread);
+ if ((rgi= rgi_free_list))
+ {
+ rgi_free_list= rgi->next;
+ rgi->reinit(rli);
+ }
+ else
+ {
+ if(!(rgi= new rpl_group_info(rli)))
+ {
+ my_error(ER_OUTOFMEMORY, MYF(0), (int)sizeof(*rgi));
+ return NULL;
+ }
+ rgi->is_parallel_exec = true;
+ }
+ if ((rgi->deferred_events_collecting= rli->mi->rpl_filter->is_on()) &&
+ !rgi->deferred_events)
+ rgi->deferred_events= new Deferred_log_events(rli);
+ if (event_group_new_gtid(rgi, gtid_ev))
+ {
+ free_rgi(rgi);
+ my_error(ER_OUT_OF_RESOURCES, MYF(MY_WME));
+ return NULL;
+ }
+ rgi->parallel_entry= e;
+ rgi->relay_log= rli->last_inuse_relaylog;
+ rgi->retry_start_offset= rli->future_event_relay_log_pos-event_size;
+ rgi->retry_event_count= 0;
+ rgi->killed_for_retry= false;
+
+ return rgi;
+}
+
+
+void
+rpl_parallel_thread::loc_free_rgi(rpl_group_info *rgi)
+{
+ DBUG_ASSERT(rgi->commit_orderer.waitee == NULL);
+ rgi->free_annotate_event();
+ if (!loc_rgi_list)
+ loc_rgi_last_ptr_ptr= &rgi->next;
+ else
+ rgi->next= loc_rgi_list;
+ loc_rgi_list= rgi;
+}
+
+
+void
+rpl_parallel_thread::free_rgi(rpl_group_info *rgi)
+{
+ mysql_mutex_assert_owner(&LOCK_rpl_thread);
+ DBUG_ASSERT(rgi->commit_orderer.waitee == NULL);
+ rgi->free_annotate_event();
+ rgi->next= rgi_free_list;
+ rgi_free_list= rgi;
+}
+
+
+group_commit_orderer *
+rpl_parallel_thread::get_gco(uint64 wait_count, group_commit_orderer *prev,
+ uint64 prior_sub_id)
+{
+ group_commit_orderer *gco;
+ mysql_mutex_assert_owner(&LOCK_rpl_thread);
+ if ((gco= gco_free_list))
+ gco_free_list= gco->next_gco;
+ else if(!(gco= (group_commit_orderer *)my_malloc(sizeof(*gco), MYF(0))))
+ {
+ my_error(ER_OUTOFMEMORY, MYF(0), (int)sizeof(*gco));
+ return NULL;
+ }
+ mysql_cond_init(key_COND_group_commit_orderer,
+ &gco->COND_group_commit_orderer, NULL);
+ gco->wait_count= wait_count;
+ gco->prev_gco= prev;
+ gco->next_gco= NULL;
+ gco->prior_sub_id= prior_sub_id;
+ gco->installed= false;
+ return gco;
+}
+
+
+void
+rpl_parallel_thread::loc_free_gco(group_commit_orderer *gco)
+{
+ if (!loc_gco_list)
+ loc_gco_last_ptr_ptr= &gco->next_gco;
+ else
+ gco->next_gco= loc_gco_list;
+ loc_gco_list= gco;
+}
+
+
+rpl_parallel_thread_pool::rpl_parallel_thread_pool()
+ : threads(0), free_list(0), count(0), inited(false), busy(false)
+{
+}
+
+
+int
+rpl_parallel_thread_pool::init(uint32 size)
+{
+ threads= NULL;
+ free_list= NULL;
+ count= 0;
+ busy= false;
+
+ mysql_mutex_init(key_LOCK_rpl_thread_pool, &LOCK_rpl_thread_pool,
+ MY_MUTEX_INIT_SLOW);
+ mysql_cond_init(key_COND_rpl_thread_pool, &COND_rpl_thread_pool, NULL);
+ inited= true;
+
+ /*
+ The pool is initially empty. Threads will be spawned when a slave SQL
+ thread is started.
+ */
+
+ return 0;
+}
+
+
+void
+rpl_parallel_thread_pool::destroy()
+{
+ if (!inited)
+ return;
+ rpl_parallel_change_thread_count(this, 0);
+ mysql_mutex_destroy(&LOCK_rpl_thread_pool);
+ mysql_cond_destroy(&COND_rpl_thread_pool);
+ inited= false;
+}
+
+
+/*
+ Wait for a worker thread to become idle. When one does, grab the thread for
+ our use and return it.
+
+ Note that we return with the worker threads's LOCK_rpl_thread mutex locked.
+*/
+struct rpl_parallel_thread *
+rpl_parallel_thread_pool::get_thread(rpl_parallel_thread **owner,
+ rpl_parallel_entry *entry)
+{
+ rpl_parallel_thread *rpt;
+
+ mysql_mutex_lock(&LOCK_rpl_thread_pool);
+ while (unlikely(busy) || !(rpt= free_list))
+ mysql_cond_wait(&COND_rpl_thread_pool, &LOCK_rpl_thread_pool);
+ free_list= rpt->next;
+ mysql_mutex_unlock(&LOCK_rpl_thread_pool);
+ mysql_mutex_lock(&rpt->LOCK_rpl_thread);
+ rpt->current_owner= owner;
+ rpt->current_entry= entry;
+
+ return rpt;
+}
+
+
+/*
+ Release a thread to the thread pool.
+ The thread should be locked, and should not have any work queued for it.
+*/
+void
+rpl_parallel_thread_pool::release_thread(rpl_parallel_thread *rpt)
+{
+ rpl_parallel_thread *list;
+
+ mysql_mutex_assert_owner(&rpt->LOCK_rpl_thread);
+ DBUG_ASSERT(rpt->current_owner == NULL);
+ mysql_mutex_lock(&LOCK_rpl_thread_pool);
+ list= free_list;
+ rpt->next= list;
+ free_list= rpt;
+ if (!list)
+ mysql_cond_broadcast(&COND_rpl_thread_pool);
+ mysql_mutex_unlock(&LOCK_rpl_thread_pool);
+}
+
+
+/*
+ Obtain a worker thread that we can queue an event to.
+
+ Each invocation allocates a new worker thread, to maximise
+ parallelism. However, only up to a maximum of
+ --slave-domain-parallel-threads workers can be occupied by a single
+ replication domain; after that point, we start re-using worker threads that
+ are still executing events that were queued earlier for this thread.
+
+ We never queue more than --rpl-parallel-wait-queue_max amount of events
+ for one worker, to avoid the SQL driver thread using up all memory with
+ queued events while worker threads are stalling.
+
+ Note that this function returns with rpl_parallel_thread::LOCK_rpl_thread
+ locked. Exception is if we were killed, in which case NULL is returned.
+
+ The *did_enter_cond flag is set true if we had to wait for a worker thread
+ to become free (with mysql_cond_wait()). If so, old_stage will also be set,
+ and the LOCK_rpl_thread must be released with THD::EXIT_COND() instead
+ of mysql_mutex_unlock.
+
+ If the flag `reuse' is set, the last worker thread will be returned again,
+ if it is still available. Otherwise a new worker thread is allocated.
+*/
+rpl_parallel_thread *
+rpl_parallel_entry::choose_thread(rpl_group_info *rgi, bool *did_enter_cond,
+ PSI_stage_info *old_stage, bool reuse)
+{
+ uint32 idx;
+ Relay_log_info *rli= rgi->rli;
+ rpl_parallel_thread *thr;
+
+ idx= rpl_thread_idx;
+ if (!reuse)
+ {
+ ++idx;
+ if (idx >= rpl_thread_max)
+ idx= 0;
+ rpl_thread_idx= idx;
+ }
+ thr= rpl_threads[idx];
+ if (thr)
+ {
+ *did_enter_cond= false;
+ mysql_mutex_lock(&thr->LOCK_rpl_thread);
+ for (;;)
+ {
+ if (thr->current_owner != &rpl_threads[idx])
+ {
+ /*
+ The worker thread became idle, and returned to the free list and
+ possibly was allocated to a different request. So we should allocate
+ a new worker thread.
+ */
+ unlock_or_exit_cond(rli->sql_driver_thd, &thr->LOCK_rpl_thread,
+ did_enter_cond, old_stage);
+ thr= NULL;
+ break;
+ }
+ else if (thr->queued_size <= opt_slave_parallel_max_queued)
+ {
+ /* The thread is ready to queue into. */
+ break;
+ }
+ else if (rli->sql_driver_thd->check_killed())
+ {
+ unlock_or_exit_cond(rli->sql_driver_thd, &thr->LOCK_rpl_thread,
+ did_enter_cond, old_stage);
+ my_error(ER_CONNECTION_KILLED, MYF(0));
+ DBUG_EXECUTE_IF("rpl_parallel_wait_queue_max",
+ {
+ debug_sync_set_action(rli->sql_driver_thd,
+ STRING_WITH_LEN("now SIGNAL wait_queue_killed"));
+ };);
+ slave_output_error_info(rgi, rli->sql_driver_thd);
+ return NULL;
+ }
+ else
+ {
+ /*
+ We have reached the limit of how much memory we are allowed to use
+ for queuing events, so wait for the thread to consume some of its
+ queue.
+ */
+ if (!*did_enter_cond)
+ {
+ /*
+ We need to do the debug_sync before ENTER_COND().
+ Because debug_sync changes the thd->mysys_var->current_mutex,
+ and this can cause THD::awake to use the wrong mutex.
+ */
+ DBUG_EXECUTE_IF("rpl_parallel_wait_queue_max",
+ {
+ debug_sync_set_action(rli->sql_driver_thd,
+ STRING_WITH_LEN("now SIGNAL wait_queue_ready"));
+ };);
+ rli->sql_driver_thd->ENTER_COND(&thr->COND_rpl_thread_queue,
+ &thr->LOCK_rpl_thread,
+ &stage_waiting_for_room_in_worker_thread,
+ old_stage);
+ *did_enter_cond= true;
+ }
+ mysql_cond_wait(&thr->COND_rpl_thread_queue, &thr->LOCK_rpl_thread);
+ }
+ }
+ }
+ if (!thr)
+ rpl_threads[idx]= thr= global_rpl_thread_pool.get_thread(&rpl_threads[idx],
+ this);
+
+ return thr;
+}
+
+static void
+free_rpl_parallel_entry(void *element)
+{
+ rpl_parallel_entry *e= (rpl_parallel_entry *)element;
+ while (e->current_gco)
+ {
+ group_commit_orderer *prev_gco= e->current_gco->prev_gco;
+ dealloc_gco(e->current_gco);
+ e->current_gco= prev_gco;
+ }
+ mysql_cond_destroy(&e->COND_parallel_entry);
+ mysql_mutex_destroy(&e->LOCK_parallel_entry);
+ my_free(e);
+}
+
+
+rpl_parallel::rpl_parallel() :
+ current(NULL), sql_thread_stopping(false)
+{
+ my_hash_init(&domain_hash, &my_charset_bin, 32,
+ offsetof(rpl_parallel_entry, domain_id), sizeof(uint32),
+ NULL, free_rpl_parallel_entry, HASH_UNIQUE);
+}
+
+
+void
+rpl_parallel::reset()
+{
+ my_hash_reset(&domain_hash);
+ current= NULL;
+ sql_thread_stopping= false;
+}
+
+
+rpl_parallel::~rpl_parallel()
+{
+ my_hash_free(&domain_hash);
+}
+
+
+rpl_parallel_entry *
+rpl_parallel::find(uint32 domain_id)
+{
+ struct rpl_parallel_entry *e;
+
+ if (!(e= (rpl_parallel_entry *)my_hash_search(&domain_hash,
+ (const uchar *)&domain_id, 0)))
+ {
+ /* Allocate a new, empty one. */
+ ulong count= opt_slave_domain_parallel_threads;
+ if (count == 0 || count > opt_slave_parallel_threads)
+ count= opt_slave_parallel_threads;
+ rpl_parallel_thread **p;
+ if (!my_multi_malloc(MYF(MY_WME|MY_ZEROFILL),
+ &e, sizeof(*e),
+ &p, count*sizeof(*p),
+ NULL))
+ {
+ my_error(ER_OUTOFMEMORY, MYF(0), (int)(sizeof(*e)+count*sizeof(*p)));
+ return NULL;
+ }
+ e->rpl_threads= p;
+ e->rpl_thread_max= count;
+ e->domain_id= domain_id;
+ e->stop_on_error_sub_id= (uint64)ULONGLONG_MAX;
+ e->pause_sub_id= (uint64)ULONGLONG_MAX;
+ if (my_hash_insert(&domain_hash, (uchar *)e))
+ {
+ my_free(e);
+ return NULL;
+ }
+ mysql_mutex_init(key_LOCK_parallel_entry, &e->LOCK_parallel_entry,
+ MY_MUTEX_INIT_FAST);
+ mysql_cond_init(key_COND_parallel_entry, &e->COND_parallel_entry, NULL);
+ }
+ else
+ e->force_abort= false;
+
+ return e;
+}
+
+
+void
+rpl_parallel::wait_for_done(THD *thd, Relay_log_info *rli)
+{
+ struct rpl_parallel_entry *e;
+ rpl_parallel_thread *rpt;
+ uint32 i, j;
+
+ /*
+ First signal all workers that they must force quit; no more events will
+ be queued to complete any partial event groups executed.
+ */
+ for (i= 0; i < domain_hash.records; ++i)
+ {
+ e= (struct rpl_parallel_entry *)my_hash_element(&domain_hash, i);
+ mysql_mutex_lock(&e->LOCK_parallel_entry);
+ /*
+ We want the worker threads to stop as quickly as is safe. If the slave
+ SQL threads are behind, we could have significant amount of events
+ queued for the workers, and we want to stop without waiting for them
+ all to be applied first. But if any event group has already started
+ executing in a worker, we want to be sure that all prior event groups
+ are also executed, so that we stop at a consistent point in the binlog
+ stream (per replication domain).
+
+ All event groups wait for e->count_committing_event_groups to reach
+ the value of group_commit_orderer::wait_count before starting to
+ execute. Thus, at this point we know that any event group with a
+ strictly larger wait_count are safe to skip, none of them can have
+ started executing yet. So we set e->stop_count here and use it to
+ decide in the worker threads whether to continue executing an event
+ group or whether to skip it, when force_abort is set.
+
+ If we stop due to reaching the START SLAVE UNTIL condition, then we
+ need to continue executing any queued events up to that point.
+ */
+ e->force_abort= true;
+ e->stop_count= rli->stop_for_until ?
+ e->count_queued_event_groups : e->count_committing_event_groups;
+ mysql_mutex_unlock(&e->LOCK_parallel_entry);
+ for (j= 0; j < e->rpl_thread_max; ++j)
+ {
+ if ((rpt= e->rpl_threads[j]))
+ {
+ mysql_mutex_lock(&rpt->LOCK_rpl_thread);
+ if (rpt->current_owner == &e->rpl_threads[j])
+ mysql_cond_signal(&rpt->COND_rpl_thread);
+ mysql_mutex_unlock(&rpt->LOCK_rpl_thread);
+ }
+ }
+ }
+ DBUG_EXECUTE_IF("rpl_parallel_wait_for_done_trigger",
+ {
+ debug_sync_set_action(thd,
+ STRING_WITH_LEN("now SIGNAL wait_for_done_waiting"));
+ };);
+
+ for (i= 0; i < domain_hash.records; ++i)
+ {
+ e= (struct rpl_parallel_entry *)my_hash_element(&domain_hash, i);
+ for (j= 0; j < e->rpl_thread_max; ++j)
+ {
+ if ((rpt= e->rpl_threads[j]))
+ {
+ mysql_mutex_lock(&rpt->LOCK_rpl_thread);
+ while (rpt->current_owner == &e->rpl_threads[j])
+ mysql_cond_wait(&rpt->COND_rpl_thread_stop, &rpt->LOCK_rpl_thread);
+ mysql_mutex_unlock(&rpt->LOCK_rpl_thread);
+ }
+ }
+ }
+}
+
+
+/*
+ This function handles the case where the SQL driver thread reached the
+ START SLAVE UNTIL position; we stop queueing more events but continue
+ processing remaining, already queued events; then use executes manual
+ STOP SLAVE; then this function signals to worker threads that they
+ should stop the processing of any remaining queued events.
+*/
+void
+rpl_parallel::stop_during_until()
+{
+ struct rpl_parallel_entry *e;
+ uint32 i;
+
+ for (i= 0; i < domain_hash.records; ++i)
+ {
+ e= (struct rpl_parallel_entry *)my_hash_element(&domain_hash, i);
+ mysql_mutex_lock(&e->LOCK_parallel_entry);
+ if (e->force_abort)
+ e->stop_count= e->count_committing_event_groups;
+ mysql_mutex_unlock(&e->LOCK_parallel_entry);
+ }
+}
+
+
+bool
+rpl_parallel::workers_idle()
+{
+ struct rpl_parallel_entry *e;
+ uint32 i, max_i;
+
+ max_i= domain_hash.records;
+ for (i= 0; i < max_i; ++i)
+ {
+ bool active;
+ e= (struct rpl_parallel_entry *)my_hash_element(&domain_hash, i);
+ mysql_mutex_lock(&e->LOCK_parallel_entry);
+ active= e->current_sub_id > e->last_committed_sub_id;
+ mysql_mutex_unlock(&e->LOCK_parallel_entry);
+ if (active)
+ break;
+ }
+ return (i == max_i);
+}
+
+
+int
+rpl_parallel_entry::queue_master_restart(rpl_group_info *rgi,
+ Format_description_log_event *fdev)
+{
+ uint32 idx;
+ rpl_parallel_thread *thr;
+ rpl_parallel_thread::queued_event *qev;
+ Relay_log_info *rli= rgi->rli;
+
+ /*
+ We only need to queue the server restart if we still have a thread working
+ on a (potentially partial) event group.
+
+ If the last thread we queued for has finished, then it cannot have any
+ partial event group that needs aborting.
+
+ Thus there is no need for the full complexity of choose_thread(). We only
+ need to check if we have a current worker thread, and queue for it if so.
+ */
+ idx= rpl_thread_idx;
+ thr= rpl_threads[idx];
+ if (!thr)
+ return 0;
+ mysql_mutex_lock(&thr->LOCK_rpl_thread);
+ if (thr->current_owner != &rpl_threads[idx])
+ {
+ /* No active worker thread, so no need to queue the master restart. */
+ mysql_mutex_unlock(&thr->LOCK_rpl_thread);
+ return 0;
+ }
+
+ if (!(qev= thr->get_qev(fdev, 0, rli)))
+ {
+ mysql_mutex_unlock(&thr->LOCK_rpl_thread);
+ return 1;
+ }
+
+ qev->rgi= rgi;
+ qev->typ= rpl_parallel_thread::queued_event::QUEUED_MASTER_RESTART;
+ qev->entry_for_queued= this;
+ qev->ir= rli->last_inuse_relaylog;
+ ++qev->ir->queued_count;
+ thr->enqueue(qev);
+ mysql_cond_signal(&thr->COND_rpl_thread);
+ mysql_mutex_unlock(&thr->LOCK_rpl_thread);
+ return 0;
+}
+
+
+int
+rpl_parallel::wait_for_workers_idle(THD *thd)
+{
+ uint32 i, max_i;
+
+ /*
+ The domain_hash is only accessed by the SQL driver thread, so it is safe
+ to iterate over without a lock.
+ */
+ max_i= domain_hash.records;
+ for (i= 0; i < max_i; ++i)
+ {
+ PSI_stage_info old_stage;
+ struct rpl_parallel_entry *e;
+ int err= 0;
+
+ e= (struct rpl_parallel_entry *)my_hash_element(&domain_hash, i);
+ mysql_mutex_lock(&e->LOCK_parallel_entry);
+ ++e->need_sub_id_signal;
+ thd->ENTER_COND(&e->COND_parallel_entry, &e->LOCK_parallel_entry,
+ &stage_waiting_for_workers_idle, &old_stage);
+ while (e->current_sub_id > e->last_committed_sub_id)
+ {
+ if (thd->check_killed())
+ {
+ thd->send_kill_message();
+ err= 1;
+ break;
+ }
+ mysql_cond_wait(&e->COND_parallel_entry, &e->LOCK_parallel_entry);
+ }
+ --e->need_sub_id_signal;
+ thd->EXIT_COND(&old_stage);
+ if (err)
+ return err;
+ }
+ return 0;
+}
+
+
+/*
+ Handle seeing a GTID during slave restart in GTID mode. If we stopped with
+ different replication domains having reached different positions in the relay
+ log, we need to skip event groups in domains that are further progressed.
+
+ Updates the state with the seen GTID, and returns true if this GTID should
+ be skipped, false otherwise.
+*/
+bool
+process_gtid_for_restart_pos(Relay_log_info *rli, rpl_gtid *gtid)
+{
+ slave_connection_state::entry *gtid_entry;
+ slave_connection_state *state= &rli->restart_gtid_pos;
+
+ if (likely(state->count() == 0) ||
+ !(gtid_entry= state->find_entry(gtid->domain_id)))
+ return false;
+ if (gtid->server_id == gtid_entry->gtid.server_id)
+ {
+ uint64 seq_no= gtid_entry->gtid.seq_no;
+ if (gtid->seq_no >= seq_no)
+ {
+ /*
+ This domain has reached its start position. So remove it, so that
+ further events will be processed normally.
+ */
+ state->remove(&gtid_entry->gtid);
+ }
+ return gtid->seq_no <= seq_no;
+ }
+ else
+ return true;
+}
+
+
+/*
+ This is used when we get an error during processing in do_event();
+ We will not queue any event to the thread, but we still need to wake it up
+ to be sure that it will be returned to the pool.
+*/
+static void
+abandon_worker_thread(THD *thd, rpl_parallel_thread *cur_thread,
+ bool *did_enter_cond, PSI_stage_info *old_stage)
+{
+ unlock_or_exit_cond(thd, &cur_thread->LOCK_rpl_thread,
+ did_enter_cond, old_stage);
+ mysql_cond_signal(&cur_thread->COND_rpl_thread);
+}
+
+
+/*
+ do_event() is executed by the sql_driver_thd thread.
+ It's main purpose is to find a thread that can execute the query.
+
+ @retval 0 ok, event was accepted
+ @retval 1 error
+ @retval -1 event should be executed serially, in the sql driver thread
+*/
+
+int
+rpl_parallel::do_event(rpl_group_info *serial_rgi, Log_event *ev,
+ ulonglong event_size)
+{
+ rpl_parallel_entry *e;
+ rpl_parallel_thread *cur_thread;
+ rpl_parallel_thread::queued_event *qev;
+ rpl_group_info *rgi= NULL;
+ Relay_log_info *rli= serial_rgi->rli;
+ enum Log_event_type typ;
+ bool is_group_event;
+ bool did_enter_cond= false;
+ PSI_stage_info old_stage;
+
+ /* Handle master log name change, seen in Rotate_log_event. */
+ typ= ev->get_type_code();
+ if (unlikely(typ == ROTATE_EVENT))
+ {
+ Rotate_log_event *rev= static_cast<Rotate_log_event *>(ev);
+ if ((rev->server_id != global_system_variables.server_id ||
+ rli->replicate_same_server_id) &&
+ !rev->is_relay_log_event() &&
+ !rli->is_in_group())
+ {
+ memcpy(rli->future_event_master_log_name,
+ rev->new_log_ident, rev->ident_len+1);
+ rli->notify_group_master_log_name_update();
+ }
+ }
+
+ /*
+ Execute queries non-parallel if slave_skip_counter is set, as it's is
+ easier to skip queries in single threaded mode.
+ */
+ if (rli->slave_skip_counter)
+ return -1;
+
+ /* Execute pre-10.0 event, which have no GTID, in single-threaded mode. */
+ is_group_event= Log_event::is_group_event(typ);
+ if (unlikely(!current) && typ != GTID_EVENT &&
+ !(unlikely(rli->gtid_skip_flag != GTID_SKIP_NOT) && is_group_event))
+ return -1;
+
+ /* ToDo: what to do with this lock?!? */
+ mysql_mutex_unlock(&rli->data_lock);
+
+ if (unlikely(typ == FORMAT_DESCRIPTION_EVENT))
+ {
+ Format_description_log_event *fdev=
+ static_cast<Format_description_log_event *>(ev);
+ if (fdev->created)
+ {
+ /*
+ This format description event marks a new binlog after a master server
+ restart. We are going to close all temporary tables to clean up any
+ possible left-overs after a prior master crash.
+
+ Thus we need to wait for all prior events to execute to completion,
+ in case they need access to any of the temporary tables.
+
+ We also need to notify the worker thread running the prior incomplete
+ event group (if any), as such event group signifies an incompletely
+ written group cut short by a master crash, and must be rolled back.
+ */
+ if (current->queue_master_restart(serial_rgi, fdev) ||
+ wait_for_workers_idle(rli->sql_driver_thd))
+ {
+ delete ev;
+ return 1;
+ }
+ }
+ }
+ else if (unlikely(typ == GTID_LIST_EVENT))
+ {
+ Gtid_list_log_event *glev= static_cast<Gtid_list_log_event *>(ev);
+ rpl_gtid *list= glev->list;
+ uint32 count= glev->count;
+ rli->update_relay_log_state(list, count);
+ while (count)
+ {
+ process_gtid_for_restart_pos(rli, list);
+ ++list;
+ --count;
+ }
+ }
+
+ /*
+ Stop queueing additional event groups once the SQL thread is requested to
+ stop.
+
+ We have to queue any remaining events of any event group that has already
+ been partially queued, but after that we will just ignore any further
+ events the SQL driver thread may try to queue, and eventually it will stop.
+ */
+ if ((typ == GTID_EVENT || !is_group_event) && rli->abort_slave)
+ sql_thread_stopping= true;
+ if (sql_thread_stopping)
+ {
+ delete ev;
+ /*
+ Return "no error"; normal stop is not an error, and otherwise the error
+ has already been recorded.
+ */
+ return 0;
+ }
+
+ if (unlikely(rli->gtid_skip_flag != GTID_SKIP_NOT) && is_group_event)
+ {
+ if (typ == GTID_EVENT)
+ rli->gtid_skip_flag= GTID_SKIP_NOT;
+ else
+ {
+ if (rli->gtid_skip_flag == GTID_SKIP_STANDALONE)
+ {
+ if (!Log_event::is_part_of_group(typ))
+ rli->gtid_skip_flag= GTID_SKIP_NOT;
+ }
+ else
+ {
+ DBUG_ASSERT(rli->gtid_skip_flag == GTID_SKIP_TRANSACTION);
+ if (typ == XID_EVENT ||
+ (typ == QUERY_EVENT &&
+ (((Query_log_event *)ev)->is_commit() ||
+ ((Query_log_event *)ev)->is_rollback())))
+ rli->gtid_skip_flag= GTID_SKIP_NOT;
+ }
+ delete_or_keep_event_post_apply(serial_rgi, typ, ev);
+ return 0;
+ }
+ }
+
+ if (typ == GTID_EVENT)
+ {
+ rpl_gtid gtid;
+ Gtid_log_event *gtid_ev= static_cast<Gtid_log_event *>(ev);
+ uint32 domain_id= (rli->mi->using_gtid == Master_info::USE_GTID_NO ?
+ 0 : gtid_ev->domain_id);
+ if (!(e= find(domain_id)))
+ {
+ my_error(ER_OUT_OF_RESOURCES, MYF(MY_WME));
+ delete ev;
+ return 1;
+ }
+ current= e;
+
+ gtid.domain_id= gtid_ev->domain_id;
+ gtid.server_id= gtid_ev->server_id;
+ gtid.seq_no= gtid_ev->seq_no;
+ rli->update_relay_log_state(&gtid, 1);
+ if (process_gtid_for_restart_pos(rli, &gtid))
+ {
+ /*
+ This domain has progressed further into the relay log before the last
+ SQL thread restart. So we need to skip this event group to not doubly
+ apply it.
+ */
+ rli->gtid_skip_flag= ((gtid_ev->flags2 & Gtid_log_event::FL_STANDALONE) ?
+ GTID_SKIP_STANDALONE : GTID_SKIP_TRANSACTION);
+ delete_or_keep_event_post_apply(serial_rgi, typ, ev);
+ return 0;
+ }
+ }
+ else
+ e= current;
+
+ /*
+ Find a worker thread to queue the event for.
+ Prefer a new thread, so we maximise parallelism (at least for the group
+ commit). But do not exceed a limit of --slave-domain-parallel-threads;
+ instead re-use a thread that we queued for previously.
+ */
+ cur_thread=
+ e->choose_thread(serial_rgi, &did_enter_cond, &old_stage,
+ typ != GTID_EVENT);
+ if (!cur_thread)
+ {
+ /* This means we were killed. The error is already signalled. */
+ delete ev;
+ return 1;
+ }
+
+ if (!(qev= cur_thread->get_qev(ev, event_size, rli)))
+ {
+ abandon_worker_thread(rli->sql_driver_thd, cur_thread,
+ &did_enter_cond, &old_stage);
+ delete ev;
+ return 1;
+ }
+
+ if (typ == GTID_EVENT)
+ {
+ Gtid_log_event *gtid_ev= static_cast<Gtid_log_event *>(ev);
+
+ if (!(rgi= cur_thread->get_rgi(rli, gtid_ev, e, event_size)))
+ {
+ cur_thread->free_qev(qev);
+ abandon_worker_thread(rli->sql_driver_thd, cur_thread,
+ &did_enter_cond, &old_stage);
+ delete ev;
+ return 1;
+ }
+
+ /*
+ We queue the event group in a new worker thread, to run in parallel
+ with previous groups.
+
+ To preserve commit order within the replication domain, we set up
+ rgi->wait_commit_sub_id to make the new group commit only after the
+ previous group has committed.
+
+ Event groups that group-committed together on the master can be run
+ in parallel with each other without restrictions. But one batch of
+ group-commits may not start before all groups in the previous batch
+ have initiated their commit phase; we set up rgi->gco to ensure that.
+ */
+ rgi->wait_commit_sub_id= e->current_sub_id;
+ rgi->wait_commit_group_info= e->current_group_info;
+
+ if (!((gtid_ev->flags2 & Gtid_log_event::FL_GROUP_COMMIT_ID) &&
+ e->last_commit_id == gtid_ev->commit_id))
+ {
+ /*
+ A new batch of transactions that group-committed together on the master.
+
+ Remember the count that marks the end of the previous group committed
+ batch, and allocate a new gco.
+ */
+ uint64 count= e->count_queued_event_groups;
+ group_commit_orderer *gco;
+
+ if (!(gco= cur_thread->get_gco(count, e->current_gco, e->current_sub_id)))
+ {
+ cur_thread->free_rgi(rgi);
+ cur_thread->free_qev(qev);
+ abandon_worker_thread(rli->sql_driver_thd, cur_thread,
+ &did_enter_cond, &old_stage);
+ delete ev;
+ return 1;
+ }
+ e->current_gco= rgi->gco= gco;
+ }
+ else
+ rgi->gco= e->current_gco;
+ if (gtid_ev->flags2 & Gtid_log_event::FL_GROUP_COMMIT_ID)
+ e->last_commit_id= gtid_ev->commit_id;
+ else
+ e->last_commit_id= 0;
+ qev->rgi= e->current_group_info= rgi;
+ e->current_sub_id= rgi->gtid_sub_id;
+ ++e->count_queued_event_groups;
+ }
+ else if (!is_group_event)
+ {
+ int err;
+ bool tmp;
+ /*
+ Events like ROTATE and FORMAT_DESCRIPTION. Do not run in worker thread.
+ Same for events not preceeded by GTID (we should not see those normally,
+ but they might be from an old master).
+ */
+ qev->rgi= serial_rgi;
+
+ tmp= serial_rgi->is_parallel_exec;
+ serial_rgi->is_parallel_exec= true;
+ err= rpt_handle_event(qev, NULL);
+ serial_rgi->is_parallel_exec= tmp;
+ if (ev->is_relay_log_event())
+ qev->future_event_master_log_pos= 0;
+ else if (typ == ROTATE_EVENT)
+ qev->future_event_master_log_pos=
+ (static_cast<Rotate_log_event *>(ev))->pos;
+ else
+ qev->future_event_master_log_pos= ev->log_pos;
+ delete_or_keep_event_post_apply(serial_rgi, typ, ev);
+
+ if (err)
+ {
+ cur_thread->free_qev(qev);
+ abandon_worker_thread(rli->sql_driver_thd, cur_thread,
+ &did_enter_cond, &old_stage);
+ return 1;
+ }
+ /*
+ Queue a position update, so that the position will be updated in a
+ reasonable way relative to other events:
+
+ - If the currently executing events are queued serially for a single
+ thread, the position will only be updated when everything before has
+ completed.
+
+ - If we are executing multiple independent events in parallel, then at
+ least the position will not be updated until one of them has reached
+ the current point.
+ */
+ qev->typ= rpl_parallel_thread::queued_event::QUEUED_POS_UPDATE;
+ qev->entry_for_queued= e;
+ }
+ else
+ {
+ qev->rgi= e->current_group_info;
+ }
+
+ /*
+ Queue the event for processing.
+ */
+ rli->event_relay_log_pos= rli->future_event_relay_log_pos;
+ qev->ir= rli->last_inuse_relaylog;
+ ++qev->ir->queued_count;
+ cur_thread->enqueue(qev);
+ unlock_or_exit_cond(rli->sql_driver_thd, &cur_thread->LOCK_rpl_thread,
+ &did_enter_cond, &old_stage);
+ mysql_cond_signal(&cur_thread->COND_rpl_thread);
+
+ return 0;
+}
diff --git a/sql/rpl_parallel.h b/sql/rpl_parallel.h
new file mode 100644
index 00000000000..3012daa8763
--- /dev/null
+++ b/sql/rpl_parallel.h
@@ -0,0 +1,351 @@
+#ifndef RPL_PARALLEL_H
+#define RPL_PARALLEL_H
+
+#include "log_event.h"
+
+
+struct rpl_parallel;
+struct rpl_parallel_entry;
+struct rpl_parallel_thread_pool;
+
+class Relay_log_info;
+struct inuse_relaylog;
+
+
+/*
+ Structure used to keep track of the parallel replication of a batch of
+ event-groups that group-committed together on the master.
+
+ It is used to ensure that every event group in one batch has reached the
+ commit stage before the next batch starts executing.
+
+ Note the lifetime of this structure:
+
+ - It is allocated when the first event in a new batch of group commits
+ is queued, from the free list rpl_parallel_entry::gco_free_list.
+
+ - The gco for the batch currently being queued is owned by
+ rpl_parallel_entry::current_gco. The gco for a previous batch that has
+ been fully queued is owned by the gco->prev_gco pointer of the gco for
+ the following batch.
+
+ - The worker thread waits on gco->COND_group_commit_orderer for
+ rpl_parallel_entry::count_committing_event_groups to reach wait_count
+ before starting; the first waiter links the gco into the next_gco
+ pointer of the gco of the previous batch for signalling.
+
+ - When an event group reaches the commit stage, it signals the
+ COND_group_commit_orderer if its gco->next_gco pointer is non-NULL and
+ rpl_parallel_entry::count_committing_event_groups has reached
+ gco->next_gco->wait_count.
+
+ - The gco lives until all its event groups have completed their commit.
+ This is detected by rpl_parallel_entry::last_committed_sub_id being
+ greater than or equal gco->last_sub_id. Once this happens, the gco is
+ freed. Note that since update of last_committed_sub_id can happen
+ out-of-order, the thread that frees a given gco can be for any later
+ event group, not necessarily an event group from the gco being freed.
+*/
+struct group_commit_orderer {
+ /* Wakeup condition, used with rpl_parallel_entry::LOCK_parallel_entry. */
+ mysql_cond_t COND_group_commit_orderer;
+ uint64 wait_count;
+ group_commit_orderer *prev_gco;
+ group_commit_orderer *next_gco;
+ /*
+ The sub_id of last event group in the previous GCO.
+ Only valid if prev_gco != NULL.
+ */
+ uint64 prior_sub_id;
+ /*
+ The sub_id of the last event group in this GCO. Only valid when next_gco
+ is non-NULL.
+ */
+ uint64 last_sub_id;
+ bool installed;
+};
+
+
+struct rpl_parallel_thread {
+ bool delay_start;
+ bool running;
+ bool stop;
+ bool pause_for_ftwrl;
+ mysql_mutex_t LOCK_rpl_thread;
+ mysql_cond_t COND_rpl_thread;
+ mysql_cond_t COND_rpl_thread_queue;
+ mysql_cond_t COND_rpl_thread_stop;
+ struct rpl_parallel_thread *next; /* For free list. */
+ struct rpl_parallel_thread_pool *pool;
+ THD *thd;
+ /*
+ Who owns the thread, if any (it's a pointer into the
+ rpl_parallel_entry::rpl_threads array.
+ */
+ struct rpl_parallel_thread **current_owner;
+ /* The rpl_parallel_entry of the owner. */
+ rpl_parallel_entry *current_entry;
+ struct queued_event {
+ queued_event *next;
+ /*
+ queued_event can hold either an event to be executed, or just a binlog
+ position to be updated without any associated event.
+ */
+ enum queued_event_t {
+ QUEUED_EVENT,
+ QUEUED_POS_UPDATE,
+ QUEUED_MASTER_RESTART
+ } typ;
+ union {
+ Log_event *ev; /* QUEUED_EVENT */
+ rpl_parallel_entry *entry_for_queued; /* QUEUED_POS_UPDATE and
+ QUEUED_MASTER_RESTART */
+ };
+ rpl_group_info *rgi;
+ inuse_relaylog *ir;
+ ulonglong future_event_relay_log_pos;
+ char event_relay_log_name[FN_REFLEN];
+ char future_event_master_log_name[FN_REFLEN];
+ ulonglong event_relay_log_pos;
+ my_off_t future_event_master_log_pos;
+ size_t event_size;
+ } *event_queue, *last_in_queue;
+ uint64 queued_size;
+ /* These free lists are protected by LOCK_rpl_thread. */
+ queued_event *qev_free_list;
+ rpl_group_info *rgi_free_list;
+ group_commit_orderer *gco_free_list;
+ /*
+ These free lists are local to the thread, so need not be protected by any
+ lock. They are moved to the global free lists in batches in the function
+ batch_free(), to reduce LOCK_rpl_thread contention.
+
+ The lists are not NULL-terminated (as we do not need to traverse them).
+ Instead, if they are non-NULL, the loc_XXX_last_ptr_ptr points to the
+ `next' pointer of the last element, which is used to link into the front
+ of the global freelists.
+ */
+ queued_event *loc_qev_list, **loc_qev_last_ptr_ptr;
+ size_t loc_qev_size;
+ uint64 qev_free_pending;
+ rpl_group_info *loc_rgi_list, **loc_rgi_last_ptr_ptr;
+ group_commit_orderer *loc_gco_list, **loc_gco_last_ptr_ptr;
+ /* These keep track of batch update of inuse_relaylog refcounts. */
+ inuse_relaylog *accumulated_ir_last;
+ uint64 accumulated_ir_count;
+
+ void enqueue(queued_event *qev)
+ {
+ if (last_in_queue)
+ last_in_queue->next= qev;
+ else
+ event_queue= qev;
+ last_in_queue= qev;
+ queued_size+= qev->event_size;
+ }
+
+ void dequeue1(queued_event *list)
+ {
+ DBUG_ASSERT(list == event_queue);
+ event_queue= last_in_queue= NULL;
+ }
+
+ void dequeue2(size_t dequeue_size)
+ {
+ queued_size-= dequeue_size;
+ }
+
+ queued_event *get_qev_common(Log_event *ev, ulonglong event_size);
+ queued_event *get_qev(Log_event *ev, ulonglong event_size,
+ Relay_log_info *rli);
+ queued_event *retry_get_qev(Log_event *ev, queued_event *orig_qev,
+ const char *relay_log_name,
+ ulonglong event_pos, ulonglong event_size);
+ /*
+ Put a qev on the local free list, to be later released to the global free
+ list by batch_free().
+ */
+ void loc_free_qev(queued_event *qev);
+ /*
+ Release an rgi immediately to the global free list. Requires holding the
+ LOCK_rpl_thread mutex.
+ */
+ void free_qev(queued_event *qev);
+ rpl_group_info *get_rgi(Relay_log_info *rli, Gtid_log_event *gtid_ev,
+ rpl_parallel_entry *e, ulonglong event_size);
+ /*
+ Put an gco on the local free list, to be later released to the global free
+ list by batch_free().
+ */
+ void loc_free_rgi(rpl_group_info *rgi);
+ /*
+ Release an rgi immediately to the global free list. Requires holding the
+ LOCK_rpl_thread mutex.
+ */
+ void free_rgi(rpl_group_info *rgi);
+ group_commit_orderer *get_gco(uint64 wait_count, group_commit_orderer *prev,
+ uint64 first_sub_id);
+ /*
+ Put a gco on the local free list, to be later released to the global free
+ list by batch_free().
+ */
+ void loc_free_gco(group_commit_orderer *gco);
+ /*
+ Move all local free lists to the global ones. Requires holding
+ LOCK_rpl_thread.
+ */
+ void batch_free();
+ /* Update inuse_relaylog refcounts with what we have accumulated so far. */
+ void inuse_relaylog_refcount_update();
+};
+
+
+struct rpl_parallel_thread_pool {
+ struct rpl_parallel_thread **threads;
+ struct rpl_parallel_thread *free_list;
+ mysql_mutex_t LOCK_rpl_thread_pool;
+ mysql_cond_t COND_rpl_thread_pool;
+ uint32 count;
+ bool inited;
+ /*
+ While FTWRL runs, this counter is incremented to make SQL thread or
+ STOP/START slave not try to start new activity while that operation
+ is in progress.
+ */
+ bool busy;
+
+ rpl_parallel_thread_pool();
+ int init(uint32 size);
+ void destroy();
+ struct rpl_parallel_thread *get_thread(rpl_parallel_thread **owner,
+ rpl_parallel_entry *entry);
+ void release_thread(rpl_parallel_thread *rpt);
+};
+
+
+struct rpl_parallel_entry {
+ mysql_mutex_t LOCK_parallel_entry;
+ mysql_cond_t COND_parallel_entry;
+ uint32 domain_id;
+ /*
+ Incremented by wait_for_workers_idle() and rpl_pause_for_ftwrl() to show
+ that they are waiting, so that finish_event_group knows to signal them
+ when last_committed_sub_id is increased.
+ */
+ uint32 need_sub_id_signal;
+ uint64 last_commit_id;
+ bool active;
+ /*
+ Set when SQL thread is shutting down, and no more events can be processed,
+ so worker threads must force abort any current transactions without
+ waiting for event groups to complete.
+ */
+ bool force_abort;
+ /*
+ At STOP SLAVE (force_abort=true), we do not want to process all events in
+ the queue (which could unnecessarily delay stop, if a lot of events happen
+ to be queued). The stop_count provides a safe point at which to stop, so
+ that everything before becomes committed and nothing after does. The value
+ corresponds to group_commit_orderer::wait_count; if wait_count is less than
+ or equal to stop_count, we execute the associated event group, else we
+ skip it (and all following) and stop.
+ */
+ uint64 stop_count;
+
+ /*
+ Cyclic array recording the last rpl_thread_max worker threads that we
+ queued event for. This is used to limit how many workers a single domain
+ can occupy (--slave-domain-parallel-threads).
+
+ Note that workers are never explicitly deleted from the array. Instead,
+ we need to check (under LOCK_rpl_thread) that the thread still belongs
+ to us before re-using (rpl_thread::current_owner).
+ */
+ rpl_parallel_thread **rpl_threads;
+ uint32 rpl_thread_max;
+ uint32 rpl_thread_idx;
+ /*
+ The sub_id of the last transaction to commit within this domain_id.
+ Must be accessed under LOCK_parallel_entry protection.
+
+ Event groups commit in order, so the rpl_group_info for an event group
+ will be alive (at least) as long as
+ rpl_group_info::gtid_sub_id > last_committed_sub_id. This can be used to
+ safely refer back to previous event groups if they are still executing,
+ and ignore them if they completed, without requiring explicit
+ synchronisation between the threads.
+ */
+ uint64 last_committed_sub_id;
+ /*
+ The sub_id of the last event group in this replication domain that was
+ queued for execution by a worker thread.
+ */
+ uint64 current_sub_id;
+ /*
+ The largest sub_id that has started its transaction. Protected by
+ LOCK_parallel_entry.
+
+ (Transactions can start out-of-order, so this value signifies that no
+ transactions with larger sub_id have started, but not necessarily that all
+ transactions with smaller sub_id have started).
+ */
+ uint64 largest_started_sub_id;
+ rpl_group_info *current_group_info;
+ /*
+ If we get an error in some event group, we set the sub_id of that event
+ group here. Then later event groups (with higher sub_id) can know not to
+ try to start (event groups that already started will be rolled back when
+ wait_for_prior_commit() returns error).
+ The value is ULONGLONG_MAX when no error occured.
+ */
+ uint64 stop_on_error_sub_id;
+ /*
+ During FLUSH TABLES WITH READ LOCK, transactions with sub_id larger than
+ this value must not start, but wait until the global read lock is released.
+ The value is set to ULONGLONG_MAX when no FTWRL is pending.
+ */
+ uint64 pause_sub_id;
+ /* Total count of event groups queued so far. */
+ uint64 count_queued_event_groups;
+ /*
+ Count of event groups that have started (but not necessarily completed)
+ the commit phase. We use this to know when every event group in a previous
+ batch of master group commits have started committing on the slave, so
+ that it is safe to start executing the events in the following batch.
+ */
+ uint64 count_committing_event_groups;
+ /* The group_commit_orderer object for the events currently being queued. */
+ group_commit_orderer *current_gco;
+
+ rpl_parallel_thread * choose_thread(rpl_group_info *rgi, bool *did_enter_cond,
+ PSI_stage_info *old_stage, bool reuse);
+ int queue_master_restart(rpl_group_info *rgi,
+ Format_description_log_event *fdev);
+};
+struct rpl_parallel {
+ HASH domain_hash;
+ rpl_parallel_entry *current;
+ bool sql_thread_stopping;
+
+ rpl_parallel();
+ ~rpl_parallel();
+ void reset();
+ rpl_parallel_entry *find(uint32 domain_id);
+ void wait_for_done(THD *thd, Relay_log_info *rli);
+ void stop_during_until();
+ bool workers_idle();
+ int wait_for_workers_idle(THD *thd);
+ int do_event(rpl_group_info *serial_rgi, Log_event *ev, ulonglong event_size);
+};
+
+
+extern struct rpl_parallel_thread_pool global_rpl_thread_pool;
+
+
+extern int rpl_parallel_activate_pool(rpl_parallel_thread_pool *pool);
+extern int rpl_parallel_inactivate_pool(rpl_parallel_thread_pool *pool);
+extern bool process_gtid_for_restart_pos(Relay_log_info *rli, rpl_gtid *gtid);
+extern int rpl_pause_for_ftwrl(THD *thd);
+extern void rpl_unpause_after_ftwrl(THD *thd);
+
+#endif /* RPL_PARALLEL_H */
diff --git a/sql/rpl_record.cc b/sql/rpl_record.cc
index b3b3e836638..633e71a963c 100644
--- a/sql/rpl_record.cc
+++ b/sql/rpl_record.cc
@@ -14,6 +14,7 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+#include <my_global.h>
#include "sql_priv.h"
#include "unireg.h"
#include "rpl_rli.h"
@@ -189,7 +190,7 @@ pack_row(TABLE *table, MY_BITMAP const* cols,
*/
#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
int
-unpack_row(Relay_log_info const *rli,
+unpack_row(rpl_group_info *rgi,
TABLE *table, uint const colcnt,
uchar const *const row_data, MY_BITMAP const *cols,
uchar const **const current_row_end, ulong *const master_reclength,
@@ -217,18 +218,18 @@ unpack_row(Relay_log_info const *rli,
uint i= 0;
table_def *tabledef= NULL;
TABLE *conv_table= NULL;
- bool table_found= rli && rli->get_table_data(table, &tabledef, &conv_table);
+ bool table_found= rgi && rgi->get_table_data(table, &tabledef, &conv_table);
DBUG_PRINT("debug", ("Table data: table_found: %d, tabldef: %p, conv_table: %p",
table_found, tabledef, conv_table));
DBUG_ASSERT(table_found);
/*
- If rli is NULL it means that there is no source table and that the
+ If rgi is NULL it means that there is no source table and that the
row shall just be unpacked without doing any checks. This feature
is used by MySQL Backup, but can be used for other purposes as
well.
*/
- if (rli && !table_found)
+ if (rgi && !table_found)
DBUG_RETURN(HA_ERR_GENERIC);
for (field_ptr= begin_ptr ; field_ptr < end_ptr && *field_ptr ; ++field_ptr)
@@ -290,7 +291,7 @@ unpack_row(Relay_log_info const *rli,
else
{
f->set_default();
- push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
ER_BAD_NULL_ERROR, ER(ER_BAD_NULL_ERROR),
f->field_name);
}
@@ -311,7 +312,7 @@ unpack_row(Relay_log_info const *rli,
#ifdef WITH_WSREP
uchar const *const old_pack_ptr= pack_ptr;
#endif /* WITH_WSREP */
-#endif /* !DBUG_OFF */
+#endif /* !DBUF_OFF */
pack_ptr= f->unpack(f->ptr, pack_ptr, row_end, metadata);
DBUG_PRINT("debug", ("field: %s; metadata: 0x%x;"
" pack_ptr: 0x%lx; pack_ptr': 0x%lx; bytes: %d",
@@ -322,7 +323,7 @@ unpack_row(Relay_log_info const *rli,
{
#ifdef WITH_WSREP
/*
- Debug message to troubleshoot bug
+ Debug message to troubleshoot bug:
https://mariadb.atlassian.net/browse/MDEV-4404
*/
WSREP_WARN("ROW event unpack field: %s metadata: 0x%x;"
@@ -334,7 +335,8 @@ unpack_row(Relay_log_info const *rli,
);
#endif /* WITH_WSREP */
- rli->report(ERROR_LEVEL, ER_SLAVE_CORRUPT_EVENT,
+ rgi->rli->report(ERROR_LEVEL, ER_SLAVE_CORRUPT_EVENT,
+ rgi->gtid_info(),
"Could not read field '%s' of table '%s.%s'",
f->field_name, table->s->db.str,
table->s->table_name.str);
@@ -383,7 +385,7 @@ unpack_row(Relay_log_info const *rli,
/*
throw away master's extra fields
*/
- uint max_cols= min(tabledef->size(), cols->n_bits);
+ uint max_cols= MY_MIN(tabledef->size(), cols->n_bits);
for (; i < max_cols; i++)
{
if (bitmap_is_set(cols, i))
@@ -468,7 +470,7 @@ int prepare_record(TABLE *const table, const uint skip, const bool check)
{
f->set_default();
push_warning_printf(current_thd,
- MYSQL_ERROR::WARN_LEVEL_WARN,
+ Sql_condition::WARN_LEVEL_WARN,
ER_NO_DEFAULT_FOR_FIELD,
ER(ER_NO_DEFAULT_FOR_FIELD),
f->field_name);
diff --git a/sql/rpl_record.h b/sql/rpl_record.h
index d4eb8986846..c10eb8225b0 100644
--- a/sql/rpl_record.h
+++ b/sql/rpl_record.h
@@ -20,7 +20,7 @@
#include <rpl_reporting.h>
#include "my_global.h" /* uchar */
-class Relay_log_info;
+struct rpl_group_info;
struct TABLE;
typedef struct st_bitmap MY_BITMAP;
@@ -30,7 +30,7 @@ size_t pack_row(TABLE* table, MY_BITMAP const* cols,
#endif
#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
-int unpack_row(Relay_log_info const *rli,
+int unpack_row(rpl_group_info *rgi,
TABLE *table, uint const colcnt,
uchar const *const row_data, MY_BITMAP const *cols,
uchar const **const curr_row_end, ulong *const master_reclength,
diff --git a/sql/rpl_record_old.cc b/sql/rpl_record_old.cc
index fa0c49b413c..5b876373b9c 100644
--- a/sql/rpl_record_old.cc
+++ b/sql/rpl_record_old.cc
@@ -13,8 +13,8 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+#include <my_global.h>
#include "sql_priv.h"
-#include "unireg.h" // REQUIRED by other includes
#include "rpl_rli.h"
#include "rpl_record_old.h"
#include "log_event.h" // Log_event_type
@@ -88,7 +88,7 @@ pack_row_old(TABLE *table, MY_BITMAP const* cols,
*/
#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
int
-unpack_row_old(Relay_log_info *rli,
+unpack_row_old(rpl_group_info *rgi,
TABLE *table, uint const colcnt, uchar *record,
uchar const *row, const uchar *row_buffer_end,
MY_BITMAP const *cols,
@@ -141,7 +141,7 @@ unpack_row_old(Relay_log_info *rli,
f->move_field_offset(-offset);
if (!ptr)
{
- rli->report(ERROR_LEVEL, ER_SLAVE_CORRUPT_EVENT,
+ rgi->rli->report(ERROR_LEVEL, ER_SLAVE_CORRUPT_EVENT, NULL,
"Could not read field `%s` of table `%s`.`%s`",
f->field_name, table->s->db.str,
table->s->table_name.str);
@@ -183,7 +183,7 @@ unpack_row_old(Relay_log_info *rli,
if (event_type == WRITE_ROWS_EVENT &&
((*field_ptr)->flags & mask) == mask)
{
- rli->report(ERROR_LEVEL, ER_NO_DEFAULT_FOR_FIELD,
+ rgi->rli->report(ERROR_LEVEL, ER_NO_DEFAULT_FOR_FIELD, NULL,
"Field `%s` of table `%s`.`%s` "
"has no default value and cannot be NULL",
(*field_ptr)->field_name, table->s->db.str,
diff --git a/sql/rpl_record_old.h b/sql/rpl_record_old.h
index ea981fb23c3..34ef9f11c47 100644
--- a/sql/rpl_record_old.h
+++ b/sql/rpl_record_old.h
@@ -23,7 +23,7 @@ size_t pack_row_old(TABLE *table, MY_BITMAP const* cols,
uchar *row_data, const uchar *record);
#ifdef HAVE_REPLICATION
-int unpack_row_old(Relay_log_info *rli,
+int unpack_row_old(rpl_group_info *rgi,
TABLE *table, uint const colcnt, uchar *record,
uchar const *row, uchar const *row_buffer_end,
MY_BITMAP const *cols,
diff --git a/sql/rpl_reporting.cc b/sql/rpl_reporting.cc
index ebb0dbec1cb..ad949402511 100644
--- a/sql/rpl_reporting.cc
+++ b/sql/rpl_reporting.cc
@@ -14,6 +14,7 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+#include <my_global.h>
#include "sql_priv.h"
#include "rpl_reporting.h"
#include "log.h" // sql_print_error, sql_print_warning,
@@ -28,6 +29,7 @@ Slave_reporting_capability::Slave_reporting_capability(char const *thread_name)
void
Slave_reporting_capability::report(loglevel level, int err_code,
+ const char *extra_info,
const char *msg, ...) const
{
void (*report_function)(const char *, ...);
@@ -68,9 +70,10 @@ Slave_reporting_capability::report(loglevel level, int err_code,
va_end(args);
/* If the msg string ends with '.', do not add a ',' it would be ugly */
- report_function("Slave %s: %s%s Error_code: %d",
+ report_function("Slave %s: %s%s %s%sInternal MariaDB error code: %d",
m_thread_name, pbuff,
(pbuff[0] && *(strend(pbuff)-1) == '.') ? "" : ",",
+ (extra_info ? extra_info : ""), (extra_info ? ", " : ""),
err_code);
}
diff --git a/sql/rpl_reporting.h b/sql/rpl_reporting.h
index 2b5e0527b9b..d90b7ad6650 100644
--- a/sql/rpl_reporting.h
+++ b/sql/rpl_reporting.h
@@ -52,8 +52,9 @@ public:
code, but can contain more information), in
printf() format.
*/
- void report(loglevel level, int err_code, const char *msg, ...) const
- ATTRIBUTE_FORMAT(printf, 4, 5);
+ void report(loglevel level, int err_code, const char *extra_info,
+ const char *msg, ...) const
+ ATTRIBUTE_FORMAT(printf, 5, 6);
/**
Clear errors. They will not show up under <code>SHOW SLAVE
diff --git a/sql/rpl_rli.cc b/sql/rpl_rli.cc
index 5007b8e1237..1fc92d4ecec 100644
--- a/sql/rpl_rli.cc
+++ b/sql/rpl_rli.cc
@@ -1,5 +1,5 @@
/* Copyright (c) 2006, 2013, Oracle and/or its affiliates.
- Copyright (c) 2011, 2013, Monty Program Ab
+ Copyright (c) 2010, 2013, Monty Program Ab
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -11,9 +11,10 @@
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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+ along with this program; if not, write to the Free Software Foundation,
+ 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */
+#include <my_global.h>
#include "sql_priv.h"
#include "unireg.h" // HAVE_*
#include "rpl_mi.h"
@@ -32,6 +33,15 @@
static int count_relay_log_space(Relay_log_info* rli);
+/**
+ Current replication state (hash of last GTID executed, per replication
+ domain).
+*/
+rpl_slave_state *rpl_global_gtid_slave_state;
+/* Object used for MASTER_GTID_WAIT(). */
+gtid_waiting rpl_global_gtid_waiting;
+
+
// Defined in slave.cc
int init_intvar_from_file(int* var, IO_CACHE* f, int default_val);
int init_strvar_from_file(char *var, int max_size, IO_CACHE *f,
@@ -42,23 +52,24 @@ Relay_log_info::Relay_log_info(bool is_slave_recovery)
no_storage(FALSE), replicate_same_server_id(::replicate_same_server_id),
info_fd(-1), cur_log_fd(-1), relay_log(&sync_relaylog_period),
sync_counter(0), is_relay_log_recovery(is_slave_recovery),
- save_temporary_tables(0), cur_log_old_open_count(0), group_relay_log_pos(0),
+ save_temporary_tables(0), mi(0),
+ inuse_relaylog_list(0), last_inuse_relaylog(0),
+ cur_log_old_open_count(0), group_relay_log_pos(0),
event_relay_log_pos(0),
#if HAVE_valgrind
is_fake(FALSE),
#endif
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),
- tables_to_lock(0), tables_to_lock_count(0),
- last_event_start_time(0), deferred_events(NULL),m_flags(0),
- row_stmt_start_timestamp(0), long_find_row_note_printed(false),
- m_annotate_event(0)
+ last_master_timestamp(0), sql_thread_caught_up(true), slave_skip_counter(0),
+ abort_pos_wait(0), slave_run_id(0), sql_driver_thd(),
+ gtid_skip_flag(GTID_SKIP_NOT), inited(0), abort_slave(0), stop_for_until(0),
+ slave_running(MYSQL_SLAVE_NOT_RUN), until_condition(UNTIL_NONE),
+ until_log_pos(0), retried_trans(0), executed_entries(0),
+ m_flags(0)
{
DBUG_ENTER("Relay_log_info::Relay_log_info");
+ relay_log.is_relay_log= TRUE;
#ifdef HAVE_PSI_INTERFACE
relay_log.set_psi_keys(key_RELAYLOG_LOCK_index,
key_RELAYLOG_update_cond,
@@ -70,20 +81,18 @@ Relay_log_info::Relay_log_info(bool is_slave_recovery)
group_relay_log_name[0]= event_relay_log_name[0]=
group_master_log_name[0]= 0;
until_log_name[0]= ign_master_log_name_end[0]= 0;
+ max_relay_log_size= global_system_variables.max_relay_log_size;
bzero((char*) &info_file, sizeof(info_file));
bzero((char*) &cache_buf, sizeof(cache_buf));
- cached_charset_invalidate();
mysql_mutex_init(key_relay_log_info_run_lock, &run_lock, MY_MUTEX_INIT_FAST);
mysql_mutex_init(key_relay_log_info_data_lock,
&data_lock, MY_MUTEX_INIT_FAST);
mysql_mutex_init(key_relay_log_info_log_space_lock,
&log_space_lock, MY_MUTEX_INIT_FAST);
- mysql_mutex_init(key_relay_log_info_sleep_lock, &sleep_lock, MY_MUTEX_INIT_FAST);
mysql_cond_init(key_relay_log_info_data_cond, &data_cond, NULL);
mysql_cond_init(key_relay_log_info_start_cond, &start_cond, NULL);
mysql_cond_init(key_relay_log_info_stop_cond, &stop_cond, NULL);
mysql_cond_init(key_relay_log_info_log_space_cond, &log_space_cond, NULL);
- mysql_cond_init(key_relay_log_info_sleep_cond, &sleep_cond, NULL);
relay_log.init_pthread_objects();
DBUG_VOID_RETURN;
}
@@ -93,17 +102,15 @@ Relay_log_info::~Relay_log_info()
{
DBUG_ENTER("Relay_log_info::~Relay_log_info");
+ reset_inuse_relaylog();
mysql_mutex_destroy(&run_lock);
mysql_mutex_destroy(&data_lock);
mysql_mutex_destroy(&log_space_lock);
- mysql_mutex_destroy(&sleep_lock);
mysql_cond_destroy(&data_cond);
mysql_cond_destroy(&start_cond);
mysql_cond_destroy(&stop_cond);
mysql_cond_destroy(&log_space_cond);
- mysql_cond_destroy(&sleep_cond);
relay_log.cleanup();
- free_annotate_event();
DBUG_VOID_RETURN;
}
@@ -128,8 +135,6 @@ int init_relay_log_info(Relay_log_info* rli,
rli->abort_pos_wait=0;
rli->log_space_limit= relay_log_space_limit;
rli->log_space_total= 0;
- rli->tables_to_lock= 0;
- rli->tables_to_lock_count= 0;
char pattern[FN_REFLEN];
(void) my_realpath(pattern, slave_load_tmpdir, 0);
@@ -150,15 +155,6 @@ int init_relay_log_info(Relay_log_info* rli,
event, in flush_master_info(mi, 1, ?).
*/
- /*
- For the maximum log size, we choose max_relay_log_size if it is
- non-zero, max_binlog_size otherwise. If later the user does SET
- GLOBAL on one of these variables, fix_max_binlog_size and
- fix_max_relay_log_size will reconsider the choice (for example
- if the user changes max_relay_log_size to zero, we have to
- switch to using max_binlog_size for the relay log) and update
- rli->relay_log.max_size (and mysql_bin_log.max_size).
- */
{
/* Reports an error and returns, if the --relay-log's path
is a directory.*/
@@ -208,19 +204,35 @@ a file name for --relay-log-index option", opt_relaylog_index_name);
name_warning_sent= 1;
}
- rli->relay_log.is_relay_log= TRUE;
+ /* For multimaster, add connection name to relay log filenames */
+ Master_info* mi= rli->mi;
+ char buf_relay_logname[FN_REFLEN], buf_relaylog_index_name_buff[FN_REFLEN];
+ char *buf_relaylog_index_name= opt_relaylog_index_name;
+
+ create_logfile_name_with_suffix(buf_relay_logname,
+ sizeof(buf_relay_logname),
+ ln, 1, &mi->cmp_connection_name);
+ ln= buf_relay_logname;
+
+ if (opt_relaylog_index_name)
+ {
+ buf_relaylog_index_name= buf_relaylog_index_name_buff;
+ create_logfile_name_with_suffix(buf_relaylog_index_name_buff,
+ sizeof(buf_relaylog_index_name_buff),
+ opt_relaylog_index_name, 0,
+ &mi->cmp_connection_name);
+ }
/*
note, that if open() fails, we'll still have index file open
but a destructor will take care of that
*/
- if (rli->relay_log.open_index_file(opt_relaylog_index_name, ln, TRUE) ||
- rli->relay_log.open(ln, LOG_BIN, 0, SEQ_READ_APPEND, 0,
- (max_relay_log_size ? max_relay_log_size :
- max_binlog_size), 1, TRUE))
+ if (rli->relay_log.open_index_file(buf_relaylog_index_name, ln, TRUE) ||
+ rli->relay_log.open(ln, LOG_BIN, 0, SEQ_READ_APPEND,
+ mi->rli.max_relay_log_size, 1, TRUE))
{
mysql_mutex_unlock(&rli->data_lock);
- sql_print_error("Failed in open_log() called from init_relay_log_info()");
+ sql_print_error("Failed when trying to open logs for '%s' in init_relay_log_info(). Error: %M", ln, my_errno);
DBUG_RETURN(1);
}
}
@@ -239,7 +251,7 @@ a file name for --relay-log-index option", opt_relaylog_index_name);
{
sql_print_error("Failed to create a new relay log info file (\
file '%s', errno %d)", fname, my_errno);
- msg= current_thd->stmt_da->message();
+ msg= current_thd->get_stmt_da()->message();
goto err;
}
if (init_io_cache(&rli->info_file, info_fd, IO_SIZE*2, READ_CACHE, 0L,0,
@@ -247,7 +259,7 @@ file '%s', errno %d)", fname, my_errno);
{
sql_print_error("Failed to create a cache on relay log info file '%s'",
fname);
- msg= current_thd->stmt_da->message();
+ msg= current_thd->get_stmt_da()->message();
goto err;
}
@@ -296,20 +308,80 @@ Failed to open the existing relay log info file '%s' (errno %d)",
}
rli->info_fd = info_fd;
- int relay_log_pos, master_log_pos;
+ int relay_log_pos, master_log_pos, lines;
+ char *first_non_digit;
+ /*
+ In MySQL 5.6, there is a MASTER_DELAY option to CHANGE MASTER. This is
+ not yet merged into MariaDB (as of 10.0.13). However, we detect the
+ presense of the new option in relay-log.info, as a placeholder for
+ possible later merge of the feature, and to maintain file format
+ compatibility with MySQL 5.6+.
+ */
+ int dummy_sql_delay;
+
+ /*
+ Starting from MySQL 5.6.x, relay-log.info has a new format.
+ Now, its first line contains the number of lines in the file.
+ By reading this number we can determine which version our master.info
+ comes from. We can't simply count the lines in the file, since
+ versions before 5.6.x could generate files with more lines than
+ needed. If first line doesn't contain a number, or if it
+ contains a number less than LINES_IN_RELAY_LOG_INFO_WITH_DELAY,
+ then the file is treated like a file from pre-5.6.x version.
+ There is no ambiguity when reading an old master.info: before
+ 5.6.x, the first line contained the binlog's name, which is
+ either empty or has an extension (contains a '.'), so can't be
+ confused with an integer.
+
+ So we're just reading first line and trying to figure which
+ version is this.
+ */
+
+ /*
+ The first row is temporarily stored in mi->master_log_name, if
+ it is line count and not binlog name (new format) it will be
+ overwritten by the second row later.
+ */
if (init_strvar_from_file(rli->group_relay_log_name,
sizeof(rli->group_relay_log_name),
+ &rli->info_file, ""))
+ {
+ msg="Error reading slave log configuration";
+ goto err;
+ }
+
+ lines= strtoul(rli->group_relay_log_name, &first_non_digit, 10);
+
+ if (rli->group_relay_log_name[0] != '\0' &&
+ *first_non_digit == '\0' &&
+ lines >= LINES_IN_RELAY_LOG_INFO_WITH_DELAY)
+ {
+ DBUG_PRINT("info", ("relay_log_info file is in new format."));
+ /* Seems to be new format => read relay log name from next line */
+ if (init_strvar_from_file(rli->group_relay_log_name,
+ sizeof(rli->group_relay_log_name),
+ &rli->info_file, ""))
+ {
+ msg="Error reading slave log configuration";
+ goto err;
+ }
+ }
+ else
+ DBUG_PRINT("info", ("relay_log_info file is in old format."));
+
+ if (init_intvar_from_file(&relay_log_pos,
+ &rli->info_file, BIN_LOG_HEADER_SIZE) ||
+ init_strvar_from_file(rli->group_master_log_name,
+ sizeof(rli->group_master_log_name),
&rli->info_file, "") ||
- init_intvar_from_file(&relay_log_pos,
- &rli->info_file, BIN_LOG_HEADER_SIZE) ||
- init_strvar_from_file(rli->group_master_log_name,
- sizeof(rli->group_master_log_name),
- &rli->info_file, "") ||
- init_intvar_from_file(&master_log_pos, &rli->info_file, 0))
+ init_intvar_from_file(&master_log_pos, &rli->info_file, 0) ||
+ (lines >= LINES_IN_RELAY_LOG_INFO_WITH_DELAY &&
+ init_intvar_from_file(&dummy_sql_delay, &rli->info_file, 0)))
{
msg="Error reading slave log configuration";
goto err;
}
+
strmake_buf(rli->event_relay_log_name,rli->group_relay_log_name);
rli->group_relay_log_pos= rli->event_relay_log_pos= relay_log_pos;
rli->group_master_log_pos= master_log_pos;
@@ -317,6 +389,7 @@ Failed to open the existing relay log info file '%s' (errno %d)",
if (rli->is_relay_log_recovery && init_recovery(rli->mi, &msg))
goto err;
+ rli->relay_log_state.load(rpl_global_gtid_slave_state);
if (init_relay_log_pos(rli,
rli->group_relay_log_name,
rli->group_relay_log_pos,
@@ -438,6 +511,90 @@ void Relay_log_info::clear_until_condition()
/*
+ Read the correct format description event for starting to replicate from
+ a given position in a relay log file.
+*/
+Format_description_log_event *
+read_relay_log_description_event(IO_CACHE *cur_log, ulonglong start_pos,
+ const char **errmsg)
+{
+ Log_event *ev;
+ Format_description_log_event *fdev;
+ bool found= false;
+
+ /*
+ By default the relay log is in binlog format 3 (4.0).
+ Even if format is 4, this will work enough to read the first event
+ (Format_desc) (remember that format 4 is just lenghtened compared to format
+ 3; format 3 is a prefix of format 4).
+ */
+ fdev= new Format_description_log_event(3);
+
+ while (!found)
+ {
+ Log_event_type typ;
+
+ /*
+ Read the possible Format_description_log_event; if position
+ was 4, no need, it will be read naturally.
+ */
+ DBUG_PRINT("info",("looking for a Format_description_log_event"));
+
+ if (my_b_tell(cur_log) >= start_pos)
+ break;
+
+ if (!(ev= Log_event::read_log_event(cur_log, 0, fdev,
+ opt_slave_sql_verify_checksum)))
+ {
+ DBUG_PRINT("info",("could not read event, cur_log->error=%d",
+ cur_log->error));
+ if (cur_log->error) /* not EOF */
+ {
+ *errmsg= "I/O error reading event at position 4";
+ delete fdev;
+ return NULL;
+ }
+ break;
+ }
+ typ= ev->get_type_code();
+ if (typ == FORMAT_DESCRIPTION_EVENT)
+ {
+ DBUG_PRINT("info",("found Format_description_log_event"));
+ delete fdev;
+ fdev= (Format_description_log_event*) ev;
+ /*
+ As ev was returned by read_log_event, it has passed is_valid(), so
+ my_malloc() in ctor worked, no need to check again.
+ */
+ /*
+ Ok, we found a Format_description event. But it is not sure that this
+ describes the whole relay log; indeed, one can have this sequence
+ (starting from position 4):
+ Format_desc (of slave)
+ Rotate (of master)
+ Format_desc (of master)
+ So the Format_desc which really describes the rest of the relay log
+ is the 3rd event (it can't be further than that, because we rotate
+ the relay log when we queue a Rotate event from the master).
+ But what describes the Rotate is the first Format_desc.
+ So what we do is:
+ go on searching for Format_description events, until you exceed the
+ position (argument 'pos') or until you find another event than Rotate
+ or Format_desc.
+ */
+ }
+ else
+ {
+ DBUG_PRINT("info",("found event of another type=%d", typ));
+ found= (typ != ROTATE_EVENT);
+ delete ev;
+ }
+ }
+ return fdev;
+}
+
+
+/*
Open the given relay log
SYNOPSIS
@@ -512,6 +669,8 @@ int init_relay_log_pos(Relay_log_info* rli,const char* log,
}
rli->group_relay_log_pos = rli->event_relay_log_pos = pos;
+ rli->clear_flag(Relay_log_info::IN_STMT);
+ rli->clear_flag(Relay_log_info::IN_TRANSACTION);
/*
Test to see if the previous run was with the skip of purging
@@ -558,68 +717,13 @@ int init_relay_log_pos(Relay_log_info* rli,const char* log,
*/
if (pos > BIN_LOG_HEADER_SIZE) /* If pos<=4, we stay at 4 */
{
- Log_event* ev;
- while (look_for_description_event)
+ if (look_for_description_event)
{
- /*
- Read the possible Format_description_log_event; if position
- was 4, no need, it will be read naturally.
- */
- DBUG_PRINT("info",("looking for a Format_description_log_event"));
-
- if (my_b_tell(rli->cur_log) >= pos)
- break;
-
- /*
- Because of we have rli->data_lock and log_lock, we can safely read an
- event
- */
- if (!(ev= Log_event::read_log_event(rli->cur_log, 0,
- rli->relay_log.description_event_for_exec,
- opt_slave_sql_verify_checksum)))
- {
- DBUG_PRINT("info",("could not read event, rli->cur_log->error=%d",
- rli->cur_log->error));
- if (rli->cur_log->error) /* not EOF */
- {
- *errmsg= "I/O error reading event at position 4";
- goto err;
- }
- break;
- }
- else if (ev->get_type_code() == FORMAT_DESCRIPTION_EVENT)
- {
- DBUG_PRINT("info",("found Format_description_log_event"));
- delete rli->relay_log.description_event_for_exec;
- rli->relay_log.description_event_for_exec= (Format_description_log_event*) ev;
- /*
- As ev was returned by read_log_event, it has passed is_valid(), so
- my_malloc() in ctor worked, no need to check again.
- */
- /*
- Ok, we found a Format_description event. But it is not sure that this
- describes the whole relay log; indeed, one can have this sequence
- (starting from position 4):
- Format_desc (of slave)
- Rotate (of master)
- Format_desc (of master)
- So the Format_desc which really describes the rest of the relay log
- is the 3rd event (it can't be further than that, because we rotate
- the relay log when we queue a Rotate event from the master).
- But what describes the Rotate is the first Format_desc.
- So what we do is:
- go on searching for Format_description events, until you exceed the
- position (argument 'pos') or until you find another event than Rotate
- or Format_desc.
- */
- }
- else
- {
- DBUG_PRINT("info",("found event of another type=%d",
- ev->get_type_code()));
- look_for_description_event= (ev->get_type_code() == ROTATE_EVENT);
- delete ev;
- }
+ Format_description_log_event *fdev;
+ if (!(fdev= read_relay_log_description_event(rli->cur_log, pos, errmsg)))
+ goto err;
+ delete rli->relay_log.description_event_for_exec;
+ rli->relay_log.description_event_for_exec= fdev;
}
my_b_seek(rli->cur_log,(off_t)pos);
#ifndef DBUG_OFF
@@ -686,7 +790,7 @@ int Relay_log_info::wait_for_pos(THD* thd, String* log_name,
ulong init_abort_pos_wait;
int error=0;
struct timespec abstime; // for timeout checking
- const char *msg;
+ PSI_stage_info old_stage;
DBUG_ENTER("Relay_log_info::wait_for_pos");
if (!inited)
@@ -697,9 +801,9 @@ int Relay_log_info::wait_for_pos(THD* thd, String* log_name,
set_timespec(abstime,timeout);
mysql_mutex_lock(&data_lock);
- msg= thd->enter_cond(&data_cond, &data_lock,
- "Waiting for the slave SQL thread to "
- "advance position");
+ thd->ENTER_COND(&data_cond, &data_lock,
+ &stage_waiting_for_the_slave_thread_to_advance_position,
+ &old_stage);
/*
This function will abort when it notices that some CHANGE MASTER or
RESET MASTER has changed the master info.
@@ -724,7 +828,7 @@ int Relay_log_info::wait_for_pos(THD* thd, String* log_name,
ulong log_name_extension;
char log_name_tmp[FN_REFLEN]; //make a char[] from String
- strmake(log_name_tmp, log_name->ptr(), min(log_name->length(), FN_REFLEN-1));
+ strmake(log_name_tmp, log_name->ptr(), MY_MIN(log_name->length(), FN_REFLEN-1));
char *p= fn_ext(log_name_tmp);
char *p_end;
@@ -734,7 +838,7 @@ int Relay_log_info::wait_for_pos(THD* thd, String* log_name,
goto err;
}
// Convert 0-3 to 4
- log_pos= max(log_pos, BIN_LOG_HEADER_SIZE);
+ log_pos= MY_MAX(log_pos, BIN_LOG_HEADER_SIZE);
/* p points to '.' */
log_name_extension= strtoul(++p, &p_end, 10);
/*
@@ -843,7 +947,7 @@ int Relay_log_info::wait_for_pos(THD* thd, String* log_name,
}
err:
- thd->exit_cond(msg);
+ thd->EXIT_COND(&old_stage);
DBUG_PRINT("exit",("killed: %d abort: %d slave_running: %d \
improper_arguments: %d timed_out: %d",
thd->killed_errno(),
@@ -861,17 +965,65 @@ improper_arguments: %d timed_out: %d",
void Relay_log_info::inc_group_relay_log_pos(ulonglong log_pos,
- bool skip_lock)
+ rpl_group_info *rgi,
+ bool skip_lock)
{
DBUG_ENTER("Relay_log_info::inc_group_relay_log_pos");
if (!skip_lock)
mysql_mutex_lock(&data_lock);
- inc_event_relay_log_pos();
- group_relay_log_pos= event_relay_log_pos;
- strmake_buf(group_relay_log_name,event_relay_log_name);
+ rgi->inc_event_relay_log_pos();
+ DBUG_PRINT("info", ("log_pos: %lu group_master_log_pos: %lu",
+ (long) log_pos, (long) group_master_log_pos));
+ if (rgi->is_parallel_exec)
+ {
+ /* In case of parallel replication, do not update the position backwards. */
+ int cmp= strcmp(group_relay_log_name, rgi->event_relay_log_name);
+ if (cmp < 0)
+ {
+ group_relay_log_pos= rgi->future_event_relay_log_pos;
+ strmake_buf(group_relay_log_name, rgi->event_relay_log_name);
+ notify_group_relay_log_name_update();
+ } else if (cmp == 0 && group_relay_log_pos < rgi->future_event_relay_log_pos)
+ group_relay_log_pos= rgi->future_event_relay_log_pos;
+
+ /*
+ In the parallel case we need to update the master_log_name here, rather
+ than in Rotate_log_event::do_update_pos().
+ */
+ cmp= strcmp(group_master_log_name, rgi->future_event_master_log_name);
+ if (cmp <= 0)
+ {
+ if (cmp < 0)
+ {
+ strcpy(group_master_log_name, rgi->future_event_master_log_name);
+ group_master_log_pos= log_pos;
+ }
+ else if (group_master_log_pos < log_pos)
+ group_master_log_pos= log_pos;
+ }
- notify_group_relay_log_name_update();
+ /*
+ In the parallel case, we only update the Seconds_Behind_Master at the
+ end of a transaction. In the non-parallel case, the value is updated as
+ soon as an event is read from the relay log; however this would be too
+ confusing for the user, seeing the slave reported as up-to-date when
+ potentially thousands of events are still queued up for worker threads
+ waiting for execution.
+ */
+ if (rgi->last_master_timestamp &&
+ rgi->last_master_timestamp > last_master_timestamp)
+ last_master_timestamp= rgi->last_master_timestamp;
+ }
+ else
+ {
+ /* Non-parallel case. */
+ group_relay_log_pos= event_relay_log_pos;
+ strmake_buf(group_relay_log_name, event_relay_log_name);
+ notify_group_relay_log_name_update();
+ if (log_pos) // 3.23 binlogs don't have log_posx
+ group_master_log_pos= log_pos;
+ }
/*
If the slave does not support transactions and replicates a transaction,
@@ -903,12 +1055,6 @@ void Relay_log_info::inc_group_relay_log_pos(ulonglong log_pos,
the relay log is not "val".
With the end_log_pos solution, we avoid computations involving lengthes.
*/
- DBUG_PRINT("info", ("log_pos: %lu group_master_log_pos: %lu",
- (long) log_pos, (long) group_master_log_pos));
- if (log_pos) // 3.23 binlogs don't have log_posx
- {
- group_master_log_pos= log_pos;
- }
mysql_cond_broadcast(&data_cond);
if (!skip_lock)
mysql_mutex_unlock(&data_lock);
@@ -924,6 +1070,9 @@ void Relay_log_info::close_temporary_tables()
for (table=save_temporary_tables ; table ; table=next)
{
next=table->next;
+
+ /* Reset in_use as the table may have been created by another thd */
+ table->in_use=0;
/*
Don't ask for disk deletion. For now, anyway they will be deleted when
slave restarts, but it is a better intention to not delete them.
@@ -979,7 +1128,6 @@ int purge_relay_logs(Relay_log_info* rli, THD *thd, bool just_reset,
DBUG_ASSERT(rli->slave_running == 0);
DBUG_ASSERT(rli->mi->slave_running == 0);
- rli->slave_skip_counter=0;
mysql_mutex_lock(&rli->data_lock);
/*
@@ -995,26 +1143,36 @@ int purge_relay_logs(Relay_log_info* rli, THD *thd, bool just_reset,
rli->cur_log_fd= -1;
}
- if (rli->relay_log.reset_logs(thd))
+ if (rli->relay_log.reset_logs(thd, !just_reset, NULL, 0))
{
*errmsg = "Failed during log reset";
error=1;
goto err;
}
- /* Save name of used relay log file */
- strmake_buf(rli->group_relay_log_name, rli->relay_log.get_log_fname());
- strmake_buf(rli->event_relay_log_name, rli->relay_log.get_log_fname());
- rli->group_relay_log_pos= rli->event_relay_log_pos= BIN_LOG_HEADER_SIZE;
- if (count_relay_log_space(rli))
- {
- *errmsg= "Error counting relay log space";
- error=1;
- goto err;
- }
+ rli->relay_log_state.load(rpl_global_gtid_slave_state);
if (!just_reset)
+ {
+ /* Save name of used relay log file */
+ strmake_buf(rli->group_relay_log_name, rli->relay_log.get_log_fname());
+ strmake_buf(rli->event_relay_log_name, rli->relay_log.get_log_fname());
+ rli->group_relay_log_pos= rli->event_relay_log_pos= BIN_LOG_HEADER_SIZE;
+ rli->log_space_total= 0;
+
+ if (count_relay_log_space(rli))
+ {
+ *errmsg= "Error counting relay log space";
+ error=1;
+ goto err;
+ }
error= init_relay_log_pos(rli, rli->group_relay_log_name,
rli->group_relay_log_pos,
0 /* do not need data lock */, errmsg, 0);
+ }
+ else
+ {
+ /* Ensure relay log names are not used */
+ rli->group_relay_log_name[0]= rli->event_relay_log_name[0]= 0;
+ }
err:
#ifndef DBUG_OFF
@@ -1065,16 +1223,19 @@ bool Relay_log_info::is_until_satisfied(THD *thd, Log_event *ev)
ulonglong log_pos;
DBUG_ENTER("Relay_log_info::is_until_satisfied");
- DBUG_ASSERT(until_condition != UNTIL_NONE);
+ DBUG_ASSERT(until_condition == UNTIL_MASTER_POS ||
+ until_condition == UNTIL_RELAY_POS);
if (until_condition == UNTIL_MASTER_POS)
{
- if (ev && ev->server_id == (uint32) ::server_id && !replicate_same_server_id)
+ if (ev && ev->server_id == (uint32) global_system_variables.server_id &&
+ !replicate_same_server_id)
DBUG_RETURN(FALSE);
- log_name= group_master_log_name;
- log_pos= (!ev)? group_master_log_pos :
- ((thd->variables.option_bits & OPTION_BEGIN || !ev->log_pos) ?
- group_master_log_pos : ev->log_pos - ev->data_written);
+ log_name= (opt_slave_parallel_threads > 0 ?
+ future_event_master_log_name : group_master_log_name);
+ log_pos= ((!ev)? group_master_log_pos :
+ (get_flag(IN_TRANSACTION) || !ev->log_pos) ?
+ group_master_log_pos : ev->log_pos - ev->data_written);
}
else
{ /* until_condition == UNTIL_RELAY_POS */
@@ -1143,43 +1304,21 @@ bool Relay_log_info::is_until_satisfied(THD *thd, Log_event *ev)
}
-void Relay_log_info::cached_charset_invalidate()
+void Relay_log_info::stmt_done(my_off_t event_master_log_pos, THD *thd,
+ rpl_group_info *rgi)
{
- DBUG_ENTER("Relay_log_info::cached_charset_invalidate");
-
- /* Full of zeroes means uninitialized. */
- bzero(cached_charset, sizeof(cached_charset));
- DBUG_VOID_RETURN;
-}
-
-
-bool Relay_log_info::cached_charset_compare(char *charset) const
-{
- DBUG_ENTER("Relay_log_info::cached_charset_compare");
-
- if (memcmp(cached_charset, charset, sizeof(cached_charset)))
- {
- memcpy(const_cast<char*>(cached_charset), charset, sizeof(cached_charset));
- DBUG_RETURN(1);
- }
- DBUG_RETURN(0);
-}
-
-
-void Relay_log_info::stmt_done(my_off_t event_master_log_pos,
- time_t event_creation_time)
-{
-#ifndef DBUG_OFF
- extern uint debug_not_change_ts_if_art_event;
-#endif
- clear_flag(IN_STMT);
+ DBUG_ENTER("Relay_log_info::stmt_done");
+ DBUG_ASSERT(rgi->rli == this);
/*
If in a transaction, and if the slave supports transactions, just
inc_event_relay_log_pos(). We only have to check for OPTION_BEGIN
(not OPTION_NOT_AUTOCOMMIT) as transactions are logged with
BEGIN/COMMIT, not with SET AUTOCOMMIT= .
+ We can't use rgi->rli->get_flag(IN_TRANSACTION) here as OPTION_BEGIN
+ is also used for single row transactions.
+
CAUTION: opt_using_transactions means innodb || bdb ; suppose the
master supports InnoDB and BDB, but the slave supports only BDB,
problems will arise: - suppose an InnoDB table is created on the
@@ -1197,32 +1336,369 @@ void Relay_log_info::stmt_done(my_off_t event_master_log_pos,
middle of the "transaction". START SLAVE will resume at BEGIN
while the MyISAM table has already been updated.
*/
- if ((sql_thd->variables.option_bits & OPTION_BEGIN) && opt_using_transactions)
- inc_event_relay_log_pos();
+ if ((rgi->thd->variables.option_bits & OPTION_BEGIN) &&
+ opt_using_transactions)
+ rgi->inc_event_relay_log_pos();
else
{
- inc_group_relay_log_pos(event_master_log_pos);
- flush_relay_log_info(this);
- /*
- Note that Rotate_log_event::do_apply_event() does not call this
- function, so there is no chance that a fake rotate event resets
- last_master_timestamp. Note that we update without mutex
- (probably ok - except in some very rare cases, only consequence
- is that value may take some time to display in
- Seconds_Behind_Master - not critical).
- */
- if (!(event_creation_time == 0 &&
- IF_DBUG(debug_not_change_ts_if_art_event > 0, 1)))
- last_master_timestamp= event_creation_time;
+ inc_group_relay_log_pos(event_master_log_pos, rgi);
+ if (rpl_global_gtid_slave_state->record_and_update_gtid(thd, rgi))
+ {
+ report(WARNING_LEVEL, ER_CANNOT_UPDATE_GTID_STATE, rgi->gtid_info(),
+ "Failed to update GTID state in %s.%s, slave state may become "
+ "inconsistent: %d: %s",
+ "mysql", rpl_gtid_slave_state_table_name.str,
+ thd->get_stmt_da()->sql_errno(), thd->get_stmt_da()->message());
+ /*
+ At this point we are not in a transaction (for example after DDL),
+ so we can not roll back. Anyway, normally updates to the slave
+ state table should not fail, and if they do, at least we made the
+ DBA aware of the problem in the error log.
+ */
+ }
+ DBUG_EXECUTE_IF("inject_crash_before_flush_rli", DBUG_SUICIDE(););
+ if (mi->using_gtid == Master_info::USE_GTID_NO)
+ flush_relay_log_info(this);
+ DBUG_EXECUTE_IF("inject_crash_after_flush_rli", DBUG_SUICIDE(););
+ }
+ DBUG_VOID_RETURN;
+}
+
+
+int
+Relay_log_info::alloc_inuse_relaylog(const char *name)
+{
+ inuse_relaylog *ir;
+ uint32 gtid_count;
+ rpl_gtid *gtid_list;
+
+ if (!(ir= (inuse_relaylog *)my_malloc(sizeof(*ir), MYF(MY_WME|MY_ZEROFILL))))
+ {
+ my_error(ER_OUTOFMEMORY, MYF(0), (int)sizeof(*ir));
+ return 1;
+ }
+ gtid_count= relay_log_state.count();
+ if (!(gtid_list= (rpl_gtid *)my_malloc(sizeof(*gtid_list)*gtid_count,
+ MYF(MY_WME))))
+ {
+ my_free(ir);
+ my_error(ER_OUTOFMEMORY, MYF(0), (int)sizeof(*gtid_list)*gtid_count);
+ return 1;
+ }
+ if (relay_log_state.get_gtid_list(gtid_list, gtid_count))
+ {
+ my_free(gtid_list);
+ my_free(ir);
+ DBUG_ASSERT(0 /* Should not be possible as we allocated correct length */);
+ my_error(ER_OUT_OF_RESOURCES, MYF(0));
+ return 1;
+ }
+ ir->rli= this;
+ strmake_buf(ir->name, name);
+ ir->relay_log_state= gtid_list;
+ ir->relay_log_state_count= gtid_count;
+
+ if (!inuse_relaylog_list)
+ inuse_relaylog_list= ir;
+ else
+ {
+ last_inuse_relaylog->completed= true;
+ last_inuse_relaylog->next= ir;
+ }
+ last_inuse_relaylog= ir;
+ my_atomic_rwlock_init(&ir->inuse_relaylog_atomic_lock);
+
+ return 0;
+}
+
+
+void
+Relay_log_info::free_inuse_relaylog(inuse_relaylog *ir)
+{
+ my_free(ir->relay_log_state);
+ my_atomic_rwlock_destroy(&ir->inuse_relaylog_atomic_lock);
+ my_free(ir);
+}
+
+
+void
+Relay_log_info::reset_inuse_relaylog()
+{
+ inuse_relaylog *cur= inuse_relaylog_list;
+ while (cur)
+ {
+ DBUG_ASSERT(cur->queued_count == cur->dequeued_count);
+ inuse_relaylog *next= cur->next;
+ free_inuse_relaylog(cur);
+ cur= next;
+ }
+ inuse_relaylog_list= last_inuse_relaylog= NULL;
+}
+
+
+int
+Relay_log_info::update_relay_log_state(rpl_gtid *gtid_list, uint32 count)
+{
+ int res= 0;
+ while (count)
+ {
+ if (relay_log_state.update_nolock(gtid_list, false))
+ res= 1;
+ ++gtid_list;
+ --count;
}
+ return res;
}
+
#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
+int
+rpl_load_gtid_slave_state(THD *thd)
+{
+ TABLE_LIST tlist;
+ TABLE *table;
+ bool table_opened= false;
+ bool table_scanned= false;
+ bool array_inited= false;
+ struct local_element { uint64 sub_id; rpl_gtid gtid; };
+ struct local_element tmp_entry, *entry;
+ HASH hash;
+ DYNAMIC_ARRAY array;
+ int err= 0;
+ uint32 i;
+ DBUG_ENTER("rpl_load_gtid_slave_state");
+
+ mysql_mutex_lock(&rpl_global_gtid_slave_state->LOCK_slave_state);
+ bool loaded= rpl_global_gtid_slave_state->loaded;
+ mysql_mutex_unlock(&rpl_global_gtid_slave_state->LOCK_slave_state);
+ if (loaded)
+ DBUG_RETURN(0);
+
+ my_hash_init(&hash, &my_charset_bin, 32,
+ offsetof(local_element, gtid) + offsetof(rpl_gtid, domain_id),
+ sizeof(uint32), NULL, my_free, HASH_UNIQUE);
+ if ((err= my_init_dynamic_array(&array, sizeof(local_element), 0, 0, MYF(0))))
+ goto end;
+ array_inited= true;
+
+ mysql_reset_thd_for_next_command(thd);
+
+ tlist.init_one_table(STRING_WITH_LEN("mysql"),
+ rpl_gtid_slave_state_table_name.str,
+ rpl_gtid_slave_state_table_name.length,
+ NULL, TL_READ);
+ if ((err= open_and_lock_tables(thd, &tlist, FALSE, 0)))
+ goto end;
+ table_opened= true;
+ table= tlist.table;
+
+ if ((err= gtid_check_rpl_slave_state_table(table)))
+ goto end;
+
+ bitmap_set_all(table->read_set);
+ if ((err= table->file->ha_rnd_init_with_error(1)))
+ {
+ table->file->print_error(err, MYF(0));
+ goto end;
+ }
+ table_scanned= true;
+ for (;;)
+ {
+ uint32 domain_id, server_id;
+ uint64 sub_id, seq_no;
+ uchar *rec;
+
+ if ((err= table->file->ha_rnd_next(table->record[0])))
+ {
+ if (err == HA_ERR_RECORD_DELETED)
+ continue;
+ else if (err == HA_ERR_END_OF_FILE)
+ break;
+ else
+ {
+ table->file->print_error(err, MYF(0));
+ goto end;
+ }
+ }
+ domain_id= (ulonglong)table->field[0]->val_int();
+ sub_id= (ulonglong)table->field[1]->val_int();
+ server_id= (ulonglong)table->field[2]->val_int();
+ seq_no= (ulonglong)table->field[3]->val_int();
+ DBUG_PRINT("info", ("Read slave state row: %u-%u-%lu sub_id=%lu\n",
+ (unsigned)domain_id, (unsigned)server_id,
+ (ulong)seq_no, (ulong)sub_id));
+
+ tmp_entry.sub_id= sub_id;
+ tmp_entry.gtid.domain_id= domain_id;
+ tmp_entry.gtid.server_id= server_id;
+ tmp_entry.gtid.seq_no= seq_no;
+ if ((err= insert_dynamic(&array, (uchar *)&tmp_entry)))
+ {
+ my_error(ER_OUT_OF_RESOURCES, MYF(0));
+ goto end;
+ }
+
+ if ((rec= my_hash_search(&hash, (const uchar *)&domain_id, 0)))
+ {
+ entry= (struct local_element *)rec;
+ if (entry->sub_id >= sub_id)
+ continue;
+ entry->sub_id= sub_id;
+ DBUG_ASSERT(entry->gtid.domain_id == domain_id);
+ entry->gtid.server_id= server_id;
+ entry->gtid.seq_no= seq_no;
+ }
+ else
+ {
+ if (!(entry= (struct local_element *)my_malloc(sizeof(*entry),
+ MYF(MY_WME))))
+ {
+ my_error(ER_OUTOFMEMORY, MYF(0), (int)sizeof(*entry));
+ err= 1;
+ goto end;
+ }
+ entry->sub_id= sub_id;
+ entry->gtid.domain_id= domain_id;
+ entry->gtid.server_id= server_id;
+ entry->gtid.seq_no= seq_no;
+ if ((err= my_hash_insert(&hash, (uchar *)entry)))
+ {
+ my_free(entry);
+ my_error(ER_OUT_OF_RESOURCES, MYF(0));
+ goto end;
+ }
+ }
+ }
+
+ mysql_mutex_lock(&rpl_global_gtid_slave_state->LOCK_slave_state);
+ if (rpl_global_gtid_slave_state->loaded)
+ {
+ mysql_mutex_unlock(&rpl_global_gtid_slave_state->LOCK_slave_state);
+ goto end;
+ }
+
+ for (i= 0; i < array.elements; ++i)
+ {
+ get_dynamic(&array, (uchar *)&tmp_entry, i);
+ if ((err= rpl_global_gtid_slave_state->update(tmp_entry.gtid.domain_id,
+ tmp_entry.gtid.server_id,
+ tmp_entry.sub_id,
+ tmp_entry.gtid.seq_no,
+ NULL)))
+ {
+ mysql_mutex_unlock(&rpl_global_gtid_slave_state->LOCK_slave_state);
+ my_error(ER_OUT_OF_RESOURCES, MYF(0));
+ goto end;
+ }
+ }
+
+ for (i= 0; i < hash.records; ++i)
+ {
+ entry= (struct local_element *)my_hash_element(&hash, i);
+ if (opt_bin_log &&
+ mysql_bin_log.bump_seq_no_counter_if_needed(entry->gtid.domain_id,
+ entry->gtid.seq_no))
+ {
+ mysql_mutex_unlock(&rpl_global_gtid_slave_state->LOCK_slave_state);
+ my_error(ER_OUT_OF_RESOURCES, MYF(0));
+ goto end;
+ }
+ }
+
+ rpl_global_gtid_slave_state->loaded= true;
+ mysql_mutex_unlock(&rpl_global_gtid_slave_state->LOCK_slave_state);
+
+ err= 0; /* Clear HA_ERR_END_OF_FILE */
+
+end:
+ if (table_scanned)
+ {
+ table->file->ha_index_or_rnd_end();
+ ha_commit_trans(thd, FALSE);
+ ha_commit_trans(thd, TRUE);
+ }
+ if (table_opened)
+ {
+ close_thread_tables(thd);
+ thd->mdl_context.release_transactional_locks();
+ }
+ if (array_inited)
+ delete_dynamic(&array);
+ my_hash_free(&hash);
+ DBUG_RETURN(err);
+}
+
+
+void
+rpl_group_info::reinit(Relay_log_info *rli)
+{
+ this->rli= rli;
+ tables_to_lock= NULL;
+ tables_to_lock_count= 0;
+ trans_retries= 0;
+ last_event_start_time= 0;
+ gtid_sub_id= 0;
+ commit_id= 0;
+ gtid_pending= false;
+ worker_error= 0;
+ row_stmt_start_timestamp= 0;
+ long_find_row_note_printed= false;
+ did_mark_start_commit= false;
+ last_master_timestamp = 0;
+ gtid_ignore_duplicate_state= GTID_DUPLICATE_NULL;
+ commit_orderer.reinit();
+}
+
+rpl_group_info::rpl_group_info(Relay_log_info *rli)
+ : thd(0), wait_commit_sub_id(0),
+ wait_commit_group_info(0), parallel_entry(0),
+ deferred_events(NULL), m_annotate_event(0), is_parallel_exec(false)
+{
+ reinit(rli);
+ bzero(&current_gtid, sizeof(current_gtid));
+ mysql_mutex_init(key_rpl_group_info_sleep_lock, &sleep_lock,
+ MY_MUTEX_INIT_FAST);
+ mysql_cond_init(key_rpl_group_info_sleep_cond, &sleep_cond, NULL);
+}
+
+
+rpl_group_info::~rpl_group_info()
+{
+ free_annotate_event();
+ delete deferred_events;
+ mysql_mutex_destroy(&sleep_lock);
+ mysql_cond_destroy(&sleep_cond);
+}
+
+
+int
+event_group_new_gtid(rpl_group_info *rgi, Gtid_log_event *gev)
+{
+ uint64 sub_id= rpl_global_gtid_slave_state->next_sub_id(gev->domain_id);
+ if (!sub_id)
+ {
+ /* Out of memory caused hash insertion to fail. */
+ return 1;
+ }
+ rgi->gtid_sub_id= sub_id;
+ rgi->current_gtid.domain_id= gev->domain_id;
+ rgi->current_gtid.server_id= gev->server_id;
+ rgi->current_gtid.seq_no= gev->seq_no;
+ rgi->commit_id= gev->commit_id;
+ rgi->gtid_pending= true;
+ return 0;
+}
+
void
-delete_or_keep_event_post_apply(Relay_log_info *rli,
+delete_or_keep_event_post_apply(rpl_group_info *rgi,
Log_event_type typ, Log_event *ev)
{
+ /*
+ ToDo: This needs to work on rpl_group_info, not Relay_log_info, to be
+ thread-safe for parallel replication.
+ */
+
switch (typ) {
case FORMAT_DESCRIPTION_EVENT:
/*
@@ -1239,8 +1715,11 @@ delete_or_keep_event_post_apply(Relay_log_info *rli,
The thd->query will be used to generate new Annotate_rows event
during applying the subsequent Rows events.
*/
- rli->set_annotate_event((Annotate_rows_log_event*) ev);
+ rgi->set_annotate_event((Annotate_rows_log_event*) ev);
break;
+ case DELETE_ROWS_EVENT_V1:
+ case UPDATE_ROWS_EVENT_V1:
+ case WRITE_ROWS_EVENT_V1:
case DELETE_ROWS_EVENT:
case UPDATE_ROWS_EVENT:
case WRITE_ROWS_EVENT:
@@ -1249,21 +1728,23 @@ delete_or_keep_event_post_apply(Relay_log_info *rli,
event (if any) is not needed anymore and can be deleted.
*/
if (((Rows_log_event*)ev)->get_flags(Rows_log_event::STMT_END_F))
- rli->free_annotate_event();
+ rgi->free_annotate_event();
/* fall through */
default:
DBUG_PRINT("info", ("Deleting the event after it has been executed"));
- if (!rli->is_deferred_event(ev))
+ if (!rgi->is_deferred_event(ev))
delete ev;
break;
}
}
-void Relay_log_info::cleanup_context(THD *thd, bool error)
-{
- DBUG_ENTER("Relay_log_info::cleanup_context");
- DBUG_ASSERT(sql_thd == thd);
+void rpl_group_info::cleanup_context(THD *thd, bool error)
+{
+ DBUG_ENTER("rpl_group_info::cleanup_context");
+ DBUG_PRINT("enter", ("error: %d", (int) error));
+
+ DBUG_ASSERT(this->thd == thd);
/*
1) Instances of Table_map_log_event, if ::do_apply_event() was called on them,
may have opened tables, which we cannot be sure have been closed (because
@@ -1279,13 +1760,38 @@ void Relay_log_info::cleanup_context(THD *thd, bool error)
if (error)
{
trans_rollback_stmt(thd); // if a "statement transaction"
+ /* trans_rollback() also resets OPTION_GTID_BEGIN */
trans_rollback(thd); // if a "real transaction"
+ /*
+ Now that we have rolled back the transaction, make sure we do not
+ erroneously update the GTID position.
+ */
+ gtid_pending= false;
}
m_table_map.clear_tables();
slave_close_thread_tables(thd);
if (error)
+ {
thd->mdl_context.release_transactional_locks();
- clear_flag(IN_STMT);
+
+ if (thd == rli->sql_driver_thd)
+ {
+ /*
+ Reset flags. This is needed to handle incident events and errors in
+ the relay log noticed by the sql driver thread.
+ */
+ rli->clear_flag(Relay_log_info::IN_STMT);
+ rli->clear_flag(Relay_log_info::IN_TRANSACTION);
+ }
+
+ /*
+ Ensure we always release the domain for others to process, when using
+ --gtid-ignore-duplicates.
+ */
+ if (gtid_ignore_duplicate_state != GTID_DUPLICATE_NULL)
+ rpl_global_gtid_slave_state->release_domain_owner(this);
+ }
+
/*
Cleanup for the flags that have been set at do_apply_event.
*/
@@ -1300,12 +1806,18 @@ void Relay_log_info::cleanup_context(THD *thd, bool error)
reset_row_stmt_start_timestamp();
unset_long_find_row_note_printed();
+ DBUG_EXECUTE_IF("inject_sleep_gtid_100_x_x", {
+ if (current_gtid.domain_id == 100)
+ my_sleep(50000);
+ };);
+
DBUG_VOID_RETURN;
}
-void Relay_log_info::clear_tables_to_lock()
+
+void rpl_group_info::clear_tables_to_lock()
{
- DBUG_ENTER("Relay_log_info::clear_tables_to_lock()");
+ DBUG_ENTER("rpl_group_info::clear_tables_to_lock()");
#ifndef DBUG_OFF
/**
When replicating in RBR and MyISAM Merge tables are involved
@@ -1349,12 +1861,13 @@ void Relay_log_info::clear_tables_to_lock()
DBUG_VOID_RETURN;
}
-void Relay_log_info::slave_close_thread_tables(THD *thd)
+
+void rpl_group_info::slave_close_thread_tables(THD *thd)
{
- DBUG_ENTER("Relay_log_info::slave_close_thread_tables(THD *thd)");
- thd->stmt_da->can_overwrite_status= TRUE;
+ DBUG_ENTER("rpl_group_info::slave_close_thread_tables(THD *thd)");
+ thd->get_stmt_da()->set_overwrite_status(true);
thd->is_error() ? trans_rollback_stmt(thd) : trans_commit_stmt(thd);
- thd->stmt_da->can_overwrite_status= FALSE;
+ thd->get_stmt_da()->set_overwrite_status(false);
close_thread_tables(thd);
/*
@@ -1382,4 +1895,128 @@ void Relay_log_info::slave_close_thread_tables(THD *thd)
clear_tables_to_lock();
DBUG_VOID_RETURN;
}
+
+
+
+static void
+mark_start_commit_inner(rpl_parallel_entry *e, group_commit_orderer *gco,
+ rpl_group_info *rgi)
+{
+ group_commit_orderer *tmp;
+ uint64 count= ++e->count_committing_event_groups;
+ /* Signal any following GCO whose wait_count has been reached now. */
+ tmp= gco;
+ while ((tmp= tmp->next_gco))
+ {
+ uint64 wait_count= tmp->wait_count;
+ if (wait_count > count)
+ break;
+ mysql_cond_broadcast(&tmp->COND_group_commit_orderer);
+ }
+}
+
+
+void
+rpl_group_info::mark_start_commit_no_lock()
+{
+ if (did_mark_start_commit)
+ return;
+ mark_start_commit_inner(parallel_entry, gco, this);
+ did_mark_start_commit= true;
+}
+
+
+void
+rpl_group_info::mark_start_commit()
+{
+ rpl_parallel_entry *e;
+
+ if (did_mark_start_commit)
+ return;
+
+ e= this->parallel_entry;
+ mysql_mutex_lock(&e->LOCK_parallel_entry);
+ mark_start_commit_inner(e, gco, this);
+ mysql_mutex_unlock(&e->LOCK_parallel_entry);
+ did_mark_start_commit= true;
+}
+
+
+/*
+ Format the current GTID as a string suitable for printing in error messages.
+
+ The string is stored in a buffer inside rpl_group_info, so remains valid
+ until next call to gtid_info() or until destruction of rpl_group_info.
+
+ If no GTID is available, then NULL is returned.
+*/
+char *
+rpl_group_info::gtid_info()
+{
+ if (!gtid_sub_id || !current_gtid.seq_no)
+ return NULL;
+ my_snprintf(gtid_info_buf, sizeof(gtid_info_buf), "Gtid %u-%u-%llu",
+ current_gtid.domain_id, current_gtid.server_id,
+ current_gtid.seq_no);
+ return gtid_info_buf;
+}
+
+
+/*
+ Undo the effect of a prior mark_start_commit().
+
+ This is only used for retrying a transaction in parallel replication, after
+ we have encountered a deadlock or other temporary error.
+
+ When we get such a deadlock, it means that the current group of transactions
+ did not yet all start committing (else they would not have deadlocked). So
+ we will not yet have woken up anything in the next group, our rgi->gco is
+ still live, and we can simply decrement the counter (to be incremented again
+ later, when the retry succeeds and reaches the commit step).
+*/
+void
+rpl_group_info::unmark_start_commit()
+{
+ rpl_parallel_entry *e;
+
+ if (!did_mark_start_commit)
+ return;
+
+ e= this->parallel_entry;
+ mysql_mutex_lock(&e->LOCK_parallel_entry);
+ --e->count_committing_event_groups;
+ mysql_mutex_unlock(&e->LOCK_parallel_entry);
+ did_mark_start_commit= false;
+}
+
+
+rpl_sql_thread_info::rpl_sql_thread_info(Rpl_filter *filter)
+ : rpl_filter(filter)
+{
+ cached_charset_invalidate();
+}
+
+
+void rpl_sql_thread_info::cached_charset_invalidate()
+{
+ DBUG_ENTER("rpl_group_info::cached_charset_invalidate");
+
+ /* Full of zeroes means uninitialized. */
+ bzero(cached_charset, sizeof(cached_charset));
+ DBUG_VOID_RETURN;
+}
+
+
+bool rpl_sql_thread_info::cached_charset_compare(char *charset) const
+{
+ DBUG_ENTER("rpl_group_info::cached_charset_compare");
+
+ if (memcmp(cached_charset, charset, sizeof(cached_charset)))
+ {
+ memcpy(const_cast<char*>(cached_charset), charset, sizeof(cached_charset));
+ DBUG_RETURN(1);
+ }
+ DBUG_RETURN(0);
+}
+
#endif
diff --git a/sql/rpl_rli.h b/sql/rpl_rli.h
index 5f15d2f1fd8..efcec83b880 100644
--- a/sql/rpl_rli.h
+++ b/sql/rpl_rli.h
@@ -22,10 +22,17 @@
#include "log.h" /* LOG_INFO, MYSQL_BIN_LOG */
#include "sql_class.h" /* THD */
#include "log_event.h"
+#include "rpl_parallel.h"
struct RPL_TABLE_LIST;
class Master_info;
-extern uint sql_slave_skip_counter;
+class Rpl_filter;
+
+
+enum {
+ LINES_IN_RELAY_LOG_INFO_WITH_DELAY= 5
+};
+
/****************************************************************************
@@ -53,18 +60,21 @@ extern uint sql_slave_skip_counter;
*****************************************************************************/
+struct rpl_group_info;
+struct inuse_relaylog;
+
class Relay_log_info : public Slave_reporting_capability
{
public:
/**
- Flags for the state of the replication.
- */
+ Flags for the state of reading the relay log. Note that these are
+ bit masks.
+ */
enum enum_state_flag {
- /** The replication thread is inside a statement */
- IN_STMT,
-
- /** Flag counter. Should always be last */
- STATE_FLAGS_COUNT
+ /** We are inside a group of events forming a statement */
+ IN_STMT=1,
+ /** We have inside a transaction */
+ IN_TRANSACTION=2
};
/*
@@ -129,9 +139,14 @@ public:
IO_CACHE info_file;
/*
- When we restart slave thread we need to have access to the previously
- created temporary tables. Modified only on init/end and by the SQL
- thread, read only by SQL thread.
+ List of temporary tables used by this connection.
+ This is updated when a temporary table is created or dropped by
+ a replication thread.
+
+ Not reset when replication ends, to allow one to access the tables
+ when replication restarts.
+
+ Protected by data_lock.
*/
TABLE *save_temporary_tables;
@@ -139,17 +154,24 @@ public:
standard lock acquisition order to avoid deadlocks:
run_lock, data_lock, relay_log.LOCK_log, relay_log.LOCK_index
*/
- mysql_mutex_t data_lock, run_lock, sleep_lock;
+ mysql_mutex_t data_lock, run_lock;
/*
start_cond is broadcast when SQL thread is started
stop_cond - when stopped
data_cond - when data protected by data_lock changes
*/
- mysql_cond_t start_cond, stop_cond, data_cond, sleep_cond;
+ mysql_cond_t start_cond, stop_cond, data_cond;
/* parent Master_info structure */
Master_info *mi;
/*
+ List of active relay log files.
+ (This can be more than one in case of parallel replication).
+ */
+ inuse_relaylog *inuse_relaylog_list;
+ inuse_relaylog *last_inuse_relaylog;
+
+ /*
Needed to deal properly with cur_log getting closed and re-opened with
a different log under our feet
*/
@@ -162,8 +184,8 @@ public:
- an autocommiting query + its associated events (INSERT_ID,
TIMESTAMP...)
We need these rli coordinates :
- - relay log name and position of the beginning of the group we currently are
- executing. Needed to know where we have to restart when replication has
+ - relay log name and position of the beginning of the group we currently
+ are executing. Needed to know where we have to restart when replication has
stopped in the middle of a group (which has been rolled back by the slave).
- relay log name and position just after the event we have just
executed. This event is part of the current group.
@@ -178,6 +200,10 @@ public:
char event_relay_log_name[FN_REFLEN];
ulonglong event_relay_log_pos;
ulonglong future_event_relay_log_pos;
+ /*
+ The master log name for current event. Only used in parallel replication.
+ */
+ char future_event_master_log_name[FN_REFLEN];
#ifdef HAVE_valgrind
bool is_fake; /* Mark that this is a fake relay log info structure */
@@ -209,19 +235,13 @@ public:
*/
bool sql_force_rotate_relay;
+ time_t last_master_timestamp;
/*
- When it commits, InnoDB internally stores the master log position it has
- processed so far; the position to store is the one of the end of the
- committing event (the COMMIT query event, or the event if in autocommit
- mode).
+ The SQL driver thread sets this true while it is waiting at the end of the
+ relay log for more events to arrive. SHOW SLAVE STATUS uses this to report
+ Seconds_Behind_Master as zero while the SQL thread is so waiting.
*/
-#if MYSQL_VERSION_ID < 40100
- ulonglong future_master_log_pos;
-#else
- ulonglong future_group_master_log_pos;
-#endif
-
- time_t last_master_timestamp;
+ bool sql_thread_caught_up;
void clear_until_condition();
@@ -229,17 +249,28 @@ public:
Needed for problems when slave stops and we want to restart it
skipping one or more events in the master log that have caused
errors, and have been manually applied by DBA already.
+ Must be ulong as it's refered to from set_var.cc
*/
- volatile uint32 slave_skip_counter;
+ volatile ulonglong slave_skip_counter;
+ ulonglong max_relay_log_size;
+
volatile ulong abort_pos_wait; /* Incremented on change master */
volatile ulong slave_run_id; /* Incremented on slave start */
mysql_mutex_t log_space_lock;
mysql_cond_t log_space_cond;
- THD * sql_thd;
+ /*
+ THD for the main sql thread, the one that starts threads to process
+ slave requests. If there is only one thread, then this THD is also
+ used for SQL processing.
+ A kill sent to this THD will kill the replication.
+ */
+ THD *sql_driver_thd;
#ifndef DBUG_OFF
int events_till_abort;
#endif
+ enum_gtid_skip_type gtid_skip_flag;
+
/*
inited changes its value within LOCK_active_mi-guarded critical
sections at times of start_slave_threads() (0->1) and end_slave() (1->0).
@@ -249,6 +280,7 @@ public:
*/
volatile bool inited;
volatile bool abort_slave;
+ volatile bool stop_for_until;
volatile uint slave_running;
/*
@@ -262,7 +294,9 @@ public:
thread is running).
*/
- enum {UNTIL_NONE= 0, UNTIL_MASTER_POS, UNTIL_RELAY_POS} until_condition;
+ enum {
+ UNTIL_NONE= 0, UNTIL_MASTER_POS, UNTIL_RELAY_POS, UNTIL_GTID
+ } until_condition;
char until_log_name[FN_REFLEN];
ulonglong until_log_pos;
/* extension extracted from log_name and converted to int */
@@ -276,16 +310,20 @@ public:
UNTIL_LOG_NAMES_CMP_UNKNOWN= -2, UNTIL_LOG_NAMES_CMP_LESS= -1,
UNTIL_LOG_NAMES_CMP_EQUAL= 0, UNTIL_LOG_NAMES_CMP_GREATER= 1
} until_log_names_cmp_result;
+ /* Condition for UNTIL master_gtid_pos. */
+ slave_connection_state until_gtid_pos;
- char cached_charset[6];
/*
- trans_retries varies between 0 to slave_transaction_retries and counts how
- many times the slave has retried the present transaction; gets reset to 0
- when the transaction finally succeeds. retried_trans is a cumulative
- counter: how many times the slave has retried a transaction (any) since
- slave started.
+ retried_trans is a cumulative counter: how many times the slave
+ has retried a transaction (any) since slave started.
+ Protected by data_lock.
+ */
+ ulong retried_trans;
+ /*
+ Number of executed events for SLAVE STATUS.
+ Protected by slave_executed_entries_lock
*/
- ulong trans_retries, retried_trans;
+ int64 executed_entries;
/*
If the end of the hot relay log is made of master's events ignored by the
@@ -297,6 +335,8 @@ public:
*/
char ign_master_log_name_end[FN_REFLEN];
ulonglong ign_master_log_pos_end;
+ /* Similar for ignored GTID events. */
+ slave_connection_state ign_gtids;
/*
Indentifies where the SQL Thread should create temporary files for the
@@ -305,6 +345,23 @@ public:
char slave_patternload_file[FN_REFLEN];
size_t slave_patternload_file_size;
+ rpl_parallel parallel;
+ /*
+ The relay_log_state keeps track of the current binlog state of the execution
+ of the relay log. This is used to know where to resume current GTID position
+ if the slave thread is stopped and restarted.
+ It is only accessed from the SQL thread, so it does not need any locking.
+ */
+ rpl_binlog_state relay_log_state;
+ /*
+ The restart_gtid_state is used when the SQL thread restarts on a relay log
+ in GTID mode. In multi-domain parallel replication, each domain may have a
+ separat position, so some events in more progressed domains may need to be
+ skipped. This keeps track of the domains that have not yet reached their
+ starting event.
+ */
+ slave_connection_state restart_gtid_pos;
+
Relay_log_info(bool is_slave_recovery);
~Relay_log_info();
@@ -327,13 +384,9 @@ public:
if (until_condition==UNTIL_MASTER_POS)
until_log_names_cmp_result= UNTIL_LOG_NAMES_CMP_UNKNOWN;
}
-
- inline void inc_event_relay_log_pos()
- {
- event_relay_log_pos= future_event_relay_log_pos;
- }
void inc_group_relay_log_pos(ulonglong log_pos,
+ rpl_group_info *rgi,
bool skip_lock=0);
int wait_for_pos(THD* thd, String* log_name, longlong log_pos,
@@ -344,86 +397,12 @@ public:
bool is_until_satisfied(THD *thd, Log_event *ev);
inline ulonglong until_pos()
{
+ DBUG_ASSERT(until_condition == UNTIL_MASTER_POS ||
+ until_condition == UNTIL_RELAY_POS);
return ((until_condition == UNTIL_MASTER_POS) ? group_master_log_pos :
group_relay_log_pos);
}
- RPL_TABLE_LIST *tables_to_lock; /* RBR: Tables to lock */
- uint tables_to_lock_count; /* RBR: Count of tables to lock */
- table_mapping m_table_map; /* RBR: Mapping table-id to table */
-
- bool get_table_data(TABLE *table_arg, table_def **tabledef_var, TABLE **conv_table_var) const
- {
- DBUG_ASSERT(tabledef_var && conv_table_var);
- for (TABLE_LIST *ptr= tables_to_lock ; ptr != NULL ; ptr= ptr->next_global)
- if (ptr->table == table_arg)
- {
- *tabledef_var= &static_cast<RPL_TABLE_LIST*>(ptr)->m_tabledef;
- *conv_table_var= static_cast<RPL_TABLE_LIST*>(ptr)->m_conv_table;
- DBUG_PRINT("debug", ("Fetching table data for table %s.%s:"
- " tabledef: %p, conv_table: %p",
- table_arg->s->db.str, table_arg->s->table_name.str,
- *tabledef_var, *conv_table_var));
- return true;
- }
- return false;
- }
-
- /*
- Last charset (6 bytes) seen by slave SQL thread is cached here; it helps
- the thread save 3 get_charset() per Query_log_event if the charset is not
- changing from event to event (common situation).
- When the 6 bytes are equal to 0 is used to mean "cache is invalidated".
- */
- void cached_charset_invalidate();
- bool cached_charset_compare(char *charset) const;
-
- void cleanup_context(THD *, bool);
- void slave_close_thread_tables(THD *);
- void clear_tables_to_lock();
-
- /*
- Used to defer stopping the SQL thread to give it a chance
- to finish up the current group of events.
- The timestamp is set and reset in @c sql_slave_killed().
- */
- time_t last_event_start_time;
-
- /*
- A container to hold on Intvar-, Rand-, Uservar- log-events in case
- the slave is configured with table filtering rules.
- The withhold events are executed when their parent Query destiny is
- determined for execution as well.
- */
- Deferred_log_events *deferred_events;
-
- /*
- State of the container: true stands for IRU events gathering,
- false does for execution, either deferred or direct.
- */
- bool deferred_events_collecting;
-
- /*
- Returns true if the argument event resides in the containter;
- more specifically, the checking is done against the last added event.
- */
- bool is_deferred_event(Log_event * ev)
- {
- return deferred_events_collecting ? deferred_events->is_last(ev) : false;
- };
- /* The general cleanup that slave applier may need at the end of query. */
- inline void cleanup_after_query()
- {
- if (deferred_events)
- deferred_events->rewind();
- };
- /* The general cleanup that slave applier may need at the end of session. */
- void cleanup_after_session()
- {
- if (deferred_events)
- delete deferred_events;
- };
-
/**
Helper function to do after statement completion.
@@ -436,15 +415,32 @@ public:
Master log position of the event. The position is recorded in the
relay log info and used to produce information for <code>SHOW
SLAVE STATUS</code>.
-
- @param event_creation_time
- Timestamp for the creation of the event on the master side. The
- time stamp is recorded in the relay log info and used to compute
- the <code>Seconds_behind_master</code> field.
*/
- void stmt_done(my_off_t event_log_pos,
- time_t event_creation_time);
+ void stmt_done(my_off_t event_log_pos, THD *thd, rpl_group_info *rgi);
+ int alloc_inuse_relaylog(const char *name);
+ void free_inuse_relaylog(inuse_relaylog *ir);
+ void reset_inuse_relaylog();
+ int update_relay_log_state(rpl_gtid *gtid_list, uint32 count);
+
+ /**
+ Is the replication inside a group?
+ The reader of the relay log is inside a group if either:
+ - The IN_TRANSACTION flag is set, meaning we're inside a transaction
+ - The IN_STMT flag is set, meaning we have read at least one row from
+ a multi-event entry.
+
+ This flag reflects the state of the log 'just now', ie after the last
+ read event would be executed.
+ This allow us to test if we can stop replication before reading
+ the next entry.
+
+ @retval true Replication thread is currently inside a group
+ @retval false Replication thread is currently not inside a group
+ */
+ bool is_in_group() const {
+ return (m_flags & (IN_STMT | IN_TRANSACTION));
+ }
/**
Set the value of a replication state flag.
@@ -453,7 +449,7 @@ public:
*/
void set_flag(enum_state_flag flag)
{
- m_flags |= (1UL << flag);
+ m_flags|= flag;
}
/**
@@ -465,7 +461,7 @@ public:
*/
bool get_flag(enum_state_flag flag)
{
- return m_flags & (1UL << flag);
+ return m_flags & flag;
}
/**
@@ -475,34 +471,257 @@ public:
*/
void clear_flag(enum_state_flag flag)
{
- m_flags &= ~(1UL << flag);
+ m_flags&= ~flag;
}
- /**
- Is the replication inside a group?
+private:
- Replication is inside a group if either:
- - The OPTION_BEGIN flag is set, meaning we're inside a transaction
- - The RLI_IN_STMT flag is set, meaning we're inside a statement
+ /*
+ Holds the state of the data in the relay log.
+ We need this to ensure that we are not in the middle of a
+ statement or inside BEGIN ... COMMIT when should rotate the
+ relay log.
+ */
+ uint32 m_flags;
+};
- @retval true Replication thread is currently inside a group
- @retval false Replication thread is currently not inside a group
+
+/*
+ In parallel replication, if we need to re-try a transaction due to a
+ deadlock or other temporary error, we may need to go back and re-read events
+ out of an earlier relay log.
+
+ This structure keeps track of the relaylogs that are potentially in use.
+ Each rpl_group_info has a pointer to one of those, corresponding to the
+ first GTID event.
+
+ A pair of reference count keeps track of how long a relay log is potentially
+ in use. When the `completed' flag is set, all events have been read out of
+ the relay log, but the log might still be needed for retry in worker
+ threads. As worker threads complete an event group, they increment
+ atomically the `dequeued_count' with number of events queued. Thus, when
+ completed is set and dequeued_count equals queued_count, the relay log file
+ is finally done with and can be purged.
+
+ By separating the queued and dequeued count, only the dequeued_count needs
+ multi-thread synchronisation; the completed flag and queued_count fields
+ are only accessed by the SQL driver thread and need no synchronisation.
+*/
+struct inuse_relaylog {
+ inuse_relaylog *next;
+ Relay_log_info *rli;
+ /*
+ relay_log_state holds the binlog state corresponding to the start of this
+ relay log file. It is an array with relay_log_state_count elements.
+ */
+ rpl_gtid *relay_log_state;
+ uint32 relay_log_state_count;
+ /* Number of events in this relay log queued for worker threads. */
+ int64 queued_count;
+ /* Number of events completed by worker threads. */
+ volatile int64 dequeued_count;
+ /* Set when all events have been read from a relaylog. */
+ bool completed;
+ char name[FN_REFLEN];
+ /* Lock used to protect inuse_relaylog::dequeued_count */
+ my_atomic_rwlock_t inuse_relaylog_atomic_lock;
+};
+
+
+/*
+ This is data for various state needed to be kept for the processing of
+ one event group (transaction) during replication.
+
+ In single-threaded replication, there will be one global rpl_group_info and
+ one global Relay_log_info per master connection. They will be linked
+ together.
+
+ In parallel replication, there will be one rpl_group_info object for
+ each running sql thread, each having their own thd.
+
+ All rpl_group_info will share the same Relay_log_info.
+*/
+
+struct rpl_group_info
+{
+ rpl_group_info *next; /* For free list in rpl_parallel_thread */
+ Relay_log_info *rli;
+ THD *thd;
+ /*
+ Current GTID being processed.
+ The sub_id gives the binlog order within one domain_id. A zero sub_id
+ means that there is no active GTID.
+ */
+ uint64 gtid_sub_id;
+ rpl_gtid current_gtid;
+ uint64 commit_id;
+ /*
+ This is used to keep transaction commit order.
+ We will signal this when we commit, and can register it to wait for the
+ commit_orderer of the previous commit to signal us.
+ */
+ wait_for_commit commit_orderer;
+ /*
+ If non-zero, the sub_id of a prior event group whose commit we have to wait
+ for before committing ourselves. Then wait_commit_group_info points to the
+ event group to wait for.
+
+ Before using this, rpl_parallel_entry::last_committed_sub_id should be
+ compared against wait_commit_sub_id. Only if last_committed_sub_id is
+ smaller than wait_commit_sub_id must the wait be done (otherwise the
+ waited-for transaction is already committed, so we would otherwise wait
+ for the wrong commit).
+ */
+ uint64 wait_commit_sub_id;
+ rpl_group_info *wait_commit_group_info;
+ /*
+ This holds a pointer to a struct that keeps track of the need to wait
+ for the previous batch of event groups to reach the commit stage, before
+ this batch can start to execute.
+
+ (When we execute in parallel the transactions that group committed
+ together on the master, we still need to wait for any prior transactions
+ to have reached the commit stage).
+
+ The pointed-to gco is only valid for as long as
+ gtid_sub_id < parallel_entry->last_committed_sub_id. After that, it can
+ be freed by another thread.
+ */
+ group_commit_orderer *gco;
+
+ struct rpl_parallel_entry *parallel_entry;
+
+ /*
+ A container to hold on Intvar-, Rand-, Uservar- log-events in case
+ the slave is configured with table filtering rules.
+ The withhold events are executed when their parent Query destiny is
+ determined for execution as well.
+ */
+ Deferred_log_events *deferred_events;
+
+ /*
+ State of the container: true stands for IRU events gathering,
+ false does for execution, either deferred or direct.
+ */
+ bool deferred_events_collecting;
+
+ Annotate_rows_log_event *m_annotate_event;
+
+ RPL_TABLE_LIST *tables_to_lock; /* RBR: Tables to lock */
+ uint tables_to_lock_count; /* RBR: Count of tables to lock */
+ table_mapping m_table_map; /* RBR: Mapping table-id to table */
+ mysql_mutex_t sleep_lock;
+ mysql_cond_t sleep_cond;
+
+ /*
+ trans_retries varies between 0 to slave_transaction_retries and counts how
+ many times the slave has retried the present transaction; gets reset to 0
+ when the transaction finally succeeds.
+ */
+ ulong trans_retries;
+
+ /*
+ Used to defer stopping the SQL thread to give it a chance
+ to finish up the current group of events.
+ The timestamp is set and reset in @c sql_slave_killed().
+ */
+ time_t last_event_start_time;
+
+ char *event_relay_log_name;
+ char event_relay_log_name_buf[FN_REFLEN];
+ ulonglong event_relay_log_pos;
+ ulonglong future_event_relay_log_pos;
+ /*
+ The master log name for current event. Only used in parallel replication.
+ */
+ char future_event_master_log_name[FN_REFLEN];
+ bool is_parallel_exec;
+ /* When gtid_pending is true, we have not yet done record_gtid(). */
+ bool gtid_pending;
+ int worker_error;
+ /*
+ Set true when we signalled that we reach the commit phase. Used to avoid
+ counting one event group twice.
+ */
+ bool did_mark_start_commit;
+ enum {
+ GTID_DUPLICATE_NULL=0,
+ GTID_DUPLICATE_IGNORE=1,
+ GTID_DUPLICATE_OWNER=2
+ };
+ /*
+ When --gtid-ignore-duplicates, this is set to one of the above three
+ values:
+ GTID_DUPLICATE_NULL - Not using --gtid-ignore-duplicates.
+ GTID_DUPLICATE_IGNORE - This gtid already applied, skip the event group.
+ GTID_DUPLICATE_OWNER - We are the current owner of the domain, and must
+ apply the event group and then release the domain.
+ */
+ uint8 gtid_ignore_duplicate_state;
+
+ /*
+ Runtime state for printing a note when slave is taking
+ too long while processing a row event.
*/
- bool is_in_group() const {
- return (sql_thd->variables.option_bits & OPTION_BEGIN) ||
- (m_flags & (1UL << IN_STMT));
- }
+ time_t row_stmt_start_timestamp;
+ bool long_find_row_note_printed;
+ /* Needs room for "Gtid D-S-N\x00". */
+ char gtid_info_buf[5+10+1+10+1+20+1];
+
+ /*
+ The timestamp, from the master, of the commit event.
+ Used to do delayed update of rli->last_master_timestamp, for getting
+ reasonable values out of Seconds_Behind_Master in SHOW SLAVE STATUS.
+ */
+ time_t last_master_timestamp;
+
+ /*
+ Information to be able to re-try an event group in case of a deadlock or
+ other temporary error.
+ */
+ inuse_relaylog *relay_log;
+ uint64 retry_start_offset;
+ uint64 retry_event_count;
+ bool killed_for_retry;
+
+ rpl_group_info(Relay_log_info *rli_);
+ ~rpl_group_info();
+ void reinit(Relay_log_info *rli);
+
+ /*
+ Returns true if the argument event resides in the containter;
+ more specifically, the checking is done against the last added event.
+ */
+ bool is_deferred_event(Log_event * ev)
+ {
+ return deferred_events_collecting ? deferred_events->is_last(ev) : false;
+ };
+ /* The general cleanup that slave applier may need at the end of query. */
+ inline void cleanup_after_query()
+ {
+ if (deferred_events)
+ deferred_events->rewind();
+ };
+ /* The general cleanup that slave applier may need at the end of session. */
+ void cleanup_after_session()
+ {
+ if (deferred_events)
+ {
+ delete deferred_events;
+ deferred_events= NULL;
+ }
+ };
/**
Save pointer to Annotate_rows event and switch on the
binlog_annotate_row_events for this sql thread.
- To be called when sql thread recieves an Annotate_rows event.
+ To be called when sql thread receives an Annotate_rows event.
*/
inline void set_annotate_event(Annotate_rows_log_event *event)
{
free_annotate_event();
m_annotate_event= event;
- sql_thd->variables.binlog_annotate_row_events= 1;
+ this->thd->variables.binlog_annotate_row_events= 1;
}
/**
@@ -524,12 +743,37 @@ public:
{
if (m_annotate_event)
{
- sql_thd->variables.binlog_annotate_row_events= 0;
+ this->thd->variables.binlog_annotate_row_events= 0;
delete m_annotate_event;
m_annotate_event= 0;
}
}
+ bool get_table_data(TABLE *table_arg, table_def **tabledef_var, TABLE **conv_table_var) const
+ {
+ DBUG_ASSERT(tabledef_var && conv_table_var);
+ for (TABLE_LIST *ptr= tables_to_lock ; ptr != NULL ; ptr= ptr->next_global)
+ if (ptr->table == table_arg)
+ {
+ *tabledef_var= &static_cast<RPL_TABLE_LIST*>(ptr)->m_tabledef;
+ *conv_table_var= static_cast<RPL_TABLE_LIST*>(ptr)->m_conv_table;
+ DBUG_PRINT("debug", ("Fetching table data for table %s.%s:"
+ " tabledef: %p, conv_table: %p",
+ table_arg->s->db.str, table_arg->s->table_name.str,
+ *tabledef_var, *conv_table_var));
+ return true;
+ }
+ return false;
+ }
+
+ void clear_tables_to_lock();
+ void cleanup_context(THD *, bool);
+ void slave_close_thread_tables(THD *);
+ void mark_start_commit_no_lock();
+ void mark_start_commit();
+ char *gtid_info();
+ void unmark_start_commit();
+
time_t get_row_stmt_start_timestamp()
{
return row_stmt_start_timestamp;
@@ -563,24 +807,48 @@ public:
return long_find_row_note_printed;
}
-private:
+ inline void inc_event_relay_log_pos()
+ {
+ if (!is_parallel_exec)
+ rli->event_relay_log_pos= future_event_relay_log_pos;
+ }
+};
- uint32 m_flags;
- /*
- Runtime state for printing a note when slave is taking
- too long while processing a row event.
- */
- time_t row_stmt_start_timestamp;
- bool long_find_row_note_printed;
+/*
+ The class rpl_sql_thread_info is the THD::system_thread_info for an SQL
+ thread; this is either the driver SQL thread or a worker thread for parallel
+ replication.
+*/
+class rpl_sql_thread_info
+{
+public:
+ char cached_charset[6];
+ Rpl_filter* rpl_filter;
- Annotate_rows_log_event *m_annotate_event;
+ rpl_sql_thread_info(Rpl_filter *filter);
+
+ /*
+ Last charset (6 bytes) seen by slave SQL thread is cached here; it helps
+ the thread save 3 get_charset() per Query_log_event if the charset is not
+ changing from event to event (common situation).
+ When the 6 bytes are equal to 0 is used to mean "cache is invalidated".
+ */
+ void cached_charset_invalidate();
+ bool cached_charset_compare(char *charset) const;
};
// Defined in rpl_rli.cc
int init_relay_log_info(Relay_log_info* rli, const char* info_fname);
-void delete_or_keep_event_post_apply(Relay_log_info *rli,
+
+
+extern struct rpl_slave_state *rpl_global_gtid_slave_state;
+extern gtid_waiting rpl_global_gtid_waiting;
+
+int rpl_load_gtid_slave_state(THD *thd);
+int event_group_new_gtid(rpl_group_info *rgi, Gtid_log_event *gev);
+void delete_or_keep_event_post_apply(rpl_group_info *rgi,
Log_event_type typ, Log_event *ev);
#endif /* RPL_RLI_H */
diff --git a/sql/rpl_tblmap.cc b/sql/rpl_tblmap.cc
index b7ac1b2d091..4c521cf0c16 100644
--- a/sql/rpl_tblmap.cc
+++ b/sql/rpl_tblmap.cc
@@ -13,8 +13,8 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+#include <my_global.h>
#include "sql_priv.h"
-#include "my_global.h" // HAVE_REPLICATION
#ifdef HAVE_REPLICATION
@@ -46,7 +46,7 @@ table_mapping::table_mapping()
offsetof(entry,table_id),sizeof(ulong),
0,0,0);
/* We don't preallocate any block, this is consistent with m_free=0 above */
- init_alloc_root(&m_mem_root, TABLE_ID_HASH_SIZE*sizeof(entry), 0);
+ init_alloc_root(&m_mem_root, TABLE_ID_HASH_SIZE*sizeof(entry), 0, MYF(0));
DBUG_VOID_RETURN;
}
diff --git a/sql/rpl_utility.cc b/sql/rpl_utility.cc
index 9a20f71f4f7..27a7a0478b6 100644
--- a/sql/rpl_utility.cc
+++ b/sql/rpl_utility.cc
@@ -14,6 +14,7 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+#include <my_global.h>
#include "rpl_utility.h"
#include "log_event.h"
@@ -46,6 +47,19 @@ uint32 uint_max(int bits) {
/**
+ Calculate display length for MySQL56 temporal data types from their metadata.
+ It contains fractional precision in the low 16-bit word.
+*/
+static uint32
+max_display_length_for_temporal2_field(uint32 int_display_length,
+ unsigned int metadata)
+{
+ metadata&= 0x00ff;
+ return int_display_length + metadata + (metadata ? 1 : 0);
+}
+
+
+/**
Compute the maximum display length of a field.
@param sql_type Type of the field
@@ -110,12 +124,21 @@ max_display_length_for_field(enum_field_types sql_type, unsigned int metadata)
case MYSQL_TYPE_TIME:
return 3;
+ case MYSQL_TYPE_TIME2:
+ return max_display_length_for_temporal2_field(MIN_TIME_WIDTH, metadata);
+
case MYSQL_TYPE_TIMESTAMP:
return 4;
+ case MYSQL_TYPE_TIMESTAMP2:
+ return max_display_length_for_temporal2_field(MAX_DATETIME_WIDTH, metadata);
+
case MYSQL_TYPE_DATETIME:
return 8;
+ case MYSQL_TYPE_DATETIME2:
+ return max_display_length_for_temporal2_field(MAX_DATETIME_WIDTH, metadata);
+
case MYSQL_TYPE_BIT:
/*
Decode the size of the bit field from the master.
@@ -261,12 +284,21 @@ uint32 table_def::calc_field_size(uint col, uchar *master_data) const
case MYSQL_TYPE_TIME:
length= 3;
break;
+ case MYSQL_TYPE_TIME2:
+ length= my_time_binary_length(m_field_metadata[col]);
+ break;
case MYSQL_TYPE_TIMESTAMP:
length= 4;
break;
+ case MYSQL_TYPE_TIMESTAMP2:
+ length= my_timestamp_binary_length(m_field_metadata[col]);
+ break;
case MYSQL_TYPE_DATETIME:
length= 8;
break;
+ case MYSQL_TYPE_DATETIME2:
+ length= my_datetime_binary_length(m_field_metadata[col]);
+ break;
case MYSQL_TYPE_BIT:
{
/*
@@ -362,6 +394,7 @@ void show_sql_type(enum_field_types type, uint16 metadata, String *str, CHARSET_
break;
case MYSQL_TYPE_TIMESTAMP:
+ case MYSQL_TYPE_TIMESTAMP2:
str->set_ascii(STRING_WITH_LEN("timestamp"));
break;
@@ -379,10 +412,12 @@ void show_sql_type(enum_field_types type, uint16 metadata, String *str, CHARSET_
break;
case MYSQL_TYPE_TIME:
+ case MYSQL_TYPE_TIME2:
str->set_ascii(STRING_WITH_LEN("time"));
break;
case MYSQL_TYPE_DATETIME:
+ case MYSQL_TYPE_DATETIME2:
str->set_ascii(STRING_WITH_LEN("datetime"));
break;
@@ -508,9 +543,9 @@ bool is_conversion_ok(int order, Relay_log_info *rli)
bool allow_non_lossy, allow_lossy;
allow_non_lossy = slave_type_conversions_options &
- (ULL(1) << SLAVE_TYPE_CONVERSIONS_ALL_NON_LOSSY);
+ (1ULL << SLAVE_TYPE_CONVERSIONS_ALL_NON_LOSSY);
allow_lossy= slave_type_conversions_options &
- (ULL(1) << SLAVE_TYPE_CONVERSIONS_ALL_LOSSY);
+ (1ULL << SLAVE_TYPE_CONVERSIONS_ALL_LOSSY);
DBUG_PRINT("enter", ("order: %d, flags:%s%s", order,
allow_non_lossy ? " ALL_NON_LOSSY" : "",
@@ -601,6 +636,36 @@ can_convert_field_to(Field *field,
else
DBUG_RETURN(false);
}
+ else if (
+ /*
+ Conversion from MariaDB TIMESTAMP(0), TIME(0), DATETIME(0)
+ to the corresponding MySQL56 types is non-lossy.
+ */
+ (metadata == 0 &&
+ ((field->real_type() == MYSQL_TYPE_TIMESTAMP2 &&
+ source_type == MYSQL_TYPE_TIMESTAMP) ||
+ (field->real_type() == MYSQL_TYPE_TIME2 &&
+ source_type == MYSQL_TYPE_TIME) ||
+ (field->real_type() == MYSQL_TYPE_DATETIME2 &&
+ source_type == MYSQL_TYPE_DATETIME))) ||
+ /*
+ Conversion from MySQL56 TIMESTAMP(N), TIME(N), DATETIME(N)
+ to the corresponding MariaDB or MySQL55 types is non-lossy.
+ */
+ (metadata == field->decimals() &&
+ ((field->real_type() == MYSQL_TYPE_TIMESTAMP &&
+ source_type == MYSQL_TYPE_TIMESTAMP2) ||
+ (field->real_type() == MYSQL_TYPE_TIME &&
+ source_type == MYSQL_TYPE_TIME2) ||
+ (field->real_type() == MYSQL_TYPE_DATETIME &&
+ source_type == MYSQL_TYPE_DATETIME2))))
+ {
+ /*
+ TS-TODO: conversion from FSP1>FSP2.
+ */
+ *order_var= -1;
+ DBUG_RETURN(true);
+ }
else if (!slave_type_conversions_options)
DBUG_RETURN(false);
@@ -725,6 +790,9 @@ can_convert_field_to(Field *field,
case MYSQL_TYPE_NULL:
case MYSQL_TYPE_ENUM:
case MYSQL_TYPE_SET:
+ case MYSQL_TYPE_TIMESTAMP2:
+ case MYSQL_TYPE_DATETIME2:
+ case MYSQL_TYPE_TIME2:
DBUG_RETURN(false);
}
DBUG_RETURN(false); // To keep GCC happy
@@ -759,14 +827,15 @@ can_convert_field_to(Field *field,
@retval false Master table is not compatible with slave table.
*/
bool
-table_def::compatible_with(THD *thd, Relay_log_info *rli,
+table_def::compatible_with(THD *thd, rpl_group_info *rgi,
TABLE *table, TABLE **conv_table_var)
const
{
/*
We only check the initial columns for the tables.
*/
- uint const cols_to_check= min(table->s->fields, size());
+ uint const cols_to_check= MY_MIN(table->s->fields, size());
+ Relay_log_info *rli= rgi->rli;
TABLE *tmp_table= NULL;
for (uint col= 0 ; col < cols_to_check ; ++col)
@@ -790,7 +859,7 @@ table_def::compatible_with(THD *thd, Relay_log_info *rli,
This will create the full table with all fields. This is
necessary to ge the correct field lengths for the record.
*/
- tmp_table= create_conversion_table(thd, rli, table);
+ tmp_table= create_conversion_table(thd, rgi, table);
if (tmp_table == NULL)
return false;
/*
@@ -818,7 +887,7 @@ table_def::compatible_with(THD *thd, Relay_log_info *rli,
String target_type(target_buf, sizeof(target_buf), &my_charset_latin1);
show_sql_type(type(col), field_metadata(col), &source_type, field->charset());
field->sql_type(target_type);
- rli->report(ERROR_LEVEL, ER_SLAVE_CONVERSION_FAILED,
+ rli->report(ERROR_LEVEL, ER_SLAVE_CONVERSION_FAILED, rgi->gtid_info(),
ER(ER_SLAVE_CONVERSION_FAILED),
col, db_name, tbl_name,
source_type.c_ptr_safe(), target_type.c_ptr_safe());
@@ -860,18 +929,20 @@ table_def::compatible_with(THD *thd, Relay_log_info *rli,
conversion table.
*/
-TABLE *table_def::create_conversion_table(THD *thd, Relay_log_info *rli, TABLE *target_table) const
+TABLE *table_def::create_conversion_table(THD *thd, rpl_group_info *rgi,
+ TABLE *target_table) const
{
DBUG_ENTER("table_def::create_conversion_table");
List<Create_field> field_list;
TABLE *conv_table= NULL;
+ Relay_log_info *rli= rgi->rli;
/*
At slave, columns may differ. So we should create
- min(columns@master, columns@slave) columns in the
+ MY_MIN(columns@master, columns@slave) columns in the
conversion table.
*/
- uint const cols_to_create= min(target_table->s->fields, size());
+ uint const cols_to_create= MY_MIN(target_table->s->fields, size());
for (uint col= 0 ; col < cols_to_create; ++col)
{
Create_field *field_def=
@@ -943,7 +1014,7 @@ TABLE *table_def::create_conversion_table(THD *thd, Relay_log_info *rli, TABLE *
DBUG_PRINT("debug", ("sql_type: %d, target_field: '%s', max_length: %d, decimals: %d,"
" maybe_null: %d, unsigned_flag: %d, pack_length: %u",
- type(col), target_table->field[col]->field_name,
+ binlog_type(col), target_table->field[col]->field_name,
max_length, decimals, TRUE, unsigned_flag,
pack_length));
field_def->init_for_tmp_table(type(col),
@@ -960,7 +1031,7 @@ TABLE *table_def::create_conversion_table(THD *thd, Relay_log_info *rli, TABLE *
err:
if (conv_table == NULL)
- rli->report(ERROR_LEVEL, ER_SLAVE_CANT_CREATE_CONVERSION,
+ rli->report(ERROR_LEVEL, ER_SLAVE_CANT_CREATE_CONVERSION, rgi->gtid_info(),
ER(ER_SLAVE_CANT_CREATE_CONVERSION),
target_table->s->db.str,
target_table->s->table_name.str);
@@ -1000,7 +1071,7 @@ table_def::table_def(unsigned char *types, ulong size,
int index= 0;
for (unsigned int i= 0; i < m_size; i++)
{
- switch (m_type[i]) {
+ switch (binlog_type(i)) {
case MYSQL_TYPE_TINY_BLOB:
case MYSQL_TYPE_BLOB:
case MYSQL_TYPE_MEDIUM_BLOB:
@@ -1049,6 +1120,11 @@ table_def::table_def(unsigned char *types, ulong size,
m_field_metadata[i]= x;
break;
}
+ case MYSQL_TYPE_TIME2:
+ case MYSQL_TYPE_DATETIME2:
+ case MYSQL_TYPE_TIMESTAMP2:
+ m_field_metadata[i]= field_metadata[index++];
+ break;
default:
m_field_metadata[i]= 0;
break;
@@ -1111,7 +1187,7 @@ bool event_checksum_test(uchar *event_buf, ulong event_len, uint8 alg)
compile_time_assert(BINLOG_CHECKSUM_ALG_ENUM_END <= 0x80);
}
incoming= uint4korr(event_buf + event_len - BINLOG_CHECKSUM_LEN);
- computed= my_checksum(0L, NULL, 0);
+ computed= 0;
/* checksum the event content but the checksum part itself */
computed= my_checksum(computed, (const uchar*) event_buf,
event_len - BINLOG_CHECKSUM_LEN);
@@ -1130,7 +1206,7 @@ bool event_checksum_test(uchar *event_buf, ulong event_len, uint8 alg)
Deferred_log_events::Deferred_log_events(Relay_log_info *rli) : last_added(NULL)
{
- my_init_dynamic_array(&array, sizeof(Log_event *), 32, 16);
+ my_init_dynamic_array(&array, sizeof(Log_event *), 32, 16, MYF(0));
}
Deferred_log_events::~Deferred_log_events()
@@ -1150,27 +1226,27 @@ bool Deferred_log_events::is_empty()
return array.elements == 0;
}
-bool Deferred_log_events::execute(Relay_log_info *rli)
+bool Deferred_log_events::execute(rpl_group_info *rgi)
{
bool res= false;
DBUG_ENTER("Deferred_log_events::execute");
- DBUG_ASSERT(rli->deferred_events_collecting);
+ DBUG_ASSERT(rgi->deferred_events_collecting);
- rli->deferred_events_collecting= false;
+ rgi->deferred_events_collecting= false;
for (uint i= 0; !res && i < array.elements; i++)
{
Log_event *ev= (* (Log_event **)
dynamic_array_ptr(&array, i));
- res= ev->apply_event(rli);
+ res= ev->apply_event(rgi);
}
- rli->deferred_events_collecting= true;
+ rgi->deferred_events_collecting= true;
DBUG_RETURN(res);
}
void Deferred_log_events::rewind()
{
/*
- Reset preceeding Query log event events which execution was
+ Reset preceding Query log event events which execution was
deferred because of slave side filtering.
*/
if (!is_empty())
diff --git a/sql/rpl_utility.h b/sql/rpl_utility.h
index 79f4517c492..ed0ce16363b 100644
--- a/sql/rpl_utility.h
+++ b/sql/rpl_utility.h
@@ -30,6 +30,7 @@
class Relay_log_info;
class Log_event;
+struct rpl_group_info;
/**
A table definition from the master.
@@ -65,6 +66,14 @@ public:
ulong size() const { return m_size; }
+ /**
+ Returns internal binlog type code for one field,
+ without translation to real types.
+ */
+ enum_field_types binlog_type(ulong index) const
+ {
+ return static_cast<enum_field_types>(m_type[index]);
+ }
/*
Return a representation of the type data for one field.
@@ -82,7 +91,7 @@ public:
either MYSQL_TYPE_STRING, MYSQL_TYPE_ENUM, or MYSQL_TYPE_SET, so
we might need to modify the type to get the real type.
*/
- enum_field_types source_type= static_cast<enum_field_types>(m_type[index]);
+ enum_field_types source_type= binlog_type(index);
uint16 source_metadata= m_field_metadata[index];
switch (source_type)
{
@@ -179,7 +188,7 @@ public:
@retval 0 if the table definition is compatible with @c table
*/
#ifndef MYSQL_CLIENT
- bool compatible_with(THD *thd, Relay_log_info *rli, TABLE *table,
+ bool compatible_with(THD *thd, rpl_group_info *rgi, TABLE *table,
TABLE **conv_table_var) const;
/**
@@ -204,7 +213,8 @@ public:
@return A pointer to a temporary table with memory allocated in the
thread's memroot, NULL if the table could not be created
*/
- TABLE *create_conversion_table(THD *thd, Relay_log_info *rli, TABLE *target_table) const;
+ TABLE *create_conversion_table(THD *thd, rpl_group_info *rgi,
+ TABLE *target_table) const;
#endif
@@ -230,6 +240,7 @@ struct RPL_TABLE_LIST
bool m_tabledef_valid;
table_def m_tabledef;
TABLE *m_conv_table;
+ bool master_had_triggers;
};
@@ -275,7 +286,7 @@ public:
/* queue for exection at Query-log-event time prior the Query */
int add(Log_event *ev);
bool is_empty();
- bool execute(Relay_log_info *rli);
+ bool execute(struct rpl_group_info *rgi);
void rewind();
bool is_last(Log_event *ev) { return ev == last_added; };
};
@@ -287,7 +298,7 @@ public:
do { \
char buf[256]; \
uint i; \
- for (i = 0 ; i < min(sizeof(buf) - 1, (BS)->n_bits) ; i++) \
+ for (i = 0 ; i < MY_MIN(sizeof(buf) - 1, (BS)->n_bits) ; i++) \
buf[i] = bitmap_is_set((BS), i) ? '1' : '0'; \
buf[i] = '\0'; \
DBUG_PRINT((N), ((FRM), buf)); \
diff --git a/sql/scheduler.cc b/sql/scheduler.cc
index 1cdda089a6b..bc3166210b5 100644
--- a/sql/scheduler.cc
+++ b/sql/scheduler.cc
@@ -27,6 +27,7 @@
#include "mysqld.h"
#include "sql_class.h"
#include "sql_callback.h"
+#include <violite.h>
/*
End connection, in case when we are using 'no-threads'
@@ -61,6 +62,15 @@ static void scheduler_wait_sync_begin(void) {
static void scheduler_wait_sync_end(void) {
thd_wait_end(NULL);
}
+
+static void scheduler_wait_net_begin(void) {
+ thd_wait_begin(NULL, THD_WAIT_NET);
+}
+
+static void scheduler_wait_net_end(void) {
+ thd_wait_end(NULL);
+}
+
};
/**@}*/
@@ -76,6 +86,9 @@ void scheduler_init() {
scheduler_wait_lock_end);
thr_set_sync_wait_callback(scheduler_wait_sync_begin,
scheduler_wait_sync_end);
+
+ vio_set_wait_callback(scheduler_wait_net_begin,
+ scheduler_wait_net_end);
}
@@ -122,7 +135,7 @@ void one_thread_per_connection_scheduler(scheduler_functions *func,
#endif
/*
- Initailize scheduler for --thread-handling=no-threads
+ Initialize scheduler for --thread-handling=no-threads
*/
void one_thread_scheduler(scheduler_functions *func)
@@ -138,52 +151,3 @@ void one_thread_scheduler(scheduler_functions *func)
func->end_thread= no_threads_end;
}
-
-
-/*
- no pluggable schedulers in mariadb.
- when we'll want it, we'll do it properly
-*/
-#if 0
-
-static scheduler_functions *saved_thread_scheduler;
-static uint saved_thread_handling;
-
-extern "C"
-int my_thread_scheduler_set(scheduler_functions *scheduler)
-{
- DBUG_ASSERT(scheduler != 0);
-
- if (scheduler == NULL)
- return 1;
-
- saved_thread_scheduler= thread_scheduler;
- saved_thread_handling= thread_handling;
- thread_scheduler= scheduler;
- // Scheduler loaded dynamically
- thread_handling= SCHEDULER_TYPES_COUNT;
- return 0;
-}
-
-
-extern "C"
-int my_thread_scheduler_reset()
-{
- DBUG_ASSERT(saved_thread_scheduler != NULL);
-
- if (saved_thread_scheduler == NULL)
- return 1;
-
- thread_scheduler= saved_thread_scheduler;
- thread_handling= saved_thread_handling;
- saved_thread_scheduler= 0;
- return 0;
-}
-#else
-extern "C" int my_thread_scheduler_set(scheduler_functions *scheduler)
-{ return 1; }
-
-extern "C" int my_thread_scheduler_reset()
-{ return 1; }
-#endif
-
diff --git a/sql/set_var.cc b/sql/set_var.cc
index 9efa29d8041..7ad528f0eae 100644
--- a/sql/set_var.cc
+++ b/sql/set_var.cc
@@ -14,13 +14,9 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
-#ifdef USE_PRAGMA_IMPLEMENTATION
-#pragma implementation
-#endif
-
/* variable declarations are in sys_vars.cc now !!! */
-#include "my_global.h" /* NO_EMBEDDED_ACCESS_CHECKS */
+#include "sql_plugin.h" // Includes my_global.h
#include "sql_class.h" // set_var.h: session_var_ptr
#include "set_var.h"
#include "sql_priv.h"
@@ -264,6 +260,114 @@ bool sys_var::set_default(THD *thd, set_var* var)
return check(thd, var) || update(thd, var);
}
+
+#define do_num_val(T,CMD) \
+do { \
+ mysql_mutex_lock(&LOCK_global_system_variables); \
+ T val= *(T*) value_ptr(thd, type, base); \
+ mysql_mutex_unlock(&LOCK_global_system_variables); \
+ CMD; \
+} while (0)
+
+#define case_for_integers(CMD) \
+ case SHOW_SINT: do_num_val (int,CMD); \
+ case SHOW_SLONG: do_num_val (long,CMD); \
+ case SHOW_SLONGLONG:do_num_val (longlong,CMD); \
+ case SHOW_UINT: do_num_val (uint,CMD); \
+ case SHOW_ULONG: do_num_val (ulong,CMD); \
+ case SHOW_ULONGLONG:do_num_val (ulonglong,CMD); \
+ case SHOW_HA_ROWS: do_num_val (ha_rows,CMD); \
+ case SHOW_BOOL: do_num_val (bool,CMD); \
+ case SHOW_MY_BOOL: do_num_val (my_bool,CMD)
+
+#define case_for_double(CMD) \
+ case SHOW_DOUBLE: do_num_val (double,CMD)
+
+#define case_get_string_as_lex_string \
+ case SHOW_CHAR: \
+ mysql_mutex_lock(&LOCK_global_system_variables); \
+ sval.str= (char*) value_ptr(thd, type, base); \
+ sval.length= sval.str ? strlen(sval.str) : 0; \
+ break; \
+ case SHOW_CHAR_PTR: \
+ mysql_mutex_lock(&LOCK_global_system_variables); \
+ sval.str= *(char**) value_ptr(thd, type, base); \
+ sval.length= sval.str ? strlen(sval.str) : 0; \
+ break; \
+ case SHOW_LEX_STRING: \
+ mysql_mutex_lock(&LOCK_global_system_variables); \
+ sval= *(LEX_STRING *) value_ptr(thd, type, base); \
+ break
+
+longlong sys_var::val_int(bool *is_null,
+ THD *thd, enum_var_type type, LEX_STRING *base)
+{
+ LEX_STRING sval;
+ *is_null= false;
+ switch (show_type())
+ {
+ case_get_string_as_lex_string;
+ case_for_integers(return val);
+ case_for_double(return (longlong) val);
+ default:
+ my_error(ER_VAR_CANT_BE_READ, MYF(0), name.str);
+ return 0;
+ }
+
+ longlong ret= 0;
+ if (!(*is_null= !sval.str))
+ ret= longlong_from_string_with_check(system_charset_info,
+ sval.str, sval.str + sval.length);
+ mysql_mutex_unlock(&LOCK_global_system_variables);
+ return ret;
+}
+
+
+String *sys_var::val_str(String *str,
+ THD *thd, enum_var_type type, LEX_STRING *base)
+{
+ LEX_STRING sval;
+ switch (show_type())
+ {
+ case_get_string_as_lex_string;
+ case_for_integers(return str->set((ulonglong)val, system_charset_info) ? 0 : str);
+ case_for_double(return str->set_real(val, 6, system_charset_info) ? 0 : str);
+ default:
+ my_error(ER_VAR_CANT_BE_READ, MYF(0), name.str);
+ return 0;
+ }
+
+ if (!sval.str || str->copy(sval.str, sval.length, system_charset_info))
+ str= NULL;
+ mysql_mutex_unlock(&LOCK_global_system_variables);
+ return str;
+}
+
+
+double sys_var::val_real(bool *is_null,
+ THD *thd, enum_var_type type, LEX_STRING *base)
+{
+ LEX_STRING sval;
+ *is_null= false;
+ switch (show_type())
+ {
+ case_get_string_as_lex_string;
+ case_for_integers(return val);
+ case_for_double(return val);
+ default:
+ my_error(ER_VAR_CANT_BE_READ, MYF(0), name.str);
+ return 0;
+ }
+
+ double ret= 0;
+ if (!(*is_null= !sval.str))
+ ret= double_from_string_with_check(system_charset_info,
+ sval.str, sval.str + sval.length);
+ mysql_mutex_unlock(&LOCK_global_system_variables);
+ return ret;
+}
+
+
void sys_var::do_deprecated_warning(THD *thd)
{
if (deprecation_substitute != NULL)
@@ -279,7 +383,7 @@ void sys_var::do_deprecated_warning(THD *thd)
? ER_WARN_DEPRECATED_SYNTAX_NO_REPLACEMENT
: ER_WARN_DEPRECATED_SYNTAX;
if (thd)
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_WARN_DEPRECATED_SYNTAX, ER(errmsg),
buf1, deprecation_substitute);
else
@@ -311,12 +415,12 @@ bool throw_bounds_warning(THD *thd, const char *name,
else
llstr(v, buf);
- if (thd->variables.sql_mode & MODE_STRICT_ALL_TABLES)
+ if (thd->is_strict_mode())
{
my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), name, buf);
return true;
}
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_TRUNCATED_WRONG_VALUE,
ER(ER_TRUNCATED_WRONG_VALUE), name, buf);
}
@@ -331,12 +435,12 @@ bool throw_bounds_warning(THD *thd, const char *name, bool fixed, double v)
my_gcvt(v, MY_GCVT_ARG_DOUBLE, sizeof(buf) - 1, buf, NULL);
- if (thd->variables.sql_mode & MODE_STRICT_ALL_TABLES)
+ if (thd->is_strict_mode())
{
my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), name, buf);
return true;
}
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_TRUNCATED_WRONG_VALUE,
ER(ER_TRUNCATED_WRONG_VALUE), name, buf);
}
@@ -452,7 +556,11 @@ int mysql_del_sys_var_chain(sys_var *first)
static int show_cmp(SHOW_VAR *a, SHOW_VAR *b)
{
+#ifdef WITH_WSREP
+ return my_strcasecmp(system_charset_info, a->name, b->name);
+#else
return strcmp(a->name, b->name);
+#endif /* WITH_WSREP */
}
@@ -527,6 +635,7 @@ sys_var *intern_find_sys_var(const char *str, uint length)
*/
var= (sys_var*) my_hash_search(&system_variable_hash,
(uchar*) str, length ? length : strlen(str));
+
return var;
}
@@ -563,7 +672,7 @@ int sql_set_variables(THD *thd, List<set_var_base> *var_list)
if ((error= var->check(thd)))
goto err;
}
- if (!(error= test(thd->is_error())))
+ if (!(error= MY_TEST(thd->is_error())))
{
it.rewind();
while ((var= it++))
@@ -724,26 +833,7 @@ int set_var_user::update(THD *thd)
int set_var_password::check(THD *thd)
{
#ifndef NO_EMBEDDED_ACCESS_CHECKS
- if (!user->host.str)
- {
- DBUG_ASSERT(thd->security_ctx->priv_host);
- if (*thd->security_ctx->priv_host != 0)
- {
- user->host.str= (char *) thd->security_ctx->priv_host;
- user->host.length= strlen(thd->security_ctx->priv_host);
- }
- else
- {
- user->host.str= (char *)"%";
- user->host.length= 1;
- }
- }
- if (!user->user.str)
- {
- DBUG_ASSERT(thd->security_ctx->user);
- user->user.str= (char *) thd->security_ctx->user;
- user->user.length= strlen(thd->security_ctx->user);
- }
+ user= get_current_user(thd, user);
/* Returns 1 as the function sends error to client */
return check_change_password(thd, user->host.str, user->user.str,
password, strlen(password)) ? 1 : 0;
@@ -764,6 +854,29 @@ int set_var_password::update(THD *thd)
}
/*****************************************************************************
+ Functions to handle SET ROLE
+*****************************************************************************/
+int set_var_role::check(THD *thd)
+{
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ int status= acl_check_setrole(thd, role.str, &access);
+ return status;
+#else
+ return 0;
+#endif
+}
+
+int set_var_role::update(THD *thd)
+{
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ return acl_setrole(thd, role.str, access);
+#else
+ return 0;
+#endif
+}
+
+
+/*****************************************************************************
Functions to handle SET NAMES and SET CHARACTER SET
*****************************************************************************/
diff --git a/sql/set_var.h b/sql/set_var.h
index 4a499421f07..064da1404fa 100644
--- a/sql/set_var.h
+++ b/sql/set_var.h
@@ -61,7 +61,7 @@ public:
sys_var *next;
LEX_CSTRING name;
enum flag_enum { GLOBAL, SESSION, ONLY_SESSION, SCOPE_MASK=1023,
- READONLY=1024, ALLOCATED=2048, PARSE_EARLY=4096 };
+ READONLY=1024, ALLOCATED=2048, PARSE_EARLY=4096, SHOW_VALUE_IN_HELP=8192 };
/**
Enumeration type to indicate for a system variable whether
it will be written to the binlog or not.
@@ -69,13 +69,14 @@ public:
enum binlog_status_enum { VARIABLE_NOT_IN_BINLOG,
SESSION_VARIABLE_IN_BINLOG } binlog_status;
+ my_option option; ///< min, max, default values are stored here
+
protected:
typedef bool (*on_check_function)(sys_var *self, THD *thd, set_var *var);
typedef bool (*on_update_function)(sys_var *self, THD *thd, enum_var_type type);
int flags; ///< or'ed flag_enum values
const SHOW_TYPE show_val_type; ///< what value_ptr() returns for sql_show.cc
- my_option option; ///< min, max, default values are stored here
PolyLock *guard; ///< *second* lock that protects the variable
ptrdiff_t offset; ///< offset to the value from global_system_variables
on_check_function on_check;
@@ -114,6 +115,10 @@ public:
bool set_default(THD *thd, set_var *var);
bool update(THD *thd, set_var *var);
+ longlong val_int(bool *is_null, THD *thd, enum_var_type type, LEX_STRING *base);
+ String *val_str(String *str, THD *thd, enum_var_type type, LEX_STRING *base);
+ double val_real(bool *is_null, THD *thd, enum_var_type type, LEX_STRING *base);
+
SHOW_TYPE show_type() { return show_val_type; }
int scope() const { return flags & SCOPE_MASK; }
CHARSET_INFO *charset(THD *thd);
@@ -138,8 +143,9 @@ public:
}
bool register_option(DYNAMIC_ARRAY *array, int parse_flags)
{
- return (option.id != -1) && ((flags & PARSE_EARLY) == parse_flags) &&
- insert_dynamic(array, (uchar*)&option);
+ return ((((option.id != -1) && ((flags & PARSE_EARLY) == parse_flags)) ||
+ (flags & parse_flags)) &&
+ insert_dynamic(array, (uchar*)&option));
}
void do_deprecated_warning(THD *thd);
@@ -232,9 +238,7 @@ public:
if (value_arg && value_arg->type() == Item::FIELD_ITEM)
{
Item_field *item= (Item_field*) value_arg;
- if (!(value=new Item_string(item->field_name,
- (uint) strlen(item->field_name),
- system_charset_info))) // names are utf8
+ if (!(value=new Item_string_sys(item->field_name))) // names are utf8
value=value_arg; /* Give error message later */
}
else
@@ -276,6 +280,18 @@ public:
int update(THD *thd);
};
+/* For SET ROLE */
+
+class set_var_role: public set_var_base
+{
+ LEX_STRING role;
+ ulonglong access;
+public:
+ set_var_role(LEX_STRING role_arg) : role(role_arg) {}
+ int check(THD *thd);
+ int update(THD *thd);
+};
+
/* For SET NAMES and SET CHARACTER SET */
@@ -307,6 +323,7 @@ extern SHOW_COMP_OPTION have_query_cache;
extern SHOW_COMP_OPTION have_geometry, have_rtree_keys;
extern SHOW_COMP_OPTION have_crypt;
extern SHOW_COMP_OPTION have_compress;
+extern SHOW_COMP_OPTION have_openssl;
/*
Prototypes for helper functions
@@ -321,6 +338,7 @@ bool fix_delay_key_write(sys_var *self, THD *thd, enum_var_type type);
ulonglong expand_sql_mode(ulonglong sql_mode);
bool sql_mode_string_representation(THD *thd, ulonglong sql_mode, LEX_STRING *ls);
+int default_regex_flags_pcre(const THD *thd);
extern sys_var *Sys_autocommit_ptr;
diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt
index 6fcb460e6c6..4394e796a1a 100644
--- a/sql/share/errmsg-utf8.txt
+++ b/sql/share/errmsg-utf8.txt
@@ -1,4 +1,4 @@
-languages czech=cze latin2, danish=dan latin1, dutch=nla latin1, english=eng latin1, estonian=est latin7, french=fre latin1, german=ger latin1, greek=greek greek, hungarian=hun latin2, italian=ita latin1, japanese=jpn ujis, japanese-sjis=jps sjis, korean=kor euckr, norwegian-ny=norwegian-ny latin1, norwegian=nor latin1, polish=pol latin2, portuguese=por latin1, romanian=rum latin2, russian=rus koi8r, serbian=serbian cp1250, slovak=slo latin2, spanish=spa latin1, swedish=swe latin1, ukrainian=ukr koi8u;
+languages czech=cze latin2, danish=dan latin1, dutch=nla latin1, english=eng latin1, estonian=est latin7, french=fre latin1, german=ger latin1, greek=greek greek, hungarian=hun latin2, italian=ita latin1, japanese=jpn ujis, korean=kor euckr, norwegian-ny=norwegian-ny latin1, norwegian=nor latin1, polish=pol latin2, portuguese=por latin1, romanian=rum latin2, russian=rus koi8r, serbian=serbian cp1250, slovak=slo latin2, spanish=spa latin1, swedish=swe latin1, ukrainian=ukr koi8u, bulgarian=bgn cp1251;
default-language eng
@@ -51,92 +51,90 @@ ER_YES
spa "SI"
ukr "ТÐК"
ER_CANT_CREATE_FILE
- cze "Nemohu vytvo-Břit soubor '%-.200s' (chybový kód: %d)"
- dan "Kan ikke oprette filen '%-.200s' (Fejlkode: %d)"
- nla "Kan file '%-.200s' niet aanmaken (Errcode: %d)"
- eng "Can't create file '%-.200s' (errno: %d)"
- est "Ei suuda luua faili '%-.200s' (veakood: %d)"
- fre "Ne peut créer le fichier '%-.200s' (Errcode: %d)"
- ger "Kann Datei '%-.200s' nicht erzeugen (Fehler: %d)"
- greek "ΑδÏνατη η δημιουÏγία του αÏχείου '%-.200s' (κωδικός λάθους: %d)"
- hun "A '%-.200s' file nem hozhato letre (hibakod: %d)"
- ita "Impossibile creare il file '%-.200s' (errno: %d)"
- jpn "'%-.200s' ファイルãŒä½œã‚Œã¾ã›ã‚“ (errno: %d)"
- kor "í™”ì¼ '%-.200s'를 만들지 못했습니다. (ì—러번호: %d)"
- nor "Kan ikke opprette fila '%-.200s' (Feilkode: %d)"
- norwegian-ny "Kan ikkje opprette fila '%-.200s' (Feilkode: %d)"
- pol "Nie można stworzyć pliku '%-.200s' (Kod błędu: %d)"
- por "Não pode criar o arquivo '%-.200s' (erro no. %d)"
- rum "Nu pot sa creez fisierul '%-.200s' (Eroare: %d)"
- rus "Ðевозможно Ñоздать файл '%-.200s' (ошибка: %d)"
- serbian "Ne mogu da kreiram file '%-.200s' (errno: %d)"
- slo "Nemôžem vytvoriť súbor '%-.200s' (chybový kód: %d)"
- spa "No puedo crear archivo '%-.200s' (Error: %d)"
- swe "Kan inte skapa filen '%-.200s' (Felkod: %d)"
- ukr "Ðе можу Ñтворити файл '%-.200s' (помилка: %d)"
+ cze "Nemohu vytvořit soubor '%-.200s' (chybový kód: %M)"
+ dan "Kan ikke oprette filen '%-.200s' (Fejlkode: %M)"
+ nla "Kan file '%-.200s' niet aanmaken (Errcode: %M)"
+ eng "Can't create file '%-.200s' (errno: %M)"
+ est "Ei suuda luua faili '%-.200s' (veakood: %M)"
+ fre "Ne peut créer le fichier '%-.200s' (Errcode: %M)"
+ ger "Kann Datei '%-.200s' nicht erzeugen (Fehler: %M)"
+ greek "ΑδÏνατη η δημιουÏγία του αÏχείου '%-.200s' (κωδικός λάθους: %M)"
+ hun "A '%-.200s' file nem hozhato letre (hibakod: %M)"
+ ita "Impossibile creare il file '%-.200s' (errno: %M)"
+ jpn "ファイル '%-.200s' を作æˆã§ãã¾ã›ã‚“。(エラー番å·: %M)"
+ kor "í™”ì¼ '%-.200s'를 만들지 못했습니다. (ì—러번호: %M)"
+ nor "Kan ikke opprette fila '%-.200s' (Feilkode: %M)"
+ norwegian-ny "Kan ikkje opprette fila '%-.200s' (Feilkode: %M)"
+ pol "Nie można stworzyć pliku '%-.200s' (Kod błędu: %M)"
+ por "Não pode criar o arquivo '%-.200s' (erro no. %M)"
+ rum "Nu pot sa creez fisierul '%-.200s' (Eroare: %M)"
+ rus "Ðевозможно Ñоздать файл '%-.200s' (ошибка: %M)"
+ serbian "Ne mogu da kreiram file '%-.200s' (errno: %M)"
+ slo "Nemôžem vytvoriť súbor '%-.200s' (chybový kód: %M)"
+ spa "No puedo crear archivo '%-.200s' (Error: %M)"
+ swe "Kan inte skapa filen '%-.200s' (Felkod: %M)"
+ ukr "Ðе можу Ñтворити файл '%-.200s' (помилка: %M)"
ER_CANT_CREATE_TABLE
- cze "Nemohu vytvo-Břit tabulku '%-.200s' (chybový kód: %d)"
- dan "Kan ikke oprette tabellen '%-.200s' (Fejlkode: %d)"
- nla "Kan tabel '%-.200s' niet aanmaken (Errcode: %d)"
- eng "Can't create table '%-.200s' (errno: %d)"
- jps "'%-.200s' テーブルãŒä½œã‚Œã¾ã›ã‚“.(errno: %d)",
- est "Ei suuda luua tabelit '%-.200s' (veakood: %d)"
- fre "Ne peut créer la table '%-.200s' (Errcode: %d)"
- ger "Kann Tabelle '%-.200s' nicht erzeugen (Fehler: %d)"
- greek "ΑδÏνατη η δημιουÏγία του πίνακα '%-.200s' (κωδικός λάθους: %d)"
- hun "A '%-.200s' tabla nem hozhato letre (hibakod: %d)"
- ita "Impossibile creare la tabella '%-.200s' (errno: %d)"
- jpn "'%-.200s' テーブルãŒä½œã‚Œã¾ã›ã‚“.(errno: %d)"
- kor "í…Œì´ë¸” '%-.200s'를 만들지 못했습니다. (ì—러번호: %d)"
- nor "Kan ikke opprette tabellen '%-.200s' (Feilkode: %d)"
- norwegian-ny "Kan ikkje opprette tabellen '%-.200s' (Feilkode: %d)"
- pol "Nie można stworzyć tabeli '%-.200s' (Kod błędu: %d)"
- por "Não pode criar a tabela '%-.200s' (erro no. %d)"
- rum "Nu pot sa creez tabla '%-.200s' (Eroare: %d)"
- rus "Ðевозможно Ñоздать таблицу '%-.200s' (ошибка: %d)"
- serbian "Ne mogu da kreiram tabelu '%-.200s' (errno: %d)"
- slo "Nemôžem vytvoriť tabuľku '%-.200s' (chybový kód: %d)"
- spa "No puedo crear tabla '%-.200s' (Error: %d)"
- swe "Kan inte skapa tabellen '%-.200s' (Felkod: %d)"
- ukr "Ðе можу Ñтворити таблицю '%-.200s' (помилка: %d)"
+ cze "Nemohu vytvořit tabulku %`s.%`s (chybový kód: %M)"
+ dan "Kan ikke oprette tabellen %`s.%`s (Fejlkode: %M)"
+ nla "Kan tabel %`s.%`s niet aanmaken (Errcode: %M)"
+ eng "Can't create table %`s.%`s (errno: %M)"
+ jps "%`s.%`s テーブルãŒä½œã‚Œã¾ã›ã‚“.(errno: %M)",
+ est "Ei suuda luua tabelit %`s.%`s (veakood: %M)"
+ fre "Ne peut créer la table %`s.%`s (Errcode: %M)"
+ ger "Kann Tabelle %`s.%`s nicht erzeugen (Fehler: %M)"
+ greek "ΑδÏνατη η δημιουÏγία του πίνακα %`s.%`s (κωδικός λάθους: %M)"
+ hun "A %`s.%`s tabla nem hozhato letre (hibakod: %M)"
+ ita "Impossibile creare la tabella %`s.%`s (errno: %M)"
+ jpn "%`s.%`s テーブルãŒä½œã‚Œã¾ã›ã‚“.(errno: %M)"
+ kor "í…Œì´ë¸” %`s.%`s를 만들지 못했습니다. (ì—러번호: %M)"
+ nor "Kan ikke opprette tabellen %`s.%`s (Feilkode: %M)"
+ norwegian-ny "Kan ikkje opprette tabellen %`s.%`s (Feilkode: %M)"
+ pol "Nie można stworzyć tabeli %`s.%`s (Kod błędu: %M)"
+ por "Não pode criar a tabela %`s.%`s (erro no. %M)"
+ rum "Nu pot sa creez tabla %`s.%`s (Eroare: %M)"
+ rus "Ðевозможно Ñоздать таблицу %`s.%`s (ошибка: %M)"
+ serbian "Ne mogu da kreiram tabelu %`s.%`s (errno: %M)"
+ slo "Nemôžem vytvoriť tabuľku %`s.%`s (chybový kód: %M)"
+ spa "No puedo crear tabla %`s.%`s (Error: %M)"
+ swe "Kan inte skapa tabellen %`s.%`s (Felkod: %M)"
+ ukr "Ðе можу Ñтворити таблицю %`s.%`s (помилка: %M)"
ER_CANT_CREATE_DB
- cze "Nemohu vytvo-Břit databázi '%-.192s' (chybový kód: %d)"
- dan "Kan ikke oprette databasen '%-.192s' (Fejlkode: %d)"
- nla "Kan database '%-.192s' niet aanmaken (Errcode: %d)"
- eng "Can't create database '%-.192s' (errno: %d)"
- jps "'%-.192s' データベースãŒä½œã‚Œã¾ã›ã‚“ (errno: %d)",
- est "Ei suuda luua andmebaasi '%-.192s' (veakood: %d)"
- fre "Ne peut créer la base '%-.192s' (Erreur %d)"
- ger "Kann Datenbank '%-.192s' nicht erzeugen (Fehler: %d)"
- greek "ΑδÏνατη η δημιουÏγία της βάσης δεδομένων '%-.192s' (κωδικός λάθους: %d)"
- hun "Az '%-.192s' adatbazis nem hozhato letre (hibakod: %d)"
- ita "Impossibile creare il database '%-.192s' (errno: %d)"
- jpn "'%-.192s' データベースãŒä½œã‚Œã¾ã›ã‚“ (errno: %d)"
- kor "ë°ì´íƒ€ë² ì´ìŠ¤ '%-.192s'를 만들지 못했습니다.. (ì—러번호: %d)"
- nor "Kan ikke opprette databasen '%-.192s' (Feilkode: %d)"
- norwegian-ny "Kan ikkje opprette databasen '%-.192s' (Feilkode: %d)"
- pol "Nie można stworzyć bazy danych '%-.192s' (Kod błędu: %d)"
- por "Não pode criar o banco de dados '%-.192s' (erro no. %d)"
- rum "Nu pot sa creez baza de date '%-.192s' (Eroare: %d)"
- rus "Ðевозможно Ñоздать базу данных '%-.192s' (ошибка: %d)"
- serbian "Ne mogu da kreiram bazu '%-.192s' (errno: %d)"
- slo "Nemôžem vytvoriť databázu '%-.192s' (chybový kód: %d)"
- spa "No puedo crear base de datos '%-.192s' (Error: %d)"
- swe "Kan inte skapa databasen '%-.192s' (Felkod: %d)"
- ukr "Ðе можу Ñтворити базу данних '%-.192s' (помилка: %d)"
+ cze "Nemohu vytvořit databázi '%-.192s' (chybový kód: %M)"
+ dan "Kan ikke oprette databasen '%-.192s' (Fejlkode: %M)"
+ nla "Kan database '%-.192s' niet aanmaken (Errcode: %M)"
+ eng "Can't create database '%-.192s' (errno: %M)"
+ est "Ei suuda luua andmebaasi '%-.192s' (veakood: %M)"
+ fre "Ne peut créer la base '%-.192s' (Erreur %M)"
+ ger "Kann Datenbank '%-.192s' nicht erzeugen (Fehler: %M)"
+ greek "ΑδÏνατη η δημιουÏγία της βάσης δεδομένων '%-.192s' (κωδικός λάθους: %M)"
+ hun "Az '%-.192s' adatbazis nem hozhato letre (hibakod: %M)"
+ ita "Impossibile creare il database '%-.192s' (errno: %M)"
+ jpn "データベース '%-.192s' を作æˆã§ãã¾ã›ã‚“。(エラー番å·: %M)"
+ kor "ë°ì´íƒ€ë² ì´ìŠ¤ '%-.192s'를 만들지 못했습니다.. (ì—러번호: %M)"
+ nor "Kan ikke opprette databasen '%-.192s' (Feilkode: %M)"
+ norwegian-ny "Kan ikkje opprette databasen '%-.192s' (Feilkode: %M)"
+ pol "Nie można stworzyć bazy danych '%-.192s' (Kod błędu: %M)"
+ por "Não pode criar o banco de dados '%-.192s' (erro no. %M)"
+ rum "Nu pot sa creez baza de date '%-.192s' (Eroare: %M)"
+ rus "Ðевозможно Ñоздать базу данных '%-.192s' (ошибка: %M)"
+ serbian "Ne mogu da kreiram bazu '%-.192s' (errno: %M)"
+ slo "Nemôžem vytvoriť databázu '%-.192s' (chybový kód: %M)"
+ spa "No puedo crear base de datos '%-.192s' (Error: %M)"
+ swe "Kan inte skapa databasen '%-.192s' (Felkod: %M)"
+ ukr "Ðе можу Ñтворити базу данних '%-.192s' (помилка: %M)"
ER_DB_CREATE_EXISTS
- cze "Nemohu vytvo-Břit databázi '%-.192s'; databáze již existuje"
+ cze "Nemohu vytvořit databázi '%-.192s'; databáze již existuje"
dan "Kan ikke oprette databasen '%-.192s'; databasen eksisterer"
nla "Kan database '%-.192s' niet aanmaken; database bestaat reeds"
eng "Can't create database '%-.192s'; database exists"
- jps "'%-.192s' データベースãŒä½œã‚Œã¾ã›ã‚“.æ—¢ã«ãã®ãƒ‡ãƒ¼ã‚¿ãƒ™ãƒ¼ã‚¹ãŒå­˜åœ¨ã—ã¾ã™",
est "Ei suuda luua andmebaasi '%-.192s': andmebaas juba eksisteerib"
fre "Ne peut créer la base '%-.192s'; elle existe déjà"
ger "Kann Datenbank '%-.192s' nicht erzeugen. Datenbank existiert bereits"
greek "ΑδÏνατη η δημιουÏγία της βάσης δεδομένων '%-.192s'; Η βάση δεδομένων υπάÏχει ήδη"
hun "Az '%-.192s' adatbazis nem hozhato letre Az adatbazis mar letezik"
ita "Impossibile creare il database '%-.192s'; il database esiste"
- jpn "'%-.192s' データベースãŒä½œã‚Œã¾ã›ã‚“.æ—¢ã«ãã®ãƒ‡ãƒ¼ã‚¿ãƒ™ãƒ¼ã‚¹ãŒå­˜åœ¨ã—ã¾ã™"
+ jpn "データベース '%-.192s' を作æˆã§ãã¾ã›ã‚“。データベースã¯ã™ã§ã«å­˜åœ¨ã—ã¾ã™ã€‚"
kor "ë°ì´íƒ€ë² ì´ìŠ¤ '%-.192s'를 만들지 못했습니다.. ë°ì´íƒ€ë² ì´ìŠ¤ê°€ 존재함"
nor "Kan ikke opprette databasen '%-.192s'; databasen eksisterer"
norwegian-ny "Kan ikkje opprette databasen '%-.192s'; databasen eksisterer"
@@ -150,18 +148,17 @@ ER_DB_CREATE_EXISTS
swe "Databasen '%-.192s' existerar redan"
ukr "Ðе можу Ñтворити базу данних '%-.192s'. База данних Ñ–Ñнує"
ER_DB_DROP_EXISTS
- cze "Nemohu zru-Bšit databázi '%-.192s', databáze neexistuje"
+ cze "Nemohu zrušit databázi '%-.192s', databáze neexistuje"
dan "Kan ikke slette (droppe) '%-.192s'; databasen eksisterer ikke"
nla "Kan database '%-.192s' niet verwijderen; database bestaat niet"
eng "Can't drop database '%-.192s'; database doesn't exist"
- jps "'%-.192s' データベースを破棄ã§ãã¾ã›ã‚“. ãã®ãƒ‡ãƒ¼ã‚¿ãƒ™ãƒ¼ã‚¹ãŒãªã„ã®ã§ã™.",
est "Ei suuda kustutada andmebaasi '%-.192s': andmebaasi ei eksisteeri"
fre "Ne peut effacer la base '%-.192s'; elle n'existe pas"
ger "Kann Datenbank '%-.192s' nicht löschen; Datenbank nicht vorhanden"
greek "ΑδÏνατη η διαγÏαφή της βάσης δεδομένων '%-.192s'. Η βάση δεδομένων δεν υπάÏχει"
hun "A(z) '%-.192s' adatbazis nem szuntetheto meg. Az adatbazis nem letezik"
ita "Impossibile cancellare '%-.192s'; il database non esiste"
- jpn "'%-.192s' データベースを破棄ã§ãã¾ã›ã‚“. ãã®ãƒ‡ãƒ¼ã‚¿ãƒ™ãƒ¼ã‚¹ãŒãªã„ã®ã§ã™."
+ jpn "データベース '%-.192s' を削除ã§ãã¾ã›ã‚“。データベースã¯å­˜åœ¨ã—ã¾ã›ã‚“。"
kor "ë°ì´íƒ€ë² ì´ìŠ¤ '%-.192s'를 제거하지 못했습니다. ë°ì´íƒ€ë² ì´ìŠ¤ê°€ 존재하지 ì•ŠìŒ "
nor "Kan ikke fjerne (drop) '%-.192s'; databasen eksisterer ikke"
norwegian-ny "Kan ikkje fjerne (drop) '%-.192s'; databasen eksisterer ikkje"
@@ -175,93 +172,89 @@ ER_DB_DROP_EXISTS
swe "Kan inte radera databasen '%-.192s'; databasen finns inte"
ukr "Ðе можу видалити базу данних '%-.192s'. База данних не Ñ–Ñнує"
ER_DB_DROP_DELETE
- cze "Chyba p-Bři rušení databáze (nemohu vymazat '%-.192s', chyba %d)"
- dan "Fejl ved sletning (drop) af databasen (kan ikke slette '%-.192s', Fejlkode %d)"
- nla "Fout bij verwijderen database (kan '%-.192s' niet verwijderen, Errcode: %d)"
- eng "Error dropping database (can't delete '%-.192s', errno: %d)"
- jps "データベース破棄エラー ('%-.192s' を削除ã§ãã¾ã›ã‚“, errno: %d)",
- est "Viga andmebaasi kustutamisel (ei suuda kustutada faili '%-.192s', veakood: %d)"
- fre "Ne peut effacer la base '%-.192s' (erreur %d)"
- ger "Fehler beim Löschen der Datenbank ('%-.192s' kann nicht gelöscht werden, Fehler: %d)"
- greek "ΠαÏουσιάστηκε Ï€Ïόβλημα κατά τη διαγÏαφή της βάσης δεδομένων (αδÏνατη η διαγÏαφή '%-.192s', κωδικός λάθους: %d)"
- hun "Adatbazis megszuntetesi hiba ('%-.192s' nem torolheto, hibakod: %d)"
- ita "Errore durante la cancellazione del database (impossibile cancellare '%-.192s', errno: %d)"
- jpn "データベース破棄エラー ('%-.192s' を削除ã§ãã¾ã›ã‚“, errno: %d)"
- kor "ë°ì´íƒ€ë² ì´ìŠ¤ 제거 ì—러('%-.192s'를 삭제할 수 ì—†ì니다, ì—러번호: %d)"
- nor "Feil ved fjerning (drop) av databasen (kan ikke slette '%-.192s', feil %d)"
- norwegian-ny "Feil ved fjerning (drop) av databasen (kan ikkje slette '%-.192s', feil %d)"
- pol "Bł?d podczas usuwania bazy danych (nie można usun?ć '%-.192s', bł?d %d)"
- por "Erro ao eliminar banco de dados (não pode eliminar '%-.192s' - erro no. %d)"
- rum "Eroare dropuind baza de date (nu pot sa sterg '%-.192s', Eroare: %d)"
- rus "Ошибка при удалении базы данных (невозможно удалить '%-.192s', ошибка: %d)"
- serbian "Ne mogu da izbrišem bazu (ne mogu da izbrišem '%-.192s', errno: %d)"
- slo "Chyba pri mazaní databázy (nemôžem zmazať '%-.192s', chybový kód: %d)"
- spa "Error eliminando la base de datos(no puedo borrar '%-.192s', error %d)"
- swe "Fel vid radering av databasen (Kan inte radera '%-.192s'. Felkod: %d)"
- ukr "Ðе можу видалити базу данних (Ðе можу видалити '%-.192s', помилка: %d)"
+ cze "Chyba při rušení databáze (nemohu vymazat '%-.192s', chyba %M)"
+ dan "Fejl ved sletning (drop) af databasen (kan ikke slette '%-.192s', Fejlkode %M)"
+ nla "Fout bij verwijderen database (kan '%-.192s' niet verwijderen, Errcode: %M)"
+ eng "Error dropping database (can't delete '%-.192s', errno: %M)"
+ est "Viga andmebaasi kustutamisel (ei suuda kustutada faili '%-.192s', veakood: %M)"
+ fre "Ne peut effacer la base '%-.192s' (erreur %M)"
+ ger "Fehler beim Löschen der Datenbank ('%-.192s' kann nicht gelöscht werden, Fehler: %M)"
+ greek "ΠαÏουσιάστηκε Ï€Ïόβλημα κατά τη διαγÏαφή της βάσης δεδομένων (αδÏνατη η διαγÏαφή '%-.192s', κωδικός λάθους: %M)"
+ hun "Adatbazis megszuntetesi hiba ('%-.192s' nem torolheto, hibakod: %M)"
+ ita "Errore durante la cancellazione del database (impossibile cancellare '%-.192s', errno: %M)"
+ jpn "データベース削除エラー ('%-.192s' を削除ã§ãã¾ã›ã‚“。エラー番å·: %M)"
+ kor "ë°ì´íƒ€ë² ì´ìŠ¤ 제거 ì—러('%-.192s'를 삭제할 수 ì—†ì니다, ì—러번호: %M)"
+ nor "Feil ved fjerning (drop) av databasen (kan ikke slette '%-.192s', feil %M)"
+ norwegian-ny "Feil ved fjerning (drop) av databasen (kan ikkje slette '%-.192s', feil %M)"
+ pol "Bł?d podczas usuwania bazy danych (nie można usun?ć '%-.192s', bł?d %M)"
+ por "Erro ao eliminar banco de dados (não pode eliminar '%-.192s' - erro no. %M)"
+ rum "Eroare dropuind baza de date (nu pot sa sterg '%-.192s', Eroare: %M)"
+ rus "Ошибка при удалении базы данных (невозможно удалить '%-.192s', ошибка: %M)"
+ serbian "Ne mogu da izbrišem bazu (ne mogu da izbrišem '%-.192s', errno: %M)"
+ slo "Chyba pri mazaní databázy (nemôžem zmazať '%-.192s', chybový kód: %M)"
+ spa "Error eliminando la base de datos(no puedo borrar '%-.192s', error %M)"
+ swe "Fel vid radering av databasen (Kan inte radera '%-.192s'. Felkod: %M)"
+ ukr "Ðе можу видалити базу данних (Ðе можу видалити '%-.192s', помилка: %M)"
ER_DB_DROP_RMDIR
- cze "Chyba p-Bři rušení databáze (nemohu vymazat adresář '%-.192s', chyba %d)"
- dan "Fejl ved sletting af database (kan ikke slette folderen '%-.192s', Fejlkode %d)"
- nla "Fout bij verwijderen database (kan rmdir '%-.192s' niet uitvoeren, Errcode: %d)"
- eng "Error dropping database (can't rmdir '%-.192s', errno: %d)"
- jps "データベース破棄エラー ('%-.192s' ã‚’ rmdir ã§ãã¾ã›ã‚“, errno: %d)",
- est "Viga andmebaasi kustutamisel (ei suuda kustutada kataloogi '%-.192s', veakood: %d)"
- fre "Erreur en effaçant la base (rmdir '%-.192s', erreur %d)"
- ger "Fehler beim Löschen der Datenbank (Verzeichnis '%-.192s' kann nicht gelöscht werden, Fehler: %d)"
- greek "ΠαÏουσιάστηκε Ï€Ïόβλημα κατά τη διαγÏαφή της βάσης δεδομένων (αδÏνατη η διαγÏαφή του φακέλλου '%-.192s', κωδικός λάθους: %d)"
- hun "Adatbazis megszuntetesi hiba ('%-.192s' nem szuntetheto meg, hibakod: %d)"
- ita "Errore durante la cancellazione del database (impossibile rmdir '%-.192s', errno: %d)"
- jpn "データベース破棄エラー ('%-.192s' ã‚’ rmdir ã§ãã¾ã›ã‚“, errno: %d)"
- kor "ë°ì´íƒ€ë² ì´ìŠ¤ 제거 ì—러(rmdir '%-.192s'를 í•  수 ì—†ì니다, ì—러번호: %d)"
- nor "Feil ved sletting av database (kan ikke slette katalogen '%-.192s', feil %d)"
- norwegian-ny "Feil ved sletting av database (kan ikkje slette katalogen '%-.192s', feil %d)"
- pol "Bł?d podczas usuwania bazy danych (nie można wykonać rmdir '%-.192s', bł?d %d)"
- por "Erro ao eliminar banco de dados (não pode remover diretório '%-.192s' - erro no. %d)"
- rum "Eroare dropuind baza de date (nu pot sa rmdir '%-.192s', Eroare: %d)"
- rus "Ðевозможно удалить базу данных (невозможно удалить каталог '%-.192s', ошибка: %d)"
- serbian "Ne mogu da izbrišem bazu (ne mogu da izbrišem direktorijum '%-.192s', errno: %d)"
- slo "Chyba pri mazaní databázy (nemôžem vymazať adresár '%-.192s', chybový kód: %d)"
- spa "Error eliminando la base de datos (No puedo borrar directorio '%-.192s', error %d)"
- swe "Fel vid radering av databasen (Kan inte radera biblioteket '%-.192s'. Felkod: %d)"
- ukr "Ðе можу видалити базу данних (Ðе можу видалити теку '%-.192s', помилка: %d)"
+ cze "Chyba při rušení databáze (nemohu vymazat adresář '%-.192s', chyba %M)"
+ dan "Fejl ved sletting af database (kan ikke slette folderen '%-.192s', Fejlkode %M)"
+ nla "Fout bij verwijderen database (kan rmdir '%-.192s' niet uitvoeren, Errcode: %M)"
+ eng "Error dropping database (can't rmdir '%-.192s', errno: %M)"
+ est "Viga andmebaasi kustutamisel (ei suuda kustutada kataloogi '%-.192s', veakood: %M)"
+ fre "Erreur en effaçant la base (rmdir '%-.192s', erreur %M)"
+ ger "Fehler beim Löschen der Datenbank (Verzeichnis '%-.192s' kann nicht gelöscht werden, Fehler: %M)"
+ greek "ΠαÏουσιάστηκε Ï€Ïόβλημα κατά τη διαγÏαφή της βάσης δεδομένων (αδÏνατη η διαγÏαφή του φακέλλου '%-.192s', κωδικός λάθους: %M)"
+ hun "Adatbazis megszuntetesi hiba ('%-.192s' nem szuntetheto meg, hibakod: %M)"
+ ita "Errore durante la cancellazione del database (impossibile rmdir '%-.192s', errno: %M)"
+ jpn "データベース削除エラー (ディレクトリ '%-.192s' を削除ã§ãã¾ã›ã‚“。エラー番å·: %M)"
+ kor "ë°ì´íƒ€ë² ì´ìŠ¤ 제거 ì—러(rmdir '%-.192s'를 í•  수 ì—†ì니다, ì—러번호: %M)"
+ nor "Feil ved sletting av database (kan ikke slette katalogen '%-.192s', feil %M)"
+ norwegian-ny "Feil ved sletting av database (kan ikkje slette katalogen '%-.192s', feil %M)"
+ pol "Bł?d podczas usuwania bazy danych (nie można wykonać rmdir '%-.192s', bł?d %M)"
+ por "Erro ao eliminar banco de dados (não pode remover diretório '%-.192s' - erro no. %M)"
+ rum "Eroare dropuind baza de date (nu pot sa rmdir '%-.192s', Eroare: %M)"
+ rus "Ðевозможно удалить базу данных (невозможно удалить каталог '%-.192s', ошибка: %M)"
+ serbian "Ne mogu da izbrišem bazu (ne mogu da izbrišem direktorijum '%-.192s', errno: %M)"
+ slo "Chyba pri mazaní databázy (nemôžem vymazať adresár '%-.192s', chybový kód: %M)"
+ spa "Error eliminando la base de datos (No puedo borrar directorio '%-.192s', error %M)"
+ swe "Fel vid radering av databasen (Kan inte radera biblioteket '%-.192s'. Felkod: %M)"
+ ukr "Ðе можу видалити базу данних (Ðе можу видалити теку '%-.192s', помилка: %M)"
ER_CANT_DELETE_FILE
- cze "Chyba p-Bři výmazu '%-.192s' (chybový kód: %d)"
- dan "Fejl ved sletning af '%-.192s' (Fejlkode: %d)"
- nla "Fout bij het verwijderen van '%-.192s' (Errcode: %d)"
- eng "Error on delete of '%-.192s' (errno: %d)"
- jps "'%-.192s' ã®å‰Šé™¤ãŒã‚¨ãƒ©ãƒ¼ (errno: %d)",
- est "Viga '%-.192s' kustutamisel (veakood: %d)"
- fre "Erreur en effaçant '%-.192s' (Errcode: %d)"
- ger "Fehler beim Löschen von '%-.192s' (Fehler: %d)"
- greek "ΠαÏουσιάστηκε Ï€Ïόβλημα κατά τη διαγÏαφή '%-.192s' (κωδικός λάθους: %d)"
- hun "Torlesi hiba: '%-.192s' (hibakod: %d)"
- ita "Errore durante la cancellazione di '%-.192s' (errno: %d)"
- jpn "'%-.192s' ã®å‰Šé™¤ãŒã‚¨ãƒ©ãƒ¼ (errno: %d)"
- kor "'%-.192s' ì‚­ì œ 중 ì—러 (ì—러번호: %d)"
- nor "Feil ved sletting av '%-.192s' (Feilkode: %d)"
- norwegian-ny "Feil ved sletting av '%-.192s' (Feilkode: %d)"
- pol "Bł?d podczas usuwania '%-.192s' (Kod błędu: %d)"
- por "Erro na remoção de '%-.192s' (erro no. %d)"
- rum "Eroare incercind sa delete '%-.192s' (Eroare: %d)"
- rus "Ошибка при удалении '%-.192s' (ошибка: %d)"
- serbian "Greška pri brisanju '%-.192s' (errno: %d)"
- slo "Chyba pri mazaní '%-.192s' (chybový kód: %d)"
- spa "Error en el borrado de '%-.192s' (Error: %d)"
- swe "Kan inte radera filen '%-.192s' (Felkod: %d)"
- ukr "Ðе можу видалити '%-.192s' (помилка: %d)"
+ cze "Chyba při výmazu '%-.192s' (chybový kód: %M)"
+ dan "Fejl ved sletning af '%-.192s' (Fejlkode: %M)"
+ nla "Fout bij het verwijderen van '%-.192s' (Errcode: %M)"
+ eng "Error on delete of '%-.192s' (errno: %M)"
+ est "Viga '%-.192s' kustutamisel (veakood: %M)"
+ fre "Erreur en effaçant '%-.192s' (Errcode: %M)"
+ ger "Fehler beim Löschen von '%-.192s' (Fehler: %M)"
+ greek "ΠαÏουσιάστηκε Ï€Ïόβλημα κατά τη διαγÏαφή '%-.192s' (κωδικός λάθους: %M)"
+ hun "Torlesi hiba: '%-.192s' (hibakod: %M)"
+ ita "Errore durante la cancellazione di '%-.192s' (errno: %M)"
+ jpn "ファイル '%-.192s' ã®å‰Šé™¤ã‚¨ãƒ©ãƒ¼ (エラー番å·: %M)"
+ kor "'%-.192s' ì‚­ì œ 중 ì—러 (ì—러번호: %M)"
+ nor "Feil ved sletting av '%-.192s' (Feilkode: %M)"
+ norwegian-ny "Feil ved sletting av '%-.192s' (Feilkode: %M)"
+ pol "Bł?d podczas usuwania '%-.192s' (Kod błędu: %M)"
+ por "Erro na remoção de '%-.192s' (erro no. %M)"
+ rum "Eroare incercind sa delete '%-.192s' (Eroare: %M)"
+ rus "Ошибка при удалении '%-.192s' (ошибка: %M)"
+ serbian "Greška pri brisanju '%-.192s' (errno: %M)"
+ slo "Chyba pri mazaní '%-.192s' (chybový kód: %M)"
+ spa "Error en el borrado de '%-.192s' (Error: %M)"
+ swe "Kan inte radera filen '%-.192s' (Felkod: %M)"
+ ukr "Ðе можу видалити '%-.192s' (помилка: %M)"
ER_CANT_FIND_SYSTEM_REC
- cze "Nemohu -BÄíst záznam v systémové tabulce"
+ cze "Nemohu Äíst záznam v systémové tabulce"
dan "Kan ikke læse posten i systemfolderen"
nla "Kan record niet lezen in de systeem tabel"
eng "Can't read record in system table"
- jps "system table ã®ãƒ¬ã‚³ãƒ¼ãƒ‰ã‚’読む事ãŒã§ãã¾ã›ã‚“ã§ã—ãŸ",
est "Ei suuda lugeda kirjet süsteemsest tabelist"
fre "Ne peut lire un enregistrement de la table 'system'"
ger "Datensatz in der Systemtabelle nicht lesbar"
greek "ΑδÏνατη η ανάγνωση εγγÏαφής από πίνακα του συστήματος"
hun "Nem olvashato rekord a rendszertablaban"
ita "Impossibile leggere il record dalla tabella di sistema"
- jpn "system table ã®ãƒ¬ã‚³ãƒ¼ãƒ‰ã‚’読む事ãŒã§ãã¾ã›ã‚“ã§ã—ãŸ"
+ jpn "システム表ã®ãƒ¬ã‚³ãƒ¼ãƒ‰ã‚’読ã¿è¾¼ã‚ã¾ã›ã‚“。"
kor "system í…Œì´ë¸”ì—ì„œ 레코드를 ì½ì„ 수 없습니다."
nor "Kan ikke lese posten i systemkatalogen"
norwegian-ny "Kan ikkje lese posten i systemkatalogen"
@@ -275,182 +268,175 @@ ER_CANT_FIND_SYSTEM_REC
swe "Hittar inte posten i systemregistret"
ukr "Ðе можу зчитати Ð·Ð°Ð¿Ð¸Ñ Ð· ÑиÑтемної таблиці"
ER_CANT_GET_STAT
- cze "Nemohu z-Bískat stav '%-.200s' (chybový kód: %d)"
- dan "Kan ikke læse status af '%-.200s' (Fejlkode: %d)"
- nla "Kan de status niet krijgen van '%-.200s' (Errcode: %d)"
- eng "Can't get status of '%-.200s' (errno: %d)"
- jps "'%-.200s' ã®ã‚¹ãƒ†ã‚¤ã‚¿ã‚¹ãŒå¾—られã¾ã›ã‚“. (errno: %d)",
- est "Ei suuda lugeda '%-.200s' olekut (veakood: %d)"
- fre "Ne peut obtenir le status de '%-.200s' (Errcode: %d)"
- ger "Kann Status von '%-.200s' nicht ermitteln (Fehler: %d)"
- greek "ΑδÏνατη η λήψη πληÏοφοÏιών για την κατάσταση του '%-.200s' (κωδικός λάθους: %d)"
- hun "A(z) '%-.200s' statusza nem allapithato meg (hibakod: %d)"
- ita "Impossibile leggere lo stato di '%-.200s' (errno: %d)"
- jpn "'%-.200s' ã®ã‚¹ãƒ†ã‚¤ã‚¿ã‚¹ãŒå¾—られã¾ã›ã‚“. (errno: %d)"
- kor "'%-.200s'ì˜ ìƒíƒœë¥¼ 얻지 못했습니다. (ì—러번호: %d)"
- nor "Kan ikke lese statusen til '%-.200s' (Feilkode: %d)"
- norwegian-ny "Kan ikkje lese statusen til '%-.200s' (Feilkode: %d)"
- pol "Nie można otrzymać statusu '%-.200s' (Kod błędu: %d)"
- por "Não pode obter o status de '%-.200s' (erro no. %d)"
- rum "Nu pot sa obtin statusul lui '%-.200s' (Eroare: %d)"
- rus "Ðевозможно получить ÑтатуÑную информацию о '%-.200s' (ошибка: %d)"
- serbian "Ne mogu da dobijem stanje file-a '%-.200s' (errno: %d)"
- slo "Nemôžem zistiť stav '%-.200s' (chybový kód: %d)"
- spa "No puedo obtener el estado de '%-.200s' (Error: %d)"
- swe "Kan inte läsa filinformationen (stat) från '%-.200s' (Felkod: %d)"
- ukr "Ðе можу отримати ÑÑ‚Ð°Ñ‚ÑƒÑ '%-.200s' (помилка: %d)"
+ cze "Nemohu získat stav '%-.200s' (chybový kód: %M)"
+ dan "Kan ikke læse status af '%-.200s' (Fejlkode: %M)"
+ nla "Kan de status niet krijgen van '%-.200s' (Errcode: %M)"
+ eng "Can't get status of '%-.200s' (errno: %M)"
+ est "Ei suuda lugeda '%-.200s' olekut (veakood: %M)"
+ fre "Ne peut obtenir le status de '%-.200s' (Errcode: %M)"
+ ger "Kann Status von '%-.200s' nicht ermitteln (Fehler: %M)"
+ greek "ΑδÏνατη η λήψη πληÏοφοÏιών για την κατάσταση του '%-.200s' (κωδικός λάθους: %M)"
+ hun "A(z) '%-.200s' statusza nem allapithato meg (hibakod: %M)"
+ ita "Impossibile leggere lo stato di '%-.200s' (errno: %M)"
+ jpn "'%-.200s' ã®çŠ¶æ…‹ã‚’å–å¾—ã§ãã¾ã›ã‚“。(エラー番å·: %M)"
+ kor "'%-.200s'ì˜ ìƒíƒœë¥¼ 얻지 못했습니다. (ì—러번호: %M)"
+ nor "Kan ikke lese statusen til '%-.200s' (Feilkode: %M)"
+ norwegian-ny "Kan ikkje lese statusen til '%-.200s' (Feilkode: %M)"
+ pol "Nie można otrzymać statusu '%-.200s' (Kod błędu: %M)"
+ por "Não pode obter o status de '%-.200s' (erro no. %M)"
+ rum "Nu pot sa obtin statusul lui '%-.200s' (Eroare: %M)"
+ rus "Ðевозможно получить ÑтатуÑную информацию о '%-.200s' (ошибка: %M)"
+ serbian "Ne mogu da dobijem stanje file-a '%-.200s' (errno: %M)"
+ slo "Nemôžem zistiť stav '%-.200s' (chybový kód: %M)"
+ spa "No puedo obtener el estado de '%-.200s' (Error: %M)"
+ swe "Kan inte läsa filinformationen (stat) från '%-.200s' (Felkod: %M)"
+ ukr "Ðе можу отримати ÑÑ‚Ð°Ñ‚ÑƒÑ '%-.200s' (помилка: %M)"
ER_CANT_GET_WD
- cze "Chyba p-Bři zjišťování pracovní adresář (chybový kód: %d)"
- dan "Kan ikke læse aktive folder (Fejlkode: %d)"
- nla "Kan de werkdirectory niet krijgen (Errcode: %d)"
- eng "Can't get working directory (errno: %d)"
- jps "working directory を得る事ãŒã§ãã¾ã›ã‚“ã§ã—㟠(errno: %d)",
- est "Ei suuda identifitseerida jooksvat kataloogi (veakood: %d)"
- fre "Ne peut obtenir le répertoire de travail (Errcode: %d)"
- ger "Kann Arbeitsverzeichnis nicht ermitteln (Fehler: %d)"
- greek "Ο φάκελλος εÏγασίας δεν βÏέθηκε (κωδικός λάθους: %d)"
- hun "A munkakonyvtar nem allapithato meg (hibakod: %d)"
- ita "Impossibile leggere la directory di lavoro (errno: %d)"
- jpn "working directory を得る事ãŒã§ãã¾ã›ã‚“ã§ã—㟠(errno: %d)"
- kor "수행 디렉토리를 찾지 못했습니다. (ì—러번호: %d)"
- nor "Kan ikke lese aktiv katalog(Feilkode: %d)"
- norwegian-ny "Kan ikkje lese aktiv katalog(Feilkode: %d)"
- pol "Nie można rozpoznać aktualnego katalogu (Kod błędu: %d)"
- por "Não pode obter o diretório corrente (erro no. %d)"
- rum "Nu pot sa obtin directorul current (working directory) (Eroare: %d)"
- rus "Ðевозможно определить рабочий каталог (ошибка: %d)"
- serbian "Ne mogu da dobijem trenutni direktorijum (errno: %d)"
- slo "Nemôžem zistiť pracovný adresár (chybový kód: %d)"
- spa "No puedo acceder al directorio (Error: %d)"
- swe "Kan inte inte läsa aktivt bibliotek. (Felkod: %d)"
- ukr "Ðе можу визначити робочу теку (помилка: %d)"
+ cze "Chyba při zjišťování pracovní adresář (chybový kód: %M)"
+ dan "Kan ikke læse aktive folder (Fejlkode: %M)"
+ nla "Kan de werkdirectory niet krijgen (Errcode: %M)"
+ eng "Can't get working directory (errno: %M)"
+ est "Ei suuda identifitseerida jooksvat kataloogi (veakood: %M)"
+ fre "Ne peut obtenir le répertoire de travail (Errcode: %M)"
+ ger "Kann Arbeitsverzeichnis nicht ermitteln (Fehler: %M)"
+ greek "Ο φάκελλος εÏγασίας δεν βÏέθηκε (κωδικός λάθους: %M)"
+ hun "A munkakonyvtar nem allapithato meg (hibakod: %M)"
+ ita "Impossibile leggere la directory di lavoro (errno: %M)"
+ jpn "作業ディレクトリをå–å¾—ã§ãã¾ã›ã‚“。(エラー番å·: %M)"
+ kor "수행 디렉토리를 찾지 못했습니다. (ì—러번호: %M)"
+ nor "Kan ikke lese aktiv katalog(Feilkode: %M)"
+ norwegian-ny "Kan ikkje lese aktiv katalog(Feilkode: %M)"
+ pol "Nie można rozpoznać aktualnego katalogu (Kod błędu: %M)"
+ por "Não pode obter o diretório corrente (erro no. %M)"
+ rum "Nu pot sa obtin directorul current (working directory) (Eroare: %M)"
+ rus "Ðевозможно определить рабочий каталог (ошибка: %M)"
+ serbian "Ne mogu da dobijem trenutni direktorijum (errno: %M)"
+ slo "Nemôžem zistiť pracovný adresár (chybový kód: %M)"
+ spa "No puedo acceder al directorio (Error: %M)"
+ swe "Kan inte inte läsa aktivt bibliotek. (Felkod: %M)"
+ ukr "Ðе можу визначити робочу теку (помилка: %M)"
ER_CANT_LOCK
- cze "Nemohu uzamknout soubor (chybov-Bý kód: %d)"
- dan "Kan ikke låse fil (Fejlkode: %d)"
- nla "Kan de file niet blokeren (Errcode: %d)"
- eng "Can't lock file (errno: %d)"
- jps "ファイルをロックã§ãã¾ã›ã‚“ (errno: %d)",
- est "Ei suuda lukustada faili (veakood: %d)"
- fre "Ne peut verrouiller le fichier (Errcode: %d)"
- ger "Datei kann nicht gesperrt werden (Fehler: %d)"
- greek "Το αÏχείο δεν μποÏεί να κλειδωθεί (κωδικός λάθους: %d)"
- hun "A file nem zarolhato. (hibakod: %d)"
- ita "Impossibile il locking il file (errno: %d)"
- jpn "ファイルをロックã§ãã¾ã›ã‚“ (errno: %d)"
- kor "í™”ì¼ì„ 잠그지(lock) 못했습니다. (ì—러번호: %d)"
- nor "Kan ikke låse fila (Feilkode: %d)"
- norwegian-ny "Kan ikkje låse fila (Feilkode: %d)"
- pol "Nie można zablokować pliku (Kod błędu: %d)"
- por "Não pode travar o arquivo (erro no. %d)"
- rum "Nu pot sa lock fisierul (Eroare: %d)"
- rus "Ðевозможно поÑтавить блокировку на файле (ошибка: %d)"
- serbian "Ne mogu da zakljuÄam file (errno: %d)"
- slo "Nemôžem zamknúť súbor (chybový kód: %d)"
- spa "No puedo bloquear archivo: (Error: %d)"
- swe "Kan inte låsa filen. (Felkod: %d)"
- ukr "Ðе можу заблокувати файл (помилка: %d)"
+ cze "Nemohu uzamknout soubor (chybový kód: %M)"
+ dan "Kan ikke låse fil (Fejlkode: %M)"
+ nla "Kan de file niet blokeren (Errcode: %M)"
+ eng "Can't lock file (errno: %M)"
+ est "Ei suuda lukustada faili (veakood: %M)"
+ fre "Ne peut verrouiller le fichier (Errcode: %M)"
+ ger "Datei kann nicht gesperrt werden (Fehler: %M)"
+ greek "Το αÏχείο δεν μποÏεί να κλειδωθεί (κωδικός λάθους: %M)"
+ hun "A file nem zarolhato. (hibakod: %M)"
+ ita "Impossibile il locking il file (errno: %M)"
+ jpn "ファイルをロックã§ãã¾ã›ã‚“。(エラー番å·: %M)"
+ kor "í™”ì¼ì„ 잠그지(lock) 못했습니다. (ì—러번호: %M)"
+ nor "Kan ikke låse fila (Feilkode: %M)"
+ norwegian-ny "Kan ikkje låse fila (Feilkode: %M)"
+ pol "Nie można zablokować pliku (Kod błędu: %M)"
+ por "Não pode travar o arquivo (erro no. %M)"
+ rum "Nu pot sa lock fisierul (Eroare: %M)"
+ rus "Ðевозможно поÑтавить блокировку на файле (ошибка: %M)"
+ serbian "Ne mogu da zakljuÄam file (errno: %M)"
+ slo "Nemôžem zamknúť súbor (chybový kód: %M)"
+ spa "No puedo bloquear archivo: (Error: %M)"
+ swe "Kan inte låsa filen. (Felkod: %M)"
+ ukr "Ðе можу заблокувати файл (помилка: %M)"
ER_CANT_OPEN_FILE
- cze "Nemohu otev-Břít soubor '%-.200s' (chybový kód: %d)"
- dan "Kan ikke åbne fil: '%-.200s' (Fejlkode: %d)"
- nla "Kan de file '%-.200s' niet openen (Errcode: %d)"
- eng "Can't open file: '%-.200s' (errno: %d)"
- jps "'%-.200s' ファイルを開ã事ãŒã§ãã¾ã›ã‚“ (errno: %d)",
- est "Ei suuda avada faili '%-.200s' (veakood: %d)"
- fre "Ne peut ouvrir le fichier: '%-.200s' (Errcode: %d)"
- ger "Kann Datei '%-.200s' nicht öffnen (Fehler: %d)"
- greek "Δεν είναι δυνατό να ανοιχτεί το αÏχείο: '%-.200s' (κωδικός λάθους: %d)"
- hun "A '%-.200s' file nem nyithato meg (hibakod: %d)"
- ita "Impossibile aprire il file: '%-.200s' (errno: %d)"
- jpn "'%-.200s' ファイルを開ã事ãŒã§ãã¾ã›ã‚“ (errno: %d)"
- kor "í™”ì¼ì„ 열지 못했습니다.: '%-.200s' (ì—러번호: %d)"
- nor "Kan ikke åpne fila: '%-.200s' (Feilkode: %d)"
- norwegian-ny "Kan ikkje åpne fila: '%-.200s' (Feilkode: %d)"
- pol "Nie można otworzyć pliku: '%-.200s' (Kod błędu: %d)"
- por "Não pode abrir o arquivo '%-.200s' (erro no. %d)"
- rum "Nu pot sa deschid fisierul: '%-.200s' (Eroare: %d)"
- rus "Ðевозможно открыть файл: '%-.200s' (ошибка: %d)"
- serbian "Ne mogu da otvorim file: '%-.200s' (errno: %d)"
- slo "Nemôžem otvoriť súbor: '%-.200s' (chybový kód: %d)"
- spa "No puedo abrir archivo: '%-.200s' (Error: %d)"
- swe "Kan inte använda '%-.200s' (Felkod: %d)"
- ukr "Ðе можу відкрити файл: '%-.200s' (помилка: %d)"
+ cze "Nemohu otevřít soubor '%-.200s' (chybový kód: %M)"
+ dan "Kan ikke åbne fil: '%-.200s' (Fejlkode: %M)"
+ nla "Kan de file '%-.200s' niet openen (Errcode: %M)"
+ eng "Can't open file: '%-.200s' (errno: %M)"
+ est "Ei suuda avada faili '%-.200s' (veakood: %M)"
+ fre "Ne peut ouvrir le fichier: '%-.200s' (Errcode: %M)"
+ ger "Kann Datei '%-.200s' nicht öffnen (Fehler: %M)"
+ greek "Δεν είναι δυνατό να ανοιχτεί το αÏχείο: '%-.200s' (κωδικός λάθους: %M)"
+ hun "A '%-.200s' file nem nyithato meg (hibakod: %M)"
+ ita "Impossibile aprire il file: '%-.200s' (errno: %M)"
+ jpn "ファイル '%-.200s' をオープンã§ãã¾ã›ã‚“。(エラー番å·: %M)"
+ kor "í™”ì¼ì„ 열지 못했습니다.: '%-.200s' (ì—러번호: %M)"
+ nor "Kan ikke åpne fila: '%-.200s' (Feilkode: %M)"
+ norwegian-ny "Kan ikkje åpne fila: '%-.200s' (Feilkode: %M)"
+ pol "Nie można otworzyć pliku: '%-.200s' (Kod błędu: %M)"
+ por "Não pode abrir o arquivo '%-.200s' (erro no. %M)"
+ rum "Nu pot sa deschid fisierul: '%-.200s' (Eroare: %M)"
+ rus "Ðевозможно открыть файл: '%-.200s' (ошибка: %M)"
+ serbian "Ne mogu da otvorim file: '%-.200s' (errno: %M)"
+ slo "Nemôžem otvoriť súbor: '%-.200s' (chybový kód: %M)"
+ spa "No puedo abrir archivo: '%-.200s' (Error: %M)"
+ swe "Kan inte använda '%-.200s' (Felkod: %M)"
+ ukr "Ðе можу відкрити файл: '%-.200s' (помилка: %M)"
ER_FILE_NOT_FOUND
- cze "Nemohu naj-Bít soubor '%-.200s' (chybový kód: %d)"
- dan "Kan ikke finde fila: '%-.200s' (Fejlkode: %d)"
- nla "Kan de file: '%-.200s' niet vinden (Errcode: %d)"
- eng "Can't find file: '%-.200s' (errno: %d)"
- jps "'%-.200s' ファイルを見付ã‘る事ãŒã§ãã¾ã›ã‚“.(errno: %d)",
- est "Ei suuda leida faili '%-.200s' (veakood: %d)"
- fre "Ne peut trouver le fichier: '%-.200s' (Errcode: %d)"
- ger "Kann Datei '%-.200s' nicht finden (Fehler: %d)"
- greek "Δεν βÏέθηκε το αÏχείο: '%-.200s' (κωδικός λάθους: %d)"
- hun "A(z) '%-.200s' file nem talalhato (hibakod: %d)"
- ita "Impossibile trovare il file: '%-.200s' (errno: %d)"
- jpn "'%-.200s' ファイルを見付ã‘る事ãŒã§ãã¾ã›ã‚“.(errno: %d)"
- kor "í™”ì¼ì„ 찾지 못했습니다.: '%-.200s' (ì—러번호: %d)"
- nor "Kan ikke finne fila: '%-.200s' (Feilkode: %d)"
- norwegian-ny "Kan ikkje finne fila: '%-.200s' (Feilkode: %d)"
- pol "Nie można znaleĽć pliku: '%-.200s' (Kod błędu: %d)"
- por "Não pode encontrar o arquivo '%-.200s' (erro no. %d)"
- rum "Nu pot sa gasesc fisierul: '%-.200s' (Eroare: %d)"
- rus "Ðевозможно найти файл: '%-.200s' (ошибка: %d)"
- serbian "Ne mogu da pronađem file: '%-.200s' (errno: %d)"
- slo "Nemôžem nájsť súbor: '%-.200s' (chybový kód: %d)"
- spa "No puedo encontrar archivo: '%-.200s' (Error: %d)"
- swe "Hittar inte filen '%-.200s' (Felkod: %d)"
- ukr "Ðе можу знайти файл: '%-.200s' (помилка: %d)"
+ cze "Nemohu najít soubor '%-.200s' (chybový kód: %M)"
+ dan "Kan ikke finde fila: '%-.200s' (Fejlkode: %M)"
+ nla "Kan de file: '%-.200s' niet vinden (Errcode: %M)"
+ eng "Can't find file: '%-.200s' (errno: %M)"
+ est "Ei suuda leida faili '%-.200s' (veakood: %M)"
+ fre "Ne peut trouver le fichier: '%-.200s' (Errcode: %M)"
+ ger "Kann Datei '%-.200s' nicht finden (Fehler: %M)"
+ greek "Δεν βÏέθηκε το αÏχείο: '%-.200s' (κωδικός λάθους: %M)"
+ hun "A(z) '%-.200s' file nem talalhato (hibakod: %M)"
+ ita "Impossibile trovare il file: '%-.200s' (errno: %M)"
+ jpn "ファイル '%-.200s' ãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“。(エラー番å·: %M)"
+ kor "í™”ì¼ì„ 찾지 못했습니다.: '%-.200s' (ì—러번호: %M)"
+ nor "Kan ikke finne fila: '%-.200s' (Feilkode: %M)"
+ norwegian-ny "Kan ikkje finne fila: '%-.200s' (Feilkode: %M)"
+ pol "Nie można znaleĽć pliku: '%-.200s' (Kod błędu: %M)"
+ por "Não pode encontrar o arquivo '%-.200s' (erro no. %M)"
+ rum "Nu pot sa gasesc fisierul: '%-.200s' (Eroare: %M)"
+ rus "Ðевозможно найти файл: '%-.200s' (ошибка: %M)"
+ serbian "Ne mogu da pronađem file: '%-.200s' (errno: %M)"
+ slo "Nemôžem nájsť súbor: '%-.200s' (chybový kód: %M)"
+ spa "No puedo encontrar archivo: '%-.200s' (Error: %M)"
+ swe "Hittar inte filen '%-.200s' (Felkod: %M)"
+ ukr "Ðе можу знайти файл: '%-.200s' (помилка: %M)"
ER_CANT_READ_DIR
- cze "Nemohu -BÄíst adresář '%-.192s' (chybový kód: %d)"
- dan "Kan ikke læse folder '%-.192s' (Fejlkode: %d)"
- nla "Kan de directory niet lezen van '%-.192s' (Errcode: %d)"
- eng "Can't read dir of '%-.192s' (errno: %d)"
- jps "'%-.192s' ディレクトリãŒèª­ã‚ã¾ã›ã‚“.(errno: %d)",
- est "Ei suuda lugeda kataloogi '%-.192s' (veakood: %d)"
- fre "Ne peut lire le répertoire de '%-.192s' (Errcode: %d)"
- ger "Verzeichnis von '%-.192s' nicht lesbar (Fehler: %d)"
- greek "Δεν είναι δυνατό να διαβαστεί ο φάκελλος του '%-.192s' (κωδικός λάθους: %d)"
- hun "A(z) '%-.192s' konyvtar nem olvashato. (hibakod: %d)"
- ita "Impossibile leggere la directory di '%-.192s' (errno: %d)"
- jpn "'%-.192s' ディレクトリãŒèª­ã‚ã¾ã›ã‚“.(errno: %d)"
- kor "'%-.192s'디렉토리를 ì½ì§€ 못했습니다. (ì—러번호: %d)"
- nor "Kan ikke lese katalogen '%-.192s' (Feilkode: %d)"
- norwegian-ny "Kan ikkje lese katalogen '%-.192s' (Feilkode: %d)"
- pol "Nie można odczytać katalogu '%-.192s' (Kod błędu: %d)"
- por "Não pode ler o diretório de '%-.192s' (erro no. %d)"
- rum "Nu pot sa citesc directorul '%-.192s' (Eroare: %d)"
- rus "Ðевозможно прочитать каталог '%-.192s' (ошибка: %d)"
- serbian "Ne mogu da proÄitam direktorijum '%-.192s' (errno: %d)"
- slo "Nemôžem ÄítaÅ¥ adresár '%-.192s' (chybový kód: %d)"
- spa "No puedo leer el directorio de '%-.192s' (Error: %d)"
- swe "Kan inte läsa från bibliotek '%-.192s' (Felkod: %d)"
- ukr "Ðе можу прочитати теку '%-.192s' (помилка: %d)"
+ cze "Nemohu Äíst adresář '%-.192s' (chybový kód: %M)"
+ dan "Kan ikke læse folder '%-.192s' (Fejlkode: %M)"
+ nla "Kan de directory niet lezen van '%-.192s' (Errcode: %M)"
+ eng "Can't read dir of '%-.192s' (errno: %M)"
+ est "Ei suuda lugeda kataloogi '%-.192s' (veakood: %M)"
+ fre "Ne peut lire le répertoire de '%-.192s' (Errcode: %M)"
+ ger "Verzeichnis von '%-.192s' nicht lesbar (Fehler: %M)"
+ greek "Δεν είναι δυνατό να διαβαστεί ο φάκελλος του '%-.192s' (κωδικός λάθους: %M)"
+ hun "A(z) '%-.192s' konyvtar nem olvashato. (hibakod: %M)"
+ ita "Impossibile leggere la directory di '%-.192s' (errno: %M)"
+ jpn "ディレクトリ '%-.192s' を読ã¿è¾¼ã‚ã¾ã›ã‚“。(エラー番å·: %M)"
+ kor "'%-.192s'디렉토리를 ì½ì§€ 못했습니다. (ì—러번호: %M)"
+ nor "Kan ikke lese katalogen '%-.192s' (Feilkode: %M)"
+ norwegian-ny "Kan ikkje lese katalogen '%-.192s' (Feilkode: %M)"
+ pol "Nie można odczytać katalogu '%-.192s' (Kod błędu: %M)"
+ por "Não pode ler o diretório de '%-.192s' (erro no. %M)"
+ rum "Nu pot sa citesc directorul '%-.192s' (Eroare: %M)"
+ rus "Ðевозможно прочитать каталог '%-.192s' (ошибка: %M)"
+ serbian "Ne mogu da proÄitam direktorijum '%-.192s' (errno: %M)"
+ slo "Nemôžem ÄítaÅ¥ adresár '%-.192s' (chybový kód: %M)"
+ spa "No puedo leer el directorio de '%-.192s' (Error: %M)"
+ swe "Kan inte läsa från bibliotek '%-.192s' (Felkod: %M)"
+ ukr "Ðе можу прочитати теку '%-.192s' (помилка: %M)"
ER_CANT_SET_WD
- cze "Nemohu zm-Běnit adresář na '%-.192s' (chybový kód: %d)"
- dan "Kan ikke skifte folder til '%-.192s' (Fejlkode: %d)"
- nla "Kan de directory niet veranderen naar '%-.192s' (Errcode: %d)"
- eng "Can't change dir to '%-.192s' (errno: %d)"
- jps "'%-.192s' ディレクトリ㫠chdir ã§ãã¾ã›ã‚“.(errno: %d)",
- est "Ei suuda siseneda kataloogi '%-.192s' (veakood: %d)"
- fre "Ne peut changer le répertoire pour '%-.192s' (Errcode: %d)"
- ger "Kann nicht in das Verzeichnis '%-.192s' wechseln (Fehler: %d)"
- greek "ΑδÏνατη η αλλαγή του Ï„Ïέχοντος καταλόγου σε '%-.192s' (κωδικός λάθους: %d)"
- hun "Konyvtarvaltas nem lehetseges a(z) '%-.192s'-ba. (hibakod: %d)"
- ita "Impossibile cambiare la directory in '%-.192s' (errno: %d)"
- jpn "'%-.192s' ディレクトリ㫠chdir ã§ãã¾ã›ã‚“.(errno: %d)"
- kor "'%-.192s'디렉토리로 ì´ë™í•  수 없었습니다. (ì—러번호: %d)"
- nor "Kan ikke skifte katalog til '%-.192s' (Feilkode: %d)"
- norwegian-ny "Kan ikkje skifte katalog til '%-.192s' (Feilkode: %d)"
- pol "Nie można zmienić katalogu na '%-.192s' (Kod błędu: %d)"
- por "Não pode mudar para o diretório '%-.192s' (erro no. %d)"
- rum "Nu pot sa schimb directorul '%-.192s' (Eroare: %d)"
- rus "Ðевозможно перейти в каталог '%-.192s' (ошибка: %d)"
- serbian "Ne mogu da promenim direktorijum na '%-.192s' (errno: %d)"
- slo "Nemôžem vojsť do adresára '%-.192s' (chybový kód: %d)"
- spa "No puedo cambiar al directorio de '%-.192s' (Error: %d)"
- swe "Kan inte byta till '%-.192s' (Felkod: %d)"
- ukr "Ðе можу перейти у теку '%-.192s' (помилка: %d)"
+ cze "Nemohu změnit adresář na '%-.192s' (chybový kód: %M)"
+ dan "Kan ikke skifte folder til '%-.192s' (Fejlkode: %M)"
+ nla "Kan de directory niet veranderen naar '%-.192s' (Errcode: %M)"
+ eng "Can't change dir to '%-.192s' (errno: %M)"
+ est "Ei suuda siseneda kataloogi '%-.192s' (veakood: %M)"
+ fre "Ne peut changer le répertoire pour '%-.192s' (Errcode: %M)"
+ ger "Kann nicht in das Verzeichnis '%-.192s' wechseln (Fehler: %M)"
+ greek "ΑδÏνατη η αλλαγή του Ï„Ïέχοντος καταλόγου σε '%-.192s' (κωδικός λάθους: %M)"
+ hun "Konyvtarvaltas nem lehetseges a(z) '%-.192s'-ba. (hibakod: %M)"
+ ita "Impossibile cambiare la directory in '%-.192s' (errno: %M)"
+ jpn "ディレクトリ '%-.192s' ã«ç§»å‹•ã§ãã¾ã›ã‚“。(エラー番å·: %M)"
+ kor "'%-.192s'디렉토리로 ì´ë™í•  수 없었습니다. (ì—러번호: %M)"
+ nor "Kan ikke skifte katalog til '%-.192s' (Feilkode: %M)"
+ norwegian-ny "Kan ikkje skifte katalog til '%-.192s' (Feilkode: %M)"
+ pol "Nie można zmienić katalogu na '%-.192s' (Kod błędu: %M)"
+ por "Não pode mudar para o diretório '%-.192s' (erro no. %M)"
+ rum "Nu pot sa schimb directorul '%-.192s' (Eroare: %M)"
+ rus "Ðевозможно перейти в каталог '%-.192s' (ошибка: %M)"
+ serbian "Ne mogu da promenim direktorijum na '%-.192s' (errno: %M)"
+ slo "Nemôžem vojsť do adresára '%-.192s' (chybový kód: %M)"
+ spa "No puedo cambiar al directorio de '%-.192s' (Error: %M)"
+ swe "Kan inte byta till '%-.192s' (Felkod: %M)"
+ ukr "Ðе можу перейти у теку '%-.192s' (помилка: %M)"
ER_CHECKREAD
- cze "Z-Báznam byl zmÄ›nÄ›n od posledního Ätení v tabulce '%-.192s'"
+ cze "Záznam byl zmÄ›nÄ›n od posledního Ätení v tabulce '%-.192s'"
dan "Posten er ændret siden sidste læsning '%-.192s'"
nla "Record is veranderd sinds de laatste lees activiteit in de tabel '%-.192s'"
eng "Record has changed since last read in table '%-.192s'"
@@ -460,6 +446,7 @@ ER_CHECKREAD
greek "Η εγγÏαφή έχει αλλάξει από την τελευταία φοÏά που ανασÏÏθηκε από τον πίνακα '%-.192s'"
hun "A(z) '%-.192s' tablaban talalhato rekord megvaltozott az utolso olvasas ota"
ita "Il record e` cambiato dall'ultima lettura della tabella '%-.192s'"
+ jpn "表 '%-.192s' ã®æœ€å¾Œã®èª­ã¿è¾¼ã¿æ™‚点ã‹ã‚‰ã€ãƒ¬ã‚³ãƒ¼ãƒ‰ãŒå¤‰åŒ–ã—ã¾ã—ãŸã€‚"
kor "í…Œì´ë¸” '%-.192s'ì—ì„œ 마지막으로 ì½ì€ 후 Recordê°€ 변경ë˜ì—ˆìŠµë‹ˆë‹¤."
nor "Posten har blitt endret siden den ble lest '%-.192s'"
norwegian-ny "Posten har vorte endra sidan den sist vart lesen '%-.192s'"
@@ -472,44 +459,42 @@ ER_CHECKREAD
spa "El registro ha cambiado desde la ultima lectura de la tabla '%-.192s'"
swe "Posten har förändrats sedan den lästes i register '%-.192s'"
ukr "Ð—Ð°Ð¿Ð¸Ñ Ð±ÑƒÐ»Ð¾ змінено з чаÑу оÑтаннього Ñ‡Ð¸Ñ‚Ð°Ð½Ð½Ñ Ð· таблиці '%-.192s'"
-ER_DISK_FULL
- cze "Disk je pln-Bý (%s), Äekám na uvolnÄ›ní nÄ›jakého místa ..."
- dan "Ikke mere diskplads (%s). Venter på at få frigjort plads..."
- nla "Schijf vol (%s). Aan het wachten totdat er ruimte vrij wordt gemaakt..."
- eng "Disk full (%s); waiting for someone to free some space..."
- jps "Disk full (%s). 誰ã‹ãŒä½•ã‹ã‚’減らã™ã¾ã§ã¾ã£ã¦ãã ã•ã„...",
- est "Ketas täis (%s). Ootame kuni tekib vaba ruumi..."
- fre "Disque plein (%s). J'attend que quelqu'un libère de l'espace..."
- ger "Festplatte voll (%s). Warte, bis jemand Platz schafft ..."
- greek "Δεν υπάÏχει χώÏος στο δίσκο (%s). ΠαÏακαλώ, πεÏιμένετε να ελευθεÏωθεί χώÏος..."
- hun "A lemez megtelt (%s)."
- ita "Disco pieno (%s). In attesa che qualcuno liberi un po' di spazio..."
- jpn "Disk full (%s). 誰ã‹ãŒä½•ã‹ã‚’減らã™ã¾ã§ã¾ã£ã¦ãã ã•ã„..."
- kor "Disk full (%s). 다른 ì‚¬ëžŒì´ ì§€ìš¸ë•Œê¹Œì§€ 기다립니다..."
- nor "Ikke mer diskplass (%s). Venter på å få frigjort plass..."
- norwegian-ny "Ikkje meir diskplass (%s). Ventar på å få frigjort plass..."
- pol "Dysk pełny (%s). Oczekiwanie na zwolnienie miejsca..."
- por "Disco cheio (%s). Aguardando alguém liberar algum espaço..."
- rum "Hard-disk-ul este plin (%s). Astept sa se elibereze ceva spatiu..."
- rus "ДиÑк заполнен. (%s). Ожидаем, пока кто-то не уберет поÑле ÑÐµÐ±Ñ Ð¼ÑƒÑор..."
- serbian "Disk je pun (%s). Čekam nekoga da dođe i oslobodi nešto mesta..."
- slo "Disk je plný (%s), Äakám na uvoľnenie miesta..."
- spa "Disco lleno (%s). Esperando para que se libere algo de espacio..."
- swe "Disken är full (%s). Väntar tills det finns ledigt utrymme..."
- ukr "ДиÑк заповнений (%s). Вичикую, доки звільнитьÑÑ Ñ‚Ñ€Ð¾Ñ…Ð¸ міÑцÑ..."
+ER_DISK_FULL
+ cze "Disk je plný (%s), Äekám na uvolnÄ›ní nÄ›jakého místa ... (chybový kód: %M)"
+ dan "Ikke mere diskplads (%s). Venter på at få frigjort plads... (Fejlkode: %M)"
+ nla "Schijf vol (%s). Aan het wachten totdat er ruimte vrij wordt gemaakt... (Errcode: %M)"
+ eng "Disk full (%s); waiting for someone to free some space... (errno: %M)"
+ est "Ketas täis (%s). Ootame kuni tekib vaba ruumi... (veakood: %M)"
+ fre "Disque plein (%s). J'attend que quelqu'un libère de l'espace... (Errcode: %M)"
+ ger "Festplatte voll (%s). Warte, bis jemand Platz schafft ... (Fehler: %M)"
+ greek "Δεν υπάÏχει χώÏος στο δίσκο (%s). ΠαÏακαλώ, πεÏιμένετε να ελευθεÏωθεί χώÏος... (κωδικός λάθους: %M)"
+ hun "A lemez megtelt (%s). (hibakod: %M)"
+ ita "Disco pieno (%s). In attesa che qualcuno liberi un po' di spazio... (errno: %M)"
+ jpn "ディスク領域ä¸è¶³ã§ã™(%s)。(エラー番å·: %M)"
+ kor "Disk full (%s). 다른 ì‚¬ëžŒì´ ì§€ìš¸ë•Œê¹Œì§€ 기다립니다... (ì—러번호: %M)"
+ nor "Ikke mer diskplass (%s). Venter på å få frigjort plass... (Feilkode: %M)"
+ norwegian-ny "Ikkje meir diskplass (%s). Ventar på å få frigjort plass... (Feilkode: %M)"
+ pol "Dysk pełny (%s). Oczekiwanie na zwolnienie miejsca... (Kod błędu: %M)"
+ por "Disco cheio (%s). Aguardando alguém liberar algum espaço... (erro no. %M)"
+ rum "Hard-disk-ul este plin (%s). Astept sa se elibereze ceva spatiu... (Eroare: %M)"
+ rus "ДиÑк заполнен. (%s). Ожидаем, пока кто-то не уберет поÑле ÑÐµÐ±Ñ Ð¼ÑƒÑор... (ошибка: %M)"
+ serbian "Disk je pun (%s). Čekam nekoga da dođe i oslobodi nešto mesta... (errno: %M)"
+ slo "Disk je plný (%s), Äakám na uvoľnenie miesta... (chybový kód: %M)"
+ spa "Disco lleno (%s). Esperando para que se libere algo de espacio... (Error: %M)"
+ swe "Disken är full (%s). Väntar tills det finns ledigt utrymme... (Felkod: %M)"
+ ukr "ДиÑк заповнений (%s). Вичикую, доки звільнитьÑÑ Ñ‚Ñ€Ð¾Ñ…Ð¸ міÑцÑ... (помилка: %M)"
ER_DUP_KEY 23000
- cze "Nemohu zapsat, zdvojen-Bý klÃ­Ä v tabulce '%-.192s'"
+ cze "Nemohu zapsat, zdvojený klÃ­Ä v tabulce '%-.192s'"
dan "Kan ikke skrive, flere ens nøgler i tabellen '%-.192s'"
nla "Kan niet schrijven, dubbele zoeksleutel in tabel '%-.192s'"
eng "Can't write; duplicate key in table '%-.192s'"
- jps "table '%-.192s' ã« key ãŒé‡è¤‡ã—ã¦ã„ã¦æ›¸ãã“ã‚ã¾ã›ã‚“",
est "Ei saa kirjutada, korduv võti tabelis '%-.192s'"
fre "Ecriture impossible, doublon dans une clé de la table '%-.192s'"
ger "Kann nicht speichern, Grund: doppelter Schlüssel in Tabelle '%-.192s'"
greek "Δεν είναι δυνατή η καταχώÏηση, η τιμή υπάÏχει ήδη στον πίνακα '%-.192s'"
hun "Irasi hiba, duplikalt kulcs a '%-.192s' tablaban."
ita "Scrittura impossibile: chiave duplicata nella tabella '%-.192s'"
- jpn "table '%-.192s' ã« key ãŒé‡è¤‡ã—ã¦ã„ã¦æ›¸ãã“ã‚ã¾ã›ã‚“"
+ jpn "書ãè¾¼ã‚ã¾ã›ã‚“。表 '%-.192s' ã«é‡è¤‡ã™ã‚‹ã‚­ãƒ¼ãŒã‚ã‚Šã¾ã™ã€‚"
kor "기ë¡í•  수 ì—†ì니다., í…Œì´ë¸” '%-.192s'ì—ì„œ 중복 키"
nor "Kan ikke skrive, flere like nøkler i tabellen '%-.192s'"
norwegian-ny "Kan ikkje skrive, flere like nyklar i tabellen '%-.192s'"
@@ -523,116 +508,113 @@ ER_DUP_KEY 23000
swe "Kan inte skriva, dubbel söknyckel i register '%-.192s'"
ukr "Ðе можу запиÑати, дублюючийÑÑ ÐºÐ»ÑŽÑ‡ в таблиці '%-.192s'"
ER_ERROR_ON_CLOSE
- cze "Chyba p-Bři zavírání '%-.192s' (chybový kód: %d)"
- dan "Fejl ved lukning af '%-.192s' (Fejlkode: %d)"
- nla "Fout bij het sluiten van '%-.192s' (Errcode: %d)"
- eng "Error on close of '%-.192s' (errno: %d)"
- est "Viga faili '%-.192s' sulgemisel (veakood: %d)"
- fre "Erreur a la fermeture de '%-.192s' (Errcode: %d)"
- ger "Fehler beim Schließen von '%-.192s' (Fehler: %d)"
- greek "ΠαÏουσιάστηκε Ï€Ïόβλημα κλείνοντας το '%-.192s' (κωδικός λάθους: %d)"
- hun "Hiba a(z) '%-.192s' zarasakor. (hibakod: %d)"
- ita "Errore durante la chiusura di '%-.192s' (errno: %d)"
- kor "'%-.192s'닫는 중 ì—러 (ì—러번호: %d)"
- nor "Feil ved lukking av '%-.192s' (Feilkode: %d)"
- norwegian-ny "Feil ved lukking av '%-.192s' (Feilkode: %d)"
- pol "Bł?d podczas zamykania '%-.192s' (Kod błędu: %d)"
- por "Erro ao fechar '%-.192s' (erro no. %d)"
- rum "Eroare inchizind '%-.192s' (errno: %d)"
- rus "Ошибка при закрытии '%-.192s' (ошибка: %d)"
- serbian "Greška pri zatvaranju '%-.192s' (errno: %d)"
- slo "Chyba pri zatváraní '%-.192s' (chybový kód: %d)"
- spa "Error en el cierre de '%-.192s' (Error: %d)"
- swe "Fick fel vid stängning av '%-.192s' (Felkod: %d)"
- ukr "Ðе можу закрити '%-.192s' (помилка: %d)"
+ cze "Chyba při zavírání '%-.192s' (chybový kód: %M)"
+ dan "Fejl ved lukning af '%-.192s' (Fejlkode: %M)"
+ nla "Fout bij het sluiten van '%-.192s' (Errcode: %M)"
+ eng "Error on close of '%-.192s' (errno: %M)"
+ est "Viga faili '%-.192s' sulgemisel (veakood: %M)"
+ fre "Erreur a la fermeture de '%-.192s' (Errcode: %M)"
+ ger "Fehler beim Schließen von '%-.192s' (Fehler: %M)"
+ greek "ΠαÏουσιάστηκε Ï€Ïόβλημα κλείνοντας το '%-.192s' (κωδικός λάθους: %M)"
+ hun "Hiba a(z) '%-.192s' zarasakor. (hibakod: %M)"
+ ita "Errore durante la chiusura di '%-.192s' (errno: %M)"
+ jpn "'%-.192s' ã®ã‚¯ãƒ­ãƒ¼ã‚ºæ™‚エラー (エラー番å·: %M)"
+ kor "'%-.192s'닫는 중 ì—러 (ì—러번호: %M)"
+ nor "Feil ved lukking av '%-.192s' (Feilkode: %M)"
+ norwegian-ny "Feil ved lukking av '%-.192s' (Feilkode: %M)"
+ pol "Bł?d podczas zamykania '%-.192s' (Kod błędu: %M)"
+ por "Erro ao fechar '%-.192s' (erro no. %M)"
+ rum "Eroare inchizind '%-.192s' (errno: %M)"
+ rus "Ошибка при закрытии '%-.192s' (ошибка: %M)"
+ serbian "Greška pri zatvaranju '%-.192s' (errno: %M)"
+ slo "Chyba pri zatváraní '%-.192s' (chybový kód: %M)"
+ spa "Error en el cierre de '%-.192s' (Error: %M)"
+ swe "Fick fel vid stängning av '%-.192s' (Felkod: %M)"
+ ukr "Ðе можу закрити '%-.192s' (помилка: %M)"
ER_ERROR_ON_READ
- cze "Chyba p-BÅ™i Ätení souboru '%-.200s' (chybový kód: %d)"
- dan "Fejl ved læsning af '%-.200s' (Fejlkode: %d)"
- nla "Fout bij het lezen van file '%-.200s' (Errcode: %d)"
- eng "Error reading file '%-.200s' (errno: %d)"
- jps "'%-.200s' ファイルã®èª­ã¿è¾¼ã¿ã‚¨ãƒ©ãƒ¼ (errno: %d)",
- est "Viga faili '%-.200s' lugemisel (veakood: %d)"
- fre "Erreur en lecture du fichier '%-.200s' (Errcode: %d)"
- ger "Fehler beim Lesen der Datei '%-.200s' (Fehler: %d)"
- greek "ΠÏόβλημα κατά την ανάγνωση του αÏχείου '%-.200s' (κωδικός λάθους: %d)"
- hun "Hiba a '%-.200s'file olvasasakor. (hibakod: %d)"
- ita "Errore durante la lettura del file '%-.200s' (errno: %d)"
- jpn "'%-.200s' ファイルã®èª­ã¿è¾¼ã¿ã‚¨ãƒ©ãƒ¼ (errno: %d)"
- kor "'%-.200s'í™”ì¼ ì½ê¸° ì—러 (ì—러번호: %d)"
- nor "Feil ved lesing av '%-.200s' (Feilkode: %d)"
- norwegian-ny "Feil ved lesing av '%-.200s' (Feilkode: %d)"
- pol "Bł?d podczas odczytu pliku '%-.200s' (Kod błędu: %d)"
- por "Erro ao ler arquivo '%-.200s' (erro no. %d)"
- rum "Eroare citind fisierul '%-.200s' (errno: %d)"
- rus "Ошибка Ñ‡Ñ‚ÐµÐ½Ð¸Ñ Ñ„Ð°Ð¹Ð»Ð° '%-.200s' (ошибка: %d)"
- serbian "GreÅ¡ka pri Äitanju file-a '%-.200s' (errno: %d)"
- slo "Chyba pri Äítaní súboru '%-.200s' (chybový kód: %d)"
- spa "Error leyendo el fichero '%-.200s' (Error: %d)"
- swe "Fick fel vid läsning av '%-.200s' (Felkod %d)"
- ukr "Ðе можу прочитати файл '%-.200s' (помилка: %d)"
+ cze "Chyba pÅ™i Ätení souboru '%-.200s' (chybový kód: %M)"
+ dan "Fejl ved læsning af '%-.200s' (Fejlkode: %M)"
+ nla "Fout bij het lezen van file '%-.200s' (Errcode: %M)"
+ eng "Error reading file '%-.200s' (errno: %M)"
+ est "Viga faili '%-.200s' lugemisel (veakood: %M)"
+ fre "Erreur en lecture du fichier '%-.200s' (Errcode: %M)"
+ ger "Fehler beim Lesen der Datei '%-.200s' (Fehler: %M)"
+ greek "ΠÏόβλημα κατά την ανάγνωση του αÏχείου '%-.200s' (κωδικός λάθους: %M)"
+ hun "Hiba a '%-.200s'file olvasasakor. (hibakod: %M)"
+ ita "Errore durante la lettura del file '%-.200s' (errno: %M)"
+ jpn "ファイル '%-.200s' ã®èª­ã¿è¾¼ã¿ã‚¨ãƒ©ãƒ¼ (エラー番å·: %M)"
+ kor "'%-.200s'í™”ì¼ ì½ê¸° ì—러 (ì—러번호: %M)"
+ nor "Feil ved lesing av '%-.200s' (Feilkode: %M)"
+ norwegian-ny "Feil ved lesing av '%-.200s' (Feilkode: %M)"
+ pol "Bł?d podczas odczytu pliku '%-.200s' (Kod błędu: %M)"
+ por "Erro ao ler arquivo '%-.200s' (erro no. %M)"
+ rum "Eroare citind fisierul '%-.200s' (errno: %M)"
+ rus "Ошибка Ñ‡Ñ‚ÐµÐ½Ð¸Ñ Ñ„Ð°Ð¹Ð»Ð° '%-.200s' (ошибка: %M)"
+ serbian "GreÅ¡ka pri Äitanju file-a '%-.200s' (errno: %M)"
+ slo "Chyba pri Äítaní súboru '%-.200s' (chybový kód: %M)"
+ spa "Error leyendo el fichero '%-.200s' (Error: %M)"
+ swe "Fick fel vid läsning av '%-.200s' (Felkod %M)"
+ ukr "Ðе можу прочитати файл '%-.200s' (помилка: %M)"
ER_ERROR_ON_RENAME
- cze "Chyba p-Bři přejmenování '%-.210s' na '%-.210s' (chybový kód: %d)"
- dan "Fejl ved omdøbning af '%-.210s' til '%-.210s' (Fejlkode: %d)"
- nla "Fout bij het hernoemen van '%-.210s' naar '%-.210s' (Errcode: %d)"
- eng "Error on rename of '%-.210s' to '%-.210s' (errno: %d)"
- jps "'%-.210s' ã‚’ '%-.210s' ã« rename ã§ãã¾ã›ã‚“ (errno: %d)",
- est "Viga faili '%-.210s' ümbernimetamisel '%-.210s'-ks (veakood: %d)"
- fre "Erreur en renommant '%-.210s' en '%-.210s' (Errcode: %d)"
- ger "Fehler beim Umbenennen von '%-.210s' in '%-.210s' (Fehler: %d)"
- greek "ΠÏόβλημα κατά την μετονομασία του αÏχείου '%-.210s' to '%-.210s' (κωδικός λάθους: %d)"
- hun "Hiba a '%-.210s' file atnevezesekor '%-.210s'. (hibakod: %d)"
- ita "Errore durante la rinominazione da '%-.210s' a '%-.210s' (errno: %d)"
- jpn "'%-.210s' ã‚’ '%-.210s' ã« rename ã§ãã¾ã›ã‚“ (errno: %d)"
- kor "'%-.210s'를 '%-.210s'ë¡œ ì´ë¦„ 변경중 ì—러 (ì—러번호: %d)"
- nor "Feil ved omdøping av '%-.210s' til '%-.210s' (Feilkode: %d)"
- norwegian-ny "Feil ved omdøyping av '%-.210s' til '%-.210s' (Feilkode: %d)"
- pol "Bł?d podczas zmieniania nazwy '%-.210s' na '%-.210s' (Kod błędu: %d)"
- por "Erro ao renomear '%-.210s' para '%-.210s' (erro no. %d)"
- rum "Eroare incercind sa renumesc '%-.210s' in '%-.210s' (errno: %d)"
- rus "Ошибка при переименовании '%-.210s' в '%-.210s' (ошибка: %d)"
- serbian "Greška pri promeni imena '%-.210s' na '%-.210s' (errno: %d)"
- slo "Chyba pri premenovávaní '%-.210s' na '%-.210s' (chybový kód: %d)"
- spa "Error en el renombrado de '%-.210s' a '%-.210s' (Error: %d)"
- swe "Kan inte byta namn från '%-.210s' till '%-.210s' (Felkod: %d)"
- ukr "Ðе можу перейменувати '%-.210s' у '%-.210s' (помилка: %d)"
+ cze "Chyba při přejmenování '%-.210s' na '%-.210s' (chybový kód: %M)"
+ dan "Fejl ved omdøbning af '%-.210s' til '%-.210s' (Fejlkode: %M)"
+ nla "Fout bij het hernoemen van '%-.210s' naar '%-.210s' (Errcode: %M)"
+ eng "Error on rename of '%-.210s' to '%-.210s' (errno: %M)"
+ est "Viga faili '%-.210s' ümbernimetamisel '%-.210s'-ks (veakood: %M)"
+ fre "Erreur en renommant '%-.210s' en '%-.210s' (Errcode: %M)"
+ ger "Fehler beim Umbenennen von '%-.210s' in '%-.210s' (Fehler: %M)"
+ greek "ΠÏόβλημα κατά την μετονομασία του αÏχείου '%-.210s' to '%-.210s' (κωδικός λάθους: %M)"
+ hun "Hiba a '%-.210s' file atnevezesekor '%-.210s'. (hibakod: %M)"
+ ita "Errore durante la rinominazione da '%-.210s' a '%-.210s' (errno: %M)"
+ jpn "'%-.210s' ã®åå‰ã‚’ '%-.210s' ã«å¤‰æ›´ã§ãã¾ã›ã‚“ (エラー番å·: %M)"
+ kor "'%-.210s'를 '%-.210s'ë¡œ ì´ë¦„ 변경중 ì—러 (ì—러번호: %M)"
+ nor "Feil ved omdøping av '%-.210s' til '%-.210s' (Feilkode: %M)"
+ norwegian-ny "Feil ved omdøyping av '%-.210s' til '%-.210s' (Feilkode: %M)"
+ pol "Bł?d podczas zmieniania nazwy '%-.210s' na '%-.210s' (Kod błędu: %M)"
+ por "Erro ao renomear '%-.210s' para '%-.210s' (erro no. %M)"
+ rum "Eroare incercind sa renumesc '%-.210s' in '%-.210s' (errno: %M)"
+ rus "Ошибка при переименовании '%-.210s' в '%-.210s' (ошибка: %M)"
+ serbian "Greška pri promeni imena '%-.210s' na '%-.210s' (errno: %M)"
+ slo "Chyba pri premenovávaní '%-.210s' na '%-.210s' (chybový kód: %M)"
+ spa "Error en el renombrado de '%-.210s' a '%-.210s' (Error: %M)"
+ swe "Kan inte byta namn från '%-.210s' till '%-.210s' (Felkod: %M)"
+ ukr "Ðе можу перейменувати '%-.210s' у '%-.210s' (помилка: %M)"
ER_ERROR_ON_WRITE
- cze "Chyba p-Bři zápisu do souboru '%-.200s' (chybový kód: %d)"
- dan "Fejl ved skriving av filen '%-.200s' (Fejlkode: %d)"
- nla "Fout bij het wegschrijven van file '%-.200s' (Errcode: %d)"
- eng "Error writing file '%-.200s' (errno: %d)"
- jps "'%-.200s' ファイルを書ã事ãŒã§ãã¾ã›ã‚“ (errno: %d)",
- est "Viga faili '%-.200s' kirjutamisel (veakood: %d)"
- fre "Erreur d'écriture du fichier '%-.200s' (Errcode: %d)"
- ger "Fehler beim Speichern der Datei '%-.200s' (Fehler: %d)"
- greek "ΠÏόβλημα κατά την αποθήκευση του αÏχείου '%-.200s' (κωδικός λάθους: %d)"
- hun "Hiba a '%-.200s' file irasakor. (hibakod: %d)"
- ita "Errore durante la scrittura del file '%-.200s' (errno: %d)"
- jpn "'%-.200s' ファイルを書ã事ãŒã§ãã¾ã›ã‚“ (errno: %d)"
- kor "'%-.200s'í™”ì¼ ê¸°ë¡ ì¤‘ ì—러 (ì—러번호: %d)"
- nor "Feil ved skriving av fila '%-.200s' (Feilkode: %d)"
- norwegian-ny "Feil ved skriving av fila '%-.200s' (Feilkode: %d)"
- pol "Bł?d podczas zapisywania pliku '%-.200s' (Kod błędu: %d)"
- por "Erro ao gravar arquivo '%-.200s' (erro no. %d)"
- rum "Eroare scriind fisierul '%-.200s' (errno: %d)"
- rus "Ошибка запиÑи в файл '%-.200s' (ошибка: %d)"
- serbian "Greška pri upisu '%-.200s' (errno: %d)"
- slo "Chyba pri zápise do súboru '%-.200s' (chybový kód: %d)"
- spa "Error escribiendo el archivo '%-.200s' (Error: %d)"
- swe "Fick fel vid skrivning till '%-.200s' (Felkod %d)"
- ukr "Ðе можу запиÑати файл '%-.200s' (помилка: %d)"
+ cze "Chyba při zápisu do souboru '%-.200s' (chybový kód: %M)"
+ dan "Fejl ved skriving av filen '%-.200s' (Fejlkode: %M)"
+ nla "Fout bij het wegschrijven van file '%-.200s' (Errcode: %M)"
+ eng "Error writing file '%-.200s' (errno: %M)"
+ est "Viga faili '%-.200s' kirjutamisel (veakood: %M)"
+ fre "Erreur d'écriture du fichier '%-.200s' (Errcode: %M)"
+ ger "Fehler beim Speichern der Datei '%-.200s' (Fehler: %M)"
+ greek "ΠÏόβλημα κατά την αποθήκευση του αÏχείου '%-.200s' (κωδικός λάθους: %M)"
+ hun "Hiba a '%-.200s' file irasakor. (hibakod: %M)"
+ ita "Errore durante la scrittura del file '%-.200s' (errno: %M)"
+ jpn "ファイル '%-.200s' ã®æ›¸ãè¾¼ã¿ã‚¨ãƒ©ãƒ¼ (エラー番å·: %M)"
+ kor "'%-.200s'í™”ì¼ ê¸°ë¡ ì¤‘ ì—러 (ì—러번호: %M)"
+ nor "Feil ved skriving av fila '%-.200s' (Feilkode: %M)"
+ norwegian-ny "Feil ved skriving av fila '%-.200s' (Feilkode: %M)"
+ pol "Bł?d podczas zapisywania pliku '%-.200s' (Kod błędu: %M)"
+ por "Erro ao gravar arquivo '%-.200s' (erro no. %M)"
+ rum "Eroare scriind fisierul '%-.200s' (errno: %M)"
+ rus "Ошибка запиÑи в файл '%-.200s' (ошибка: %M)"
+ serbian "Greška pri upisu '%-.200s' (errno: %M)"
+ slo "Chyba pri zápise do súboru '%-.200s' (chybový kód: %M)"
+ spa "Error escribiendo el archivo '%-.200s' (Error: %M)"
+ swe "Fick fel vid skrivning till '%-.200s' (Felkod %M)"
+ ukr "Ðе можу запиÑати файл '%-.200s' (помилка: %M)"
ER_FILE_USED
- cze "'%-.192s' je zam-BÄen proti zmÄ›nám"
+ cze "'%-.192s' je zamÄen proti zmÄ›nám"
dan "'%-.192s' er låst mod opdateringer"
nla "'%-.192s' is geblokeerd tegen veranderingen"
eng "'%-.192s' is locked against change"
- jps "'%-.192s' ã¯ãƒ­ãƒƒã‚¯ã•ã‚Œã¦ã„ã¾ã™",
est "'%-.192s' on lukustatud muudatuste vastu"
fre "'%-.192s' est verrouillé contre les modifications"
ger "'%-.192s' ist für Änderungen gesperrt"
greek "'%-.192s' δεν επιτÏέπονται αλλαγές"
hun "'%-.192s' a valtoztatas ellen zarolva"
ita "'%-.192s' e` soggetto a lock contro i cambiamenti"
- jpn "'%-.192s' ã¯ãƒ­ãƒƒã‚¯ã•ã‚Œã¦ã„ã¾ã™"
+ jpn "'%-.192s' ã¯ãƒ­ãƒƒã‚¯ã•ã‚Œã¦ã„ã¾ã™ã€‚"
kor "'%-.192s'ê°€ 변경할 수 ì—†ë„ë¡ ìž ê²¨ìžˆì니다."
nor "'%-.192s' er låst mot oppdateringer"
norwegian-ny "'%-.192s' er låst mot oppdateringar"
@@ -646,18 +628,17 @@ ER_FILE_USED
swe "'%-.192s' är låst mot användning"
ukr "'%-.192s' заблокований на внеÑÐµÐ½Ð½Ñ Ð·Ð¼Ñ–Ð½"
ER_FILSORT_ABORT
- cze "T-Břídění přerušeno"
+ cze "Třídění přerušeno"
dan "Sortering afbrudt"
nla "Sorteren afgebroken"
eng "Sort aborted"
- jps "Sort 中断",
est "Sorteerimine katkestatud"
fre "Tri alphabétique abandonné"
ger "Sortiervorgang abgebrochen"
greek "Η διαδικασία ταξινόμισης ακυÏώθηκε"
hun "Sikertelen rendezes"
ita "Operazione di ordinamento abbandonata"
- jpn "Sort 中断"
+ jpn "ソート処ç†ã‚’中断ã—ã¾ã—ãŸã€‚"
kor "소트가 중단ë˜ì—ˆìŠµë‹ˆë‹¤."
nor "Sortering avbrutt"
norwegian-ny "Sortering avbrote"
@@ -675,14 +656,13 @@ ER_FORM_NOT_FOUND
dan "View '%-.192s' eksisterer ikke for '%-.192s'"
nla "View '%-.192s' bestaat niet voor '%-.192s'"
eng "View '%-.192s' doesn't exist for '%-.192s'"
- jps "View '%-.192s' ㌠'%-.192s' ã«å®šç¾©ã•ã‚Œã¦ã„ã¾ã›ã‚“",
est "Vaade '%-.192s' ei eksisteeri '%-.192s' jaoks"
fre "La vue (View) '%-.192s' n'existe pas pour '%-.192s'"
ger "View '%-.192s' existiert für '%-.192s' nicht"
greek "Το View '%-.192s' δεν υπάÏχει για '%-.192s'"
hun "A(z) '%-.192s' nezet nem letezik a(z) '%-.192s'-hoz"
ita "La view '%-.192s' non esiste per '%-.192s'"
- jpn "View '%-.192s' ㌠'%-.192s' ã«å®šç¾©ã•ã‚Œã¦ã„ã¾ã›ã‚“"
+ jpn "ビュー '%-.192s' 㯠'%-.192s' ã«å­˜åœ¨ã—ã¾ã›ã‚“。"
kor "ë·° '%-.192s'ê°€ '%-.192s'ì—서는 존재하지 ì•Šì니다."
nor "View '%-.192s' eksisterer ikke for '%-.192s'"
norwegian-ny "View '%-.192s' eksisterar ikkje for '%-.192s'"
@@ -696,66 +676,38 @@ ER_FORM_NOT_FOUND
swe "Formulär '%-.192s' finns inte i '%-.192s'"
ukr "ВиглÑд '%-.192s' не Ñ–Ñнує Ð´Ð»Ñ '%-.192s'"
ER_GET_ERRNO
- cze "Obsluha tabulky vr-Bátila chybu %d"
- dan "Modtog fejl %d fra tabel håndteringen"
- nla "Fout %d van tabel handler"
- eng "Got error %d from storage engine"
- est "Tabeli handler tagastas vea %d"
- fre "Reçu l'erreur %d du handler de la table"
- ger "Fehler %d (Speicher-Engine)"
- greek "Ελήφθη μήνυμα λάθους %d από τον χειÏιστή πίνακα (table handler)"
- hun "%d hibajelzes a tablakezelotol"
- ita "Rilevato l'errore %d dal gestore delle tabelle"
- jpn "Got error %d from table handler"
- kor "í…Œì´ë¸” handlerì—ì„œ %d ì—러가 ë°œìƒ í•˜ì˜€ìŠµë‹ˆë‹¤."
- nor "Mottok feil %d fra tabell håndterer"
- norwegian-ny "Mottok feil %d fra tabell handterar"
- pol "Otrzymano bł?d %d z obsługi tabeli"
- por "Obteve erro %d no manipulador de tabelas"
- rum "Eroarea %d obtinuta din handlerul tabelei"
- rus "Получена ошибка %d от обработчика таблиц"
- serbian "Handler tabela je vratio grešku %d"
- slo "Obsluha tabuľky vrátila chybu %d"
- spa "Error %d desde el manejador de la tabla"
- swe "Fick felkod %d från databashanteraren"
- ukr "Отримано помилку %d від деÑкриптора таблиці"
+ nla "Fout %M van tabel handler %s"
+ eng "Got error %M from storage engine %s"
+ fre "Reçu l'erreur %M du handler de la table %s"
+ ger "Fehler %M von Speicher-Engine %s"
+ greek "Ελήφθη μήνυμα λάθους %M από τον χειÏιστή πίνακα (table handler) %s"
+ ita "Rilevato l'errore %M dal gestore delle tabelle %s"
+ nor "Mottok feil %M fra tabell håndterer %s"
+ norwegian-ny "Mottok feil %M fra tabell handterar %s"
+ pol "Otrzymano bł?d %M z obsługi tabeli %s"
+ por "Obteve erro %M no manipulador de tabelas %s"
+ rum "Eroarea %M obtinuta din handlerul tabelei %s"
+ rus "Получена ошибка %M от обработчика таблиц %s"
+ spa "Error %M desde el manejador de la tabla %s"
+ swe "Fick felkod %M från databashanteraren %s"
+ ukr "Отримано помилку %M від деÑкриптора таблиці %s"
ER_ILLEGAL_HA
- cze "Obsluha tabulky '%-.192s' nem-Bá tento parametr"
- dan "Denne mulighed eksisterer ikke for tabeltypen '%-.192s'"
- nla "Tabel handler voor '%-.192s' heeft deze optie niet"
- eng "Table storage engine for '%-.192s' doesn't have this option"
- est "Tabeli '%-.192s' handler ei toeta antud operatsiooni"
- fre "Le handler de la table '%-.192s' n'a pas cette option"
- ger "Diese Option gibt es nicht (Speicher-Engine für '%-.192s')"
- greek "Ο χειÏιστής πίνακα (table handler) για '%-.192s' δεν διαθέτει αυτή την επιλογή"
- hun "A(z) '%-.192s' tablakezelonek nincs ilyen opcioja"
- ita "Il gestore delle tabelle per '%-.192s' non ha questa opzione"
- jpn "Table handler for '%-.192s' doesn't have this option"
- kor "'%-.192s'ì˜ í…Œì´ë¸” handler는 ì´ëŸ¬í•œ ì˜µì…˜ì„ ì œê³µí•˜ì§€ ì•Šì니다."
- nor "Tabell håndtereren for '%-.192s' har ikke denne muligheten"
- norwegian-ny "Tabell håndteraren for '%-.192s' har ikkje denne moglegheita"
- pol "Obsługa tabeli '%-.192s' nie posiada tej opcji"
- por "Manipulador de tabela para '%-.192s' não tem esta opção"
- rum "Handlerul tabelei pentru '%-.192s' nu are aceasta optiune"
- rus "Обработчик таблицы '%-.192s' не поддерживает Ñту возможноÑÑ‚ÑŒ"
- serbian "Handler tabela za '%-.192s' nema ovu opciju"
- slo "Obsluha tabuľky '%-.192s' nemá tento parameter"
- spa "El manejador de la tabla de '%-.192s' no tiene esta opcion"
- swe "Tabellhanteraren for tabell '%-.192s' stödjer ej detta"
- ukr "ДеÑкриптор таблиці '%-.192s' не має цієї влаÑтивоÑÑ‚Ñ–"
+ eng "Storage engine %s of the table %`s.%`s doesn't have this option"
+ ger "Diese Option gibt es nicht in Speicher-Engine %s für %`s.%`s"
+ rus "Обработчик %s таблицы %`s.%`s не поддерживает Ñту возможноÑÑ‚ÑŒ"
+ ukr "ДеÑкриптор %s таблиці %`s.%`s не має цієї влаÑтивоÑÑ‚Ñ–"
ER_KEY_NOT_FOUND
- cze "Nemohu naj-Bít záznam v '%-.192s'"
+ cze "Nemohu najít záznam v '%-.192s'"
dan "Kan ikke finde posten i '%-.192s'"
nla "Kan record niet vinden in '%-.192s'"
eng "Can't find record in '%-.192s'"
- jps "'%-.192s'ã®ãªã‹ã«ãƒ¬ã‚³ãƒ¼ãƒ‰ãŒè¦‹ä»˜ã‹ã‚Šã¾ã›ã‚“",
est "Ei suuda leida kirjet '%-.192s'-s"
fre "Ne peut trouver l'enregistrement dans '%-.192s'"
ger "Kann Datensatz in '%-.192s' nicht finden"
greek "ΑδÏνατη η ανεÏÏεση εγγÏαφής στο '%-.192s'"
hun "Nem talalhato a rekord '%-.192s'-ben"
ita "Impossibile trovare il record in '%-.192s'"
- jpn "'%-.192s'ã®ãªã‹ã«ãƒ¬ã‚³ãƒ¼ãƒ‰ãŒè¦‹ä»˜ã‹ã‚Šã¾ã›ã‚“"
+ jpn "'%-.192s' ã«ãƒ¬ã‚³ãƒ¼ãƒ‰ãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“。"
kor "'%-.192s'ì—ì„œ 레코드를 ì°¾ì„ ìˆ˜ ì—†ì니다."
nor "Kan ikke finne posten i '%-.192s'"
norwegian-ny "Kan ikkje finne posten i '%-.192s'"
@@ -769,18 +721,17 @@ ER_KEY_NOT_FOUND
swe "Hittar inte posten '%-.192s'"
ukr "Ðе можу запиÑати у '%-.192s'"
ER_NOT_FORM_FILE
- cze "Nespr-Bávná informace v souboru '%-.200s'"
+ cze "Nesprávná informace v souboru '%-.200s'"
dan "Forkert indhold i: '%-.200s'"
nla "Verkeerde info in file: '%-.200s'"
eng "Incorrect information in file: '%-.200s'"
- jps "ファイル '%-.200s' ã® info ãŒé–“é•ã£ã¦ã„るよã†ã§ã™",
est "Vigane informatsioon failis '%-.200s'"
fre "Information erronnée dans le fichier: '%-.200s'"
ger "Falsche Information in Datei '%-.200s'"
greek "Λάθος πληÏοφοÏίες στο αÏχείο: '%-.200s'"
hun "Ervenytelen info a file-ban: '%-.200s'"
ita "Informazione errata nel file: '%-.200s'"
- jpn "ファイル '%-.200s' ã® info ãŒé–“é•ã£ã¦ã„るよã†ã§ã™"
+ jpn "ファイル '%-.200s' 内ã®æƒ…å ±ãŒä¸æ­£ã§ã™ã€‚"
kor "í™”ì¼ì˜ 부정확한 ì •ë³´: '%-.200s'"
nor "Feil informasjon i filen: '%-.200s'"
norwegian-ny "Feil informasjon i fila: '%-.200s'"
@@ -794,18 +745,17 @@ ER_NOT_FORM_FILE
swe "Felaktig fil: '%-.200s'"
ukr "Хибна Ñ–Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ñ–Ñ Ñƒ файлі: '%-.200s'"
ER_NOT_KEYFILE
- cze "Nespr-Bávný klÃ­Ä pro tabulku '%-.200s'; pokuste se ho opravit"
+ cze "Nesprávný klÃ­Ä pro tabulku '%-.200s'; pokuste se ho opravit"
dan "Fejl i indeksfilen til tabellen '%-.200s'; prøv at reparere den"
nla "Verkeerde zoeksleutel file voor tabel: '%-.200s'; probeer het te repareren"
eng "Incorrect key file for table '%-.200s'; try to repair it"
- jps "'%-.200s' テーブル㮠key file ãŒé–“é•ã£ã¦ã„るよã†ã§ã™. 修復をã—ã¦ãã ã•ã„",
est "Tabeli '%-.200s' võtmefail on vigane; proovi seda parandada"
fre "Index corrompu dans la table: '%-.200s'; essayez de le réparer"
ger "Fehlerhafte Index-Datei für Tabelle '%-.200s'; versuche zu reparieren"
greek "Λάθος αÏχείο ταξινόμισης (key file) για τον πίνακα: '%-.200s'; ΠαÏακαλώ, διοÏθώστε το!"
hun "Ervenytelen kulcsfile a tablahoz: '%-.200s'; probalja kijavitani!"
ita "File chiave errato per la tabella : '%-.200s'; prova a riparalo"
- jpn "'%-.200s' テーブル㮠key file ãŒé–“é•ã£ã¦ã„るよã†ã§ã™. 修復をã—ã¦ãã ã•ã„"
+ jpn "表 '%-.200s' ã®ç´¢å¼•ãƒ•ã‚¡ã‚¤ãƒ«(key file)ã®å†…容ãŒä¸æ­£ã§ã™ã€‚修復を試行ã—ã¦ãã ã•ã„。"
kor "'%-.200s' í…Œì´ë¸”ì˜ ë¶€ì •í™•í•œ 키 존재. 수정하시오!"
nor "Tabellen '%-.200s' har feil i nøkkelfilen; forsøk å reparer den"
norwegian-ny "Tabellen '%-.200s' har feil i nykkelfila; prøv å reparere den"
@@ -819,18 +769,17 @@ ER_NOT_KEYFILE
swe "Fatalt fel vid hantering av register '%-.200s'; kör en reparation"
ukr "Хибний файл ключей Ð´Ð»Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ñ–: '%-.200s'; Спробуйте його відновити"
ER_OLD_KEYFILE
- cze "Star-Bý klíÄový soubor pro '%-.192s'; opravte ho."
+ cze "Starý klíÄový soubor pro '%-.192s'; opravte ho."
dan "Gammel indeksfil for tabellen '%-.192s'; reparer den"
nla "Oude zoeksleutel file voor tabel '%-.192s'; repareer het!"
eng "Old key file for table '%-.192s'; repair it!"
- jps "'%-.192s' テーブルã¯å¤ã„å½¢å¼ã® key file ã®ã‚ˆã†ã§ã™; 修復をã—ã¦ãã ã•ã„",
est "Tabeli '%-.192s' võtmefail on aegunud; paranda see!"
fre "Vieux fichier d'index pour la table '%-.192s'; réparez le!"
ger "Alte Index-Datei für Tabelle '%-.192s'. Bitte reparieren"
greek "Παλαιό αÏχείο ταξινόμισης (key file) για τον πίνακα '%-.192s'; ΠαÏακαλώ, διοÏθώστε το!"
hun "Regi kulcsfile a '%-.192s'tablahoz; probalja kijavitani!"
ita "File chiave vecchio per la tabella '%-.192s'; riparalo!"
- jpn "'%-.192s' テーブルã¯å¤ã„å½¢å¼ã® key file ã®ã‚ˆã†ã§ã™; 修復をã—ã¦ãã ã•ã„"
+ jpn "表 '%-.192s' ã®ç´¢å¼•ãƒ•ã‚¡ã‚¤ãƒ«(key file)ã¯å¤ã„å½¢å¼ã§ã™ã€‚修復ã—ã¦ãã ã•ã„。"
kor "'%-.192s' í…Œì´ë¸”ì˜ ì´ì „ë²„ì ¼ì˜ í‚¤ 존재. 수정하시오!"
nor "Gammel nøkkelfil for tabellen '%-.192s'; reparer den!"
norwegian-ny "Gammel nykkelfil for tabellen '%-.192s'; reparer den!"
@@ -844,18 +793,17 @@ ER_OLD_KEYFILE
swe "Gammal nyckelfil '%-.192s'; reparera registret"
ukr "Старий файл ключей Ð´Ð»Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ñ– '%-.192s'; Відновіть його!"
ER_OPEN_AS_READONLY
- cze "'%-.192s' je jen pro -BÄtení"
+ cze "'%-.192s' je jen pro Ätení"
dan "'%-.192s' er skrivebeskyttet"
nla "'%-.192s' is alleen leesbaar"
eng "Table '%-.192s' is read only"
- jps "'%-.192s' ã¯èª­ã¿è¾¼ã¿å°‚用ã§ã™",
est "Tabel '%-.192s' on ainult lugemiseks"
fre "'%-.192s' est en lecture seulement"
ger "Tabelle '%-.192s' ist nur lesbar"
greek "'%-.192s' επιτÏέπεται μόνο η ανάγνωση"
hun "'%-.192s' irasvedett"
ita "'%-.192s' e` di sola lettura"
- jpn "'%-.192s' ã¯èª­ã¿è¾¼ã¿å°‚用ã§ã™"
+ jpn "表 '%-.192s' ã¯èª­ã¿è¾¼ã¿å°‚用ã§ã™ã€‚"
kor "í…Œì´ë¸” '%-.192s'는 ì½ê¸°ì „ìš© 입니다."
nor "'%-.192s' er skrivebeskyttet"
norwegian-ny "'%-.192s' er skrivetryggja"
@@ -869,18 +817,17 @@ ER_OPEN_AS_READONLY
swe "'%-.192s' är skyddad mot förändring"
ukr "Ð¢Ð°Ð±Ð»Ð¸Ñ†Ñ '%-.192s' тільки Ð´Ð»Ñ Ñ‡Ð¸Ñ‚Ð°Ð½Ð½Ñ"
ER_OUTOFMEMORY HY001 S1001
- cze "M-Bálo paměti. Přestartujte daemona a zkuste znovu (je potřeba %d bytů)"
+ cze "Málo paměti. Přestartujte daemona a zkuste znovu (je potřeba %d bytů)"
dan "Ikke mere hukommelse. Genstart serveren og prøv igen (mangler %d bytes)"
nla "Geen geheugen meer. Herstart server en probeer opnieuw (%d bytes nodig)"
eng "Out of memory; restart server and try again (needed %d bytes)"
- jps "Out of memory. デーモンをリスタートã—ã¦ã¿ã¦ãã ã•ã„ (%d bytes å¿…è¦)",
est "Mälu sai otsa. Proovi MariaDB uuesti käivitada (puudu jäi %d baiti)"
fre "Manque de mémoire. Redémarrez le démon et ré-essayez (%d octets nécessaires)"
ger "Kein Speicher vorhanden (%d Bytes benötigt). Bitte Server neu starten"
greek "Δεν υπάÏχει διαθέσιμη μνήμη. ΠÏοσπαθήστε πάλι, επανεκινώντας τη διαδικασία (demon) (χÏειάζονται %d bytes)"
hun "Nincs eleg memoria. Inditsa ujra a demont, es probalja ismet. (%d byte szukseges.)"
ita "Memoria esaurita. Fai ripartire il demone e riprova (richiesti %d bytes)"
- jpn "Out of memory. デーモンをリスタートã—ã¦ã¿ã¦ãã ã•ã„ (%d bytes å¿…è¦)"
+ jpn "メモリãŒä¸è¶³ã—ã¦ã„ã¾ã™ã€‚サーãƒãƒ¼ã‚’å†èµ·å‹•ã—ã¦ã¿ã¦ãã ã•ã„。(%d ãƒã‚¤ãƒˆã®å‰²ã‚Šå½“ã¦ã«å¤±æ•—)"
kor "Out of memory. ë°ëª¬ì„ 재 실행 후 다시 시작하시오 (needed %d bytes)"
nor "Ikke mer minne. Star på nytt tjenesten og prøv igjen (trengte %d byter)"
norwegian-ny "Ikkje meir minne. Start på nytt tenesten og prøv igjen (trengte %d bytar)"
@@ -894,18 +841,17 @@ ER_OUTOFMEMORY HY001 S1001
swe "Oväntat slut på minnet, starta om programmet och försök på nytt (Behövde %d bytes)"
ukr "Брак пам'ÑÑ‚Ñ–. РеÑтартуйте Ñервер та Ñпробуйте знову (потрібно %d байтів)"
ER_OUT_OF_SORTMEMORY HY001 S1001
- cze "M-Bálo paměti pro třídění. Zvyšte velikost třídícího bufferu"
+ cze "Málo paměti pro třídění. Zvyšte velikost třídícího bufferu"
dan "Ikke mere sorteringshukommelse. Øg sorteringshukommelse (sort buffer size) for serveren"
nla "Geen geheugen om te sorteren. Verhoog de server sort buffer size"
eng "Out of sort memory, consider increasing server sort buffer size"
- jps "Out of sort memory. sort buffer size ãŒè¶³ã‚Šãªã„よã†ã§ã™.",
est "Mälu sai sorteerimisel otsa. Suurenda MariaDB-i sorteerimispuhvrit"
fre "Manque de mémoire pour le tri. Augmentez-la."
ger "Kein Speicher zum Sortieren vorhanden. sort_buffer_size sollte im Server erhöht werden"
greek "Δεν υπάÏχει διαθέσιμη μνήμη για ταξινόμιση. Αυξήστε το sort buffer size για τη διαδικασία (demon)"
hun "Nincs eleg memoria a rendezeshez. Novelje a rendezo demon puffermeretet"
ita "Memoria per gli ordinamenti esaurita. Incrementare il 'sort_buffer' al demone"
- jpn "Out of sort memory. sort buffer size ãŒè¶³ã‚Šãªã„よã†ã§ã™."
+ jpn "ソートメモリãŒä¸è¶³ã—ã¦ã„ã¾ã™ã€‚ソートãƒãƒƒãƒ•ã‚¡ã‚µã‚¤ã‚º(sort buffer size)ã®å¢—加を検討ã—ã¦ãã ã•ã„。"
kor "Out of sort memory. daemon sort bufferì˜ í¬ê¸°ë¥¼ ì¦ê°€ì‹œí‚¤ì„¸ìš”"
nor "Ikke mer sorteringsminne. Vurder å øke sorteringsminnet (sort buffer size) for tjenesten"
norwegian-ny "Ikkje meir sorteringsminne. Vurder å auke sorteringsminnet (sorteringsbuffer storleik) for tenesten"
@@ -919,43 +865,41 @@ ER_OUT_OF_SORTMEMORY HY001 S1001
swe "Sorteringsbufferten räcker inte till. Kontrollera startparametrarna"
ukr "Брак пам'ÑÑ‚Ñ– Ð´Ð»Ñ ÑортуваннÑ. Треба збільшити розмір буфера ÑÐ¾Ñ€Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ñƒ Ñервера"
ER_UNEXPECTED_EOF
- cze "Neo-BÄekávaný konec souboru pÅ™i Ätení '%-.192s' (chybový kód: %d)"
- dan "Uventet afslutning på fil (eof) ved læsning af filen '%-.192s' (Fejlkode: %d)"
- nla "Onverwachte eof gevonden tijdens het lezen van file '%-.192s' (Errcode: %d)"
- eng "Unexpected EOF found when reading file '%-.192s' (errno: %d)"
- jps "'%-.192s' ファイルを読ã¿è¾¼ã¿ä¸­ã« EOF ãŒäºˆæœŸã›ã¬æ‰€ã§ç¾ã‚Œã¾ã—ãŸ. (errno: %d)",
- est "Ootamatu faililõpumärgend faili '%-.192s' lugemisel (veakood: %d)"
- fre "Fin de fichier inattendue en lisant '%-.192s' (Errcode: %d)"
- ger "Unerwartetes Ende beim Lesen der Datei '%-.192s' (Fehler: %d)"
- greek "Κατά τη διάÏκεια της ανάγνωσης, βÏέθηκε απÏοσδόκητα το τέλος του αÏχείου '%-.192s' (κωδικός λάθους: %d)"
- hun "Varatlan filevege-jel a '%-.192s'olvasasakor. (hibakod: %d)"
- ita "Fine del file inaspettata durante la lettura del file '%-.192s' (errno: %d)"
- jpn "'%-.192s' ファイルを読ã¿è¾¼ã¿ä¸­ã« EOF ãŒäºˆæœŸã›ã¬æ‰€ã§ç¾ã‚Œã¾ã—ãŸ. (errno: %d)"
- kor "'%-.192s' í™”ì¼ì„ ì½ëŠ” ë„중 ìž˜ëª»ëœ eofì„ ë°œê²¬ (ì—러번호: %d)"
- nor "Uventet slutt på fil (eof) ved lesing av filen '%-.192s' (Feilkode: %d)"
- norwegian-ny "Uventa slutt på fil (eof) ved lesing av fila '%-.192s' (Feilkode: %d)"
- pol "Nieoczekiwany 'eof' napotkany podczas czytania z pliku '%-.192s' (Kod błędu: %d)"
- por "Encontrado fim de arquivo inesperado ao ler arquivo '%-.192s' (erro no. %d)"
- rum "Sfirsit de fisier neasteptat in citirea fisierului '%-.192s' (errno: %d)"
- rus "Ðеожиданный конец файла '%-.192s' (ошибка: %d)"
- serbian "NeoÄekivani kraj pri Äitanju file-a '%-.192s' (errno: %d)"
- slo "NeoÄakávaný koniec súboru pri Äítaní '%-.192s' (chybový kód: %d)"
- spa "Inesperado fin de ficheroU mientras leiamos el archivo '%-.192s' (Error: %d)"
- swe "Oväntat filslut vid läsning från '%-.192s' (Felkod: %d)"
- ukr "Хибний кінець файлу '%-.192s' (помилка: %d)"
+ cze "NeoÄekávaný konec souboru pÅ™i Ätení '%-.192s' (chybový kód: %M)"
+ dan "Uventet afslutning på fil (eof) ved læsning af filen '%-.192s' (Fejlkode: %M)"
+ nla "Onverwachte eof gevonden tijdens het lezen van file '%-.192s' (Errcode: %M)"
+ eng "Unexpected EOF found when reading file '%-.192s' (errno: %M)"
+ est "Ootamatu faililõpumärgend faili '%-.192s' lugemisel (veakood: %M)"
+ fre "Fin de fichier inattendue en lisant '%-.192s' (Errcode: %M)"
+ ger "Unerwartetes Ende beim Lesen der Datei '%-.192s' (Fehler: %M)"
+ greek "Κατά τη διάÏκεια της ανάγνωσης, βÏέθηκε απÏοσδόκητα το τέλος του αÏχείου '%-.192s' (κωδικός λάθους: %M)"
+ hun "Varatlan filevege-jel a '%-.192s'olvasasakor. (hibakod: %M)"
+ ita "Fine del file inaspettata durante la lettura del file '%-.192s' (errno: %M)"
+ jpn "ファイル '%-.192s' を読ã¿è¾¼ã¿ä¸­ã«äºˆæœŸã›ãšãƒ•ã‚¡ã‚¤ãƒ«ã®çµ‚端ã«é”ã—ã¾ã—ãŸã€‚(エラー番å·: %M)"
+ kor "'%-.192s' í™”ì¼ì„ ì½ëŠ” ë„중 ìž˜ëª»ëœ eofì„ ë°œê²¬ (ì—러번호: %M)"
+ nor "Uventet slutt på fil (eof) ved lesing av filen '%-.192s' (Feilkode: %M)"
+ norwegian-ny "Uventa slutt på fil (eof) ved lesing av fila '%-.192s' (Feilkode: %M)"
+ pol "Nieoczekiwany 'eof' napotkany podczas czytania z pliku '%-.192s' (Kod błędu: %M)"
+ por "Encontrado fim de arquivo inesperado ao ler arquivo '%-.192s' (erro no. %M)"
+ rum "Sfirsit de fisier neasteptat in citirea fisierului '%-.192s' (errno: %M)"
+ rus "Ðеожиданный конец файла '%-.192s' (ошибка: %M)"
+ serbian "NeoÄekivani kraj pri Äitanju file-a '%-.192s' (errno: %M)"
+ slo "NeoÄakávaný koniec súboru pri Äítaní '%-.192s' (chybový kód: %M)"
+ spa "Inesperado fin de ficheroU mientras leiamos el archivo '%-.192s' (Error: %M)"
+ swe "Oväntat filslut vid läsning från '%-.192s' (Felkod: %M)"
+ ukr "Хибний кінець файлу '%-.192s' (помилка: %M)"
ER_CON_COUNT_ERROR 08004
- cze "P-Bříliš mnoho spojení"
+ cze "Příliš mnoho spojení"
dan "For mange forbindelser (connections)"
nla "Te veel verbindingen"
eng "Too many connections"
- jps "接続ãŒå¤šã™ãŽã¾ã™",
est "Liiga palju samaaegseid ühendusi"
fre "Trop de connexions"
ger "Zu viele Verbindungen"
greek "ΥπάÏχουν πολλές συνδέσεις..."
hun "Tul sok kapcsolat"
ita "Troppe connessioni"
- jpn "接続ãŒå¤šã™ãŽã¾ã™"
+ jpn "接続ãŒå¤šã™ãŽã¾ã™ã€‚"
kor "너무 ë§Žì€ ì—°ê²°... max_connectionì„ ì¦ê°€ 시키시오..."
nor "For mange tilkoblinger (connections)"
norwegian-ny "For mange tilkoplingar (connections)"
@@ -969,18 +913,17 @@ ER_CON_COUNT_ERROR 08004
swe "För många anslutningar"
ukr "Забагато з'єднань"
ER_OUT_OF_RESOURCES
- cze "M-Bálo prostoru/paměti pro thread"
+ cze "Málo prostoru/paměti pro thread"
dan "Udgået for tråde/hukommelse"
nla "Geen thread geheugen meer; controleer of mysqld of andere processen al het beschikbare geheugen gebruikt. Zo niet, dan moet u wellicht 'ulimit' gebruiken om mysqld toe te laten meer geheugen te benutten, of u kunt extra swap ruimte toevoegen"
eng "Out of memory; check if mysqld or some other process uses all available memory; if not, you may have to use 'ulimit' to allow mysqld to use more memory or you can add more swap space"
- jps "Out of memory; mysqld ã‹ãã®ä»–ã®ãƒ—ロセスãŒãƒ¡ãƒ¢ãƒªãƒ¼ã‚’å…¨ã¦ä½¿ã£ã¦ã„ã‚‹ã‹ç¢ºèªã—ã¦ãã ã•ã„. メモリーを使ã„切ã£ã¦ã„ãªã„å ´åˆã€'ulimit' を設定ã—㦠mysqld ã®ãƒ¡ãƒ¢ãƒªãƒ¼ä½¿ç”¨é™ç•Œé‡ã‚’多ãã™ã‚‹ã‹ã€swap space を増やã—ã¦ã¿ã¦ãã ã•ã„",
est "Mälu sai otsa. Võimalik, et aitab swap-i lisamine või käsu 'ulimit' abil MariaDB-le rohkema mälu kasutamise lubamine"
fre "Manque de 'threads'/mémoire"
ger "Kein Speicher mehr vorhanden. Prüfen Sie, ob mysqld oder ein anderer Prozess den gesamten Speicher verbraucht. Wenn nicht, sollten Sie mit 'ulimit' dafür sorgen, dass mysqld mehr Speicher benutzen darf, oder mehr Swap-Speicher einrichten"
greek "ΠÏόβλημα με τη διαθέσιμη μνήμη (Out of thread space/memory)"
hun "Elfogyott a thread-memoria"
ita "Fine dello spazio/memoria per i thread"
- jpn "Out of memory; mysqld ã‹ãã®ä»–ã®ãƒ—ロセスãŒãƒ¡ãƒ¢ãƒªãƒ¼ã‚’å…¨ã¦ä½¿ã£ã¦ã„ã‚‹ã‹ç¢ºèªã—ã¦ãã ã•ã„. メモリーを使ã„切ã£ã¦ã„ãªã„å ´åˆã€'ulimit' を設定ã—㦠mysqld ã®ãƒ¡ãƒ¢ãƒªãƒ¼ä½¿ç”¨é™ç•Œé‡ã‚’多ãã™ã‚‹ã‹ã€swap space を増やã—ã¦ã¿ã¦ãã ã•ã„"
+ jpn "メモリãŒä¸è¶³ã—ã¦ã„ã¾ã™ã€‚mysqld ã‚„ãã®ä»–ã®ãƒ—ロセスãŒãƒ¡ãƒ¢ãƒªãƒ¼ã‚’使ã„切ã£ã¦ã„ãªã„ã‹ç¢ºèªã—ã¦ä¸‹ã•ã„。メモリーを使ã„切ã£ã¦ã„ãªã„å ´åˆã€'ulimit'ã®è¨­å®šç­‰ã§ mysqld ã®ãƒ¡ãƒ¢ãƒªãƒ¼ä½¿ç”¨æœ€å¤§é‡ã‚’多ãã™ã‚‹ã‹ã€ã‚¹ãƒ¯ãƒƒãƒ—領域を増やã™å¿…è¦ãŒã‚ã‚‹ã‹ã‚‚ã—ã‚Œã¾ã›ã‚“。"
# This message failed to convert from euc-kr, skipped
nor "Tomt for tråd plass/minne"
norwegian-ny "Tomt for tråd plass/minne"
@@ -994,18 +937,17 @@ ER_OUT_OF_RESOURCES
swe "Fick slut på minnet. Kontrollera om mysqld eller någon annan process använder allt tillgängligt minne. Om inte, försök använda 'ulimit' eller allokera mera swap"
ukr "Брак пам'ÑÑ‚Ñ–; Перевірте чи mysqld або ÑкіÑÑŒ інші процеÑи викориÑтовують уÑÑŽ доÑтупну пам'ÑÑ‚ÑŒ. Як ні, то ви можете ÑкориÑтатиÑÑ 'ulimit', аби дозволити mysqld викориÑтовувати більше пам'ÑÑ‚Ñ– або ви можете додати більше міÑÑ†Ñ Ð¿Ñ–Ð´ Ñвап"
ER_BAD_HOST_ERROR 08S01
- cze "Nemohu zjistit jm-Béno stroje pro Vaši adresu"
+ cze "Nemohu zjistit jméno stroje pro Vaši adresu"
dan "Kan ikke få værtsnavn for din adresse"
nla "Kan de hostname niet krijgen van uw adres"
eng "Can't get hostname for your address"
- jps "ãã® address ã® hostname ãŒå¼•ã‘ã¾ã›ã‚“.",
est "Ei suuda lahendada IP aadressi masina nimeks"
fre "Ne peut obtenir de hostname pour votre adresse"
ger "Kann Hostnamen für diese Adresse nicht erhalten"
greek "Δεν έγινε γνωστό το hostname για την address σας"
hun "A gepnev nem allapithato meg a cimbol"
ita "Impossibile risalire al nome dell'host dall'indirizzo (risoluzione inversa)"
- jpn "ãã® address ã® hostname ãŒå¼•ã‘ã¾ã›ã‚“."
+ jpn "IPアドレスã‹ã‚‰ãƒ›ã‚¹ãƒˆåを解決ã§ãã¾ã›ã‚“。"
kor "ë‹¹ì‹ ì˜ ì»´í“¨í„°ì˜ í˜¸ìŠ¤íŠ¸ì´ë¦„ì„ ì–»ì„ ìˆ˜ ì—†ì니다."
nor "Kan ikke få tak i vertsnavn for din adresse"
norwegian-ny "Kan ikkje få tak i vertsnavn for di adresse"
@@ -1019,7 +961,7 @@ ER_BAD_HOST_ERROR 08S01
swe "Kan inte hitta 'hostname' för din adress"
ukr "Ðе можу визначити ім'Ñ Ñ…Ð¾Ñту Ð´Ð»Ñ Ð²Ð°ÑˆÐ¾Ñ— адреÑи"
ER_HANDSHAKE_ERROR 08S01
- cze "Chyba p-Bři ustavování spojení"
+ cze "Chyba při ustavování spojení"
dan "Forkert håndtryk (handshake)"
nla "Verkeerde handshake"
eng "Bad handshake"
@@ -1029,6 +971,7 @@ ER_HANDSHAKE_ERROR 08S01
greek "Η αναγνώÏιση (handshake) δεν έγινε σωστά"
hun "A kapcsolatfelvetel nem sikerult (Bad handshake)"
ita "Negoziazione impossibile"
+ jpn "ãƒãƒ³ãƒ‰ã‚·ã‚§ã‚¤ã‚¯ã‚¨ãƒ©ãƒ¼"
nor "Feil håndtrykk (handshake)"
norwegian-ny "Feil handtrykk (handshake)"
pol "ZÅ‚y uchwyt(handshake)"
@@ -1041,7 +984,7 @@ ER_HANDSHAKE_ERROR 08S01
swe "Fel vid initiering av kommunikationen med klienten"
ukr "Ðевірна уÑтановка зв'Ñзку"
ER_DBACCESS_DENIED_ERROR 42000
- cze "P-Břístup pro uživatele '%s'@'%s' k databázi '%-.192s' není povolen"
+ cze "Přístup pro uživatele '%s'@'%s' k databázi '%-.192s' není povolen"
dan "Adgang nægtet bruger: '%s'@'%s' til databasen '%-.192s'"
nla "Toegang geweigerd voor gebruiker: '%s'@'%s' naar database '%-.192s'"
eng "Access denied for user '%s'@'%s' to database '%-.192s'"
@@ -1065,7 +1008,7 @@ ER_DBACCESS_DENIED_ERROR 42000
swe "Användare '%s'@'%s' är ej berättigad att använda databasen %-.192s"
ukr "ДоÑтуп заборонено Ð´Ð»Ñ ÐºÐ¾Ñ€Ð¸Ñтувача: '%s'@'%s' до бази данних '%-.192s'"
ER_ACCESS_DENIED_ERROR 28000
- cze "P-Břístup pro uživatele '%s'@'%s' (s heslem %s)"
+ cze "Přístup pro uživatele '%s'@'%s' (s heslem %s)"
dan "Adgang nægtet bruger: '%s'@'%s' (Bruger adgangskode: %s)"
nla "Toegang geweigerd voor gebruiker: '%s'@'%s' (Wachtwoord gebruikt: %s)"
eng "Access denied for user '%s'@'%s' (using password: %s)"
@@ -1089,18 +1032,17 @@ ER_ACCESS_DENIED_ERROR 28000
swe "Användare '%s'@'%s' är ej berättigad att logga in (Använder lösen: %s)"
ukr "ДоÑтуп заборонено Ð´Ð»Ñ ÐºÐ¾Ñ€Ð¸Ñтувача: '%s'@'%s' (ВикориÑтано пароль: %s)"
ER_NO_DB_ERROR 3D000
- cze "Nebyla vybr-Bána žádná databáze"
+ cze "Nebyla vybrána žádná databáze"
dan "Ingen database valgt"
nla "Geen database geselecteerd"
eng "No database selected"
- jps "データベースãŒé¸æŠžã•ã‚Œã¦ã„ã¾ã›ã‚“.",
est "Andmebaasi ei ole valitud"
fre "Aucune base n'a été sélectionnée"
ger "Keine Datenbank ausgewählt"
greek "Δεν επιλέχθηκε βάση δεδομένων"
hun "Nincs kivalasztott adatbazis"
ita "Nessun database selezionato"
- jpn "データベースãŒé¸æŠžã•ã‚Œã¦ã„ã¾ã›ã‚“."
+ jpn "データベースãŒé¸æŠžã•ã‚Œã¦ã„ã¾ã›ã‚“。"
kor "ì„ íƒëœ ë°ì´íƒ€ë² ì´ìŠ¤ê°€ 없습니다."
nor "Ingen database valgt"
norwegian-ny "Ingen database vald"
@@ -1114,18 +1056,17 @@ ER_NO_DB_ERROR 3D000
swe "Ingen databas i användning"
ukr "Базу данних не вибрано"
ER_UNKNOWN_COM_ERROR 08S01
- cze "Nezn-Bámý příkaz"
+ cze "Neznámý příkaz"
dan "Ukendt kommando"
nla "Onbekend commando"
eng "Unknown command"
- jps "ãã®ã‚³ãƒžãƒ³ãƒ‰ã¯ä½•ï¼Ÿ",
est "Tundmatu käsk"
fre "Commande inconnue"
ger "Unbekannter Befehl"
greek "Αγνωστη εντολή"
hun "Ervenytelen parancs"
ita "Comando sconosciuto"
- jpn "ãã®ã‚³ãƒžãƒ³ãƒ‰ã¯ä½•ï¼Ÿ"
+ jpn "ä¸æ˜Žãªã‚³ãƒžãƒ³ãƒ‰ã§ã™ã€‚"
kor "명령어가 뭔지 모르겠어요..."
nor "Ukjent kommando"
norwegian-ny "Ukjent kommando"
@@ -1136,21 +1077,20 @@ ER_UNKNOWN_COM_ERROR 08S01
serbian "Nepoznata komanda"
slo "Neznámy príkaz"
spa "Comando desconocido"
- swe "Okänt commando"
+ swe "Okänt kommando"
ukr "Ðевідома команда"
ER_BAD_NULL_ERROR 23000
- cze "Sloupec '%-.192s' nem-Bůže být null"
+ cze "Sloupec '%-.192s' nemůže být null"
dan "Kolonne '%-.192s' kan ikke være NULL"
nla "Kolom '%-.192s' kan niet null zijn"
eng "Column '%-.192s' cannot be null"
- jps "Column '%-.192s' 㯠null ã«ã¯ã§ããªã„ã®ã§ã™",
est "Tulp '%-.192s' ei saa omada nullväärtust"
fre "Le champ '%-.192s' ne peut être vide (null)"
ger "Feld '%-.192s' darf nicht NULL sein"
greek "Το πεδίο '%-.192s' δεν μποÏεί να είναι κενό (null)"
hun "A(z) '%-.192s' oszlop erteke nem lehet nulla"
ita "La colonna '%-.192s' non puo` essere nulla"
- jpn "Column '%-.192s' 㯠null ã«ã¯ã§ããªã„ã®ã§ã™"
+ jpn "列 '%-.192s' 㯠null ã«ã§ãã¾ã›ã‚“。"
kor "칼럼 '%-.192s'는 ë„(Null)ì´ ë˜ë©´ 안ë©ë‹ˆë‹¤. "
nor "Kolonne '%-.192s' kan ikke vere null"
norwegian-ny "Kolonne '%-.192s' kan ikkje vere null"
@@ -1164,18 +1104,17 @@ ER_BAD_NULL_ERROR 23000
swe "Kolumn '%-.192s' får inte vara NULL"
ukr "Стовбець '%-.192s' не може бути нульовим"
ER_BAD_DB_ERROR 42000
- cze "Nezn-Bámá databáze '%-.192s'"
+ cze "Neznámá databáze '%-.192s'"
dan "Ukendt database '%-.192s'"
nla "Onbekende database '%-.192s'"
eng "Unknown database '%-.192s'"
- jps "'%-.192s' ãªã‚“ã¦ãƒ‡ãƒ¼ã‚¿ãƒ™ãƒ¼ã‚¹ã¯çŸ¥ã‚Šã¾ã›ã‚“.",
est "Tundmatu andmebaas '%-.192s'"
fre "Base '%-.192s' inconnue"
ger "Unbekannte Datenbank '%-.192s'"
greek "Αγνωστη βάση δεδομένων '%-.192s'"
hun "Ervenytelen adatbazis: '%-.192s'"
ita "Database '%-.192s' sconosciuto"
- jpn "'%-.192s' ãªã‚“ã¦ãƒ‡ãƒ¼ã‚¿ãƒ™ãƒ¼ã‚¹ã¯çŸ¥ã‚Šã¾ã›ã‚“."
+ jpn "'%-.192s' ã¯ä¸æ˜Žãªãƒ‡ãƒ¼ã‚¿ãƒ™ãƒ¼ã‚¹ã§ã™ã€‚"
kor "ë°ì´íƒ€ë² ì´ìŠ¤ '%-.192s'는 알수 ì—†ìŒ"
nor "Ukjent database '%-.192s'"
norwegian-ny "Ukjent database '%-.192s'"
@@ -1189,18 +1128,17 @@ ER_BAD_DB_ERROR 42000
swe "Okänd databas: '%-.192s'"
ukr "Ðевідома база данних '%-.192s'"
ER_TABLE_EXISTS_ERROR 42S01
- cze "Tabulka '%-.192s' ji-Bž existuje"
+ cze "Tabulka '%-.192s' již existuje"
dan "Tabellen '%-.192s' findes allerede"
nla "Tabel '%-.192s' bestaat al"
eng "Table '%-.192s' already exists"
- jps "Table '%-.192s' ã¯æ—¢ã«ã‚ã‚Šã¾ã™",
est "Tabel '%-.192s' juba eksisteerib"
fre "La table '%-.192s' existe déjà"
ger "Tabelle '%-.192s' bereits vorhanden"
greek "Ο πίνακας '%-.192s' υπάÏχει ήδη"
hun "A(z) '%-.192s' tabla mar letezik"
ita "La tabella '%-.192s' esiste gia`"
- jpn "Table '%-.192s' ã¯æ—¢ã«ã‚ã‚Šã¾ã™"
+ jpn "表 '%-.192s' ã¯ã™ã§ã«å­˜åœ¨ã—ã¾ã™ã€‚"
kor "í…Œì´ë¸” '%-.192s'는 ì´ë¯¸ 존재함"
nor "Tabellen '%-.192s' eksisterer allerede"
norwegian-ny "Tabellen '%-.192s' eksisterar allereide"
@@ -1214,18 +1152,17 @@ ER_TABLE_EXISTS_ERROR 42S01
swe "Tabellen '%-.192s' finns redan"
ukr "Ð¢Ð°Ð±Ð»Ð¸Ñ†Ñ '%-.192s' вже Ñ–Ñнує"
ER_BAD_TABLE_ERROR 42S02
- cze "Nezn-Bámá tabulka '%-.100s'"
+ cze "Neznámá tabulka '%-.100s'"
dan "Ukendt tabel '%-.100s'"
nla "Onbekende tabel '%-.100s'"
eng "Unknown table '%-.100s'"
- jps "table '%-.100s' ã¯ã‚ã‚Šã¾ã›ã‚“.",
est "Tundmatu tabel '%-.100s'"
fre "Table '%-.100s' inconnue"
ger "Unbekannte Tabelle '%-.100s'"
greek "Αγνωστος πίνακας '%-.100s'"
hun "Ervenytelen tabla: '%-.100s'"
ita "Tabella '%-.100s' sconosciuta"
- jpn "table '%-.100s' ã¯ã‚ã‚Šã¾ã›ã‚“."
+ jpn "'%-.100s' ã¯ä¸æ˜Žãªè¡¨ã§ã™ã€‚"
kor "í…Œì´ë¸” '%-.100s'는 알수 ì—†ìŒ"
nor "Ukjent tabell '%-.100s'"
norwegian-ny "Ukjent tabell '%-.100s'"
@@ -1239,7 +1176,7 @@ ER_BAD_TABLE_ERROR 42S02
swe "Okänd tabell '%-.100s'"
ukr "Ðевідома Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ñ '%-.100s'"
ER_NON_UNIQ_ERROR 23000
- cze "Sloupec '%-.192s' v %-.192s nen-Bí zcela jasný"
+ cze "Sloupec '%-.192s' v %-.192s není zcela jasný"
dan "Felt: '%-.192s' i tabel %-.192s er ikke entydigt"
nla "Kolom: '%-.192s' in %-.192s is niet eenduidig"
eng "Column '%-.192s' in %-.192s is ambiguous"
@@ -1249,7 +1186,7 @@ ER_NON_UNIQ_ERROR 23000
greek "Το πεδίο: '%-.192s' σε %-.192s δεν έχει καθοÏιστεί"
hun "A(z) '%-.192s' oszlop %-.192s-ben ketertelmu"
ita "Colonna: '%-.192s' di %-.192s e` ambigua"
- jpn "Column: '%-.192s' in %-.192s is ambiguous"
+ jpn "列 '%-.192s' 㯠%-.192s 内ã§æ›–昧ã§ã™ã€‚"
kor "칼럼: '%-.192s' in '%-.192s' ì´ ëª¨í˜¸í•¨"
nor "Felt: '%-.192s' i tabell %-.192s er ikke entydig"
norwegian-ny "Kolonne: '%-.192s' i tabell %-.192s er ikkje eintydig"
@@ -1263,18 +1200,17 @@ ER_NON_UNIQ_ERROR 23000
swe "Kolumn '%-.192s' i %-.192s är inte unik"
ukr "Стовбець '%-.192s' у %-.192s визначений неоднозначно"
ER_SERVER_SHUTDOWN 08S01
- cze "Prob-Bíhá ukonÄování práce serveru"
+ cze "Probíhá ukonÄování práce serveru"
dan "Database nedlukning er i gang"
nla "Bezig met het stoppen van de server"
eng "Server shutdown in progress"
- jps "Server を shutdown 中...",
est "Serveri seiskamine käib"
fre "Arrêt du serveur en cours"
ger "Der Server wird heruntergefahren"
greek "ΕναÏξη διαδικασίας αποσÏνδεσης του εξυπηÏετητή (server shutdown)"
hun "A szerver leallitasa folyamatban"
ita "Shutdown del server in corso"
- jpn "Server を shutdown 中..."
+ jpn "サーãƒãƒ¼ã‚’シャットダウン中ã§ã™ã€‚"
kor "Server가 셧다운 중입니다."
nor "Database nedkobling er i gang"
norwegian-ny "Tenar nedkopling er i gang"
@@ -1288,18 +1224,17 @@ ER_SERVER_SHUTDOWN 08S01
swe "Servern går nu ned"
ukr "ЗавершуєтьÑÑ Ñ€Ð°Ð±Ð¾Ñ‚Ð° Ñервера"
ER_BAD_FIELD_ERROR 42S22 S0022
- cze "Nezn-Bámý sloupec '%-.192s' v %-.192s"
+ cze "Neznámý sloupec '%-.192s' v %-.192s"
dan "Ukendt kolonne '%-.192s' i tabel %-.192s"
nla "Onbekende kolom '%-.192s' in %-.192s"
eng "Unknown column '%-.192s' in '%-.192s'"
- jps "'%-.192s' column 㯠'%-.192s' ã«ã¯ã‚ã‚Šã¾ã›ã‚“.",
est "Tundmatu tulp '%-.192s' '%-.192s'-s"
fre "Champ '%-.192s' inconnu dans %-.192s"
ger "Unbekanntes Tabellenfeld '%-.192s' in %-.192s"
greek "Αγνωστο πεδίο '%-.192s' σε '%-.192s'"
hun "A(z) '%-.192s' oszlop ervenytelen '%-.192s'-ben"
ita "Colonna sconosciuta '%-.192s' in '%-.192s'"
- jpn "'%-.192s' column 㯠'%-.192s' ã«ã¯ã‚ã‚Šã¾ã›ã‚“."
+ jpn "列 '%-.192s' 㯠'%-.192s' ã«ã¯ã‚ã‚Šã¾ã›ã‚“。"
kor "Unknown 칼럼 '%-.192s' in '%-.192s'"
nor "Ukjent kolonne '%-.192s' i tabell %-.192s"
norwegian-ny "Ukjent felt '%-.192s' i tabell %-.192s"
@@ -1313,17 +1248,17 @@ ER_BAD_FIELD_ERROR 42S22 S0022
swe "Okänd kolumn '%-.192s' i %-.192s"
ukr "Ðевідомий Ñтовбець '%-.192s' у '%-.192s'"
ER_WRONG_FIELD_WITH_GROUP 42000 S1009
- cze "Pou-Bžité '%-.192s' nebylo v group by"
+ cze "Použité '%-.192s' nebylo v group by"
dan "Brugte '%-.192s' som ikke var i group by"
nla "Opdracht gebruikt '%-.192s' dat niet in de GROUP BY voorkomt"
eng "'%-.192s' isn't in GROUP BY"
- jps "'%-.192s' isn't in GROUP BY",
est "'%-.192s' puudub GROUP BY klauslis"
fre "'%-.192s' n'est pas dans 'group by'"
ger "'%-.192s' ist nicht in GROUP BY vorhanden"
greek "ΧÏησιμοποιήθηκε '%-.192s' που δεν υπήÏχε στο group by"
hun "Used '%-.192s' with wasn't in group by"
ita "Usato '%-.192s' che non e` nel GROUP BY"
+ jpn "'%-.192s' ã¯GROUP BYå¥ã§æŒ‡å®šã•ã‚Œã¦ã„ã¾ã›ã‚“。"
kor "'%-.192s'ì€ GROUP BYì†ì— ì—†ìŒ"
nor "Brukte '%-.192s' som ikke var i group by"
norwegian-ny "Brukte '%-.192s' som ikkje var i group by"
@@ -1337,7 +1272,7 @@ ER_WRONG_FIELD_WITH_GROUP 42000 S1009
swe "'%-.192s' finns inte i GROUP BY"
ukr "'%-.192s' не є у GROUP BY"
ER_WRONG_GROUP_FIELD 42000 S1009
- cze "Nemohu pou-Bžít group na '%-.192s'"
+ cze "Nemohu použít group na '%-.192s'"
dan "Kan ikke gruppere på '%-.192s'"
nla "Kan '%-.192s' niet groeperen"
eng "Can't group on '%-.192s'"
@@ -1347,6 +1282,7 @@ ER_WRONG_GROUP_FIELD 42000 S1009
greek "ΑδÏνατη η ομαδοποίηση (group on) '%-.192s'"
hun "A group nem hasznalhato: '%-.192s'"
ita "Impossibile raggruppare per '%-.192s'"
+ jpn "'%-.192s' ã§ã®ã‚°ãƒ«ãƒ¼ãƒ—化ã¯ã§ãã¾ã›ã‚“。"
kor "'%-.192s'를 그룹할 수 ì—†ìŒ"
nor "Kan ikke gruppere på '%-.192s'"
norwegian-ny "Kan ikkje gruppere på '%-.192s'"
@@ -1360,7 +1296,7 @@ ER_WRONG_GROUP_FIELD 42000 S1009
swe "Kan inte använda GROUP BY med '%-.192s'"
ukr "Ðе можу групувати по '%-.192s'"
ER_WRONG_SUM_SELECT 42000 S1009
- cze "P-Bříkaz obsahuje zároveň funkci sum a sloupce"
+ cze "Příkaz obsahuje zároveň funkci sum a sloupce"
dan "Udtrykket har summer (sum) funktioner og kolonner i samme udtryk"
nla "Opdracht heeft totaliseer functies en kolommen in dezelfde opdracht"
eng "Statement has sum functions and columns in same statement"
@@ -1369,6 +1305,7 @@ ER_WRONG_SUM_SELECT 42000 S1009
ger "Die Verwendung von Summierungsfunktionen und Spalten im selben Befehl ist nicht erlaubt"
greek "Η διατÏπωση πεÏιέχει sum functions και columns στην ίδια διατÏπωση"
ita "Il comando ha una funzione SUM e una colonna non specificata nella GROUP BY"
+ jpn "集計関数ã¨é€šå¸¸ã®åˆ—ãŒåŒæ™‚ã«æŒ‡å®šã•ã‚Œã¦ã„ã¾ã™ã€‚"
kor "Statement ê°€ sumê¸°ëŠ¥ì„ ë™ìž‘중ì´ê³  ì¹¼ëŸ¼ë„ ë™ì¼í•œ statement입니다."
nor "Uttrykket har summer (sum) funksjoner og kolonner i samme uttrykk"
norwegian-ny "Uttrykket har summer (sum) funksjoner og kolonner i same uttrykk"
@@ -1382,7 +1319,7 @@ ER_WRONG_SUM_SELECT 42000 S1009
swe "Kommandot har både sum functions och enkla funktioner"
ukr "У виразі викориÑтано підÑумовуючі функції порÑд з іменами Ñтовбців"
ER_WRONG_VALUE_COUNT 21S01
- cze "Po-BÄet sloupců neodpovídá zadané hodnotÄ›"
+ cze "PoÄet sloupců neodpovídá zadané hodnotÄ›"
dan "Kolonne tæller stemmer ikke med antallet af værdier"
nla "Het aantal kolommen komt niet overeen met het aantal opgegeven waardes"
eng "Column count doesn't match value count"
@@ -1391,6 +1328,7 @@ ER_WRONG_VALUE_COUNT 21S01
greek "Το Column count δεν ταιÏιάζει με το value count"
hun "Az oszlopban levo ertek nem egyezik meg a szamitott ertekkel"
ita "Il numero delle colonne non e` uguale al numero dei valori"
+ jpn "列数ãŒå€¤ã®å€‹æ•°ã¨ä¸€è‡´ã—ã¾ã›ã‚“。"
kor "ì¹¼ëŸ¼ì˜ ì¹´ìš´íŠ¸ê°€ ê°’ì˜ ì¹´ìš´íŠ¸ì™€ ì¼ì¹˜í•˜ì§€ 않습니다."
nor "Felt telling stemmer verdi telling"
norwegian-ny "Kolonne telling stemmer verdi telling"
@@ -1404,18 +1342,17 @@ ER_WRONG_VALUE_COUNT 21S01
swe "Antalet kolumner motsvarar inte antalet värden"
ukr "КількіÑÑ‚ÑŒ Ñтовбців не Ñпівпадає з кількіÑÑ‚ÑŽ значень"
ER_TOO_LONG_IDENT 42000 S1009
- cze "Jm-Béno identifikátoru '%-.100s' je příliš dlouhé"
+ cze "Jméno identifikátoru '%-.100s' je příliš dlouhé"
dan "Navnet '%-.100s' er for langt"
nla "Naam voor herkenning '%-.100s' is te lang"
eng "Identifier name '%-.100s' is too long"
- jps "Identifier name '%-.100s' ã¯é•·ã™ãŽã¾ã™",
est "Identifikaatori '%-.100s' nimi on liiga pikk"
fre "Le nom de l'identificateur '%-.100s' est trop long"
ger "Name des Bezeichners '%-.100s' ist zu lang"
greek "Το identifier name '%-.100s' είναι Ï€Î¿Î»Ï Î¼ÎµÎ³Î¬Î»Î¿"
hun "A(z) '%-.100s' azonositonev tul hosszu."
ita "Il nome dell'identificatore '%-.100s' e` troppo lungo"
- jpn "Identifier name '%-.100s' ã¯é•·ã™ãŽã¾ã™"
+ jpn "識別å­å '%-.100s' ã¯é•·ã™ãŽã¾ã™ã€‚"
kor "Identifier '%-.100s'는 너무 길군요."
nor "Identifikator '%-.100s' er for lang"
norwegian-ny "Identifikator '%-.100s' er for lang"
@@ -1429,18 +1366,17 @@ ER_TOO_LONG_IDENT 42000 S1009
swe "Kolumnnamn '%-.100s' är för långt"
ukr "Ім'Ñ Ñ–Ð´ÐµÐ½Ñ‚Ð¸Ñ„Ñ–ÐºÐ°Ñ‚Ð¾Ñ€Ð° '%-.100s' задовге"
ER_DUP_FIELDNAME 42S21 S1009
- cze "Zdvojen-Bé jméno sloupce '%-.192s'"
+ cze "Zdvojené jméno sloupce '%-.192s'"
dan "Feltnavnet '%-.192s' findes allerede"
nla "Dubbele kolom naam '%-.192s'"
eng "Duplicate column name '%-.192s'"
- jps "'%-.192s' ã¨ã„ㆠcolumn åã¯é‡è¤‡ã—ã¦ã¾ã™",
est "Kattuv tulba nimi '%-.192s'"
fre "Nom du champ '%-.192s' déjà utilisé"
ger "Doppelter Spaltenname: '%-.192s'"
greek "Επανάληψη column name '%-.192s'"
hun "Duplikalt oszlopazonosito: '%-.192s'"
ita "Nome colonna duplicato '%-.192s'"
- jpn "'%-.192s' ã¨ã„ㆠcolumn åã¯é‡è¤‡ã—ã¦ã¾ã™"
+ jpn "列å '%-.192s' ã¯é‡è¤‡ã—ã¦ã¾ã™ã€‚"
kor "ì¤‘ë³µëœ ì¹¼ëŸ¼ ì´ë¦„: '%-.192s'"
nor "Feltnavnet '%-.192s' eksisterte fra før"
norwegian-ny "Feltnamnet '%-.192s' eksisterte frå før"
@@ -1454,18 +1390,17 @@ ER_DUP_FIELDNAME 42S21 S1009
swe "Kolumnnamn '%-.192s finns flera gånger"
ukr "Дублююче ім'Ñ ÑÑ‚Ð¾Ð²Ð±Ñ†Ñ '%-.192s'"
ER_DUP_KEYNAME 42000 S1009
- cze "Zdvojen-Bé jméno klíÄe '%-.192s'"
+ cze "Zdvojené jméno klíÄe '%-.192s'"
dan "Indeksnavnet '%-.192s' findes allerede"
nla "Dubbele zoeksleutel naam '%-.192s'"
eng "Duplicate key name '%-.192s'"
- jps "'%-.192s' ã¨ã„ㆠkey ã®åå‰ã¯é‡è¤‡ã—ã¦ã„ã¾ã™",
est "Kattuv võtme nimi '%-.192s'"
fre "Nom de clef '%-.192s' déjà utilisé"
ger "Doppelter Name für Schlüssel vorhanden: '%-.192s'"
greek "Επανάληψη key name '%-.192s'"
hun "Duplikalt kulcsazonosito: '%-.192s'"
ita "Nome chiave duplicato '%-.192s'"
- jpn "'%-.192s' ã¨ã„ㆠkey ã®åå‰ã¯é‡è¤‡ã—ã¦ã„ã¾ã™"
+ jpn "索引å '%-.192s' ã¯é‡è¤‡ã—ã¦ã„ã¾ã™ã€‚"
kor "ì¤‘ë³µëœ í‚¤ ì´ë¦„ : '%-.192s'"
nor "Nøkkelnavnet '%-.192s' eksisterte fra før"
norwegian-ny "Nøkkelnamnet '%-.192s' eksisterte frå før"
@@ -1481,32 +1416,31 @@ ER_DUP_KEYNAME 42000 S1009
# When using this error code, please use ER(ER_DUP_ENTRY_WITH_KEY_NAME)
# for the message string. See, for example, code in handler.cc.
ER_DUP_ENTRY 23000 S1009
- cze "Zdvojen-Bý klÃ­Ä '%-.192s' (Äíslo klíÄe %d)"
+ cze "Zdvojený klÃ­Ä '%-.192s' (Äíslo klíÄe %d)"
dan "Ens værdier '%-.192s' for indeks %d"
nla "Dubbele ingang '%-.192s' voor zoeksleutel %d"
eng "Duplicate entry '%-.192s' for key %d"
- jps "'%-.192s' 㯠key %d ã«ãŠã„ã¦é‡è¤‡ã—ã¦ã„ã¾ã™",
est "Kattuv väärtus '%-.192s' võtmele %d"
fre "Duplicata du champ '%-.192s' pour la clef %d"
ger "Doppelter Eintrag '%-.192s' für Schlüssel %d"
greek "Διπλή εγγÏαφή '%-.192s' για το κλειδί %d"
hun "Duplikalt bejegyzes '%-.192s' a %d kulcs szerint."
ita "Valore duplicato '%-.192s' per la chiave %d"
- jpn "'%-.192s' 㯠key %d ã«ãŠã„ã¦é‡è¤‡ã—ã¦ã„ã¾ã™"
+ jpn "'%-.192s' ã¯ç´¢å¼• %d ã§é‡è¤‡ã—ã¦ã„ã¾ã™ã€‚"
kor "ì¤‘ë³µëœ ìž…ë ¥ ê°’ '%-.192s': key %d"
nor "Like verdier '%-.192s' for nøkkel %d"
norwegian-ny "Like verdiar '%-.192s' for nykkel %d"
- pol "Powtórzone wyst?pienie '%-.192s' dla klucza %d"
+ pol "Powtórzone wystąpienie '%-.192s' dla klucza %d"
por "Entrada '%-.192s' duplicada para a chave %d"
rum "Cimpul '%-.192s' e duplicat pentru cheia %d"
rus "ДублирующаÑÑÑ Ð·Ð°Ð¿Ð¸ÑÑŒ '%-.192s' по ключу %d"
serbian "Dupliran unos '%-.192s' za kljuÄ '%d'"
slo "Opakovaný kÄ¾ÃºÄ '%-.192s' (Äíslo kľúÄa %d)"
spa "Entrada duplicada '%-.192s' para la clave %d"
- swe "Dubbel nyckel '%-.192s' för nyckel %d"
+ swe "Dublett '%-.192s' för nyckel %d"
ukr "Дублюючий Ð·Ð°Ð¿Ð¸Ñ '%-.192s' Ð´Ð»Ñ ÐºÐ»ÑŽÑ‡Ð° %d"
ER_WRONG_FIELD_SPEC 42000 S1009
- cze "Chybn-Bá specifikace sloupce '%-.192s'"
+ cze "Chybná specifikace sloupce '%-.192s'"
dan "Forkert kolonnespecifikaton for felt '%-.192s'"
nla "Verkeerde kolom specificatie voor kolom '%-.192s'"
eng "Incorrect column specifier for column '%-.192s'"
@@ -1516,6 +1450,7 @@ ER_WRONG_FIELD_SPEC 42000 S1009
greek "Εσφαλμένο column specifier για το πεδίο '%-.192s'"
hun "Rossz oszlopazonosito: '%-.192s'"
ita "Specifica errata per la colonna '%-.192s'"
+ jpn "列 '%-.192s' ã®å®šç¾©ãŒä¸æ­£ã§ã™ã€‚"
kor "칼럼 '%-.192s'ì˜ ë¶€ì •í™•í•œ 칼럼 ì •ì˜ìž"
nor "Feil kolonne spesifikator for felt '%-.192s'"
norwegian-ny "Feil kolonne spesifikator for kolonne '%-.192s'"
@@ -1529,18 +1464,17 @@ ER_WRONG_FIELD_SPEC 42000 S1009
swe "Felaktigt kolumntyp för kolumn '%-.192s'"
ukr "Ðевірний Ñпецифікатор ÑÑ‚Ð¾Ð²Ð±Ñ†Ñ '%-.192s'"
ER_PARSE_ERROR 42000 s1009
- cze "%s bl-Bízko '%-.80s' na řádku %d"
+ cze "%s blízko '%-.80s' na řádku %d"
dan "%s nær '%-.80s' på linje %d"
nla "%s bij '%-.80s' in regel %d"
eng "%s near '%-.80s' at line %d"
- jps "%s : '%-.80s' 付近 : %d 行目",
est "%s '%-.80s' ligidal real %d"
fre "%s près de '%-.80s' à la ligne %d"
ger "%s bei '%-.80s' in Zeile %d"
greek "%s πλησίον '%-.80s' στη γÏαμμή %d"
hun "A %s a '%-.80s'-hez kozeli a %d sorban"
ita "%s vicino a '%-.80s' linea %d"
- jpn "%s : '%-.80s' 付近 : %d 行目"
+ jpn "%s : '%-.80s' 付近 %d 行目"
kor "'%s' ì—러 ê°™ì니다. ('%-.80s' 명령어 ë¼ì¸ %d)"
nor "%s nær '%-.80s' på linje %d"
norwegian-ny "%s attmed '%-.80s' på line %d"
@@ -1554,18 +1488,17 @@ ER_PARSE_ERROR 42000 s1009
swe "%s nära '%-.80s' på rad %d"
ukr "%s Ð±Ñ–Ð»Ñ '%-.80s' в Ñтроці %d"
ER_EMPTY_QUERY 42000
- cze "V-Býsledek dotazu je prázdný"
+ cze "Výsledek dotazu je prázdný"
dan "Forespørgsel var tom"
nla "Query was leeg"
eng "Query was empty"
- jps "Query ãŒç©ºã§ã™.",
est "Tühi päring"
fre "Query est vide"
ger "Leere Abfrage"
greek "Το εÏώτημα (query) που θέσατε ήταν κενό"
hun "Ures lekerdezes."
ita "La query e` vuota"
- jpn "Query ãŒç©ºã§ã™."
+ jpn "クエリãŒç©ºã§ã™ã€‚"
kor "쿼리결과가 없습니다."
nor "Forespørsel var tom"
norwegian-ny "Førespurnad var tom"
@@ -1579,18 +1512,17 @@ ER_EMPTY_QUERY 42000
swe "Frågan var tom"
ukr "ПуÑтий запит"
ER_NONUNIQ_TABLE 42000 S1009
- cze "Nejednozna-BÄná tabulka/alias: '%-.192s'"
+ cze "NejednoznaÄná tabulka/alias: '%-.192s'"
dan "Tabellen/aliaset: '%-.192s' er ikke unikt"
nla "Niet unieke waarde tabel/alias: '%-.192s'"
eng "Not unique table/alias: '%-.192s'"
- jps "'%-.192s' ã¯ä¸€æ„ã® table/alias åã§ã¯ã‚ã‚Šã¾ã›ã‚“",
est "Ei ole unikaalne tabel/alias '%-.192s'"
fre "Table/alias: '%-.192s' non unique"
ger "Tabellenname/Alias '%-.192s' nicht eindeutig"
greek "ΑδÏνατη η ανεÏÏεση unique table/alias: '%-.192s'"
hun "Nem egyedi tabla/alias: '%-.192s'"
ita "Tabella/alias non unico: '%-.192s'"
- jpn "'%-.192s' ã¯ä¸€æ„ã® table/alias åã§ã¯ã‚ã‚Šã¾ã›ã‚“"
+ jpn "表åï¼åˆ¥å '%-.192s' ã¯ä¸€æ„ã§ã¯ã‚ã‚Šã¾ã›ã‚“。"
kor "Unique 하지 ì•Šì€ í…Œì´ë¸”/alias: '%-.192s'"
nor "Ikke unikt tabell/alias: '%-.192s'"
norwegian-ny "Ikkje unikt tabell/alias: '%-.192s'"
@@ -1604,7 +1536,7 @@ ER_NONUNIQ_TABLE 42000 S1009
swe "Icke unikt tabell/alias: '%-.192s'"
ukr "Ðеунікальна таблицÑ/пÑевдонім: '%-.192s'"
ER_INVALID_DEFAULT 42000 S1009
- cze "Chybn-Bá defaultní hodnota pro '%-.192s'"
+ cze "Chybná defaultní hodnota pro '%-.192s'"
dan "Ugyldig standardværdi for '%-.192s'"
nla "Foutieve standaard waarde voor '%-.192s'"
eng "Invalid default value for '%-.192s'"
@@ -1614,6 +1546,7 @@ ER_INVALID_DEFAULT 42000 S1009
greek "Εσφαλμένη Ï€ÏοκαθοÏισμένη τιμή (default value) για '%-.192s'"
hun "Ervenytelen ertek: '%-.192s'"
ita "Valore di default non valido per '%-.192s'"
+ jpn "'%-.192s' ã¸ã®ãƒ‡ãƒ•ã‚©ãƒ«ãƒˆå€¤ãŒç„¡åŠ¹ã§ã™ã€‚"
kor "'%-.192s'ì˜ ìœ íš¨í•˜ì§€ 못한 ë””í´íŠ¸ ê°’ì„ ì‚¬ìš©í•˜ì…¨ìŠµë‹ˆë‹¤."
nor "Ugyldig standardverdi for '%-.192s'"
norwegian-ny "Ugyldig standardverdi for '%-.192s'"
@@ -1627,18 +1560,17 @@ ER_INVALID_DEFAULT 42000 S1009
swe "Ogiltigt DEFAULT värde för '%-.192s'"
ukr "Ðевірне Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð¿Ð¾ замовчуванню Ð´Ð»Ñ '%-.192s'"
ER_MULTIPLE_PRI_KEY 42000 S1009
- cze "Definov-Báno více primárních klíÄů"
+ cze "Definováno více primárních klíÄů"
dan "Flere primærnøgler specificeret"
nla "Meerdere primaire zoeksleutels gedefinieerd"
eng "Multiple primary key defined"
- jps "複数㮠primary key ãŒå®šç¾©ã•ã‚Œã¾ã—ãŸ",
est "Mitut primaarset võtit ei saa olla"
fre "Plusieurs clefs primaires définies"
ger "Mehrere Primärschlüssel (PRIMARY KEY) definiert"
greek "ΠεÏισσότεÏα από ένα primary key οÏίστηκαν"
hun "Tobbszoros elsodleges kulcs definialas."
ita "Definite piu` chiave primarie"
- jpn "複数㮠primary key ãŒå®šç¾©ã•ã‚Œã¾ã—ãŸ"
+ jpn "PRIMARY KEY ãŒè¤‡æ•°å®šç¾©ã•ã‚Œã¦ã„ã¾ã™ã€‚"
kor "Multiple primary keyê°€ ì •ì˜ë˜ì–´ 있슴"
nor "Fleire primærnøkle spesifisert"
norwegian-ny "Fleire primærnyklar spesifisert"
@@ -1652,18 +1584,17 @@ ER_MULTIPLE_PRI_KEY 42000 S1009
swe "Flera PRIMARY KEY använda"
ukr "Первинного ключа визначено неодноразово"
ER_TOO_MANY_KEYS 42000 S1009
- cze "Zad-Báno příliÅ¡ mnoho klíÄů, je povoleno nejvíce %d klíÄů"
+ cze "Zadáno příliÅ¡ mnoho klíÄů, je povoleno nejvíce %d klíÄů"
dan "For mange nøgler specificeret. Kun %d nøgler må bruges"
nla "Teveel zoeksleutels gedefinieerd. Maximaal zijn %d zoeksleutels toegestaan"
eng "Too many keys specified; max %d keys allowed"
- jps "key ã®æŒ‡å®šãŒå¤šã™ãŽã¾ã™. key ã¯æœ€å¤§ %d ã¾ã§ã§ã™",
est "Liiga palju võtmeid. Maksimaalselt võib olla %d võtit"
fre "Trop de clefs sont définies. Maximum de %d clefs alloué"
ger "Zu viele Schlüssel definiert. Maximal %d Schlüssel erlaubt"
greek "ΠάÏα πολλά key οÏίσθηκαν. Το Ï€Î¿Î»Ï %d επιτÏέπονται"
hun "Tul sok kulcs. Maximum %d kulcs engedelyezett."
ita "Troppe chiavi. Sono ammesse max %d chiavi"
- jpn "key ã®æŒ‡å®šãŒå¤šã™ãŽã¾ã™. key ã¯æœ€å¤§ %d ã¾ã§ã§ã™"
+ jpn "索引ã®æ•°ãŒå¤šã™ãŽã¾ã™ã€‚最大 %d 個ã¾ã§ã§ã™ã€‚"
kor "너무 ë§Žì€ í‚¤ê°€ ì •ì˜ë˜ì–´ 있ì니다.. 최대 %dì˜ í‚¤ê°€ 가능함"
nor "For mange nøkler spesifisert. Maks %d nøkler tillatt"
norwegian-ny "For mange nykler spesifisert. Maks %d nyklar tillatt"
@@ -1677,7 +1608,7 @@ ER_TOO_MANY_KEYS 42000 S1009
swe "För många nycklar använda. Man får ha högst %d nycklar"
ukr "Забагато ключів зазначено. Дозволено не більше %d ключів"
ER_TOO_MANY_KEY_PARTS 42000 S1009
- cze "Zad-Báno příliÅ¡ mnoho Äást klíÄů, je povoleno nejvíce %d Äástí"
+ cze "Zadáno příliÅ¡ mnoho Äást klíÄů, je povoleno nejvíce %d Äástí"
dan "For mange nøgledele specificeret. Kun %d dele må bruges"
nla "Teveel zoeksleutel onderdelen gespecificeerd. Maximaal %d onderdelen toegestaan"
eng "Too many key parts specified; max %d parts allowed"
@@ -1687,6 +1618,7 @@ ER_TOO_MANY_KEY_PARTS 42000 S1009
greek "ΠάÏα πολλά key parts οÏίσθηκαν. Το Ï€Î¿Î»Ï %d επιτÏέπονται"
hun "Tul sok kulcsdarabot definialt. Maximum %d resz engedelyezett"
ita "Troppe parti di chiave specificate. Sono ammesse max %d parti"
+ jpn "索引ã®ã‚­ãƒ¼åˆ—指定ãŒå¤šã™ãŽã¾ã™ã€‚最大 %d 個ã¾ã§ã§ã™ã€‚"
kor "너무 ë§Žì€ í‚¤ 부분(parts)ë“¤ì´ ì •ì˜ë˜ì–´ 있ì니다.. 최대 %d ë¶€ë¶„ì´ ê°€ëŠ¥í•¨"
nor "For mange nøkkeldeler spesifisert. Maks %d deler tillatt"
norwegian-ny "For mange nykkeldelar spesifisert. Maks %d delar tillatt"
@@ -1700,18 +1632,17 @@ ER_TOO_MANY_KEY_PARTS 42000 S1009
swe "För många nyckeldelar använda. Man får ha högst %d nyckeldelar"
ukr "Забагато чаÑтин ключа зазначено. Дозволено не більше %d чаÑтин"
ER_TOO_LONG_KEY 42000 S1009
- cze "Zadan-Bý klÃ­Ä byl příliÅ¡ dlouhý, nejvÄ›tší délka klíÄe je %d"
+ cze "Zadaný klÃ­Ä byl příliÅ¡ dlouhý, nejvÄ›tší délka klíÄe je %d"
dan "Specificeret nøgle var for lang. Maksimal nøglelængde er %d"
nla "Gespecificeerde zoeksleutel was te lang. De maximale lengte is %d"
eng "Specified key was too long; max key length is %d bytes"
- jps "key ãŒé•·ã™ãŽã¾ã™. key ã®é•·ã•ã¯æœ€å¤§ %d ã§ã™",
est "Võti on liiga pikk. Maksimaalne võtmepikkus on %d"
fre "La clé est trop longue. Longueur maximale: %d"
ger "Schlüssel ist zu lang. Die maximale Schlüssellänge beträgt %d"
greek "Το κλειδί που οÏίσθηκε είναι Ï€Î¿Î»Ï Î¼ÎµÎ³Î¬Î»Î¿. Το μέγιστο μήκος είναι %d"
hun "A megadott kulcs tul hosszu. Maximalis kulcshosszusag: %d"
ita "La chiave specificata e` troppo lunga. La max lunghezza della chiave e` %d"
- jpn "key ãŒé•·ã™ãŽã¾ã™. key ã®é•·ã•ã¯æœ€å¤§ %d ã§ã™"
+ jpn "索引ã®ã‚­ãƒ¼ãŒé•·ã™ãŽã¾ã™ã€‚最大 %d ãƒã‚¤ãƒˆã¾ã§ã§ã™ã€‚"
kor "ì •ì˜ëœ 키가 너무 ê¹ë‹ˆë‹¤. 최대 í‚¤ì˜ ê¸¸ì´ëŠ” %d입니다."
nor "Spesifisert nøkkel var for lang. Maks nøkkellengde er is %d"
norwegian-ny "Spesifisert nykkel var for lang. Maks nykkellengde er %d"
@@ -1725,18 +1656,17 @@ ER_TOO_LONG_KEY 42000 S1009
swe "För lång nyckel. Högsta tillåtna nyckellängd är %d"
ukr "Зазначений ключ задовгий. Ðайбільша довжина ключа %d байтів"
ER_KEY_COLUMN_DOES_NOT_EXITS 42000 S1009
- cze "Kl-BíÄový sloupec '%-.192s' v tabulce neexistuje"
+ cze "KlíÄový sloupec '%-.192s' v tabulce neexistuje"
dan "Nøglefeltet '%-.192s' eksisterer ikke i tabellen"
nla "Zoeksleutel kolom '%-.192s' bestaat niet in tabel"
eng "Key column '%-.192s' doesn't exist in table"
- jps "Key column '%-.192s' ãŒãƒ†ãƒ¼ãƒ–ルã«ã‚ã‚Šã¾ã›ã‚“.",
est "Võtme tulp '%-.192s' puudub tabelis"
fre "La clé '%-.192s' n'existe pas dans la table"
ger "In der Tabelle gibt es kein Schlüsselfeld '%-.192s'"
greek "Το πεδίο κλειδί '%-.192s' δεν υπάÏχει στον πίνακα"
hun "A(z) '%-.192s'kulcsoszlop nem letezik a tablaban"
ita "La colonna chiave '%-.192s' non esiste nella tabella"
- jpn "Key column '%-.192s' ãŒãƒ†ãƒ¼ãƒ–ルã«ã‚ã‚Šã¾ã›ã‚“."
+ jpn "キー列 '%-.192s' ã¯è¡¨ã«ã‚ã‚Šã¾ã›ã‚“。"
kor "Key 칼럼 '%-.192s'는 í…Œì´ë¸”ì— ì¡´ìž¬í•˜ì§€ 않습니다."
nor "Nøkkel felt '%-.192s' eksiterer ikke i tabellen"
norwegian-ny "Nykkel kolonne '%-.192s' eksiterar ikkje i tabellen"
@@ -1750,41 +1680,22 @@ ER_KEY_COLUMN_DOES_NOT_EXITS 42000 S1009
swe "Nyckelkolumn '%-.192s' finns inte"
ukr "Ключовий Ñтовбець '%-.192s' не Ñ–Ñнує у таблиці"
ER_BLOB_USED_AS_KEY 42000 S1009
- cze "Blob sloupec '%-.192s' nem-Bůže být použit jako klíÄ"
- dan "BLOB feltet '%-.192s' kan ikke bruges ved specifikation af indeks"
- nla "BLOB kolom '%-.192s' kan niet gebruikt worden bij zoeksleutel specificatie"
- eng "BLOB column '%-.192s' can't be used in key specification with the used table type"
- est "BLOB-tüüpi tulpa '%-.192s' ei saa kasutada võtmena"
- fre "Champ BLOB '%-.192s' ne peut être utilisé dans une clé"
- ger "BLOB-Feld '%-.192s' kann beim verwendeten Tabellentyp nicht als Schlüssel verwendet werden"
- greek "Πεδίο Ï„Ïπου Blob '%-.192s' δεν μποÏεί να χÏησιμοποιηθεί στον οÏισμό ενός ÎºÎ»ÎµÎ¹Î´Î¹Î¿Ï (key specification)"
- hun "Blob objektum '%-.192s' nem hasznalhato kulcskent"
- ita "La colonna BLOB '%-.192s' non puo` essere usata nella specifica della chiave"
- kor "BLOB 칼럼 '%-.192s'는 키 ì •ì˜ì—ì„œ ì‚¬ìš©ë  ìˆ˜ 없습니다."
- nor "Blob felt '%-.192s' kan ikke brukes ved spesifikasjon av nøkler"
- norwegian-ny "Blob kolonne '%-.192s' kan ikkje brukast ved spesifikasjon av nyklar"
- pol "Kolumna typu Blob '%-.192s' nie może być użyta w specyfikacji klucza"
- por "Coluna BLOB '%-.192s' não pode ser utilizada na especificação de chave para o tipo de tabela usado"
- rum "Coloana de tip BLOB '%-.192s' nu poate fi folosita in specificarea cheii cu tipul de tabla folosit"
- rus "Столбец типа BLOB '%-.192s' не может быть иÑпользован как значение ключа в таблице такого типа"
- serbian "BLOB kolona '%-.192s' ne može biti upotrebljena za navoÄ‘enje kljuÄa sa tipom tabele koji se trenutno koristi"
- slo "Blob pole '%-.192s' nemôže byÅ¥ použité ako kľúÄ"
- spa "La columna Blob '%-.192s' no puede ser usada en una declaracion de clave"
- swe "En BLOB '%-.192s' kan inte vara nyckel med den använda tabelltypen"
- ukr "BLOB Ñтовбець '%-.192s' не може бути викориÑтаний у визначенні ключа в цьому типі таблиці"
+ eng "BLOB column %`s can't be used in key specification in the %s table"
+ ger "BLOB-Feld %`s kann beim %s Tabellen nicht als Schlüssel verwendet werden"
+ rus "Столбец типа BLOB %`s не может быть иÑпользован как значение ключа в %s таблице"
+ ukr "BLOB Ñтовбець %`s не може бути викориÑтаний у визначенні ключа в %s таблиці"
ER_TOO_BIG_FIELDLENGTH 42000 S1009
- cze "P-Bříliš velká délka sloupce '%-.192s' (nejvíce %lu). Použijte BLOB"
+ cze "Příliš velká délka sloupce '%-.192s' (nejvíce %lu). Použijte BLOB"
dan "For stor feltlængde for kolonne '%-.192s' (maks = %lu). Brug BLOB i stedet"
nla "Te grote kolomlengte voor '%-.192s' (max = %lu). Maak hiervoor gebruik van het type BLOB"
eng "Column length too big for column '%-.192s' (max = %lu); use BLOB or TEXT instead"
- jps "column '%-.192s' ã¯,確ä¿ã™ã‚‹ column ã®å¤§ãã•ãŒå¤šã™ãŽã¾ã™. (最大 %lu ã¾ã§). BLOB ã‚’ã‹ã‚ã‚Šã«ä½¿ç”¨ã—ã¦ãã ã•ã„."
est "Tulba '%-.192s' pikkus on liiga pikk (maksimaalne pikkus: %lu). Kasuta BLOB väljatüüpi"
fre "Champ '%-.192s' trop long (max = %lu). Utilisez un BLOB"
ger "Feldlänge für Feld '%-.192s' zu groß (maximal %lu). BLOB- oder TEXT-Spaltentyp verwenden!"
greek "Î Î¿Î»Ï Î¼ÎµÎ³Î¬Î»Î¿ μήκος για το πεδίο '%-.192s' (max = %lu). ΠαÏακαλώ χÏησιμοποιείστε τον Ï„Ïπο BLOB"
hun "A(z) '%-.192s' oszlop tul hosszu. (maximum = %lu). Hasznaljon BLOB tipust inkabb."
ita "La colonna '%-.192s' e` troppo grande (max=%lu). Utilizza un BLOB."
- jpn "column '%-.192s' ã¯,確ä¿ã™ã‚‹ column ã®å¤§ãã•ãŒå¤šã™ãŽã¾ã™. (最大 %lu ã¾ã§). BLOB ã‚’ã‹ã‚ã‚Šã«ä½¿ç”¨ã—ã¦ãã ã•ã„."
+ jpn "列 '%-.192s' ã®ã‚µã‚¤ã‚ºå®šç¾©ãŒå¤§ãã™ãŽã¾ã™ (最大 %lu ã¾ã§)。代ã‚ã‚Šã« BLOB ã¾ãŸã¯ TEXT を使用ã—ã¦ãã ã•ã„。"
kor "칼럼 '%-.192s'ì˜ ì¹¼ëŸ¼ 길ì´ê°€ 너무 ê¹ë‹ˆë‹¤ (최대 = %lu). ëŒ€ì‹ ì— BLOB를 사용하세요."
nor "For stor nøkkellengde for kolonne '%-.192s' (maks = %lu). Bruk BLOB istedenfor"
norwegian-ny "For stor nykkellengde for felt '%-.192s' (maks = %lu). Bruk BLOB istadenfor"
@@ -1798,18 +1709,17 @@ ER_TOO_BIG_FIELDLENGTH 42000 S1009
swe "För stor kolumnlängd angiven för '%-.192s' (max= %lu). Använd en BLOB instället"
ukr "Задовга довжина ÑÑ‚Ð¾Ð²Ð±Ñ†Ñ '%-.192s' (max = %lu). ВикориÑтайте тип BLOB"
ER_WRONG_AUTO_KEY 42000 S1009
- cze "M-Bůžete mít pouze jedno AUTO pole a to musí být definováno jako klíÄ"
+ cze "Můžete mít pouze jedno AUTO pole a to musí být definováno jako klíÄ"
dan "Der kan kun specificeres eet AUTO_INCREMENT-felt, og det skal være indekseret"
nla "Er kan slechts 1 autofield zijn en deze moet als zoeksleutel worden gedefinieerd."
eng "Incorrect table definition; there can be only one auto column and it must be defined as a key"
- jps "テーブルã®å®šç¾©ãŒé•ã„ã¾ã™; there can be only one auto column and it must be defined as a key",
est "Vigane tabelikirjeldus; Tabelis tohib olla üks auto_increment tüüpi tulp ning see peab olema defineeritud võtmena"
fre "Un seul champ automatique est permis et il doit être indexé"
ger "Falsche Tabellendefinition. Es darf nur eine AUTO_INCREMENT-Spalte geben, und diese muss als Schlüssel definiert werden"
greek "ΜποÏεί να υπάÏχει μόνο ένα auto field και Ï€Ïέπει να έχει οÏισθεί σαν key"
hun "Csak egy auto mezo lehetseges, es azt kulcskent kell definialni."
ita "Puo` esserci solo un campo AUTO e deve essere definito come chiave"
- jpn "テーブルã®å®šç¾©ãŒé•ã„ã¾ã™; there can be only one auto column and it must be defined as a key"
+ jpn "ä¸æ­£ãªè¡¨å®šç¾©ã§ã™ã€‚AUTO_INCREMENT列ã¯ï¼‘個ã¾ã§ã§ã€ç´¢å¼•ã‚’定義ã™ã‚‹å¿…è¦ãŒã‚ã‚Šã¾ã™ã€‚"
kor "부정확한 í…Œì´ë¸” ì •ì˜; í…Œì´ë¸”ì€ í•˜ë‚˜ì˜ auto ì¹¼ëŸ¼ì´ ì¡´ìž¬í•˜ê³  키로 ì •ì˜ë˜ì–´ì ¸ì•¼ 합니다."
nor "Bare ett auto felt kan være definert som nøkkel."
norwegian-ny "Bare eitt auto felt kan være definert som nøkkel."
@@ -1822,10 +1732,10 @@ ER_WRONG_AUTO_KEY 42000 S1009
spa "Puede ser solamente un campo automatico y este debe ser definido como una clave"
swe "Det får finnas endast ett AUTO_INCREMENT-fält och detta måste vara en nyckel"
ukr "Ðевірне Ð²Ð¸Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ñ–; Може бути лише один автоматичний Ñтовбець, що повинен бути визначений Ñк ключ"
-ER_UNUSED_2
+ER_UNUSED_9
eng "You should never see it"
ER_NORMAL_SHUTDOWN
- cze "%s: norm-Bální ukonÄení\n"
+ cze "%s: normální ukonÄení\n"
dan "%s: Normal nedlukning\n"
nla "%s: Normaal afgesloten \n"
eng "%s: Normal shutdown\n"
@@ -1835,6 +1745,7 @@ ER_NORMAL_SHUTDOWN
greek "%s: Φυσιολογική διαδικασία shutdown\n"
hun "%s: Normal leallitas\n"
ita "%s: Shutdown normale\n"
+ jpn "%s: 通常シャットダウン\n"
kor "%s: ì •ìƒì ì¸ shutdown\n"
nor "%s: Normal avslutning\n"
norwegian-ny "%s: Normal nedkopling\n"
@@ -1848,18 +1759,17 @@ ER_NORMAL_SHUTDOWN
swe "%s: Normal avslutning\n"
ukr "%s: Ðормальне завершеннÑ\n"
ER_GOT_SIGNAL
- cze "%s: p-BÅ™ijat signal %d, konÄím\n"
+ cze "%s: pÅ™ijat signal %d, konÄím\n"
dan "%s: Fangede signal %d. Afslutter!!\n"
nla "%s: Signaal %d. Systeem breekt af!\n"
eng "%s: Got signal %d. Aborting!\n"
- jps "%s: Got signal %d. 中断!¥n",
est "%s: sain signaali %d. Lõpetan!\n"
fre "%s: Reçu le signal %d. Abandonne!\n"
ger "%s: Signal %d erhalten. Abbruch!\n"
greek "%s: Ελήφθη το μήνυμα %d. Η διαδικασία εγκαταλείπεται!\n"
hun "%s: %d jelzes. Megszakitva!\n"
ita "%s: Ricevuto segnale %d. Interruzione!\n"
- jpn "%s: Got signal %d. 中断!\n"
+ jpn "%s: シグナル %d ã‚’å—ä¿¡ã—ã¾ã—ãŸã€‚強制終了ã—ã¾ã™ï¼\n"
kor "%s: %d 신호가 들어왔ìŒ. 중지!\n"
nor "%s: Oppdaget signal %d. Avslutter!\n"
norwegian-ny "%s: Oppdaga signal %d. Avsluttar!\n"
@@ -1873,18 +1783,17 @@ ER_GOT_SIGNAL
swe "%s: Fick signal %d. Avslutar!\n"
ukr "%s: Отримано Ñигнал %d. ПерериваюÑÑŒ!\n"
ER_SHUTDOWN_COMPLETE
- cze "%s: ukon-BÄení práce hotovo\n"
+ cze "%s: ukonÄení práce hotovo\n"
dan "%s: Server lukket\n"
nla "%s: Afsluiten afgerond\n"
eng "%s: Shutdown complete\n"
- jps "%s: Shutdown 完了¥n",
est "%s: Lõpp\n"
fre "%s: Arrêt du serveur terminé\n"
ger "%s: Herunterfahren beendet\n"
greek "%s: Η διαδικασία Shutdown ολοκληÏώθηκε\n"
hun "%s: A leallitas kesz\n"
ita "%s: Shutdown completato\n"
- jpn "%s: Shutdown 完了\n"
+ jpn "%s: シャットダウン完了\n"
kor "%s: Shutdown ì´ ì™„ë£Œë¨!\n"
nor "%s: Avslutning komplett\n"
norwegian-ny "%s: Nedkopling komplett\n"
@@ -1898,18 +1807,17 @@ ER_SHUTDOWN_COMPLETE
swe "%s: Avslutning klar\n"
ukr "%s: Роботу завершено\n"
ER_FORCING_CLOSE 08S01
- cze "%s: n-Básilné uzavření threadu %ld uživatele '%-.48s'\n"
+ cze "%s: násilné uzavření threadu %ld uživatele '%-.48s'\n"
dan "%s: Forceret nedlukning af tråd: %ld bruger: '%-.48s'\n"
nla "%s: Afsluiten afgedwongen van thread %ld gebruiker: '%-.48s'\n"
eng "%s: Forcing close of thread %ld user: '%-.48s'\n"
- jps "%s: スレッド %ld 強制終了 user: '%-.48s'¥n",
est "%s: Sulgen jõuga lõime %ld kasutaja: '%-.48s'\n"
fre "%s: Arrêt forcé de la tâche (thread) %ld utilisateur: '%-.48s'\n"
ger "%s: Thread %ld zwangsweise beendet. Benutzer: '%-.48s'\n"
greek "%s: Το thread θα κλείσει %ld user: '%-.48s'\n"
hun "%s: A(z) %ld thread kenyszeritett zarasa. Felhasznalo: '%-.48s'\n"
ita "%s: Forzata la chiusura del thread %ld utente: '%-.48s'\n"
- jpn "%s: スレッド %ld 強制終了 user: '%-.48s'\n"
+ jpn "%s: スレッド %ld を強制終了ã—ã¾ã™ (ユーザー: '%-.48s')\n"
kor "%s: thread %ldì˜ ê°•ì œ 종료 user: '%-.48s'\n"
nor "%s: Påtvinget avslutning av tråd %ld bruker: '%-.48s'\n"
norwegian-ny "%s: Påtvinga avslutning av tråd %ld brukar: '%-.48s'\n"
@@ -1923,18 +1831,17 @@ ER_FORCING_CLOSE 08S01
swe "%s: Stänger av tråd %ld; användare: '%-.48s'\n"
ukr "%s: ПриÑкорюю Ð·Ð°ÐºÑ€Ð¸Ñ‚Ñ‚Ñ Ð³Ñ–Ð»ÐºÐ¸ %ld кориÑтувача: '%-.48s'\n"
ER_IPSOCK_ERROR 08S01
- cze "Nemohu vytvo-Břit IP socket"
+ cze "Nemohu vytvořit IP socket"
dan "Kan ikke oprette IP socket"
nla "Kan IP-socket niet openen"
eng "Can't create IP socket"
- jps "IP socket ãŒä½œã‚Œã¾ã›ã‚“",
est "Ei suuda luua IP socketit"
fre "Ne peut créer la connexion IP (socket)"
ger "Kann IP-Socket nicht erzeugen"
greek "Δεν είναι δυνατή η δημιουÏγία IP socket"
hun "Az IP socket nem hozhato letre"
ita "Impossibile creare il socket IP"
- jpn "IP socket ãŒä½œã‚Œã¾ã›ã‚“"
+ jpn "IPソケットを作æˆã§ãã¾ã›ã‚“。"
kor "IP ì†Œì¼“ì„ ë§Œë“¤ì§€ 못했습니다."
nor "Kan ikke opprette IP socket"
norwegian-ny "Kan ikkje opprette IP socket"
@@ -1948,18 +1855,17 @@ ER_IPSOCK_ERROR 08S01
swe "Kan inte skapa IP-socket"
ukr "Ðе можу Ñтворити IP роз'єм"
ER_NO_SUCH_INDEX 42S12 S1009
- cze "Tabulka '%-.192s' nem-Bá index odpovídající CREATE INDEX. Vytvořte tabulku znovu"
+ cze "Tabulka '%-.192s' nemá index odpovídající CREATE INDEX. Vytvořte tabulku znovu"
dan "Tabellen '%-.192s' har ikke den nøgle, som blev brugt i CREATE INDEX. Genopret tabellen"
nla "Tabel '%-.192s' heeft geen INDEX zoals deze gemaakt worden met CREATE INDEX. Maak de tabel opnieuw"
eng "Table '%-.192s' has no index like the one used in CREATE INDEX; recreate the table"
- jps "Table '%-.192s' ã¯ãã®ã‚ˆã†ãª index ã‚’æŒã£ã¦ã„ã¾ã›ã‚“(CREATE INDEX 実行時ã«æŒ‡å®šã•ã‚Œã¦ã„ã¾ã›ã‚“). テーブルを作り直ã—ã¦ãã ã•ã„",
est "Tabelil '%-.192s' puuduvad võtmed. Loo tabel uuesti"
fre "La table '%-.192s' n'a pas d'index comme celle utilisée dans CREATE INDEX. Recréez la table"
ger "Tabelle '%-.192s' besitzt keinen wie den in CREATE INDEX verwendeten Index. Tabelle neu anlegen"
greek "Ο πίνακας '%-.192s' δεν έχει ευÏετήÏιο (index) σαν αυτό που χÏησιμοποιείτε στην CREATE INDEX. ΠαÏακαλώ, ξαναδημιουÏγήστε τον πίνακα"
hun "A(z) '%-.192s' tablahoz nincs meg a CREATE INDEX altal hasznalt index. Alakitsa at a tablat"
ita "La tabella '%-.192s' non ha nessun indice come quello specificatato dalla CREATE INDEX. Ricrea la tabella"
- jpn "Table '%-.192s' ã¯ãã®ã‚ˆã†ãª index ã‚’æŒã£ã¦ã„ã¾ã›ã‚“(CREATE INDEX 実行時ã«æŒ‡å®šã•ã‚Œã¦ã„ã¾ã›ã‚“). テーブルを作り直ã—ã¦ãã ã•ã„"
+ jpn "表 '%-.192s' ã«ä»¥å‰CREATE INDEXã§ä½œæˆã•ã‚ŒãŸç´¢å¼•ãŒã‚ã‚Šã¾ã›ã‚“。表を作り直ã—ã¦ãã ã•ã„。"
kor "í…Œì´ë¸” '%-.192s'는 ì¸ë±ìŠ¤ë¥¼ 만들지 않았습니다. alter í…Œì´ë¸”ëª…ë ¹ì„ ì´ìš©í•˜ì—¬ í…Œì´ë¸”ì„ ìˆ˜ì •í•˜ì„¸ìš”..."
nor "Tabellen '%-.192s' har ingen index som den som er brukt i CREATE INDEX. Gjenopprett tabellen"
norwegian-ny "Tabellen '%-.192s' har ingen index som den som er brukt i CREATE INDEX. Oprett tabellen på nytt"
@@ -1973,7 +1879,7 @@ ER_NO_SUCH_INDEX 42S12 S1009
swe "Tabellen '%-.192s' har inget index som motsvarar det angivna i CREATE INDEX. Skapa om tabellen"
ukr "Ð¢Ð°Ð±Ð»Ð¸Ñ†Ñ '%-.192s' має індекÑ, що не Ñпівпадає з вказанним у CREATE INDEX. Створіть таблицю знову"
ER_WRONG_FIELD_TERMINATORS 42000 S1009
- cze "Argument separ-Bátoru položek nebyl oÄekáván. PÅ™eÄtÄ›te si manuál"
+ cze "Argument separátoru položek nebyl oÄekáván. PÅ™eÄtÄ›te si manuál"
dan "Felt adskiller er ikke som forventet, se dokumentationen"
nla "De argumenten om velden te scheiden zijn anders dan verwacht. Raadpleeg de handleiding"
eng "Field separator argument is not what is expected; check the manual"
@@ -1983,6 +1889,7 @@ ER_WRONG_FIELD_TERMINATORS 42000 S1009
greek "Ο διαχωÏιστής πεδίων δεν είναι αυτός που αναμενόταν. ΠαÏακαλώ ανατÏέξτε στο manual"
hun "A mezoelvalaszto argumentumok nem egyeznek meg a varttal. Nezze meg a kezikonyvben!"
ita "L'argomento 'Field separator' non e` quello atteso. Controlla il manuale"
+ jpn "フィールド区切り文字ãŒäºˆæœŸã›ã¬ä½¿ã‚れ方をã—ã¦ã„ã¾ã™ã€‚マニュアルを確èªã—ã¦ä¸‹ã•ã„。"
kor "í•„ë“œ êµ¬ë¶„ìž ì¸ìˆ˜ë“¤ì´ 완전하지 않습니다. ë©”ë‰´ì–¼ì„ ì°¾ì•„ 보세요."
nor "Felt skiller argumentene er ikke som forventet, se dokumentasjonen"
norwegian-ny "Felt skiljer argumenta er ikkje som venta, sjå dokumentasjonen"
@@ -1996,7 +1903,7 @@ ER_WRONG_FIELD_TERMINATORS 42000 S1009
swe "Fältseparatorerna är vad som förväntades. Kontrollera mot manualen"
ukr "Хибний розділювач полів. Почитайте документацію"
ER_BLOBS_AND_NO_TERMINATED 42000 S1009
- cze "Nen-Bí možné použít pevný rowlength s BLOBem. Použijte 'fields terminated by'."
+ cze "Není možné použít pevný rowlength s BLOBem. Použijte 'fields terminated by'."
dan "Man kan ikke bruge faste feltlængder med BLOB. Brug i stedet 'fields terminated by'."
nla "Bij het gebruik van BLOBs is het niet mogelijk om vaste rijlengte te gebruiken. Maak s.v.p. gebruik van 'fields terminated by'."
eng "You can't use fixed rowlength with BLOBs; please use 'fields terminated by'"
@@ -2006,7 +1913,7 @@ ER_BLOBS_AND_NO_TERMINATED 42000 S1009
greek "Δεν μποÏείτε να χÏησιμοποιήσετε fixed rowlength σε BLOBs. ΠαÏακαλώ χÏησιμοποιείστε 'fields terminated by'."
hun "Fix hosszusagu BLOB-ok nem hasznalhatok. Hasznalja a 'mezoelvalaszto jelet' ."
ita "Non possono essere usate righe a lunghezza fissa con i BLOB. Usa 'FIELDS TERMINATED BY'."
- jpn "You can't use fixed rowlength with BLOBs; please use 'fields terminated by'."
+ jpn "BLOBã«ã¯å›ºå®šé•·ãƒ¬ã‚³ãƒ¼ãƒ‰ãŒä½¿ç”¨ã§ãã¾ã›ã‚“。'FIELDS TERMINATED BY'å¥ã‚’使用ã—ã¦ä¸‹ã•ã„。"
kor "BLOB로는 고정길ì´ì˜ lowlength를 사용할 수 없습니다. 'fields terminated by'를 사용하세요."
nor "En kan ikke bruke faste feltlengder med BLOB. Vennlisgt bruk 'fields terminated by'."
norwegian-ny "Ein kan ikkje bruke faste feltlengder med BLOB. Vennlisgt bruk 'fields terminated by'."
@@ -2020,18 +1927,17 @@ ER_BLOBS_AND_NO_TERMINATED 42000 S1009
swe "Man kan inte använda fast radlängd med blobs. Använd 'fields terminated by'"
ukr "Ðе можна викориÑтовувати Ñталу довжину Ñтроки з BLOB. ЗкориÑтайтеÑÑ 'fields terminated by'"
ER_TEXTFILE_NOT_READABLE
- cze "Soubor '%-.128s' mus-Bí být v adresáři databáze nebo Äitelný pro vÅ¡echny"
+ cze "Soubor '%-.128s' musí být v adresáři databáze nebo Äitelný pro vÅ¡echny"
dan "Filen '%-.128s' skal være i database-folderen, eller kunne læses af alle"
nla "Het bestand '%-.128s' dient in de database directory voor the komen of leesbaar voor iedereen te zijn."
eng "The file '%-.128s' must be in the database directory or be readable by all"
- jps "ファイル '%-.128s' 㯠databse ã® directory ã«ã‚ã‚‹ã‹å…¨ã¦ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ãŒèª­ã‚るよã†ã«è¨±å¯ã•ã‚Œã¦ã„ãªã‘ã‚Œã°ãªã‚Šã¾ã›ã‚“.",
est "Fail '%-.128s' peab asuma andmebaasi kataloogis või olema kõigile loetav"
fre "Le fichier '%-.128s' doit être dans le répertoire de la base et lisible par tous"
ger "Datei '%-.128s' muss im Datenbank-Verzeichnis vorhanden oder lesbar für alle sein"
greek "Το αÏχείο '%-.128s' Ï€Ïέπει να υπάÏχει στο database directory ή να μποÏεί να διαβαστεί από όλους"
hun "A(z) '%-.128s'-nak az adatbazis konyvtarban kell lennie, vagy mindenki szamara olvashatonak"
ita "Il file '%-.128s' deve essere nella directory del database e deve essere leggibile da tutti"
- jpn "ファイル '%-.128s' 㯠databse ã® directory ã«ã‚ã‚‹ã‹å…¨ã¦ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ãŒèª­ã‚るよã†ã«è¨±å¯ã•ã‚Œã¦ã„ãªã‘ã‚Œã°ãªã‚Šã¾ã›ã‚“."
+ jpn "ファイル '%-.128s' ã¯ãƒ‡ãƒ¼ã‚¿ãƒ™ãƒ¼ã‚¹ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªã«ã‚ã‚‹ã‹ã€å…¨ã¦ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã‹ã‚‰èª­ã‚ã‚‹å¿…è¦ãŒã‚ã‚Šã¾ã™ã€‚"
kor "'%-.128s' í™”ì¼ëŠ” ë°ì´íƒ€ë² ì´ìŠ¤ ë””ë ‰í† ë¦¬ì— ì¡´ìž¬í•˜ê±°ë‚˜ 모ë‘ì—게 ì½ê¸° 가능하여야 합니다."
nor "Filen '%-.128s' må være i database-katalogen for å være lesbar for alle"
norwegian-ny "Filen '%-.128s' må være i database-katalogen for å være lesbar for alle"
@@ -2045,18 +1951,17 @@ ER_TEXTFILE_NOT_READABLE
swe "Textfilen '%-.128s' måste finnas i databasbiblioteket eller vara läsbar för alla"
ukr "Файл '%-.128s' повинен бути у теці бази данних або мати вÑтановлене право на Ñ‡Ð¸Ñ‚Ð°Ð½Ð½Ñ Ð´Ð»Ñ ÑƒÑÑ–Ñ…"
ER_FILE_EXISTS_ERROR
- cze "Soubor '%-.200s' ji-Bž existuje"
+ cze "Soubor '%-.200s' již existuje"
dan "Filen '%-.200s' eksisterer allerede"
nla "Het bestand '%-.200s' bestaat reeds"
eng "File '%-.200s' already exists"
- jps "File '%-.200s' ã¯æ—¢ã«å­˜åœ¨ã—ã¾ã™",
est "Fail '%-.200s' juba eksisteerib"
fre "Le fichier '%-.200s' existe déjà"
ger "Datei '%-.200s' bereits vorhanden"
greek "Το αÏχείο '%-.200s' υπάÏχει ήδη"
hun "A '%-.200s' file mar letezik."
ita "Il file '%-.200s' esiste gia`"
- jpn "File '%-.200s' ã¯æ—¢ã«å­˜åœ¨ã—ã¾ã™"
+ jpn "ファイル '%-.200s' ã¯ã™ã§ã«å­˜åœ¨ã—ã¾ã™ã€‚"
kor "'%-.200s' í™”ì¼ì€ ì´ë¯¸ 존재합니다."
nor "Filen '%-.200s' eksisterte allerede"
norwegian-ny "Filen '%-.200s' eksisterte allereide"
@@ -2070,18 +1975,17 @@ ER_FILE_EXISTS_ERROR
swe "Filen '%-.200s' existerar redan"
ukr "Файл '%-.200s' вже Ñ–Ñнує"
ER_LOAD_INFO
- cze "Z-Báznamů: %ld Vymazáno: %ld PÅ™eskoÄeno: %ld Varování: %ld"
+ cze "Záznamů: %ld Vymazáno: %ld PÅ™eskoÄeno: %ld Varování: %ld"
dan "Poster: %ld Fjernet: %ld Sprunget over: %ld Advarsler: %ld"
nla "Records: %ld Verwijderd: %ld Overgeslagen: %ld Waarschuwingen: %ld"
eng "Records: %ld Deleted: %ld Skipped: %ld Warnings: %ld"
- jps "レコード数: %ld 削除: %ld Skipped: %ld Warnings: %ld",
est "Kirjeid: %ld Kustutatud: %ld Vahele jäetud: %ld Hoiatusi: %ld"
fre "Enregistrements: %ld Effacés: %ld Non traités: %ld Avertissements: %ld"
ger "Datensätze: %ld Gelöscht: %ld Ausgelassen: %ld Warnungen: %ld"
greek "ΕγγÏαφές: %ld ΔιαγÏαφές: %ld ΠαÏεκάμφθησαν: %ld ΠÏοειδοποιήσεις: %ld"
hun "Rekordok: %ld Torolve: %ld Skipped: %ld Warnings: %ld"
ita "Records: %ld Cancellati: %ld Saltati: %ld Avvertimenti: %ld"
- jpn "レコード数: %ld 削除: %ld Skipped: %ld Warnings: %ld"
+ jpn "レコード数: %ld 削除: %ld スキップ: %ld 警告: %ld"
kor "레코드: %ld개 삭제: %ld개 스킵: %ld개 경고: %ld개"
nor "Poster: %ld Fjernet: %ld Hoppet over: %ld Advarsler: %ld"
norwegian-ny "Poster: %ld Fjerna: %ld Hoppa over: %ld Ã…tvaringar: %ld"
@@ -2095,11 +1999,10 @@ ER_LOAD_INFO
swe "Rader: %ld Bortagna: %ld Dubletter: %ld Varningar: %ld"
ukr "ЗапиÑів: %ld Видалено: %ld Пропущено: %ld ЗаÑтережень: %ld"
ER_ALTER_INFO
- cze "Z-Báznamů: %ld Zdvojených: %ld"
+ cze "Záznamů: %ld Zdvojených: %ld"
dan "Poster: %ld Ens: %ld"
nla "Records: %ld Dubbel: %ld"
eng "Records: %ld Duplicates: %ld"
- jps "レコード数: %ld é‡è¤‡: %ld",
est "Kirjeid: %ld Kattuvaid: %ld"
fre "Enregistrements: %ld Doublons: %ld"
ger "Datensätze: %ld Duplikate: %ld"
@@ -2120,7 +2023,7 @@ ER_ALTER_INFO
swe "Rader: %ld Dubletter: %ld"
ukr "ЗапиÑів: %ld Дублікатів: %ld"
ER_WRONG_SUB_KEY
- cze "Chybn-Bá podÄást klíÄe -- není to Å™etÄ›zec nebo je delší než délka Äásti klíÄe"
+ cze "Chybná 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 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"
@@ -2130,7 +2033,7 @@ ER_WRONG_SUB_KEY
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 prefix key; the used key part isn't a string or the used length is longer than the key part"
+ jpn "キーã®ãƒ—レフィックスãŒä¸æ­£ã§ã™ã€‚キーãŒæ–‡å­—列ã§ã¯ãªã„ã‹ã€ãƒ—レフィックス長ãŒã‚­ãƒ¼ã‚ˆã‚Šã‚‚é•·ã„ã‹ã€ã‚¹ãƒˆãƒ¬ãƒ¼ã‚¸ã‚¨ãƒ³ã‚¸ãƒ³ãŒä¸€æ„索引ã®ãƒ—レフィックス指定をサãƒãƒ¼ãƒˆã—ã¦ã„ã¾ã›ã‚“。"
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"
@@ -2144,18 +2047,17 @@ ER_WRONG_SUB_KEY
swe "Felaktig delnyckel. Nyckeldelen är inte en sträng eller den angivna längden är längre än kolumnlängden"
ukr "Ðевірна чаÑтина ключа. ВикориÑтана чаÑтина ключа не Ñ” Ñтрокою, задовга або вказівник таблиці не підтримує унікальних чаÑтин ключей"
ER_CANT_REMOVE_ALL_FIELDS 42000
- cze "Nen-Bí možné vymazat všechny položky s ALTER TABLE. Použijte DROP TABLE"
+ cze "Není možné vymazat všechny položky s ALTER TABLE. Použijte DROP TABLE"
dan "Man kan ikke slette alle felter med ALTER TABLE. Brug DROP TABLE i stedet."
nla "Het is niet mogelijk alle velden te verwijderen met ALTER TABLE. Gebruik a.u.b. DROP TABLE hiervoor!"
eng "You can't delete all columns with ALTER TABLE; use DROP TABLE instead"
- jps "ALTER TABLE ã§å…¨ã¦ã® column ã¯å‰Šé™¤ã§ãã¾ã›ã‚“. DROP TABLE を使用ã—ã¦ãã ã•ã„",
est "ALTER TABLE kasutades ei saa kustutada kõiki tulpasid. Kustuta tabel DROP TABLE abil"
fre "Vous ne pouvez effacer tous les champs avec ALTER TABLE. Utilisez DROP TABLE"
ger "Mit ALTER TABLE können nicht alle Felder auf einmal gelöscht werden. Dafür DROP TABLE verwenden"
greek "Δεν είναι δυνατή η διαγÏαφή όλων των πεδίων με ALTER TABLE. ΠαÏακαλώ χÏησιμοποιείστε DROP TABLE"
hun "Az osszes mezo nem torolheto az ALTER TABLE-lel. Hasznalja a DROP TABLE-t helyette"
ita "Non si possono cancellare tutti i campi con una ALTER TABLE. Utilizzare DROP TABLE"
- jpn "ALTER TABLE ã§å…¨ã¦ã® column ã¯å‰Šé™¤ã§ãã¾ã›ã‚“. DROP TABLE を使用ã—ã¦ãã ã•ã„"
+ jpn "ALTER TABLE ã§ã¯å…¨ã¦ã®åˆ—ã®å‰Šé™¤ã¯ã§ãã¾ã›ã‚“。DROP TABLE を使用ã—ã¦ãã ã•ã„。"
kor "ALTER TABLE 명령으로는 모든 ì¹¼ëŸ¼ì„ ì§€ìš¸ 수 없습니다. DROP TABLE ëª…ë ¹ì„ ì´ìš©í•˜ì„¸ìš”."
nor "En kan ikke slette alle felt med ALTER TABLE. Bruk DROP TABLE isteden."
norwegian-ny "Ein kan ikkje slette alle felt med ALTER TABLE. Bruk DROP TABLE istadenfor."
@@ -2169,18 +2071,17 @@ ER_CANT_REMOVE_ALL_FIELDS 42000
swe "Man kan inte radera alla fält med ALTER TABLE. Använd DROP TABLE istället"
ukr "Ðе можливо видалити вÑÑ– Ñтовбці за допомогою ALTER TABLE. Ð”Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ ÑкориÑтайтеÑÑ DROP TABLE"
ER_CANT_DROP_FIELD_OR_KEY 42000
- cze "Nemohu zru-BÅ¡it '%-.192s' (provést DROP). Zkontrolujte, zda neexistují záznamy/klíÄe"
+ cze "Nemohu zruÅ¡it '%-.192s' (provést DROP). Zkontrolujte, zda neexistují záznamy/klíÄe"
dan "Kan ikke udføre DROP '%-.192s'. Undersøg om feltet/nøglen eksisterer."
nla "Kan '%-.192s' niet weggooien. Controleer of het veld of de zoeksleutel daadwerkelijk bestaat."
eng "Can't DROP '%-.192s'; check that column/key exists"
- jps "'%-.192s' を破棄ã§ãã¾ã›ã‚“ã§ã—ãŸ; check that column/key exists",
est "Ei suuda kustutada '%-.192s'. Kontrolli kas tulp/võti eksisteerib"
fre "Ne peut effacer (DROP) '%-.192s'. Vérifiez s'il existe"
ger "Kann '%-.192s' nicht löschen. Existiert die Spalte oder der Schlüssel?"
greek "ΑδÏνατη η διαγÏαφή (DROP) '%-.192s'. ΠαÏακαλώ ελέγξτε αν το πεδίο/κλειδί υπάÏχει"
hun "A DROP '%-.192s' nem lehetseges. Ellenorizze, hogy a mezo/kulcs letezik-e"
ita "Impossibile cancellare '%-.192s'. Controllare che il campo chiave esista"
- jpn "'%-.192s' を破棄ã§ãã¾ã›ã‚“ã§ã—ãŸ; check that column/key exists"
+ jpn "'%-.192s' を削除ã§ãã¾ã›ã‚“。列ï¼ç´¢å¼•ã®å­˜åœ¨ã‚’確èªã—ã¦ä¸‹ã•ã„。"
kor "'%-.192s'를 DROPí•  수 없습니다. 칼럼ì´ë‚˜ 키가 존재하는지 채í¬í•˜ì„¸ìš”."
nor "Kan ikke DROP '%-.192s'. Undersøk om felt/nøkkel eksisterer."
norwegian-ny "Kan ikkje DROP '%-.192s'. Undersøk om felt/nøkkel eksisterar."
@@ -2194,18 +2095,17 @@ ER_CANT_DROP_FIELD_OR_KEY 42000
swe "Kan inte ta bort '%-.192s'. Kontrollera att fältet/nyckel finns"
ukr "Ðе можу DROP '%-.192s'. Перевірте, чи цей Ñтовбець/ключ Ñ–Ñнує"
ER_INSERT_INFO
- cze "Z-Báznamů: %ld Zdvojených: %ld Varování: %ld"
+ cze "Záznamů: %ld Zdvojených: %ld Varování: %ld"
dan "Poster: %ld Ens: %ld Advarsler: %ld"
nla "Records: %ld Dubbel: %ld Waarschuwing: %ld"
eng "Records: %ld Duplicates: %ld Warnings: %ld"
- jps "レコード数: %ld é‡è¤‡æ•°: %ld Warnings: %ld",
est "Kirjeid: %ld Kattuvaid: %ld Hoiatusi: %ld"
fre "Enregistrements: %ld Doublons: %ld Avertissements: %ld"
ger "Datensätze: %ld Duplikate: %ld Warnungen: %ld"
greek "ΕγγÏαφές: %ld Επαναλήψεις: %ld ΠÏοειδοποιήσεις: %ld"
hun "Rekordok: %ld Duplikalva: %ld Warnings: %ld"
ita "Records: %ld Duplicati: %ld Avvertimenti: %ld"
- jpn "レコード数: %ld é‡è¤‡æ•°: %ld Warnings: %ld"
+ jpn "レコード数: %ld é‡è¤‡æ•°: %ld 警告: %ld"
kor "레코드: %ld개 중복: %ld개 경고: %ld개"
nor "Poster: %ld Like: %ld Advarsler: %ld"
norwegian-ny "Postar: %ld Like: %ld Ã…tvaringar: %ld"
@@ -2218,25 +2118,21 @@ ER_INSERT_INFO
spa "Registros: %ld Duplicados: %ld Peligros: %ld"
swe "Rader: %ld Dubletter: %ld Varningar: %ld"
ukr "ЗапиÑів: %ld Дублікатів: %ld ЗаÑтережень: %ld"
-ER_UPDATE_TABLE_USED
- eng "You can't specify target table '%-.192s' for update in FROM clause"
- ger "Die Verwendung der zu aktualisierenden Zieltabelle '%-.192s' ist in der FROM-Klausel nicht zulässig."
- rus "Ðе допуÑкаетÑÑ ÑƒÐºÐ°Ð·Ð°Ð½Ð¸Ðµ таблицы '%-.192s' в ÑпиÑке таблиц FROM Ð´Ð»Ñ Ð²Ð½ÐµÑÐµÐ½Ð¸Ñ Ð² нее изменений"
- swe "INSERT-table '%-.192s' får inte finnas i FROM tabell-listan"
- ukr "Ð¢Ð°Ð±Ð»Ð¸Ñ†Ñ '%-.192s' що змінюєтьÑÑ Ð½Ðµ дозволена у переліку таблиць FROM"
+ER_UPDATE_TABLE_USED
+ eng "Table '%-.192s' is specified twice, both as a target for '%s' and as a separate source for data"
+ swe "Table '%-.192s' är använd två gånger. Både för '%s' och för att hämta data"
ER_NO_SUCH_THREAD
- cze "Nezn-Bámá identifikace threadu: %lu"
+ cze "Neznámá identifikace threadu: %lu"
dan "Ukendt tråd id: %lu"
nla "Onbekend thread id: %lu"
eng "Unknown thread id: %lu"
- jps "thread id: %lu ã¯ã‚ã‚Šã¾ã›ã‚“",
est "Tundmatu lõim: %lu"
fre "Numéro de tâche inconnu: %lu"
ger "Unbekannte Thread-ID: %lu"
greek "Αγνωστο thread id: %lu"
hun "Ervenytelen szal (thread) id: %lu"
ita "Thread id: %lu sconosciuto"
- jpn "thread id: %lu ã¯ã‚ã‚Šã¾ã›ã‚“"
+ jpn "ä¸æ˜Žãªã‚¹ãƒ¬ãƒƒãƒ‰IDã§ã™: %lu"
kor "알수 없는 쓰레드 id: %lu"
nor "Ukjent tråd id: %lu"
norwegian-ny "Ukjent tråd id: %lu"
@@ -2250,18 +2146,17 @@ ER_NO_SUCH_THREAD
swe "Finns ingen tråd med id %lu"
ukr "Ðевідомий ідентифікатор гілки: %lu"
ER_KILL_DENIED_ERROR
- cze "Nejste vlastn-Bíkem threadu %lu"
+ cze "Nejste vlastníkem threadu %lu"
dan "Du er ikke ejer af tråden %lu"
nla "U bent geen bezitter van thread %lu"
eng "You are not owner of thread %lu"
- jps "thread %lu ã®ã‚ªãƒ¼ãƒŠãƒ¼ã§ã¯ã‚ã‚Šã¾ã›ã‚“",
est "Ei ole lõime %lu omanik"
fre "Vous n'êtes pas propriétaire de la tâche no: %lu"
ger "Sie sind nicht Eigentümer von Thread %lu"
greek "Δεν είσθε owner του thread %lu"
hun "A %lu thread-nek mas a tulajdonosa"
ita "Utente non proprietario del thread %lu"
- jpn "thread %lu ã®ã‚ªãƒ¼ãƒŠãƒ¼ã§ã¯ã‚ã‚Šã¾ã›ã‚“"
+ jpn "スレッド %lu ã®ã‚ªãƒ¼ãƒŠãƒ¼ã§ã¯ã‚ã‚Šã¾ã›ã‚“。"
kor "쓰레드(Thread) %luì˜ ì†Œìœ ìžê°€ 아닙니다."
nor "Du er ikke eier av tråden %lu"
norwegian-ny "Du er ikkje eigar av tråd %lu"
@@ -2275,7 +2170,7 @@ ER_KILL_DENIED_ERROR
swe "Du är inte ägare till tråd %lu"
ukr "Ви не володар гілки %lu"
ER_NO_TABLES_USED
- cze "Nejsou pou-Bžity žádné tabulky"
+ cze "Nejsou použity žádné tabulky"
dan "Ingen tabeller i brug"
nla "Geen tabellen gebruikt."
eng "No tables used"
@@ -2285,6 +2180,7 @@ ER_NO_TABLES_USED
greek "Δεν χÏησιμοποιήθηκαν πίνακες"
hun "Nincs hasznalt tabla"
ita "Nessuna tabella usata"
+ jpn "表ãŒæŒ‡å®šã•ã‚Œã¦ã„ã¾ã›ã‚“。"
kor "ì–´ë–¤ í…Œì´ë¸”ë„ ì‚¬ìš©ë˜ì§€ 않았습니다."
nor "Ingen tabeller i bruk"
norwegian-ny "Ingen tabellar i bruk"
@@ -2298,7 +2194,7 @@ ER_NO_TABLES_USED
swe "Inga tabeller angivna"
ukr "Ðе викориÑтано таблиць"
ER_TOO_BIG_SET
- cze "P-Bříliš mnoho řetězců pro sloupec %-.192s a SET"
+ cze "Příliš mnoho řetězců pro sloupec %-.192s a SET"
dan "For mange tekststrenge til specifikationen af SET i kolonne %-.192s"
nla "Teveel strings voor kolom %-.192s en SET"
eng "Too many strings for column %-.192s and SET"
@@ -2308,6 +2204,7 @@ ER_TOO_BIG_SET
greek "ΠάÏα πολλά strings για το πεδίο %-.192s και SET"
hun "Tul sok karakter: %-.192s es SET"
ita "Troppe stringhe per la colonna %-.192s e la SET"
+ jpn "SETåž‹ã®åˆ— '%-.192s' ã®ãƒ¡ãƒ³ãƒãƒ¼ã®æ•°ãŒå¤šã™ãŽã¾ã™ã€‚"
kor "칼럼 %-.192s와 SETì—ì„œ 스트ë§ì´ 너무 많습니다."
nor "For mange tekststrenger kolonne %-.192s og SET"
norwegian-ny "For mange tekststrengar felt %-.192s og SET"
@@ -2321,7 +2218,7 @@ ER_TOO_BIG_SET
swe "För många alternativ till kolumn %-.192s för SET"
ukr "Забагато Ñтрок Ð´Ð»Ñ ÑÑ‚Ð¾Ð²Ð±Ñ†Ñ %-.192s та SET"
ER_NO_UNIQUE_LOGFILE
- cze "Nemohu vytvo-BÅ™it jednoznaÄné jméno logovacího souboru %-.200s.(1-999)\n"
+ cze "Nemohu vytvoÅ™it jednoznaÄné jméno logovacího souboru %-.200s.(1-999)\n"
dan "Kan ikke lave unikt log-filnavn %-.200s.(1-999)\n"
nla "Het is niet mogelijk een unieke naam te maken voor de logfile %-.200s.(1-999)\n"
eng "Can't generate a unique log-filename %-.200s.(1-999)\n"
@@ -2331,6 +2228,7 @@ ER_NO_UNIQUE_LOGFILE
greek "ΑδÏνατη η δημιουÏγία unique log-filename %-.200s.(1-999)\n"
hun "Egyedi log-filenev nem generalhato: %-.200s.(1-999)\n"
ita "Impossibile generare un nome del file log unico %-.200s.(1-999)\n"
+ jpn "一æ„ãªãƒ­ã‚°ãƒ•ã‚¡ã‚¤ãƒ«å %-.200s.(1-999) を生æˆã§ãã¾ã›ã‚“。\n"
kor "Unique ë¡œê·¸í™”ì¼ '%-.200s'를 만들수 없습니다.(1-999)\n"
nor "Kan ikke lage unikt loggfilnavn %-.200s.(1-999)\n"
norwegian-ny "Kan ikkje lage unikt loggfilnavn %-.200s.(1-999)\n"
@@ -2344,18 +2242,17 @@ ER_NO_UNIQUE_LOGFILE
swe "Kan inte generera ett unikt filnamn %-.200s.(1-999)\n"
ukr "Ðе можу згенерувати унікальне ім'Ñ log-файлу %-.200s.(1-999)\n"
ER_TABLE_NOT_LOCKED_FOR_WRITE
- cze "Tabulka '%-.192s' byla zam-BÄena s READ a nemůže být zmÄ›nÄ›na"
+ cze "Tabulka '%-.192s' byla zamÄena s READ a nemůže být zmÄ›nÄ›na"
dan "Tabellen '%-.192s' var låst med READ lås og kan ikke opdateres"
nla "Tabel '%-.192s' was gelocked met een lock om te lezen. Derhalve kunnen geen wijzigingen worden opgeslagen."
eng "Table '%-.192s' was locked with a READ lock and can't be updated"
- jps "Table '%-.192s' 㯠READ lock ã«ãªã£ã¦ã„ã¦ã€æ›´æ–°ã¯ã§ãã¾ã›ã‚“",
est "Tabel '%-.192s' on lukustatud READ lukuga ning ei ole muudetav"
fre "Table '%-.192s' verrouillée lecture (READ): modification impossible"
ger "Tabelle '%-.192s' ist mit Lesesperre versehen und kann nicht aktualisiert werden"
greek "Ο πίνακας '%-.192s' έχει κλειδωθεί με READ lock και δεν επιτÏέπονται αλλαγές"
hun "A(z) '%-.192s' tabla zarolva lett (READ lock) es nem lehet frissiteni"
ita "La tabella '%-.192s' e` soggetta a lock in lettura e non puo` essere aggiornata"
- jpn "Table '%-.192s' 㯠READ lock ã«ãªã£ã¦ã„ã¦ã€æ›´æ–°ã¯ã§ãã¾ã›ã‚“"
+ jpn "表 '%-.192s' ã¯READロックã•ã‚Œã¦ã„ã¦ã€æ›´æ–°ã§ãã¾ã›ã‚“。"
kor "í…Œì´ë¸” '%-.192s'는 READ ë½ì´ 잠겨있어서 갱신할 수 없습니다."
nor "Tabellen '%-.192s' var låst med READ lås og kan ikke oppdateres"
norwegian-ny "Tabellen '%-.192s' var låst med READ lås og kan ikkje oppdaterast"
@@ -2369,18 +2266,17 @@ ER_TABLE_NOT_LOCKED_FOR_WRITE
swe "Tabell '%-.192s' kan inte uppdateras emedan den är låst för läsning"
ukr "Таблицю '%-.192s' заблоковано тільки Ð´Ð»Ñ Ñ‡Ð¸Ñ‚Ð°Ð½Ð½Ñ, тому Ñ—Ñ— не можна оновити"
ER_TABLE_NOT_LOCKED
- cze "Tabulka '%-.192s' nebyla zam-BÄena s LOCK TABLES"
+ cze "Tabulka '%-.192s' nebyla zamÄena s LOCK TABLES"
dan "Tabellen '%-.192s' var ikke låst med LOCK TABLES"
nla "Tabel '%-.192s' was niet gelocked met LOCK TABLES"
eng "Table '%-.192s' was not locked with LOCK TABLES"
- jps "Table '%-.192s' 㯠LOCK TABLES ã«ã‚ˆã£ã¦ãƒ­ãƒƒã‚¯ã•ã‚Œã¦ã„ã¾ã›ã‚“",
est "Tabel '%-.192s' ei ole lukustatud käsuga LOCK TABLES"
fre "Table '%-.192s' non verrouillée: utilisez LOCK TABLES"
ger "Tabelle '%-.192s' wurde nicht mit LOCK TABLES gesperrt"
greek "Ο πίνακας '%-.192s' δεν έχει κλειδωθεί με LOCK TABLES"
hun "A(z) '%-.192s' tabla nincs zarolva a LOCK TABLES-szel"
ita "Non e` stato impostato il lock per la tabella '%-.192s' con LOCK TABLES"
- jpn "Table '%-.192s' 㯠LOCK TABLES ã«ã‚ˆã£ã¦ãƒ­ãƒƒã‚¯ã•ã‚Œã¦ã„ã¾ã›ã‚“"
+ jpn "表 '%-.192s' 㯠LOCK TABLES ã§ãƒ­ãƒƒã‚¯ã•ã‚Œã¦ã„ã¾ã›ã‚“。"
kor "í…Œì´ë¸” '%-.192s'는 LOCK TABLES 명령으로 잠기지 않았습니다."
nor "Tabellen '%-.192s' var ikke låst med LOCK TABLES"
norwegian-ny "Tabellen '%-.192s' var ikkje låst med LOCK TABLES"
@@ -2394,7 +2290,7 @@ ER_TABLE_NOT_LOCKED
swe "Tabell '%-.192s' är inte låst med LOCK TABLES"
ukr "Таблицю '%-.192s' не було блоковано з LOCK TABLES"
ER_BLOB_CANT_HAVE_DEFAULT 42000
- cze "Blob polo-Bžka '%-.192s' nemůže mít defaultní hodnotu"
+ cze "Blob položka '%-.192s' nemůže mít defaultní hodnotu"
dan "BLOB feltet '%-.192s' kan ikke have en standard værdi"
nla "Blob veld '%-.192s' can geen standaardwaarde bevatten"
eng "BLOB/TEXT column '%-.192s' can't have a default value"
@@ -2404,7 +2300,7 @@ ER_BLOB_CANT_HAVE_DEFAULT 42000
greek "Τα Blob πεδία '%-.192s' δεν μποÏοÏν να έχουν Ï€ÏοκαθοÏισμένες τιμές (default value)"
hun "A(z) '%-.192s' blob objektumnak nem lehet alapertelmezett erteke"
ita "Il campo BLOB '%-.192s' non puo` avere un valore di default"
- jpn "BLOB column '%-.192s' can't have a default value"
+ jpn "BLOB/TEXT 列 '%-.192s' ã«ã¯ãƒ‡ãƒ•ã‚©ãƒ«ãƒˆå€¤ã‚’指定ã§ãã¾ã›ã‚“。"
kor "BLOB 칼럼 '%-.192s' 는 ë””í´íŠ¸ ê°’ì„ ê°€ì§ˆ 수 없습니다."
nor "Blob feltet '%-.192s' kan ikke ha en standard verdi"
norwegian-ny "Blob feltet '%-.192s' kan ikkje ha ein standard verdi"
@@ -2418,18 +2314,17 @@ ER_BLOB_CANT_HAVE_DEFAULT 42000
swe "BLOB fält '%-.192s' kan inte ha ett DEFAULT-värde"
ukr "Стовбець BLOB '%-.192s' не може мати Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð¿Ð¾ замовчуванню"
ER_WRONG_DB_NAME 42000
- cze "Nep-Břípustné jméno databáze '%-.100s'"
+ cze "Nepřípustné jméno databáze '%-.100s'"
dan "Ugyldigt database navn '%-.100s'"
nla "Databasenaam '%-.100s' is niet getoegestaan"
eng "Incorrect database name '%-.100s'"
- jps "指定ã—㟠database å '%-.100s' ãŒé–“é•ã£ã¦ã„ã¾ã™",
est "Vigane andmebaasi nimi '%-.100s'"
fre "Nom de base de donnée illégal: '%-.100s'"
ger "Unerlaubter Datenbankname '%-.100s'"
greek "Λάθος όνομα βάσης δεδομένων '%-.100s'"
hun "Hibas adatbazisnev: '%-.100s'"
ita "Nome database errato '%-.100s'"
- jpn "指定ã—㟠database å '%-.100s' ãŒé–“é•ã£ã¦ã„ã¾ã™"
+ jpn "データベースå '%-.100s' ã¯ä¸æ­£ã§ã™ã€‚"
kor "'%-.100s' ë°ì´íƒ€ë² ì´ìŠ¤ì˜ ì´ë¦„ì´ ë¶€ì •í™•í•©ë‹ˆë‹¤."
nor "Ugyldig database navn '%-.100s'"
norwegian-ny "Ugyldig database namn '%-.100s'"
@@ -2443,18 +2338,17 @@ ER_WRONG_DB_NAME 42000
swe "Felaktigt databasnamn '%-.100s'"
ukr "Ðевірне ім'Ñ Ð±Ð°Ð·Ð¸ данних '%-.100s'"
ER_WRONG_TABLE_NAME 42000
- cze "Nep-Břípustné jméno tabulky '%-.100s'"
+ cze "Nepřípustné jméno tabulky '%-.100s'"
dan "Ugyldigt tabel navn '%-.100s'"
nla "Niet toegestane tabelnaam '%-.100s'"
eng "Incorrect table name '%-.100s'"
- jps "指定ã—㟠table å '%-.100s' ã¯ã¾ã¡ãŒã£ã¦ã„ã¾ã™",
est "Vigane tabeli nimi '%-.100s'"
fre "Nom de table illégal: '%-.100s'"
ger "Unerlaubter Tabellenname '%-.100s'"
greek "Λάθος όνομα πίνακα '%-.100s'"
hun "Hibas tablanev: '%-.100s'"
ita "Nome tabella errato '%-.100s'"
- jpn "指定ã—㟠table å '%-.100s' ã¯ã¾ã¡ãŒã£ã¦ã„ã¾ã™"
+ jpn "表å '%-.100s' ã¯ä¸æ­£ã§ã™ã€‚"
kor "'%-.100s' í…Œì´ë¸” ì´ë¦„ì´ ë¶€ì •í™•í•©ë‹ˆë‹¤."
nor "Ugyldig tabell navn '%-.100s'"
norwegian-ny "Ugyldig tabell namn '%-.100s'"
@@ -2468,7 +2362,7 @@ ER_WRONG_TABLE_NAME 42000
swe "Felaktigt tabellnamn '%-.100s'"
ukr "Ðевірне ім'Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ñ– '%-.100s'"
ER_TOO_BIG_SELECT 42000
- cze "Zadan-Bý SELECT by procházel příliš mnoho záznamů a trval velmi dlouho. Zkontrolujte tvar WHERE a je-li SELECT v pořádku, použijte SET SQL_BIG_SELECTS=1"
+ cze "Zadaný SELECT by procházel příliš mnoho záznamů a trval velmi dlouho. Zkontrolujte tvar WHERE a je-li SELECT v pořádku, použijte SET SQL_BIG_SELECTS=1"
dan "SELECT ville undersøge for mange poster og ville sandsynligvis tage meget lang tid. Undersøg WHERE delen og brug SET SQL_BIG_SELECTS=1 hvis udtrykket er korrekt"
nla "Het SELECT-statement zou te veel records analyseren en dus veel tijd in beslagnemen. Kijk het WHERE-gedeelte van de query na en kies SET SQL_BIG_SELECTS=1 als het stament in orde is."
eng "The SELECT would examine more than MAX_JOIN_SIZE rows; check your WHERE and use SET SQL_BIG_SELECTS=1 or SET MAX_JOIN_SIZE=# if the SELECT is okay"
@@ -2478,6 +2372,7 @@ ER_TOO_BIG_SELECT 42000
greek "Το SELECT θα εξετάσει μεγάλο αÏιθμό εγγÏαφών και πιθανώς θα καθυστεÏήσει. ΠαÏακαλώ εξετάστε τις παÏαμέτÏους του WHERE και χÏησιμοποιείστε SET SQL_BIG_SELECTS=1 αν το SELECT είναι σωστό"
hun "A SELECT tul sok rekordot fog megvizsgalni es nagyon sokaig fog tartani. Ellenorizze a WHERE-t es hasznalja a SET SQL_BIG_SELECTS=1 beallitast, ha a SELECT okay"
ita "La SELECT dovrebbe esaminare troppi record e usare troppo tempo. Controllare la WHERE e usa SET SQL_BIG_SELECTS=1 se e` tutto a posto."
+ jpn "SELECTãŒMAX_JOIN_SIZEを超ãˆã‚‹è¡Œæ•°ã‚’処ç†ã—ã¾ã—ãŸã€‚WHEREå¥ã‚’確èªã—ã€SELECTæ–‡ã«å•é¡ŒãŒãªã‘ã‚Œã°ã€ SET SQL_BIG_SELECTS=1 ã¾ãŸã¯ SET MAX_JOIN_SIZE=# を使用ã—ã¦ä¸‹ã•ã„。"
kor "SELECT 명령ì—ì„œ 너무 ë§Žì€ ë ˆì½”ë“œë¥¼ 찾기 ë•Œë¬¸ì— ë§Žì€ ì‹œê°„ì´ ì†Œìš”ë©ë‹ˆë‹¤. ë”°ë¼ì„œ WHERE ë¬¸ì„ ì ê²€í•˜ê±°ë‚˜, 만약 SELECTê°€ okë˜ë©´ SET SQL_BIG_SELECTS=1 ì˜µì…˜ì„ ì‚¬ìš©í•˜ì„¸ìš”."
nor "SELECT ville undersøke for mange poster og ville sannsynligvis ta veldig lang tid. Undersøk WHERE klausulen og bruk SET SQL_BIG_SELECTS=1 om SELECTen er korrekt"
norwegian-ny "SELECT ville undersøkje for mange postar og ville sannsynligvis ta veldig lang tid. Undersøk WHERE klausulen og bruk SET SQL_BIG_SELECTS=1 om SELECTen er korrekt"
@@ -2491,7 +2386,7 @@ ER_TOO_BIG_SELECT 42000
swe "Den angivna frågan skulle läsa mer än MAX_JOIN_SIZE rader. Kontrollera din WHERE och använd SET SQL_BIG_SELECTS=1 eller SET MAX_JOIN_SIZE=# ifall du vill hantera stora joins"
ukr "Запиту SELECT потрібно обробити багато запиÑів, що, певне, займе дуже багато чаÑу. Перевірте ваше WHERE та викориÑтовуйте SET SQL_BIG_SELECTS=1, Ñкщо цей запит SELECT Ñ” вірним"
ER_UNKNOWN_ERROR
- cze "Nezn-Bámá chyba"
+ cze "Neznámá chyba"
dan "Ukendt fejl"
nla "Onbekende Fout"
eng "Unknown error"
@@ -2501,6 +2396,7 @@ ER_UNKNOWN_ERROR
greek "ΠÏοέκυψε άγνωστο λάθος"
hun "Ismeretlen hiba"
ita "Errore sconosciuto"
+ jpn "ä¸æ˜Žãªã‚¨ãƒ©ãƒ¼"
kor "알수 없는 ì—러입니다."
nor "Ukjent feil"
norwegian-ny "Ukjend feil"
@@ -2510,10 +2406,10 @@ ER_UNKNOWN_ERROR
serbian "Nepoznata greška"
slo "Neznámá chyba"
spa "Error desconocido"
- swe "Oidentifierat fel"
+ swe "Okänt fel"
ukr "Ðевідома помилка"
ER_UNKNOWN_PROCEDURE 42000
- cze "Nezn-Bámá procedura %-.192s"
+ cze "Neznámá procedura %-.192s"
dan "Ukendt procedure %-.192s"
nla "Onbekende procedure %-.192s"
eng "Unknown procedure '%-.192s'"
@@ -2523,6 +2419,7 @@ ER_UNKNOWN_PROCEDURE 42000
greek "Αγνωστη διαδικασία '%-.192s'"
hun "Ismeretlen eljaras: '%-.192s'"
ita "Procedura '%-.192s' sconosciuta"
+ jpn "'%-.192s' ã¯ä¸æ˜Žãªãƒ—ロシージャã§ã™ã€‚"
kor "알수 없는 수행문 : '%-.192s'"
nor "Ukjent prosedyre %-.192s"
norwegian-ny "Ukjend prosedyre %-.192s"
@@ -2536,7 +2433,7 @@ ER_UNKNOWN_PROCEDURE 42000
swe "Okänd procedur: %-.192s"
ukr "Ðевідома процедура '%-.192s'"
ER_WRONG_PARAMCOUNT_TO_PROCEDURE 42000
- cze "Chybn-Bý poÄet parametrů procedury %-.192s"
+ cze "Chybný poÄet parametrů procedury %-.192s"
dan "Forkert antal parametre til proceduren %-.192s"
nla "Foutief aantal parameters doorgegeven aan procedure %-.192s"
eng "Incorrect parameter count to procedure '%-.192s'"
@@ -2546,6 +2443,7 @@ ER_WRONG_PARAMCOUNT_TO_PROCEDURE 42000
greek "Λάθος αÏιθμός παÏαμέτÏων στη διαδικασία '%-.192s'"
hun "Rossz parameter a(z) '%-.192s'eljaras szamitasanal"
ita "Numero di parametri errato per la procedura '%-.192s'"
+ jpn "プロシージャ '%-.192s' ã¸ã®ãƒ‘ラメータ数ãŒä¸æ­£ã§ã™ã€‚"
kor "'%-.192s' ìˆ˜í–‰ë¬¸ì— ëŒ€í•œ 부정확한 파ë¼ë©”í„°"
nor "Feil parameter antall til prosedyren %-.192s"
norwegian-ny "Feil parameter tal til prosedyra %-.192s"
@@ -2559,7 +2457,7 @@ ER_WRONG_PARAMCOUNT_TO_PROCEDURE 42000
swe "Felaktigt antal parametrar till procedur %-.192s"
ukr "Хибна кількіÑÑ‚ÑŒ параметрів процедури '%-.192s'"
ER_WRONG_PARAMETERS_TO_PROCEDURE
- cze "Chybn-Bé parametry procedury %-.192s"
+ cze "Chybné parametry procedury %-.192s"
dan "Forkert(e) parametre til proceduren %-.192s"
nla "Foutieve parameters voor procedure %-.192s"
eng "Incorrect parameters to procedure '%-.192s'"
@@ -2569,6 +2467,7 @@ ER_WRONG_PARAMETERS_TO_PROCEDURE
greek "Λάθος παÏάμετÏοι στην διαδικασία '%-.192s'"
hun "Rossz parameter a(z) '%-.192s' eljarasban"
ita "Parametri errati per la procedura '%-.192s'"
+ jpn "プロシージャ '%-.192s' ã¸ã®ãƒ‘ラメータãŒä¸æ­£ã§ã™ã€‚"
kor "'%-.192s' ìˆ˜í–‰ë¬¸ì— ëŒ€í•œ 부정확한 파ë¼ë©”í„°"
nor "Feil parametre til prosedyren %-.192s"
norwegian-ny "Feil parameter til prosedyra %-.192s"
@@ -2582,7 +2481,7 @@ ER_WRONG_PARAMETERS_TO_PROCEDURE
swe "Felaktiga parametrar till procedur %-.192s"
ukr "Хибний параметер процедури '%-.192s'"
ER_UNKNOWN_TABLE 42S02
- cze "Nezn-Bámá tabulka '%-.192s' v %-.32s"
+ cze "Neznámá tabulka '%-.192s' v %-.32s"
dan "Ukendt tabel '%-.192s' i %-.32s"
nla "Onbekende tabel '%-.192s' in %-.32s"
eng "Unknown table '%-.192s' in %-.32s"
@@ -2592,7 +2491,7 @@ ER_UNKNOWN_TABLE 42S02
greek "Αγνωστος πίνακας '%-.192s' σε %-.32s"
hun "Ismeretlen tabla: '%-.192s' %-.32s-ban"
ita "Tabella '%-.192s' sconosciuta in %-.32s"
- jpn "Unknown table '%-.192s' in %-.32s"
+ jpn "'%-.192s' 㯠%-.32s ã§ã¯ä¸æ˜Žãªè¡¨ã§ã™ã€‚"
kor "알수 없는 í…Œì´ë¸” '%-.192s' (ë°ì´íƒ€ë² ì´ìŠ¤ %-.32s)"
nor "Ukjent tabell '%-.192s' i %-.32s"
norwegian-ny "Ukjend tabell '%-.192s' i %-.32s"
@@ -2606,7 +2505,7 @@ ER_UNKNOWN_TABLE 42S02
swe "Okänd tabell '%-.192s' i '%-.32s'"
ukr "Ðевідома Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ñ '%-.192s' у %-.32s"
ER_FIELD_SPECIFIED_TWICE 42000
- cze "Polo-Bžka '%-.192s' je zadána dvakrát"
+ cze "Položka '%-.192s' je zadána dvakrát"
dan "Feltet '%-.192s' er anvendt to gange"
nla "Veld '%-.192s' is dubbel gespecificeerd"
eng "Column '%-.192s' specified twice"
@@ -2616,6 +2515,7 @@ ER_FIELD_SPECIFIED_TWICE 42000
greek "Το πεδίο '%-.192s' έχει οÏισθεί δÏο φοÏές"
hun "A(z) '%-.192s' mezot ketszer definialta"
ita "Campo '%-.192s' specificato 2 volte"
+ jpn "列 '%-.192s' ã¯2回指定ã•ã‚Œã¦ã„ã¾ã™ã€‚"
kor "칼럼 '%-.192s'는 ë‘번 ì •ì˜ë˜ì–´ 있ì니다."
nor "Feltet '%-.192s' er spesifisert to ganger"
norwegian-ny "Feltet '%-.192s' er spesifisert to gangar"
@@ -2629,7 +2529,7 @@ ER_FIELD_SPECIFIED_TWICE 42000
swe "Fält '%-.192s' är redan använt"
ukr "Стовбець '%-.192s' зазначено двічі"
ER_INVALID_GROUP_FUNC_USE
- cze "Nespr-Bávné použití funkce group"
+ cze "Nesprávné použití funkce group"
dan "Forkert brug af grupperings-funktion"
nla "Ongeldig gebruik van GROUP-functie"
eng "Invalid use of group function"
@@ -2639,6 +2539,7 @@ ER_INVALID_GROUP_FUNC_USE
greek "Εσφαλμένη χÏήση της group function"
hun "A group funkcio ervenytelen hasznalata"
ita "Uso non valido di una funzione di raggruppamento"
+ jpn "集計関数ã®ä½¿ç”¨æ–¹æ³•ãŒä¸æ­£ã§ã™ã€‚"
kor "ìž˜ëª»ëœ ê·¸ë£¹ 함수를 사용하였습니다."
por "Uso inválido de função de agrupamento (GROUP)"
rum "Folosire incorecta a functiei group"
@@ -2649,7 +2550,7 @@ ER_INVALID_GROUP_FUNC_USE
swe "Felaktig användning av SQL grupp function"
ukr "Хибне викориÑÑ‚Ð°Ð½Ð½Ñ Ñ„ÑƒÐ½ÐºÑ†Ñ–Ñ— групуваннÑ"
ER_UNSUPPORTED_EXTENSION 42000
- cze "Tabulka '%-.192s' pou-Bžívá rozšíření, které v této verzi MariaDB není"
+ cze "Tabulka '%-.192s' používá rozšíření, které v této verzi MySQL není"
dan "Tabellen '%-.192s' bruger et filtypenavn som ikke findes i denne MariaDB version"
nla "Tabel '%-.192s' gebruikt een extensie, die niet in deze MariaDB-versie voorkomt."
eng "Table '%-.192s' uses an extension that doesn't exist in this MariaDB version"
@@ -2659,6 +2560,7 @@ ER_UNSUPPORTED_EXTENSION 42000
greek "Ο πίνακς '%-.192s' χÏησιμοποιεί κάποιο extension που δεν υπάÏχει στην έκδοση αυτή της MariaDB"
hun "A(z) '%-.192s' tabla olyan bovitest hasznal, amely nem letezik ebben a MariaDB versioban."
ita "La tabella '%-.192s' usa un'estensione che non esiste in questa versione di MariaDB"
+ jpn "表 '%-.192s' ã¯ã€ã“ã®MySQLãƒãƒ¼ã‚¸ãƒ§ãƒ³ã«ã¯ç„¡ã„機能を使用ã—ã¦ã„ã¾ã™ã€‚"
kor "í…Œì´ë¸” '%-.192s'는 í™•ìž¥ëª…ë ¹ì„ ì´ìš©í•˜ì§€ë§Œ í˜„ìž¬ì˜ MariaDB 버젼ì—서는 존재하지 않습니다."
nor "Table '%-.192s' uses a extension that doesn't exist in this MariaDB version"
norwegian-ny "Table '%-.192s' uses a extension that doesn't exist in this MariaDB version"
@@ -2672,18 +2574,17 @@ ER_UNSUPPORTED_EXTENSION 42000
swe "Tabell '%-.192s' har en extension som inte finns i denna version av MariaDB"
ukr "Ð¢Ð°Ð±Ð»Ð¸Ñ†Ñ '%-.192s' викориÑтовує розширеннÑ, що не Ñ–Ñнує у цій верÑÑ–Ñ— MariaDB"
ER_TABLE_MUST_HAVE_COLUMNS 42000
- cze "Tabulka mus-Bí mít alespoň jeden sloupec"
+ cze "Tabulka musí mít alespoň jeden sloupec"
dan "En tabel skal have mindst een kolonne"
nla "Een tabel moet minstens 1 kolom bevatten"
eng "A table must have at least 1 column"
- jps "テーブルã¯æœ€ä½Ž 1 個㮠column ãŒå¿…è¦ã§ã™",
est "Tabelis peab olema vähemalt üks tulp"
fre "Une table doit comporter au moins une colonne"
ger "Eine Tabelle muss mindestens eine Spalte besitzen"
greek "Ενας πίνακας Ï€Ïέπει να έχει τουλάχιστον ένα πεδίο"
hun "A tablanak legalabb egy oszlopot tartalmazni kell"
ita "Una tabella deve avere almeno 1 colonna"
- jpn "テーブルã¯æœ€ä½Ž 1 個㮠column ãŒå¿…è¦ã§ã™"
+ jpn "表ã«ã¯æœ€ä½Žã§ã‚‚1個ã®åˆ—ãŒå¿…è¦ã§ã™ã€‚"
kor "í•˜ë‚˜ì˜ í…Œì´ë¸”ì—서는 ì ì–´ë„ í•˜ë‚˜ì˜ ì¹¼ëŸ¼ì´ ì¡´ìž¬í•˜ì—¬ì•¼ 합니다."
por "Uma tabela tem que ter pelo menos uma (1) coluna"
rum "O tabela trebuie sa aiba cel putin o coloana"
@@ -2694,18 +2595,17 @@ ER_TABLE_MUST_HAVE_COLUMNS 42000
swe "Tabeller måste ha minst 1 kolumn"
ukr "Ð¢Ð°Ð±Ð»Ð¸Ñ†Ñ Ð¿Ð¾Ð²Ð¸Ð½Ð½Ð° мати хочаб один Ñтовбець"
ER_RECORD_FILE_FULL
- cze "Tabulka '%-.192s' je pln-Bá"
+ cze "Tabulka '%-.192s' je plná"
dan "Tabellen '%-.192s' er fuld"
nla "De tabel '%-.192s' is vol"
eng "The table '%-.192s' is full"
- jps "table '%-.192s' ã¯ã„ã£ã±ã„ã§ã™",
est "Tabel '%-.192s' on täis"
fre "La table '%-.192s' est pleine"
ger "Tabelle '%-.192s' ist voll"
greek "Ο πίνακας '%-.192s' είναι γεμάτος"
hun "A '%-.192s' tabla megtelt"
ita "La tabella '%-.192s' e` piena"
- jpn "table '%-.192s' ã¯ã„ã£ã±ã„ã§ã™"
+ jpn "表 '%-.192s' ã¯æº€æ¯ã§ã™ã€‚"
kor "í…Œì´ë¸” '%-.192s'ê°€ full났습니다. "
por "Tabela '%-.192s' está cheia"
rum "Tabela '%-.192s' e plina"
@@ -2716,18 +2616,17 @@ ER_RECORD_FILE_FULL
swe "Tabellen '%-.192s' är full"
ukr "Ð¢Ð°Ð±Ð»Ð¸Ñ†Ñ '%-.192s' заповнена"
ER_UNKNOWN_CHARACTER_SET 42000
- cze "Nezn-Bámá znaková sada: '%-.64s'"
+ cze "Neznámá znaková sada: '%-.64s'"
dan "Ukendt tegnsæt: '%-.64s'"
nla "Onbekende character set: '%-.64s'"
eng "Unknown character set: '%-.64s'"
- jps "character set '%-.64s' ã¯ã‚µãƒãƒ¼ãƒˆã—ã¦ã„ã¾ã›ã‚“",
est "Vigane kooditabel '%-.64s'"
fre "Jeu de caractères inconnu: '%-.64s'"
ger "Unbekannter Zeichensatz: '%-.64s'"
greek "Αγνωστο character set: '%-.64s'"
hun "Ervenytelen karakterkeszlet: '%-.64s'"
ita "Set di caratteri '%-.64s' sconosciuto"
- jpn "character set '%-.64s' ã¯ã‚µãƒãƒ¼ãƒˆã—ã¦ã„ã¾ã›ã‚“"
+ jpn "ä¸æ˜Žãªæ–‡å­—コードセット: '%-.64s'"
kor "알수없는 언어 Set: '%-.64s'"
por "Conjunto de caracteres '%-.64s' desconhecido"
rum "Set de caractere invalid: '%-.64s'"
@@ -2738,18 +2637,17 @@ ER_UNKNOWN_CHARACTER_SET 42000
swe "Okänd teckenuppsättning: '%-.64s'"
ukr "Ðевідома кодова таблицÑ: '%-.64s'"
ER_TOO_MANY_TABLES
- cze "P-Bříliš mnoho tabulek, MariaDB jich může mít v joinu jen %d"
+ cze "Příliš mnoho tabulek, MySQL jich může mít v joinu jen %d"
dan "For mange tabeller. MariaDB kan kun bruge %d tabeller i et join"
nla "Teveel tabellen. MariaDB kan slechts %d tabellen in een join bevatten"
eng "Too many tables; MariaDB can only use %d tables in a join"
- jps "テーブルãŒå¤šã™ãŽã¾ã™; MariaDB can only use %d tables in a join",
est "Liiga palju tabeleid. MariaDB suudab JOINiga ühendada kuni %d tabelit"
fre "Trop de tables. MariaDB ne peut utiliser que %d tables dans un JOIN"
ger "Zu viele Tabellen. MariaDB kann in einem Join maximal %d Tabellen verwenden"
greek "Î Î¿Î»Ï Î¼ÎµÎ³Î¬Î»Î¿Ï‚ αÏιθμός πινάκων. Η MariaDB μποÏεί να χÏησιμοποιήσει %d πίνακες σε διαδικασία join"
hun "Tul sok tabla. A MariaDB csak %d tablat tud kezelni osszefuzeskor"
ita "Troppe tabelle. MariaDB puo` usare solo %d tabelle in una join"
- jpn "テーブルãŒå¤šã™ãŽã¾ã™; MariaDB can only use %d tables in a join"
+ jpn "表ãŒå¤šã™ãŽã¾ã™ã€‚MySQLãŒJOINã§ãる表㯠%d 個ã¾ã§ã§ã™ã€‚"
kor "너무 ë§Žì€ í…Œì´ë¸”ì´ Joinë˜ì—ˆìŠµë‹ˆë‹¤. MariaDBì—서는 JOINì‹œ %dê°œì˜ í…Œì´ë¸”만 사용할 수 있습니다."
por "Tabelas demais. O MariaDB pode usar somente %d tabelas em uma junção (JOIN)"
rum "Prea multe tabele. MariaDB nu poate folosi mai mult de %d tabele intr-un join"
@@ -2760,18 +2658,17 @@ ER_TOO_MANY_TABLES
swe "För många tabeller. MariaDB can ha högst %d tabeller i en och samma join"
ukr "Забагато таблиць. MariaDB може викориÑтовувати лише %d таблиць у об'єднанні"
ER_TOO_MANY_FIELDS
- cze "P-Bříliš mnoho položek"
+ cze "Příliš mnoho položek"
dan "For mange felter"
nla "Te veel velden"
eng "Too many columns"
- jps "column ãŒå¤šã™ãŽã¾ã™",
est "Liiga palju tulpasid"
fre "Trop de champs"
ger "Zu viele Felder"
greek "Î Î¿Î»Ï Î¼ÎµÎ³Î¬Î»Î¿Ï‚ αÏιθμός πεδίων"
hun "Tul sok mezo"
ita "Troppi campi"
- jpn "column ãŒå¤šã™ãŽã¾ã™"
+ jpn "列ãŒå¤šã™ãŽã¾ã™ã€‚"
kor "ì¹¼ëŸ¼ì´ ë„ˆë¬´ 많습니다."
por "Colunas demais"
rum "Prea multe coloane"
@@ -2782,18 +2679,17 @@ ER_TOO_MANY_FIELDS
swe "För många fält"
ukr "Забагато Ñтовбців"
ER_TOO_BIG_ROWSIZE 42000
- cze "-BŘádek je příliÅ¡ velký. Maximální velikost řádku, nepoÄítaje položky blob, je %ld. Musíte zmÄ›nit nÄ›které položky na blob"
+ cze "Řádek je příliÅ¡ velký. Maximální velikost řádku, nepoÄítaje položky blob, je %ld. Musíte zmÄ›nit nÄ›které položky na blob"
dan "For store poster. Max post størrelse, uden BLOB's, er %ld. Du må lave nogle felter til BLOB's"
nla "Rij-grootte is groter dan toegestaan. Maximale rij grootte, blobs niet meegeteld, is %ld. U dient sommige velden in blobs te veranderen."
- eng "Row size too large. The maximum row size for the used table type, not counting BLOBs, is %ld. You have to change some columns to TEXT or BLOBs"
- jps "row size ãŒå¤§ãã™ãŽã¾ã™. BLOB ã‚’å«ã¾ãªã„å ´åˆã® row size ã®æœ€å¤§ã¯ %ld ã§ã™. ã„ãã¤ã‹ã® field ã‚’ BLOB ã«å¤‰ãˆã¦ãã ã•ã„.",
+ eng "Row size too large. The maximum row size for the used table type, not counting BLOBs, is %ld. This includes storage overhead, check the manual. You have to change some columns to TEXT or BLOBs"
est "Liiga pikk kirje. Kirje maksimumpikkus arvestamata BLOB-tüüpi välju on %ld. Muuda mõned väljad BLOB-tüüpi väljadeks"
fre "Ligne trop grande. Le taille maximale d'une ligne, sauf les BLOBs, est %ld. Changez le type de quelques colonnes en BLOB"
ger "Zeilenlänge zu groß. Die maximale Zeilenlänge für den verwendeten Tabellentyp (ohne BLOB-Felder) beträgt %ld. Einige Felder müssen in BLOB oder TEXT umgewandelt werden"
greek "Î Î¿Î»Ï Î¼ÎµÎ³Î¬Î»Î¿ μέγεθος εγγÏαφής. Το μέγιστο μέγεθος εγγÏαφής, χωÏίς να υπολογίζονται τα blobs, είναι %ld. ΠÏέπει να οÏίσετε κάποια πεδία σαν blobs"
hun "Tul nagy sormeret. A maximalis sormeret (nem szamolva a blob objektumokat) %ld. Nehany mezot meg kell valtoztatnia"
ita "Riga troppo grande. La massima grandezza di una riga, non contando i BLOB, e` %ld. Devi cambiare alcuni campi in BLOB"
- jpn "row size ãŒå¤§ãã™ãŽã¾ã™. BLOB ã‚’å«ã¾ãªã„å ´åˆã® row size ã®æœ€å¤§ã¯ %ld ã§ã™. ã„ãã¤ã‹ã® field ã‚’ BLOB ã«å¤‰ãˆã¦ãã ã•ã„."
+ jpn "行サイズãŒå¤§ãã™ãŽã¾ã™ã€‚ã“ã®è¡¨ã®æœ€å¤§è¡Œã‚µã‚¤ã‚ºã¯ BLOB ã‚’å«ã¾ãšã« %ld ã§ã™ã€‚æ ¼ç´æ™‚ã®ã‚ªãƒ¼ãƒãƒ¼ãƒ˜ãƒƒãƒ‰ã‚‚å«ã¾ã‚Œã¾ã™(マニュアルを確èªã—ã¦ãã ã•ã„)。列をTEXTã¾ãŸã¯BLOBã«å¤‰æ›´ã™ã‚‹å¿…è¦ãŒã‚ã‚Šã¾ã™ã€‚"
kor "너무 í° row 사ì´ì¦ˆìž…니다. BLOB를 계산하지 ì•Šê³  최대 row 사ì´ì¦ˆëŠ” %ld입니다. ì–¼ë§ˆê°„ì˜ í•„ë“œë“¤ì„ BLOBë¡œ 바꾸셔야 ê² êµ°ìš”.."
por "Tamanho de linha grande demais. O máximo tamanho de linha, não contando BLOBs, é %ld. Você tem que mudar alguns campos para BLOBs"
rum "Marimea liniei (row) prea mare. Marimea maxima a liniei, excluzind BLOB-urile este de %ld. Trebuie sa schimbati unele cimpuri in BLOB-uri"
@@ -2804,17 +2700,16 @@ ER_TOO_BIG_ROWSIZE 42000
swe "För stor total radlängd. Den högst tillåtna radlängden, förutom BLOBs, är %ld. Ändra några av dina fält till BLOB"
ukr "Задовга Ñтрока. Ðайбільшою довжиною Ñтроки, не рахуючи BLOB, Ñ” %ld. Вам потрібно привеÑти деÑкі Ñтовбці до типу BLOB"
ER_STACK_OVERRUN
- cze "P-BÅ™eteÄení zásobníku threadu: použito %ld z %ld. Použijte 'mysqld --thread_stack=#' k zadání vÄ›tšího zásobníku"
+ cze "PÅ™eteÄení zásobníku threadu: použito %ld z %ld. Použijte 'mysqld --thread_stack=#' k zadání vÄ›tšího zásobníku"
dan "Thread stack brugt: Brugt: %ld af en %ld stak. Brug 'mysqld --thread_stack=#' for at allokere en større stak om nødvendigt"
nla "Thread stapel overrun: Gebruikte: %ld van een %ld stack. Gebruik 'mysqld --thread_stack=#' om een grotere stapel te definieren (indien noodzakelijk)."
eng "Thread stack overrun: Used: %ld of a %ld stack. Use 'mysqld --thread_stack=#' to specify a bigger stack if needed"
- jps "Thread stack overrun: Used: %ld of a %ld stack. スタック領域を多ãã¨ã‚ŠãŸã„å ´åˆã€'mysqld --thread_stack=#' ã¨æŒ‡å®šã—ã¦ãã ã•ã„",
fre "Débordement de la pile des tâches (Thread stack). Utilisées: %ld pour une pile de %ld. Essayez 'mysqld --thread_stack=#' pour indiquer une plus grande valeur"
ger "Thread-Stack-Überlauf. Benutzt: %ld von %ld Stack. 'mysqld --thread_stack=#' verwenden, um bei Bedarf einen größeren Stack anzulegen"
greek "Stack overrun στο thread: Used: %ld of a %ld stack. ΠαÏακαλώ χÏησιμοποιείστε 'mysqld --thread_stack=#' για να οÏίσετε ένα μεγαλÏτεÏο stack αν χÏειάζεται"
hun "Thread verem tullepes: Used: %ld of a %ld stack. Hasznalja a 'mysqld --thread_stack=#' nagyobb verem definialasahoz"
ita "Thread stack overrun: Usati: %ld di uno stack di %ld. Usa 'mysqld --thread_stack=#' per specificare uno stack piu` grande."
- jpn "Thread stack overrun: Used: %ld of a %ld stack. スタック領域を多ãã¨ã‚ŠãŸã„å ´åˆã€'mysqld --thread_stack=#' ã¨æŒ‡å®šã—ã¦ãã ã•ã„"
+ jpn "スレッドスタックä¸è¶³ã§ã™(使用: %ld ; サイズ: %ld)。必è¦ã«å¿œã˜ã¦ã€ã‚ˆã‚Šå¤§ãã„値㧠'mysqld --thread_stack=#' ã®æŒ‡å®šã‚’ã—ã¦ãã ã•ã„。"
kor "쓰레드 스íƒì´ 넘쳤습니다. 사용: %ldê°œ 스íƒ: %ldê°œ. 만약 필요시 ë”í° ìŠ¤íƒì„ ì›í• ë•Œì—는 'mysqld --thread_stack=#' 를 ì •ì˜í•˜ì„¸ìš”"
por "Estouro da pilha do 'thread'. Usados %ld de uma pilha de %ld. Use 'mysqld --thread_stack=#' para especificar uma pilha maior, se necessário"
rum "Stack-ul thread-ului a fost depasit (prea mic): Folositi: %ld intr-un stack de %ld. Folositi 'mysqld --thread_stack=#' ca sa specifici un stack mai mare"
@@ -2825,7 +2720,7 @@ ER_STACK_OVERRUN
swe "Trådstacken tog slut: Har använt %ld av %ld bytes. Använd 'mysqld --thread_stack=#' ifall du behöver en större stack"
ukr "Стек гілок переповнено: ВикориÑтано: %ld з %ld. ВикориÑтовуйте 'mysqld --thread_stack=#' аби зазначити більший Ñтек, Ñкщо необхідно"
ER_WRONG_OUTER_JOIN 42000
- cze "V OUTER JOIN byl nalezen k-Břížový odkaz. Prověřte ON podmínky"
+ cze "V OUTER JOIN byl nalezen křížový odkaz. Prověřte ON podmínky"
dan "Krydsreferencer fundet i OUTER JOIN; check dine ON conditions"
nla "Gekruiste afhankelijkheid gevonden in OUTER JOIN. Controleer uw ON-conditions"
eng "Cross dependency found in OUTER JOIN; examine your ON conditions"
@@ -2835,6 +2730,7 @@ ER_WRONG_OUTER_JOIN 42000
greek "Cross dependency βÏέθηκε σε OUTER JOIN. ΠαÏακαλώ εξετάστε τις συνθήκες που θέσατε στο ON"
hun "Keresztfuggoseg van az OUTER JOIN-ban. Ellenorizze az ON felteteleket"
ita "Trovata una dipendenza incrociata nella OUTER JOIN. Controlla le condizioni ON"
+ jpn "OUTER JOINã«ç›¸äº’ä¾å­˜ãŒè¦‹ã¤ã‹ã‚Šã¾ã—ãŸã€‚ONå¥ã®æ¡ä»¶ã‚’確èªã—ã¦ä¸‹ã•ã„。"
por "Dependência cruzada encontrada em junção externa (OUTER JOIN); examine as condições utilizadas nas cláusulas 'ON'"
rum "Dependinta incrucisata (cross dependency) gasita in OUTER JOIN. Examinati conditiile ON"
rus "Ð’ OUTER JOIN обнаружена перекреÑÑ‚Ð½Ð°Ñ Ð·Ð°Ð²Ð¸ÑимоÑÑ‚ÑŒ. Внимательно проанализируйте Ñвои уÑÐ»Ð¾Ð²Ð¸Ñ ON"
@@ -2847,18 +2743,17 @@ ER_NULL_COLUMN_IN_INDEX 42000
eng "Table handler doesn't support NULL in given index. Please change column '%-.192s' to be NOT NULL or use another handler"
swe "Tabell hanteraren kan inte indexera NULL kolumner för den givna index typen. Ändra '%-.192s' till NOT NULL eller använd en annan hanterare"
ER_CANT_FIND_UDF
- cze "Nemohu na-BÄíst funkci '%-.192s'"
+ cze "Nemohu naÄíst funkci '%-.192s'"
dan "Kan ikke læse funktionen '%-.192s'"
nla "Kan functie '%-.192s' niet laden"
eng "Can't load function '%-.192s'"
- jps "function '%-.192s' ã‚’ ロードã§ãã¾ã›ã‚“",
est "Ei suuda avada funktsiooni '%-.192s'"
fre "Imposible de charger la fonction '%-.192s'"
ger "Kann Funktion '%-.192s' nicht laden"
greek "Δεν είναι δυνατή η διαδικασία load για τη συνάÏτηση '%-.192s'"
hun "A(z) '%-.192s' fuggveny nem toltheto be"
ita "Impossibile caricare la funzione '%-.192s'"
- jpn "function '%-.192s' ã‚’ ロードã§ãã¾ã›ã‚“"
+ jpn "関数 '%-.192s' をロードã§ãã¾ã›ã‚“。"
kor "'%-.192s' 함수를 로드하지 못했습니다."
por "Não pode carregar a função '%-.192s'"
rum "Nu pot incarca functia '%-.192s'"
@@ -2873,14 +2768,13 @@ ER_CANT_INITIALIZE_UDF
dan "Kan ikke starte funktionen '%-.192s'; %-.80s"
nla "Kan functie '%-.192s' niet initialiseren; %-.80s"
eng "Can't initialize function '%-.192s'; %-.80s"
- jps "function '%-.192s' ã‚’åˆæœŸåŒ–ã§ãã¾ã›ã‚“; %-.80s",
est "Ei suuda algväärtustada funktsiooni '%-.192s'; %-.80s"
fre "Impossible d'initialiser la fonction '%-.192s'; %-.80s"
ger "Kann Funktion '%-.192s' nicht initialisieren: %-.80s"
greek "Δεν είναι δυνατή η έναÏξη της συνάÏτησης '%-.192s'; %-.80s"
hun "A(z) '%-.192s' fuggveny nem inicializalhato; %-.80s"
ita "Impossibile inizializzare la funzione '%-.192s'; %-.80s"
- jpn "function '%-.192s' ã‚’åˆæœŸåŒ–ã§ãã¾ã›ã‚“; %-.80s"
+ jpn "関数 '%-.192s' ã‚’åˆæœŸåŒ–ã§ãã¾ã›ã‚“。; %-.80s"
kor "'%-.192s' 함수를 초기화 하지 못했습니다.; %-.80s"
por "Não pode inicializar a função '%-.192s' - '%-.80s'"
rum "Nu pot initializa functia '%-.192s'; %-.80s"
@@ -2891,18 +2785,17 @@ ER_CANT_INITIALIZE_UDF
swe "Kan inte initialisera funktionen '%-.192s'; '%-.80s'"
ukr "Ðе можу ініціалізувати функцію '%-.192s'; %-.80s"
ER_UDF_NO_PATHS
- cze "Pro sd-Bílenou knihovnu nejsou povoleny cesty"
+ cze "Pro sdílenou knihovnu nejsou povoleny cesty"
dan "Angivelse af sti ikke tilladt for delt bibliotek"
nla "Geen pad toegestaan voor shared library"
eng "No paths allowed for shared library"
- jps "shared library ã¸ã®ãƒ‘スãŒé€šã£ã¦ã„ã¾ã›ã‚“",
est "Teegi nimes ei tohi olla kataloogi"
fre "Chemin interdit pour les bibliothèques partagées"
ger "Keine Pfade gestattet für Shared Library"
greek "Δεν βÏέθηκαν paths για την shared library"
hun "Nincs ut a megosztott konyvtarakhoz (shared library)"
ita "Non sono ammessi path per le librerie condivisa"
- jpn "shared library ã¸ã®ãƒ‘スãŒé€šã£ã¦ã„ã¾ã›ã‚“"
+ jpn "共有ライブラリã«ã¯ãƒ‘スを指定ã§ãã¾ã›ã‚“。"
kor "공유 ë¼ì´ë²„러리를 위한 패스가 ì •ì˜ë˜ì–´ 있지 않습니다."
por "Não há caminhos (paths) permitidos para biblioteca compartilhada"
rum "Nici un paths nu e permis pentru o librarie shared"
@@ -2913,18 +2806,17 @@ ER_UDF_NO_PATHS
swe "Man får inte ange sökväg för dynamiska bibliotek"
ukr "Ðе дозволено викориÑтовувати путі Ð´Ð»Ñ Ñ€Ð¾Ð·Ð´Ñ–Ð»ÑŽÐ²Ð°Ð½Ð¸Ñ… бібліотек"
ER_UDF_EXISTS
- cze "Funkce '%-.192s' ji-Bž existuje"
+ cze "Funkce '%-.192s' již existuje"
dan "Funktionen '%-.192s' findes allerede"
nla "Functie '%-.192s' bestaat reeds"
eng "Function '%-.192s' already exists"
- jps "Function '%-.192s' ã¯æ—¢ã«å®šç¾©ã•ã‚Œã¦ã„ã¾ã™",
est "Funktsioon '%-.192s' juba eksisteerib"
fre "La fonction '%-.192s' existe déjà"
ger "Funktion '%-.192s' existiert schon"
greek "Η συνάÏτηση '%-.192s' υπάÏχει ήδη"
hun "A '%-.192s' fuggveny mar letezik"
ita "La funzione '%-.192s' esiste gia`"
- jpn "Function '%-.192s' ã¯æ—¢ã«å®šç¾©ã•ã‚Œã¦ã„ã¾ã™"
+ jpn "関数 '%-.192s' ã¯ã™ã§ã«å®šç¾©ã•ã‚Œã¦ã„ã¾ã™ã€‚"
kor "'%-.192s' 함수는 ì´ë¯¸ 존재합니다."
por "Função '%-.192s' já existe"
rum "Functia '%-.192s' exista deja"
@@ -2935,18 +2827,17 @@ ER_UDF_EXISTS
swe "Funktionen '%-.192s' finns redan"
ukr "Ð¤ÑƒÐ½ÐºÑ†Ñ–Ñ '%-.192s' вже Ñ–Ñнує"
ER_CANT_OPEN_LIBRARY
- cze "Nemohu otev-Břít sdílenou knihovnu '%-.192s' (errno: %d, %-.128s)"
+ cze "Nemohu otevřít sdílenou knihovnu '%-.192s' (errno: %d, %-.128s)"
dan "Kan ikke åbne delt bibliotek '%-.192s' (errno: %d, %-.128s)"
nla "Kan shared library '%-.192s' niet openen (Errcode: %d, %-.128s)"
eng "Can't open shared library '%-.192s' (errno: %d, %-.128s)"
- jps "shared library '%-.192s' ã‚’é–‹ã事ãŒã§ãã¾ã›ã‚“ (errno: %d, %-.128s)",
est "Ei suuda avada jagatud teeki '%-.192s' (veakood: %d, %-.128s)"
fre "Impossible d'ouvrir la bibliothèque partagée '%-.192s' (errno: %d, %-.128s)"
ger "Kann Shared Library '%-.192s' nicht öffnen (Fehler: %d, %-.128s)"
greek "Δεν είναι δυνατή η ανάγνωση της shared library '%-.192s' (κωδικός λάθους: %d, %-.128s)"
hun "A(z) '%-.192s' megosztott konyvtar nem hasznalhato (hibakod: %d, %-.128s)"
ita "Impossibile aprire la libreria condivisa '%-.192s' (errno: %d, %-.128s)"
- jpn "shared library '%-.192s' ã‚’é–‹ã事ãŒã§ãã¾ã›ã‚“ (errno: %d, %-.128s)"
+ jpn "共有ライブラリ '%-.192s' ã‚’é–‹ã事ãŒã§ãã¾ã›ã‚“。(エラー番å·: %d, %-.128s)"
kor "'%-.192s' 공유 ë¼ì´ë²„러리를 열수 없습니다.(ì—러번호: %d, %-.128s)"
nor "Can't open shared library '%-.192s' (errno: %d, %-.128s)"
norwegian-ny "Can't open shared library '%-.192s' (errno: %d, %-.128s)"
@@ -2960,18 +2851,17 @@ ER_CANT_OPEN_LIBRARY
swe "Kan inte öppna det dynamiska biblioteket '%-.192s' (Felkod: %d, %-.128s)"
ukr "Ðе можу відкрити розділювану бібліотеку '%-.192s' (помилка: %d, %-.128s)"
ER_CANT_FIND_DL_ENTRY
- cze "Nemohu naj-Bít funkci '%-.128s' v knihovně"
+ cze "Nemohu najít funkci '%-.128s' v knihovně"
dan "Kan ikke finde funktionen '%-.128s' i bibliotek"
nla "Kan functie '%-.128s' niet in library vinden"
eng "Can't find symbol '%-.128s' in library"
- jps "function '%-.128s' をライブラリー中ã«è¦‹ä»˜ã‘る事ãŒã§ãã¾ã›ã‚“",
est "Ei leia funktsiooni '%-.128s' antud teegis"
fre "Impossible de trouver la fonction '%-.128s' dans la bibliothèque"
ger "Kann Funktion '%-.128s' in der Library nicht finden"
greek "Δεν είναι δυνατή η ανεÏÏεση της συνάÏτησης '%-.128s' στην βιβλιοθήκη"
hun "A(z) '%-.128s' fuggveny nem talalhato a konyvtarban"
ita "Impossibile trovare la funzione '%-.128s' nella libreria"
- jpn "function '%-.128s' をライブラリー中ã«è¦‹ä»˜ã‘る事ãŒã§ãã¾ã›ã‚“"
+ jpn "関数 '%-.128s' ã¯å…±æœ‰ãƒ©ã‚¤ãƒ–ラリー中ã«ã‚ã‚Šã¾ã›ã‚“。"
kor "ë¼ì´ë²„러리ì—ì„œ '%-.128s' 함수를 ì°¾ì„ ìˆ˜ 없습니다."
por "Não pode encontrar a função '%-.128s' na biblioteca"
rum "Nu pot gasi functia '%-.128s' in libraria"
@@ -2982,18 +2872,17 @@ ER_CANT_FIND_DL_ENTRY
swe "Hittar inte funktionen '%-.128s' in det dynamiska biblioteket"
ukr "Ðе можу знайти функцію '%-.128s' у бібліотеці"
ER_FUNCTION_NOT_DEFINED
- cze "Funkce '%-.192s' nen-Bí definována"
+ cze "Funkce '%-.192s' není definována"
dan "Funktionen '%-.192s' er ikke defineret"
nla "Functie '%-.192s' is niet gedefinieerd"
eng "Function '%-.192s' is not defined"
- jps "Function '%-.192s' ã¯å®šç¾©ã•ã‚Œã¦ã„ã¾ã›ã‚“",
est "Funktsioon '%-.192s' ei ole defineeritud"
fre "La fonction '%-.192s' n'est pas définie"
ger "Funktion '%-.192s' ist nicht definiert"
greek "Η συνάÏτηση '%-.192s' δεν έχει οÏισθεί"
hun "A '%-.192s' fuggveny nem definialt"
ita "La funzione '%-.192s' non e` definita"
- jpn "Function '%-.192s' ã¯å®šç¾©ã•ã‚Œã¦ã„ã¾ã›ã‚“"
+ jpn "関数 '%-.192s' ã¯å®šç¾©ã•ã‚Œã¦ã„ã¾ã›ã‚“。"
kor "'%-.192s' 함수가 ì •ì˜ë˜ì–´ 있지 않습니다."
por "Função '%-.192s' não está definida"
rum "Functia '%-.192s' nu e definita"
@@ -3004,18 +2893,17 @@ ER_FUNCTION_NOT_DEFINED
swe "Funktionen '%-.192s' är inte definierad"
ukr "Функцію '%-.192s' не визначено"
ER_HOST_IS_BLOCKED
- cze "Stroj '%-.64s' je zablokov-Bán kvůli mnoha chybám při připojování. Odblokujete použitím 'mysqladmin flush-hosts'"
+ cze "Stroj '%-.64s' je zablokován kvůli mnoha chybám při připojování. Odblokujete použitím 'mysqladmin flush-hosts'"
dan "Værten '%-.64s' er blokeret på grund af mange fejlforespørgsler. Lås op med 'mysqladmin flush-hosts'"
nla "Host '%-.64s' is geblokkeeerd vanwege te veel verbindings fouten. Deblokkeer met 'mysqladmin flush-hosts'"
eng "Host '%-.64s' is blocked because of many connection errors; unblock with 'mysqladmin flush-hosts'"
- jps "Host '%-.64s' 㯠many connection error ã®ãŸã‚ã€æ‹’å¦ã•ã‚Œã¾ã—ãŸ. 'mysqladmin flush-hosts' ã§è§£é™¤ã—ã¦ãã ã•ã„",
est "Masin '%-.64s' on blokeeritud hulgaliste ühendusvigade tõttu. Blokeeringu saab tühistada 'mysqladmin flush-hosts' käsuga"
fre "L'hôte '%-.64s' est bloqué à cause d'un trop grand nombre d'erreur de connexion. Débloquer le par 'mysqladmin flush-hosts'"
ger "Host '%-.64s' blockiert wegen zu vieler Verbindungsfehler. Aufheben der Blockierung mit 'mysqladmin flush-hosts'"
greek "Ο υπολογιστής '%-.64s' έχει αποκλεισθεί λόγω πολλαπλών λαθών σÏνδεσης. ΠÏοσπαθήστε να διοÏώσετε με 'mysqladmin flush-hosts'"
hun "A '%-.64s' host blokkolodott, tul sok kapcsolodasi hiba miatt. Hasznalja a 'mysqladmin flush-hosts' parancsot"
ita "Sistema '%-.64s' bloccato a causa di troppi errori di connessione. Per sbloccarlo: 'mysqladmin flush-hosts'"
- jpn "Host '%-.64s' 㯠many connection error ã®ãŸã‚ã€æ‹’å¦ã•ã‚Œã¾ã—ãŸ. 'mysqladmin flush-hosts' ã§è§£é™¤ã—ã¦ãã ã•ã„"
+ jpn "接続エラーãŒå¤šã„ãŸã‚ã€ãƒ›ã‚¹ãƒˆ '%-.64s' ã¯æ‹’å¦ã•ã‚Œã¾ã—ãŸã€‚'mysqladmin flush-hosts' ã§è§£é™¤ã§ãã¾ã™ã€‚"
kor "너무 ë§Žì€ ì—°ê²°ì˜¤ë¥˜ë¡œ ì¸í•˜ì—¬ 호스트 '%-.64s'는 블ë½ë˜ì—ˆìŠµë‹ˆë‹¤. 'mysqladmin flush-hosts'를 ì´ìš©í•˜ì—¬ 블ë½ì„ 해제하세요"
por "'Host' '%-.64s' está bloqueado devido a muitos erros de conexão. Desbloqueie com 'mysqladmin flush-hosts'"
rum "Host-ul '%-.64s' e blocat din cauza multelor erori de conectie. Poti deploca folosind 'mysqladmin flush-hosts'"
@@ -3025,18 +2913,17 @@ ER_HOST_IS_BLOCKED
swe "Denna dator, '%-.64s', är blockerad pga många felaktig paket. Gör 'mysqladmin flush-hosts' för att ta bort alla blockeringarna"
ukr "ХоÑÑ‚ '%-.64s' заблоковано з причини великої кількоÑÑ‚Ñ– помилок з'єднаннÑ. Ð”Ð»Ñ Ñ€Ð¾Ð·Ð±Ð»Ð¾ÐºÑƒÐ²Ð°Ð½Ð½Ñ Ð²Ð¸ÐºÐ¾Ñ€Ð¸Ñтовуйте 'mysqladmin flush-hosts'"
ER_HOST_NOT_PRIVILEGED
- cze "Stroj '%-.64s' nem-Bá povoleno se k tomuto MariaDB serveru připojit"
+ cze "Stroj '%-.64s' nemá povoleno se k tomuto MySQL serveru připojit"
dan "Værten '%-.64s' kan ikke tilkoble denne MariaDB-server"
nla "Het is host '%-.64s' is niet toegestaan verbinding te maken met deze MariaDB server"
eng "Host '%-.64s' is not allowed to connect to this MariaDB server"
- jps "Host '%-.64s' 㯠MariaDB server ã«æŽ¥ç¶šã‚’許å¯ã•ã‚Œã¦ã„ã¾ã›ã‚“",
est "Masinal '%-.64s' puudub ligipääs sellele MariaDB serverile"
fre "Le hôte '%-.64s' n'est pas authorisé à se connecter à ce serveur MariaDB"
ger "Host '%-.64s' hat keine Berechtigung, sich mit diesem MariaDB-Server zu verbinden"
greek "Ο υπολογιστής '%-.64s' δεν έχει δικαίωμα σÏνδεσης με τον MariaDB server"
hun "A '%-.64s' host szamara nem engedelyezett a kapcsolodas ehhez a MariaDB szerverhez"
ita "Al sistema '%-.64s' non e` consentita la connessione a questo server MariaDB"
- jpn "Host '%-.64s' 㯠MariaDB server ã«æŽ¥ç¶šã‚’許å¯ã•ã‚Œã¦ã„ã¾ã›ã‚“"
+ jpn "ホスト '%-.64s' ã‹ã‚‰ã®ã“ã® MySQL server ã¸ã®æŽ¥ç¶šã¯è¨±å¯ã•ã‚Œã¦ã„ã¾ã›ã‚“。"
kor "'%-.64s' 호스트는 ì´ MariaDBì„œë²„ì— ì ‘ì†í•  허가를 받지 못했습니다."
por "'Host' '%-.64s' não tem permissão para se conectar com este servidor MariaDB"
rum "Host-ul '%-.64s' nu este permis a se conecta la aceste server MariaDB"
@@ -3046,18 +2933,17 @@ ER_HOST_NOT_PRIVILEGED
swe "Denna dator, '%-.64s', har inte privileger att använda denna MariaDB server"
ukr "ХоÑту '%-.64s' не доволено зв'ÑзуватиÑÑŒ з цим Ñервером MariaDB"
ER_PASSWORD_ANONYMOUS_USER 42000
- cze "Pou-Bžíváte MariaDB jako anonymní uživatel a anonymní uživatelé nemají povoleno měnit hesla"
+ cze "Používáte MySQL jako anonymní uživatel a anonymní uživatelé nemají povoleno měnit hesla"
dan "Du bruger MariaDB som anonym bruger. Anonyme brugere må ikke ændre adgangskoder"
nla "U gebruikt MariaDB als anonieme gebruiker en deze mogen geen wachtwoorden wijzigen"
eng "You are using MariaDB as an anonymous user and anonymous users are not allowed to change passwords"
- jps "MariaDB ã‚’ anonymous users ã§ä½¿ç”¨ã—ã¦ã„る状態ã§ã¯ã€ãƒ‘スワードã®å¤‰æ›´ã¯ã§ãã¾ã›ã‚“",
est "Te kasutate MariaDB-i anonüümse kasutajana, kelledel pole parooli muutmise õigust"
fre "Vous utilisez un utilisateur anonyme et les utilisateurs anonymes ne sont pas autorisés à changer les mots de passe"
ger "Sie benutzen MariaDB als anonymer Benutzer und dürfen daher keine Passwörter ändern"
greek "ΧÏησιμοποιείτε την MariaDB σαν anonymous user και έτσι δεν μποÏείτε να αλλάξετε τα passwords άλλων χÏηστών"
hun "Nevtelen (anonymous) felhasznalokent nem negedelyezett a jelszovaltoztatas"
ita "Impossibile cambiare la password usando MariaDB come utente anonimo"
- jpn "MariaDB ã‚’ anonymous users ã§ä½¿ç”¨ã—ã¦ã„る状態ã§ã¯ã€ãƒ‘スワードã®å¤‰æ›´ã¯ã§ãã¾ã›ã‚“"
+ jpn "MySQL を匿åユーザーã§ä½¿ç”¨ã—ã¦ã„ã‚‹ã®ã§ã€ãƒ‘スワードã®å¤‰æ›´ã¯ã§ãã¾ã›ã‚“。"
kor "ë‹¹ì‹ ì€ MariaDBì„œë²„ì— ìµëª…ì˜ ì‚¬ìš©ìžë¡œ ì ‘ì†ì„ 하셨습니다.ìµëª…ì˜ ì‚¬ìš©ìžëŠ” 암호를 변경할 수 없습니다."
por "Você está usando o MariaDB como usuário anônimo e usuários anônimos não têm permissão para mudar senhas"
rum "Dumneavoastra folositi MariaDB ca un utilizator anonim si utilizatorii anonimi nu au voie sa schime parolele"
@@ -3067,18 +2953,17 @@ ER_PASSWORD_ANONYMOUS_USER 42000
swe "Du använder MariaDB som en anonym användare och som sådan får du inte ändra ditt lösenord"
ukr "Ви викориÑтовуєте MariaDB Ñк анонімний кориÑтувач, тому вам не дозволено змінювати паролі"
ER_PASSWORD_NOT_ALLOWED 42000
- cze "Na zm-Běnu hesel ostatním musíte mít právo provést update tabulek v databázi mysql"
+ cze "Na změnu hesel ostatním musíte mít právo provést update tabulek v databázi mysql"
dan "Du skal have tilladelse til at opdatere tabeller i MariaDB databasen for at ændre andres adgangskoder"
nla "U moet tabel update priveleges hebben in de mysql database om wachtwoorden voor anderen te mogen wijzigen"
eng "You must have privileges to update tables in the mysql database to be able to change passwords for others"
- jps "ä»–ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®ãƒ‘スワードを変更ã™ã‚‹ãŸã‚ã«ã¯, mysql データベースã«å¯¾ã—㦠update ã®è¨±å¯ãŒãªã‘ã‚Œã°ãªã‚Šã¾ã›ã‚“.",
est "Teiste paroolide muutmiseks on nõutav tabelite muutmisõigus 'mysql' andmebaasis"
fre "Vous devez avoir le privilège update sur les tables de la base de donnée mysql pour pouvoir changer les mots de passe des autres"
ger "Sie benötigen die Berechtigung zum Aktualisieren von Tabellen in der Datenbank 'mysql', um die Passwörter anderer Benutzer ändern zu können"
greek "ΠÏέπει να έχετε δικαίωμα διόÏθωσης πινάκων (update) στη βάση δεδομένων mysql για να μποÏείτε να αλλάξετε τα passwords άλλων χÏηστών"
hun "Onnek tabla-update joggal kell rendelkeznie a mysql adatbazisban masok jelszavanak megvaltoztatasahoz"
ita "E` necessario il privilegio di update sulle tabelle del database mysql per cambiare le password per gli altri utenti"
- jpn "ä»–ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®ãƒ‘スワードを変更ã™ã‚‹ãŸã‚ã«ã¯, mysql データベースã«å¯¾ã—㦠update ã®è¨±å¯ãŒãªã‘ã‚Œã°ãªã‚Šã¾ã›ã‚“."
+ jpn "ä»–ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®ãƒ‘スワードを変更ã™ã‚‹ãŸã‚ã«ã¯ã€mysqlデータベースã®è¡¨ã‚’æ›´æ–°ã™ã‚‹æ¨©é™ãŒå¿…è¦ã§ã™ã€‚"
kor "ë‹¹ì‹ ì€ ë‹¤ë¥¸ì‚¬ìš©ìžë“¤ì˜ 암호를 변경할 수 있ë„ë¡ ë°ì´íƒ€ë² ì´ìŠ¤ ë³€ê²½ê¶Œí•œì„ ê°€ì ¸ì•¼ 합니다."
por "Você deve ter privilégios para atualizar tabelas no banco de dados mysql para ser capaz de mudar a senha de outros"
rum "Trebuie sa aveti privilegii sa actualizati tabelele in bazele de date mysql ca sa puteti sa schimati parolele altora"
@@ -3087,8 +2972,8 @@ ER_PASSWORD_NOT_ALLOWED 42000
spa "Tu debes de tener permiso para actualizar tablas en la base de datos mysql para cambiar las claves para otros"
swe "För att ändra lösenord för andra måste du ha rättigheter att uppdatera mysql-databasen"
ukr "Ви повині мати право на Ð¾Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†ÑŒ у базі данних mysql, аби мати можливіÑÑ‚ÑŒ змінювати пароль іншим"
-ER_PASSWORD_NO_MATCH 42000
- cze "V tabulce user nen-Bí žádný odpovídající řádek"
+ER_PASSWORD_NO_MATCH 28000
+ cze "V tabulce user není žádný odpovídající řádek"
dan "Kan ikke finde nogen tilsvarende poster i bruger tabellen"
nla "Kan geen enkele passende rij vinden in de gebruikers tabel"
eng "Can't find any matching row in the user table"
@@ -3098,6 +2983,7 @@ ER_PASSWORD_NO_MATCH 42000
greek "Δεν είναι δυνατή η ανεÏÏεση της αντίστοιχης εγγÏαφής στον πίνακα των χÏηστών"
hun "Nincs megegyezo sor a user tablaban"
ita "Impossibile trovare la riga corrispondente nella tabella user"
+ jpn "ユーザーテーブルã«è©²å½“ã™ã‚‹ãƒ¬ã‚³ãƒ¼ãƒ‰ãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“。"
kor "ì‚¬ìš©ìž í…Œì´ë¸”ì—ì„œ ì¼ì¹˜í•˜ëŠ” ê²ƒì„ ì°¾ì„ ìˆ˜ ì—†ì니다."
por "Não pode encontrar nenhuma linha que combine na tabela usuário (user table)"
rum "Nu pot gasi nici o linie corespunzatoare in tabela utilizatorului"
@@ -3107,17 +2993,16 @@ ER_PASSWORD_NO_MATCH 42000
swe "Hittade inte användaren i 'user'-tabellen"
ukr "Ðе можу знайти відповідних запиÑів у таблиці кориÑтувача"
ER_UPDATE_INFO
- cze "Nalezen-Bých řádků: %ld Změněno: %ld Varování: %ld"
+ cze "Nalezených řádků: %ld Změněno: %ld Varování: %ld"
dan "Poster fundet: %ld Ændret: %ld Advarsler: %ld"
nla "Passende rijen: %ld Gewijzigd: %ld Waarschuwingen: %ld"
eng "Rows matched: %ld Changed: %ld Warnings: %ld"
- jps "一致数(Rows matched): %ld 変更: %ld Warnings: %ld",
est "Sobinud kirjeid: %ld Muudetud: %ld Hoiatusi: %ld"
fre "Enregistrements correspondants: %ld Modifiés: %ld Warnings: %ld"
ger "Datensätze gefunden: %ld Geändert: %ld Warnungen: %ld"
hun "Megegyezo sorok szama: %ld Valtozott: %ld Warnings: %ld"
ita "Rows riconosciute: %ld Cambiate: %ld Warnings: %ld"
- jpn "一致数(Rows matched): %ld 変更: %ld Warnings: %ld"
+ jpn "該当ã—ãŸè¡Œ: %ld 変更: %ld 警告: %ld"
kor "ì¼ì¹˜í•˜ëŠ” Rows : %ldê°œ 변경ë¨: %ldê°œ 경고: %ldê°œ"
por "Linhas que combinaram: %ld - Alteradas: %ld - Avisos: %ld"
rum "Linii identificate (matched): %ld Schimbate: %ld Atentionari (warnings): %ld"
@@ -3127,30 +3012,29 @@ ER_UPDATE_INFO
swe "Rader: %ld Uppdaterade: %ld Varningar: %ld"
ukr "ЗапиÑів відповідає: %ld Змінено: %ld ЗаÑтережень: %ld"
ER_CANT_CREATE_THREAD
- cze "Nemohu vytvo-BÅ™it nový thread (errno %d). Pokud je jeÅ¡tÄ› nÄ›jaká volná paměť, podívejte se do manuálu na Äást o chybách specifických pro jednotlivé operaÄní systémy"
- dan "Kan ikke danne en ny tråd (fejl nr. %d). Hvis computeren ikke er løbet tør for hukommelse, kan du se i brugervejledningen for en mulig operativ-system - afhængig fejl"
- nla "Kan geen nieuwe thread aanmaken (Errcode: %d). Indien er geen tekort aan geheugen is kunt u de handleiding consulteren over een mogelijke OS afhankelijke fout"
- eng "Can't create a new thread (errno %d); if you are not out of available memory, you can consult the manual for a possible OS-dependent bug"
- jps "æ–°è¦ã«ã‚¹ãƒ¬ãƒƒãƒ‰ãŒä½œã‚Œã¾ã›ã‚“ã§ã—㟠(errno %d). ã‚‚ã—最大使用許å¯ãƒ¡ãƒ¢ãƒªãƒ¼æ•°ã‚’越ãˆã¦ã„ãªã„ã®ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¦ã„ã‚‹ãªã‚‰, マニュアルã®ä¸­ã‹ã‚‰ 'possible OS-dependent bug' ã¨ã„ã†æ–‡å­—を探ã—ã¦ãã¿ã¦ã ã•ã„.",
- est "Ei suuda luua uut lõime (veakood %d). Kui mälu ei ole otsas, on tõenäoliselt tegemist operatsioonisüsteemispetsiifilise veaga"
- fre "Impossible de créer une nouvelle tâche (errno %d). S'il reste de la mémoire libre, consultez le manual pour trouver un éventuel bug dépendant de l'OS"
- ger "Kann keinen neuen Thread erzeugen (Fehler: %d). Sollte noch Speicher verfügbar sein, bitte im Handbuch wegen möglicher Fehler im Betriebssystem nachschlagen"
- hun "Uj thread letrehozasa nem lehetseges (Hibakod: %d). Amenyiben van meg szabad memoria, olvassa el a kezikonyv operacios rendszerfuggo hibalehetosegekrol szolo reszet"
- ita "Impossibile creare un nuovo thread (errno %d). Se non ci sono problemi di memoria disponibile puoi consultare il manuale per controllare possibili problemi dipendenti dal SO"
- jpn "æ–°è¦ã«ã‚¹ãƒ¬ãƒƒãƒ‰ãŒä½œã‚Œã¾ã›ã‚“ã§ã—㟠(errno %d). ã‚‚ã—最大使用許å¯ãƒ¡ãƒ¢ãƒªãƒ¼æ•°ã‚’越ãˆã¦ã„ãªã„ã®ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¦ã„ã‚‹ãªã‚‰, マニュアルã®ä¸­ã‹ã‚‰ 'possible OS-dependent bug' ã¨ã„ã†æ–‡å­—を探ã—ã¦ãã¿ã¦ã ã•ã„."
- kor "새로운 쓰레드를 만들 수 없습니다.(ì—러번호 %d). 만약 여유메모리가 있다면 OS-dependent버그 ì˜ ë©”ë‰´ì–¼ ë¶€ë¶„ì„ ì°¾ì•„ë³´ì‹œì˜¤."
- nor "Can't create a new thread (errno %d); if you are not out of available memory you can consult the manual for any possible OS dependent bug"
- norwegian-ny "Can't create a new thread (errno %d); if you are not out of available memory you can consult the manual for any possible OS dependent bug"
- pol "Can't create a new thread (errno %d); if you are not out of available memory you can consult the manual for any possible OS dependent bug"
- por "Não pode criar uma nova 'thread' (erro no. %d). Se você não estiver sem memória disponível, você pode consultar o manual sobre um possível 'bug' dependente do sistema operacional"
- rum "Nu pot crea un thread nou (Eroare %d). Daca mai aveti memorie disponibila in sistem, puteti consulta manualul - ar putea exista un potential bug in legatura cu sistemul de operare"
- rus "Ðевозможно Ñоздать новый поток (ошибка %d). ЕÑли Ñто не ÑитуациÑ, ÑвÑÐ·Ð°Ð½Ð½Ð°Ñ Ñ Ð½ÐµÑ…Ð²Ð°Ñ‚ÐºÐ¾Ð¹ памÑти, то вам Ñледует изучить документацию на предмет опиÑÐ°Ð½Ð¸Ñ Ð²Ð¾Ð·Ð¼Ð¾Ð¶Ð½Ð¾Ð¹ ошибки работы в конкретной ОС"
- serbian "Ne mogu da kreiram novi thread (errno %d). Ako imate joÅ¡ slobodne memorije, trebali biste da pogledate u priruÄniku da li je ovo specifiÄna greÅ¡ka vaÅ¡eg operativnog sistema"
- spa "No puedo crear un nuevo thread (errno %d). Si tu está con falta de memoria disponible, tu puedes consultar el Manual para posibles problemas con SO"
- swe "Kan inte skapa en ny tråd (errno %d)"
- ukr "Ðе можу Ñтворити нову гілку (помилка %d). Якщо ви не викориÑтали уÑÑŽ пам'ÑÑ‚ÑŒ, то прочитайте документацію до вашої ОС - можливо це помилка ОС"
+ cze "Nemohu vytvoÅ™it nový thread (errno %M). Pokud je jeÅ¡tÄ› nÄ›jaká volná paměť, podívejte se do manuálu na Äást o chybách specifických pro jednotlivé operaÄní systémy"
+ dan "Kan ikke danne en ny tråd (fejl nr. %M). Hvis computeren ikke er løbet tør for hukommelse, kan du se i brugervejledningen for en mulig operativ-system - afhængig fejl"
+ nla "Kan geen nieuwe thread aanmaken (Errcode: %M). Indien er geen tekort aan geheugen is kunt u de handleiding consulteren over een mogelijke OS afhankelijke fout"
+ eng "Can't create a new thread (errno %M); if you are not out of available memory, you can consult the manual for a possible OS-dependent bug"
+ est "Ei suuda luua uut lõime (veakood %M). Kui mälu ei ole otsas, on tõenäoliselt tegemist operatsioonisüsteemispetsiifilise veaga"
+ fre "Impossible de créer une nouvelle tâche (errno %M). S'il reste de la mémoire libre, consultez le manual pour trouver un éventuel bug dépendant de l'OS"
+ ger "Kann keinen neuen Thread erzeugen (Fehler: %M). Sollte noch Speicher verfügbar sein, bitte im Handbuch wegen möglicher Fehler im Betriebssystem nachschlagen"
+ hun "Uj thread letrehozasa nem lehetseges (Hibakod: %M). Amenyiben van meg szabad memoria, olvassa el a kezikonyv operacios rendszerfuggo hibalehetosegekrol szolo reszet"
+ ita "Impossibile creare un nuovo thread (errno %M). Se non ci sono problemi di memoria disponibile puoi consultare il manuale per controllare possibili problemi dipendenti dal SO"
+ jpn "æ–°è¦ã«ã‚¹ãƒ¬ãƒƒãƒ‰ã‚’作æˆã§ãã¾ã›ã‚“。(ã‚¨ãƒ©ãƒ¼ç•ªå· %M) ã‚‚ã—も使用å¯èƒ½ãƒ¡ãƒ¢ãƒªãƒ¼ã®ä¸è¶³ã§ãªã‘ã‚Œã°ã€OSä¾å­˜ã®ãƒã‚°ã§ã‚ã‚‹å¯èƒ½æ€§ãŒã‚ã‚Šã¾ã™ã€‚"
+ kor "새로운 쓰레드를 만들 수 없습니다.(ì—러번호 %M). 만약 여유메모리가 있다면 OS-dependent버그 ì˜ ë©”ë‰´ì–¼ ë¶€ë¶„ì„ ì°¾ì•„ë³´ì‹œì˜¤."
+ nor "Can't create a new thread (errno %M); if you are not out of available memory you can consult the manual for any possible OS dependent bug"
+ norwegian-ny "Can't create a new thread (errno %M); if you are not out of available memory you can consult the manual for any possible OS dependent bug"
+ pol "Can't create a new thread (errno %M); if you are not out of available memory you can consult the manual for any possible OS dependent bug"
+ por "Não pode criar uma nova 'thread' (erro no. %M). Se você não estiver sem memória disponível, você pode consultar o manual sobre um possível 'bug' dependente do sistema operacional"
+ rum "Nu pot crea un thread nou (Eroare %M). Daca mai aveti memorie disponibila in sistem, puteti consulta manualul - ar putea exista un potential bug in legatura cu sistemul de operare"
+ rus "Ðевозможно Ñоздать новый поток (ошибка %M). ЕÑли Ñто не ÑитуациÑ, ÑвÑÐ·Ð°Ð½Ð½Ð°Ñ Ñ Ð½ÐµÑ…Ð²Ð°Ñ‚ÐºÐ¾Ð¹ памÑти, то вам Ñледует изучить документацию на предмет опиÑÐ°Ð½Ð¸Ñ Ð²Ð¾Ð·Ð¼Ð¾Ð¶Ð½Ð¾Ð¹ ошибки работы в конкретной ОС"
+ serbian "Ne mogu da kreiram novi thread (errno %M). Ako imate joÅ¡ slobodne memorije, trebali biste da pogledate u priruÄniku da li je ovo specifiÄna greÅ¡ka vaÅ¡eg operativnog sistema"
+ spa "No puedo crear un nuevo thread (errno %M). Si tu está con falta de memoria disponible, tu puedes consultar el Manual para posibles problemas con SO"
+ swe "Kan inte skapa en ny tråd (errno %M)"
+ ukr "Ðе можу Ñтворити нову гілку (помилка %M). Якщо ви не викориÑтали уÑÑŽ пам'ÑÑ‚ÑŒ, то прочитайте документацію до вашої ОС - можливо це помилка ОС"
ER_WRONG_VALUE_COUNT_ON_ROW 21S01
- cze "Po-BÄet sloupců neodpovídá poÄtu hodnot na řádku %lu"
+ cze "PoÄet sloupců neodpovídá poÄtu hodnot na řádku %lu"
dan "Kolonne antallet stemmer ikke overens med antallet af værdier i post %lu"
nla "Kolom aantal komt niet overeen met waarde aantal in rij %lu"
eng "Column count doesn't match value count at row %lu"
@@ -3158,6 +3042,7 @@ ER_WRONG_VALUE_COUNT_ON_ROW 21S01
ger "Anzahl der Felder stimmt nicht mit der Anzahl der Werte in Zeile %lu überein"
hun "Az oszlopban talalhato ertek nem egyezik meg a %lu sorban szamitott ertekkel"
ita "Il numero delle colonne non corrisponde al conteggio alla riga %lu"
+ jpn "%lu 行目ã§ã€åˆ—ã®æ•°ãŒå€¤ã®æ•°ã¨ä¸€è‡´ã—ã¾ã›ã‚“。"
kor "Row %luì—ì„œ 칼럼 카운트와 value 카운터와 ì¼ì¹˜í•˜ì§€ 않습니다."
por "Contagem de colunas não confere com a contagem de valores na linha %lu"
rum "Numarul de coloane nu corespunde cu numarul de valori la linia %lu"
@@ -3167,7 +3052,7 @@ ER_WRONG_VALUE_COUNT_ON_ROW 21S01
swe "Antalet kolumner motsvarar inte antalet värden på rad: %lu"
ukr "КількіÑÑ‚ÑŒ Ñтовбців не Ñпівпадає з кількіÑÑ‚ÑŽ значень у Ñтроці %lu"
ER_CANT_REOPEN_TABLE
- cze "Nemohu znovuotev-Břít tabulku: '%-.192s"
+ cze "Nemohu znovuotevřít tabulku: '%-.192s"
dan "Kan ikke genåbne tabel '%-.192s"
nla "Kan tabel niet opnieuw openen: '%-.192s"
eng "Can't reopen table: '%-.192s'"
@@ -3176,6 +3061,7 @@ ER_CANT_REOPEN_TABLE
ger "Kann Tabelle'%-.192s' nicht erneut öffnen"
hun "Nem lehet ujra-megnyitni a tablat: '%-.192s"
ita "Impossibile riaprire la tabella: '%-.192s'"
+ jpn "表をå†ã‚ªãƒ¼ãƒ—ンã§ãã¾ã›ã‚“。: '%-.192s'"
kor "í…Œì´ë¸”ì„ ë‹¤ì‹œ 열수 없군요: '%-.192s"
nor "Can't reopen table: '%-.192s"
norwegian-ny "Can't reopen table: '%-.192s"
@@ -3189,17 +3075,16 @@ ER_CANT_REOPEN_TABLE
swe "Kunde inte stänga och öppna tabell '%-.192s"
ukr "Ðе можу перевідкрити таблицю: '%-.192s'"
ER_INVALID_USE_OF_NULL 22004
- cze "Neplatn-Bé užití hodnoty NULL"
+ cze "Neplatné užití hodnoty NULL"
dan "Forkert brug af nulværdi (NULL)"
nla "Foutief gebruik van de NULL waarde"
eng "Invalid use of NULL value"
- jps "NULL 値ã®ä½¿ç”¨æ–¹æ³•ãŒä¸é©åˆ‡ã§ã™",
est "NULL väärtuse väärkasutus"
fre "Utilisation incorrecte de la valeur NULL"
ger "Unerlaubte Verwendung eines NULL-Werts"
hun "A NULL ervenytelen hasznalata"
ita "Uso scorretto del valore NULL"
- jpn "NULL 値ã®ä½¿ç”¨æ–¹æ³•ãŒä¸é©åˆ‡ã§ã™"
+ jpn "NULL 値ã®ä½¿ç”¨æ–¹æ³•ãŒä¸é©åˆ‡ã§ã™ã€‚"
kor "NULL ê°’ì„ ìž˜ëª» 사용하셨군요..."
por "Uso inválido do valor NULL"
rum "Folosirea unei value NULL e invalida"
@@ -3209,7 +3094,7 @@ ER_INVALID_USE_OF_NULL 22004
swe "Felaktig använding av NULL"
ukr "Хибне викориÑÑ‚Ð°Ð½Ð½Ñ Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ NULL"
ER_REGEXP_ERROR 42000
- cze "Regul-Bární výraz vrátil chybu '%-.64s'"
+ cze "Regulární výraz vrátil chybu '%-.64s'"
dan "Fik fejl '%-.64s' fra regexp"
nla "Fout '%-.64s' ontvangen van regexp"
eng "Got error '%-.64s' from regexp"
@@ -3218,6 +3103,7 @@ ER_REGEXP_ERROR 42000
ger "regexp lieferte Fehler '%-.64s'"
hun "'%-.64s' hiba a regularis kifejezes hasznalata soran (regexp)"
ita "Errore '%-.64s' da regexp"
+ jpn "regexp ãŒã‚¨ãƒ©ãƒ¼ '%-.64s' ã‚’è¿”ã—ã¾ã—ãŸã€‚"
kor "regexpì—ì„œ '%-.64s'ê°€ 났습니다."
por "Obteve erro '%-.64s' em regexp"
rum "Eroarea '%-.64s' obtinuta din expresia regulara (regexp)"
@@ -3227,7 +3113,7 @@ ER_REGEXP_ERROR 42000
swe "Fick fel '%-.64s' från REGEXP"
ukr "Отримано помилку '%-.64s' від регулÑрного виразу"
ER_MIX_OF_GROUP_FUNC_AND_FIELDS 42000
- cze "Pokud nen-Bí žádná GROUP BY klauzule, není dovoleno souÄasné použití GROUP položek (MIN(),MAX(),COUNT()...) s ne GROUP položkami"
+ cze "Pokud není žádná GROUP BY klauzule, není dovoleno souÄasné použití GROUP položek (MIN(),MAX(),COUNT()...) s ne GROUP položkami"
dan "Sammenblanding af GROUP kolonner (MIN(),MAX(),COUNT()...) uden GROUP kolonner er ikke tilladt, hvis der ikke er noget GROUP BY prædikat"
nla "Het mixen van GROUP kolommen (MIN(),MAX(),COUNT()...) met no-GROUP kolommen is foutief indien er geen GROUP BY clausule is"
eng "Mixing of GROUP columns (MIN(),MAX(),COUNT(),...) with no GROUP columns is illegal if there is no GROUP BY clause"
@@ -3236,6 +3122,7 @@ ER_MIX_OF_GROUP_FUNC_AND_FIELDS 42000
ger "Das Vermischen von GROUP-Feldern (MIN(),MAX(),COUNT()...) mit Nicht-GROUP-Feldern ist nicht zulässig, wenn keine GROUP-BY-Klausel vorhanden ist"
hun "A GROUP mezok (MIN(),MAX(),COUNT()...) kevert hasznalata nem lehetseges GROUP BY hivatkozas nelkul"
ita "Il mescolare funzioni di aggregazione (MIN(),MAX(),COUNT()...) e non e` illegale se non c'e` una clausula GROUP BY"
+ jpn "GROUP BYå¥ãŒç„¡ã„å ´åˆã€é›†è¨ˆé–¢æ•°(MIN(),MAX(),COUNT(),...)ã¨é€šå¸¸ã®åˆ—ã‚’åŒæ™‚ã«ä½¿ç”¨ã§ãã¾ã›ã‚“。"
kor "Mixing of GROUP 칼럼s (MIN(),MAX(),COUNT(),...) with no GROUP 칼럼s is illegal if there is no GROUP BY clause"
por "Mistura de colunas agrupadas (com MIN(), MAX(), COUNT(), ...) com colunas não agrupadas é ilegal, se não existir uma cláusula de agrupamento (cláusula GROUP BY)"
rum "Amestecarea de coloane GROUP (MIN(),MAX(),COUNT()...) fara coloane GROUP este ilegala daca nu exista o clauza GROUP BY"
@@ -3245,17 +3132,16 @@ ER_MIX_OF_GROUP_FUNC_AND_FIELDS 42000
swe "Man får ha både GROUP-kolumner (MIN(),MAX(),COUNT()...) och fält i en fråga om man inte har en GROUP BY-del"
ukr "Ð—Ð¼Ñ–ÑˆÑƒÐ²Ð°Ð½Ð½Ñ GROUP Ñтовбців (MIN(),MAX(),COUNT()...) з не GROUP ÑтовбцÑми Ñ” забороненим, Ñкщо не має GROUP BY"
ER_NONEXISTING_GRANT 42000
- cze "Neexistuje odpov-Bídající grant pro uživatele '%-.48s' na stroji '%-.64s'"
+ cze "Neexistuje odpovídající grant pro uživatele '%-.48s' na stroji '%-.64s'"
dan "Denne tilladelse findes ikke for brugeren '%-.48s' på vært '%-.64s'"
nla "Deze toegang (GRANT) is niet toegekend voor gebruiker '%-.48s' op host '%-.64s'"
eng "There is no such grant defined for user '%-.48s' on host '%-.64s'"
- jps "ユーザー '%-.48s' (ホスト '%-.64s' ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼) ã¯è¨±å¯ã•ã‚Œã¦ã„ã¾ã›ã‚“",
est "Sellist õigust ei ole defineeritud kasutajale '%-.48s' masinast '%-.64s'"
fre "Un tel droit n'est pas défini pour l'utilisateur '%-.48s' sur l'hôte '%-.64s'"
ger "Für Benutzer '%-.48s' auf Host '%-.64s' gibt es keine solche Berechtigung"
hun "A '%-.48s' felhasznalonak nincs ilyen joga a '%-.64s' host-on"
ita "GRANT non definita per l'utente '%-.48s' dalla macchina '%-.64s'"
- jpn "ユーザー '%-.48s' (ホスト '%-.64s' ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼) ã¯è¨±å¯ã•ã‚Œã¦ã„ã¾ã›ã‚“"
+ jpn "ユーザー '%-.48s' (ホスト '%-.64s' 上) ã¯è¨±å¯ã•ã‚Œã¦ã„ã¾ã›ã‚“。"
kor "ì‚¬ìš©ìž '%-.48s' (호스트 '%-.64s')를 위하여 ì •ì˜ëœ 그런 승ì¸ì€ 없습니다."
por "Não existe tal permissão (grant) definida para o usuário '%-.48s' no 'host' '%-.64s'"
rum "Nu exista un astfel de grant definit pentru utilzatorul '%-.48s' de pe host-ul '%-.64s'"
@@ -3265,47 +3151,47 @@ ER_NONEXISTING_GRANT 42000
swe "Det finns inget privilegium definierat för användare '%-.48s' på '%-.64s'"
ukr "Повноважень не визначено Ð´Ð»Ñ ÐºÐ¾Ñ€Ð¸Ñтувача '%-.48s' з хоÑту '%-.64s'"
ER_TABLEACCESS_DENIED_ERROR 42000
- cze "%-.16s p-Bříkaz nepřístupný pro uživatele: '%s'@'%s' pro tabulku '%-.192s'"
- dan "%-.16s-kommandoen er ikke tilladt for brugeren '%s'@'%s' for tabellen '%-.192s'"
- nla "%-.16s commando geweigerd voor gebruiker: '%s'@'%s' voor tabel '%-.192s'"
- eng "%-.16s command denied to user '%s'@'%s' for table '%-.192s'"
- jps "コマンド %-.16s 㯠ユーザー '%s'@'%s' ,テーブル '%-.192s' ã«å¯¾ã—ã¦è¨±å¯ã•ã‚Œã¦ã„ã¾ã›ã‚“",
- est "%-.16s käsk ei ole lubatud kasutajale '%s'@'%s' tabelis '%-.192s'"
- fre "La commande '%-.16s' est interdite à l'utilisateur: '%s'@'%s' sur la table '%-.192s'"
- ger "%-.16s Befehl nicht erlaubt für Benutzer '%s'@'%s' auf Tabelle '%-.192s'"
- hun "%-.16s parancs a '%s'@'%s' felhasznalo szamara nem engedelyezett a '%-.192s' tablaban"
- ita "Comando %-.16s negato per l'utente: '%s'@'%s' sulla tabella '%-.192s'"
- jpn "コマンド %-.16s 㯠ユーザー '%s'@'%s' ,テーブル '%-.192s' ã«å¯¾ã—ã¦è¨±å¯ã•ã‚Œã¦ã„ã¾ã›ã‚“"
- kor "'%-.16s' ëª…ë ¹ì€ ë‹¤ìŒ ì‚¬ìš©ìžì—게 거부ë˜ì—ˆìŠµë‹ˆë‹¤. : '%s'@'%s' for í…Œì´ë¸” '%-.192s'"
- por "Comando '%-.16s' negado para o usuário '%s'@'%s' na tabela '%-.192s'"
- rum "Comanda %-.16s interzisa utilizatorului: '%s'@'%s' pentru tabela '%-.192s'"
- rus "Команда %-.16s запрещена пользователю '%s'@'%s' Ð´Ð»Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ñ‹ '%-.192s'"
- serbian "%-.16s komanda zabranjena za korisnika '%s'@'%s' za tabelu '%-.192s'"
- spa "%-.16s comando negado para usuario: '%s'@'%s' para tabla '%-.192s'"
- swe "%-.16s ej tillåtet för '%s'@'%s' för tabell '%-.192s'"
- ukr "%-.16s команда заборонена кориÑтувачу: '%s'@'%s' у таблиці '%-.192s'"
+ cze "%-.32s příkaz nepřístupný pro uživatele: '%s'@'%s' pro tabulku '%-.192s'"
+ dan "%-.32s-kommandoen er ikke tilladt for brugeren '%s'@'%s' for tabellen '%-.192s'"
+ nla "%-.32s commando geweigerd voor gebruiker: '%s'@'%s' voor tabel '%-.192s'"
+ eng "%-.32s command denied to user '%s'@'%s' for table '%-.192s'"
+ jps "コマンド %-.32s 㯠ユーザー '%s'@'%s' ,テーブル '%-.192s' ã«å¯¾ã—ã¦è¨±å¯ã•ã‚Œã¦ã„ã¾ã›ã‚“",
+ est "%-.32s käsk ei ole lubatud kasutajale '%s'@'%s' tabelis '%-.192s'"
+ fre "La commande '%-.32s' est interdite à l'utilisateur: '%s'@'%s' sur la table '%-.192s'"
+ ger "%-.32s Befehl nicht erlaubt für Benutzer '%s'@'%s' auf Tabelle '%-.192s'"
+ hun "%-.32s parancs a '%s'@'%s' felhasznalo szamara nem engedelyezett a '%-.192s' tablaban"
+ ita "Comando %-.32s negato per l'utente: '%s'@'%s' sulla tabella '%-.192s'"
+ jpn "コマンド %-.32s 㯠ユーザー '%s'@'%s' ,テーブル '%-.192s' ã«å¯¾ã—ã¦è¨±å¯ã•ã‚Œã¦ã„ã¾ã›ã‚“"
+ kor "'%-.32s' ëª…ë ¹ì€ ë‹¤ìŒ ì‚¬ìš©ìžì—게 거부ë˜ì—ˆìŠµë‹ˆë‹¤. : '%s'@'%s' for í…Œì´ë¸” '%-.192s'"
+ por "Comando '%-.32s' negado para o usuário '%s'@'%s' na tabela '%-.192s'"
+ rum "Comanda %-.32s interzisa utilizatorului: '%s'@'%s' pentru tabela '%-.192s'"
+ rus "Команда %-.32s запрещена пользователю '%s'@'%s' Ð´Ð»Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ñ‹ '%-.192s'"
+ serbian "%-.32s komanda zabranjena za korisnika '%s'@'%s' za tabelu '%-.192s'"
+ spa "%-.32s comando negado para usuario: '%s'@'%s' para tabla '%-.192s'"
+ swe "%-.32s ej tillåtet för '%s'@'%s' för tabell '%-.192s'"
+ ukr "%-.32s команда заборонена кориÑтувачу: '%s'@'%s' у таблиці '%-.192s'"
ER_COLUMNACCESS_DENIED_ERROR 42000
- cze "%-.16s p-Bříkaz nepřístupný pro uživatele: '%s'@'%s' pro sloupec '%-.192s' v tabulce '%-.192s'"
- dan "%-.16s-kommandoen er ikke tilladt for brugeren '%s'@'%s' for kolonne '%-.192s' in tabellen '%-.192s'"
- nla "%-.16s commando geweigerd voor gebruiker: '%s'@'%s' voor kolom '%-.192s' in tabel '%-.192s'"
- eng "%-.16s command denied to user '%s'@'%s' for column '%-.192s' in table '%-.192s'"
- jps "コマンド %-.16s 㯠ユーザー '%s'@'%s'Â¥n カラム '%-.192s' テーブル '%-.192s' ã«å¯¾ã—ã¦è¨±å¯ã•ã‚Œã¦ã„ã¾ã›ã‚“",
- est "%-.16s käsk ei ole lubatud kasutajale '%s'@'%s' tulbale '%-.192s' tabelis '%-.192s'"
- fre "La commande '%-.16s' est interdite à l'utilisateur: '%s'@'%s' sur la colonne '%-.192s' de la table '%-.192s'"
- ger "%-.16s Befehl nicht erlaubt für Benutzer '%s'@'%s' und Feld '%-.192s' in Tabelle '%-.192s'"
- hun "%-.16s parancs a '%s'@'%s' felhasznalo szamara nem engedelyezett a '%-.192s' mezo eseten a '%-.192s' tablaban"
- ita "Comando %-.16s negato per l'utente: '%s'@'%s' sulla colonna '%-.192s' della tabella '%-.192s'"
- jpn "コマンド %-.16s 㯠ユーザー '%s'@'%s'\n カラム '%-.192s' テーブル '%-.192s' ã«å¯¾ã—ã¦è¨±å¯ã•ã‚Œã¦ã„ã¾ã›ã‚“"
- kor "'%-.16s' ëª…ë ¹ì€ ë‹¤ìŒ ì‚¬ìš©ìžì—게 거부ë˜ì—ˆìŠµë‹ˆë‹¤. : '%s'@'%s' for 칼럼 '%-.192s' in í…Œì´ë¸” '%-.192s'"
- por "Comando '%-.16s' negado para o usuário '%s'@'%s' na coluna '%-.192s', na tabela '%-.192s'"
- rum "Comanda %-.16s interzisa utilizatorului: '%s'@'%s' pentru coloana '%-.192s' in tabela '%-.192s'"
- rus "Команда %-.16s запрещена пользователю '%s'@'%s' Ð´Ð»Ñ Ñтолбца '%-.192s' в таблице '%-.192s'"
- serbian "%-.16s komanda zabranjena za korisnika '%s'@'%s' za kolonu '%-.192s' iz tabele '%-.192s'"
- spa "%-.16s comando negado para usuario: '%s'@'%s' para columna '%-.192s' en la tabla '%-.192s'"
- swe "%-.16s ej tillåtet för '%s'@'%s' för kolumn '%-.192s' i tabell '%-.192s'"
- ukr "%-.16s команда заборонена кориÑтувачу: '%s'@'%s' Ð´Ð»Ñ ÑÑ‚Ð¾Ð²Ð±Ñ†Ñ '%-.192s' у таблиці '%-.192s'"
+ cze "%-.32s příkaz nepřístupný pro uživatele: '%s'@'%s' pro sloupec '%-.192s' v tabulce '%-.192s'"
+ dan "%-.32s-kommandoen er ikke tilladt for brugeren '%s'@'%s' for kolonne '%-.192s' in tabellen '%-.192s'"
+ nla "%-.32s commando geweigerd voor gebruiker: '%s'@'%s' voor kolom '%-.192s' in tabel '%-.192s'"
+ eng "%-.32s command denied to user '%s'@'%s' for column '%-.192s' in table '%-.192s'"
+ jps "コマンド %-.32s 㯠ユーザー '%s'@'%s'Â¥n カラム '%-.192s' テーブル '%-.192s' ã«å¯¾ã—ã¦è¨±å¯ã•ã‚Œã¦ã„ã¾ã›ã‚“",
+ est "%-.32s käsk ei ole lubatud kasutajale '%s'@'%s' tulbale '%-.192s' tabelis '%-.192s'"
+ fre "La commande '%-.32s' est interdite à l'utilisateur: '%s'@'%s' sur la colonne '%-.192s' de la table '%-.192s'"
+ ger "%-.32s Befehl nicht erlaubt für Benutzer '%s'@'%s' und Feld '%-.192s' in Tabelle '%-.192s'"
+ hun "%-.32s parancs a '%s'@'%s' felhasznalo szamara nem engedelyezett a '%-.192s' mezo eseten a '%-.192s' tablaban"
+ ita "Comando %-.32s negato per l'utente: '%s'@'%s' sulla colonna '%-.192s' della tabella '%-.192s'"
+ jpn "コマンド %-.32s 㯠ユーザー '%s'@'%s'\n カラム '%-.192s' テーブル '%-.192s' ã«å¯¾ã—ã¦è¨±å¯ã•ã‚Œã¦ã„ã¾ã›ã‚“"
+ kor "'%-.32s' ëª…ë ¹ì€ ë‹¤ìŒ ì‚¬ìš©ìžì—게 거부ë˜ì—ˆìŠµë‹ˆë‹¤. : '%s'@'%s' for 칼럼 '%-.192s' in í…Œì´ë¸” '%-.192s'"
+ por "Comando '%-.32s' negado para o usuário '%s'@'%s' na coluna '%-.192s', na tabela '%-.192s'"
+ rum "Comanda %-.32s interzisa utilizatorului: '%s'@'%s' pentru coloana '%-.192s' in tabela '%-.192s'"
+ rus "Команда %-.32s запрещена пользователю '%s'@'%s' Ð´Ð»Ñ Ñтолбца '%-.192s' в таблице '%-.192s'"
+ serbian "%-.32s komanda zabranjena za korisnika '%s'@'%s' za kolonu '%-.192s' iz tabele '%-.192s'"
+ spa "%-.32s comando negado para usuario: '%s'@'%s' para columna '%-.192s' en la tabla '%-.192s'"
+ swe "%-.32s ej tillåtet för '%s'@'%s' för kolumn '%-.192s' i tabell '%-.192s'"
+ ukr "%-.32s команда заборонена кориÑтувачу: '%s'@'%s' Ð´Ð»Ñ ÑÑ‚Ð¾Ð²Ð±Ñ†Ñ '%-.192s' у таблиці '%-.192s'"
ER_ILLEGAL_GRANT_FOR_TABLE 42000
- cze "Neplatn-Bý příkaz GRANT/REVOKE. Prosím, pÅ™eÄtÄ›te si v manuálu, jaká privilegia je možné použít."
+ cze "Neplatný příkaz GRANT/REVOKE. Prosím, pÅ™eÄtÄ›te si v manuálu, jaká privilegia je možné použít."
dan "Forkert GRANT/REVOKE kommando. Se i brugervejledningen hvilke privilegier der kan specificeres."
nla "Foutief GRANT/REVOKE commando. Raadpleeg de handleiding welke priveleges gebruikt kunnen worden."
eng "Illegal GRANT/REVOKE command; please consult the manual to see which privileges can be used"
@@ -3315,7 +3201,7 @@ ER_ILLEGAL_GRANT_FOR_TABLE 42000
greek "Illegal GRANT/REVOKE command; please consult the manual to see which privileges can be used."
hun "Ervenytelen GRANT/REVOKE parancs. Kerem, nezze meg a kezikonyvben, milyen jogok lehetsegesek"
ita "Comando GRANT/REVOKE illegale. Prego consultare il manuale per sapere quali privilegi possono essere usati."
- jpn "Illegal GRANT/REVOKE command; please consult the manual to see which privleges can be used."
+ jpn "ä¸æ­£ãª GRANT/REVOKE コマンドã§ã™ã€‚ã©ã®æ¨©é™ã§åˆ©ç”¨å¯èƒ½ã‹ã¯ãƒžãƒ‹ãƒ¥ã‚¢ãƒ«ã‚’å‚ç…§ã—ã¦ä¸‹ã•ã„。"
kor "ìž˜ëª»ëœ GRANT/REVOKE 명령. ì–´ë–¤ 권리와 승ì¸ì´ 사용ë˜ì–´ 질 수 있는지 ë©”ë‰´ì–¼ì„ ë³´ì‹œì˜¤."
nor "Illegal GRANT/REVOKE command; please consult the manual to see which privleges can be used."
norwegian-ny "Illegal GRANT/REVOKE command; please consult the manual to see which privleges can be used."
@@ -3329,7 +3215,7 @@ ER_ILLEGAL_GRANT_FOR_TABLE 42000
swe "Felaktigt GRANT-privilegium använt"
ukr "Хибна GRANT/REVOKE команда; прочитайте документацію ÑтоÑовно того, Ñкі права можна викориÑтовувати"
ER_GRANT_WRONG_HOST_OR_USER 42000
- cze "Argument p-Bříkazu GRANT uživatel nebo stroj je příliš dlouhý"
+ cze "Argument příkazu GRANT uživatel nebo stroj je příliš dlouhý"
dan "Værts- eller brugernavn for langt til GRANT"
nla "De host of gebruiker parameter voor GRANT is te lang"
eng "The host or user argument to GRANT is too long"
@@ -3338,6 +3224,7 @@ ER_GRANT_WRONG_HOST_OR_USER 42000
ger "Das Host- oder User-Argument für GRANT ist zu lang"
hun "A host vagy felhasznalo argumentuma tul hosszu a GRANT parancsban"
ita "L'argomento host o utente per la GRANT e` troppo lungo"
+ jpn "GRANTコマンドã¸ã®ã€ãƒ›ã‚¹ãƒˆåやユーザーåãŒé•·ã™ãŽã¾ã™ã€‚"
kor "승ì¸(GRANT)ì„ ìœ„í•˜ì—¬ 사용한 사용ìžë‚˜ í˜¸ìŠ¤íŠ¸ì˜ ê°’ë“¤ì´ ë„ˆë¬´ ê¹ë‹ˆë‹¤."
por "Argumento de 'host' ou de usuário para o GRANT é longo demais"
rum "Argumentul host-ului sau utilizatorului pentru GRANT e prea lung"
@@ -3356,7 +3243,7 @@ ER_NO_SUCH_TABLE 42S02
ger "Tabelle '%-.192s.%-.192s' existiert nicht"
hun "A '%-.192s.%-.192s' tabla nem letezik"
ita "La tabella '%-.192s.%-.192s' non esiste"
- jpn "Table '%-.192s.%-.192s' doesn't exist"
+ jpn "表 '%-.192s.%-.192s' ã¯å­˜åœ¨ã—ã¾ã›ã‚“。"
kor "í…Œì´ë¸” '%-.192s.%-.192s' 는 존재하지 않습니다."
nor "Table '%-.192s.%-.192s' doesn't exist"
norwegian-ny "Table '%-.192s.%-.192s' doesn't exist"
@@ -3370,7 +3257,7 @@ ER_NO_SUCH_TABLE 42S02
swe "Det finns ingen tabell som heter '%-.192s.%-.192s'"
ukr "Ð¢Ð°Ð±Ð»Ð¸Ñ†Ñ '%-.192s.%-.192s' не Ñ–Ñнує"
ER_NONEXISTING_TABLE_GRANT 42000
- cze "Neexistuje odpov-Bídající grant pro uživatele '%-.48s' na stroji '%-.64s' pro tabulku '%-.192s'"
+ cze "Neexistuje odpovídající grant pro uživatele '%-.48s' na stroji '%-.64s' pro tabulku '%-.192s'"
dan "Denne tilladelse eksisterer ikke for brugeren '%-.48s' på vært '%-.64s' for tabellen '%-.192s'"
nla "Deze toegang (GRANT) is niet toegekend voor gebruiker '%-.48s' op host '%-.64s' op tabel '%-.192s'"
eng "There is no such grant defined for user '%-.48s' on host '%-.64s' on table '%-.192s'"
@@ -3379,6 +3266,7 @@ ER_NONEXISTING_TABLE_GRANT 42000
ger "Eine solche Berechtigung ist für User '%-.48s' auf Host '%-.64s' an Tabelle '%-.192s' nicht definiert"
hun "A '%-.48s' felhasznalo szamara a '%-.64s' host '%-.192s' tablajaban ez a parancs nem engedelyezett"
ita "GRANT non definita per l'utente '%-.48s' dalla macchina '%-.64s' sulla tabella '%-.192s'"
+ jpn "ユーザー '%-.48s' (ホスト '%-.64s' 上) ã®è¡¨ '%-.192s' ã¸ã®æ¨©é™ã¯å®šç¾©ã•ã‚Œã¦ã„ã¾ã›ã‚“。"
kor "ì‚¬ìš©ìž '%-.48s'(호스트 '%-.64s')는 í…Œì´ë¸” '%-.192s'를 사용하기 위하여 ì •ì˜ëœ 승ì¸ì€ 없습니다. "
por "Não existe tal permissão (grant) definido para o usuário '%-.48s' no 'host' '%-.64s', na tabela '%-.192s'"
rum "Nu exista un astfel de privilegiu (grant) definit pentru utilizatorul '%-.48s' de pe host-ul '%-.64s' pentru tabela '%-.192s'"
@@ -3388,7 +3276,7 @@ ER_NONEXISTING_TABLE_GRANT 42000
swe "Det finns inget privilegium definierat för användare '%-.48s' på '%-.64s' för tabell '%-.192s'"
ukr "Повноважень не визначено Ð´Ð»Ñ ÐºÐ¾Ñ€Ð¸Ñтувача '%-.48s' з хоÑту '%-.64s' Ð´Ð»Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ñ– '%-.192s'"
ER_NOT_ALLOWED_COMMAND 42000
- cze "Pou-Bžitý příkaz není v této verzi MariaDB povolen"
+ cze "Použitý příkaz není v této verzi MySQL povolen"
dan "Den brugte kommando er ikke tilladt med denne udgave af MariaDB"
nla "Het used commando is niet toegestaan in deze MariaDB versie"
eng "The used command is not allowed with this MariaDB version"
@@ -3397,6 +3285,7 @@ ER_NOT_ALLOWED_COMMAND 42000
ger "Der verwendete Befehl ist in dieser MariaDB-Version nicht zulässig"
hun "A hasznalt parancs nem engedelyezett ebben a MariaDB verzioban"
ita "Il comando utilizzato non e` supportato in questa versione di MariaDB"
+ jpn "ã“ã®MySQLãƒãƒ¼ã‚¸ãƒ§ãƒ³ã§ã¯åˆ©ç”¨ã§ããªã„コマンドã§ã™ã€‚"
kor "ì‚¬ìš©ëœ ëª…ë ¹ì€ í˜„ìž¬ì˜ MariaDB 버젼ì—서는 ì´ìš©ë˜ì§€ 않습니다."
por "Comando usado não é permitido para esta versão do MariaDB"
rum "Comanda folosita nu este permisa pentru aceasta versiune de MariaDB"
@@ -3406,7 +3295,7 @@ ER_NOT_ALLOWED_COMMAND 42000
swe "Du kan inte använda detta kommando med denna MariaDB version"
ukr "ВикориÑтовувана команда не дозволена у цій верÑÑ–Ñ— MariaDB"
ER_SYNTAX_ERROR 42000
- cze "Va-Bše syntaxe je nějaká divná"
+ cze "Vaše syntaxe je nějaká divná"
dan "Der er en fejl i SQL syntaksen"
nla "Er is iets fout in de gebruikte syntax"
eng "You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use"
@@ -3416,7 +3305,7 @@ ER_SYNTAX_ERROR 42000
greek "You have an error in your SQL syntax"
hun "Szintaktikai hiba"
ita "Errore di sintassi nella query SQL"
- jpn "Something is wrong in your syntax"
+ jpn "SQL構文エラーã§ã™ã€‚ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã«å¯¾å¿œã™ã‚‹ãƒžãƒ‹ãƒ¥ã‚¢ãƒ«ã‚’å‚ç…§ã—ã¦æ­£ã—ã„構文を確èªã—ã¦ãã ã•ã„。"
kor "SQL êµ¬ë¬¸ì— ì˜¤ë¥˜ê°€ 있습니다."
nor "Something is wrong in your syntax"
norwegian-ny "Something is wrong in your syntax"
@@ -3430,7 +3319,7 @@ ER_SYNTAX_ERROR 42000
swe "Du har något fel i din syntax"
ukr "У Ð²Ð°Ñ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ° у ÑинтакÑиÑÑ– SQL"
ER_DELAYED_CANT_CHANGE_LOCK
- cze "Zpo-Bžděný insert threadu nebyl schopen získat požadovaný zámek pro tabulku %-.192s"
+ cze "Zpožděný insert threadu nebyl schopen získat požadovaný zámek pro tabulku %-.192s"
dan "Forsinket indsættelse tråden (delayed insert thread) kunne ikke opnå lås på tabellen %-.192s"
nla "'Delayed insert' thread kon de aangevraagde 'lock' niet krijgen voor tabel %-.192s"
eng "Delayed insert thread couldn't get requested lock for table %-.192s"
@@ -3439,6 +3328,7 @@ ER_DELAYED_CANT_CHANGE_LOCK
ger "Verzögerter (DELAYED) Einfüge-Thread konnte die angeforderte Sperre für Tabelle '%-.192s' nicht erhalten"
hun "A kesleltetett beillesztes (delayed insert) thread nem kapott zatolast a %-.192s tablahoz"
ita "Il thread di inserimento ritardato non riesce ad ottenere il lock per la tabella %-.192s"
+ jpn "'Delayed insert'スレッドãŒè¡¨ '%-.192s' ã®ãƒ­ãƒƒã‚¯ã‚’å–å¾—ã§ãã¾ã›ã‚“ã§ã—ãŸã€‚"
kor "ì§€ì—°ëœ insert 쓰레드가 í…Œì´ë¸” %-.192sì˜ ìš”êµ¬ëœ ë½í‚¹ì„ 처리할 수 없었습니다."
por "'Thread' de inserção retardada (atrasada) pois não conseguiu obter a trava solicitada para tabela '%-.192s'"
rum "Thread-ul pentru inserarea aminata nu a putut obtine lacatul (lock) pentru tabela %-.192s"
@@ -3448,7 +3338,7 @@ ER_DELAYED_CANT_CHANGE_LOCK
swe "DELAYED INSERT-tråden kunde inte låsa tabell '%-.192s'"
ukr "Гілка Ð´Ð»Ñ INSERT DELAYED не може отримати Ð±Ð»Ð¾ÐºÑƒÐ²Ð°Ð½Ð½Ñ Ð´Ð»Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ñ– %-.192s"
ER_TOO_MANY_DELAYED_THREADS
- cze "P-Bříliš mnoho zpožděných threadů"
+ cze "Příliš mnoho zpožděných threadů"
dan "For mange slettede tråde (threads) i brug"
nla "Te veel 'delayed' threads in gebruik"
eng "Too many delayed threads in use"
@@ -3457,6 +3347,7 @@ ER_TOO_MANY_DELAYED_THREADS
ger "Zu viele verzögerte (DELAYED) Threads in Verwendung"
hun "Tul sok kesletetett thread (delayed)"
ita "Troppi threads ritardati in uso"
+ jpn "'Delayed insert'スレッドãŒå¤šã™ãŽã¾ã™ã€‚"
kor "너무 ë§Žì€ ì§€ì—° 쓰레드를 사용하고 있습니다."
por "Excesso de 'threads' retardadas (atrasadas) em uso"
rum "Prea multe threaduri aminate care sint in uz"
@@ -3466,7 +3357,7 @@ ER_TOO_MANY_DELAYED_THREADS
swe "Det finns redan 'max_delayed_threads' trådar i använding"
ukr "Забагато затриманих гілок викориÑтовуєтьÑÑ"
ER_ABORTING_CONNECTION 08S01
- cze "Zru-Bšeno spojení %ld do databáze: '%-.192s' uživatel: '%-.48s' (%-.64s)"
+ cze "Zrušeno spojení %ld do databáze: '%-.192s' uživatel: '%-.48s' (%-.64s)"
dan "Afbrudt forbindelse %ld til database: '%-.192s' bruger: '%-.48s' (%-.64s)"
nla "Afgebroken verbinding %ld naar db: '%-.192s' gebruiker: '%-.48s' (%-.64s)"
eng "Aborted connection %ld to db: '%-.192s' user: '%-.48s' (%-.64s)"
@@ -3475,7 +3366,7 @@ ER_ABORTING_CONNECTION 08S01
ger "Abbruch der Verbindung %ld zur Datenbank '%-.192s'. Benutzer: '%-.48s' (%-.64s)"
hun "Megszakitott kapcsolat %ld db: '%-.192s' adatbazishoz, felhasznalo: '%-.48s' (%-.64s)"
ita "Interrotta la connessione %ld al db: '%-.192s' utente: '%-.48s' (%-.64s)"
- jpn "Aborted connection %ld to db: '%-.192s' user: '%-.48s' (%-.64s)"
+ jpn "接続 %ld ãŒä¸­æ–­ã•ã‚Œã¾ã—ãŸã€‚データベース: '%-.192s' ユーザー: '%-.48s' (%-.64s)"
kor "ë°ì´íƒ€ë² ì´ìŠ¤ ì ‘ì†ì„ 위한 ì—°ê²° %ldê°€ ì¤‘ë‹¨ë¨ : '%-.192s' 사용ìž: '%-.48s' (%-.64s)"
nor "Aborted connection %ld to db: '%-.192s' user: '%-.48s' (%-.64s)"
norwegian-ny "Aborted connection %ld to db: '%-.192s' user: '%-.48s' (%-.64s)"
@@ -3489,7 +3380,7 @@ ER_ABORTING_CONNECTION 08S01
swe "Avbröt länken för tråd %ld till db '%-.192s', användare '%-.48s' (%-.64s)"
ukr "Перервано з'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ %ld до бази данних: '%-.192s' кориÑтувача: '%-.48s' (%-.64s)"
ER_NET_PACKET_TOO_LARGE 08S01
- cze "Zji-Bštěn příchozí packet delší než 'max_allowed_packet'"
+ cze "Zjištěn příchozí packet delší než 'max_allowed_packet'"
dan "Modtog en datapakke som var større end 'max_allowed_packet'"
nla "Groter pakket ontvangen dan 'max_allowed_packet'"
eng "Got a packet bigger than 'max_allowed_packet' bytes"
@@ -3498,6 +3389,7 @@ ER_NET_PACKET_TOO_LARGE 08S01
ger "Empfangenes Paket ist größer als 'max_allowed_packet' Bytes"
hun "A kapott csomag nagyobb, mint a maximalisan engedelyezett: 'max_allowed_packet'"
ita "Ricevuto un pacchetto piu` grande di 'max_allowed_packet'"
+ jpn "'max_allowed_packet'よりも大ããªãƒ‘ケットをå—ä¿¡ã—ã¾ã—ãŸã€‚"
kor "'max_allowed_packet'보다 ë”í° íŒ¨í‚·ì„ ë°›ì•˜ìŠµë‹ˆë‹¤."
por "Obteve um pacote maior do que a taxa máxima de pacotes definida (max_allowed_packet)"
rum "Un packet mai mare decit 'max_allowed_packet' a fost primit"
@@ -3507,7 +3399,7 @@ ER_NET_PACKET_TOO_LARGE 08S01
swe "Kommunkationspaketet är större än 'max_allowed_packet'"
ukr "Отримано пакет більший ніж max_allowed_packet"
ER_NET_READ_ERROR_FROM_PIPE 08S01
- cze "Zji-BÅ¡tÄ›na chyba pÅ™i Ätení z roury spojení"
+ cze "ZjiÅ¡tÄ›na chyba pÅ™i Ätení z roury spojení"
dan "Fik læsefejl fra forbindelse (connection pipe)"
nla "Kreeg leesfout van de verbindings pipe"
eng "Got a read error from the connection pipe"
@@ -3516,6 +3408,7 @@ ER_NET_READ_ERROR_FROM_PIPE 08S01
ger "Lese-Fehler bei einer Verbindungs-Pipe"
hun "Olvasasi hiba a kapcsolat soran"
ita "Rilevato un errore di lettura dalla pipe di connessione"
+ jpn "接続パイプã®èª­ã¿è¾¼ã¿ã‚¨ãƒ©ãƒ¼ã§ã™ã€‚"
kor "ì—°ê²° 파ì´í”„로부터 ì—러가 ë°œìƒí•˜ì˜€ìŠµë‹ˆë‹¤."
por "Obteve um erro de leitura no 'pipe' da conexão"
rum "Eroare la citire din cauza lui 'connection pipe'"
@@ -3525,7 +3418,7 @@ ER_NET_READ_ERROR_FROM_PIPE 08S01
swe "Fick läsfel från klienten vid läsning från 'PIPE'"
ukr "Отримано помилку Ñ‡Ð¸Ñ‚Ð°Ð½Ð½Ñ Ð· комунікаційного каналу"
ER_NET_FCNTL_ERROR 08S01
- cze "Zji-Bštěna chyba fcntl()"
+ cze "Zjištěna chyba fcntl()"
dan "Fik fejlmeddelelse fra fcntl()"
nla "Kreeg fout van fcntl()"
eng "Got an error from fcntl()"
@@ -3534,6 +3427,7 @@ ER_NET_FCNTL_ERROR 08S01
ger "fcntl() lieferte einen Fehler"
hun "Hiba a fcntl() fuggvenyben"
ita "Rilevato un errore da fcntl()"
+ jpn "fcntl()ãŒã‚¨ãƒ©ãƒ¼ã‚’è¿”ã—ã¾ã—ãŸã€‚"
kor "fcntl() 함수로부터 ì—러가 ë°œìƒí•˜ì˜€ìŠµë‹ˆë‹¤."
por "Obteve um erro em fcntl()"
rum "Eroare obtinuta de la fcntl()"
@@ -3543,7 +3437,7 @@ ER_NET_FCNTL_ERROR 08S01
swe "Fick fatalt fel från 'fcntl()'"
ukr "Отримано помилкку від fcntl()"
ER_NET_PACKETS_OUT_OF_ORDER 08S01
- cze "P-Bříchozí packety v chybném pořadí"
+ cze "Příchozí packety v chybném pořadí"
dan "Modtog ikke datapakker i korrekt rækkefølge"
nla "Pakketten in verkeerde volgorde ontvangen"
eng "Got packets out of order"
@@ -3552,6 +3446,7 @@ ER_NET_PACKETS_OUT_OF_ORDER 08S01
ger "Pakete nicht in der richtigen Reihenfolge empfangen"
hun "Helytelen sorrendben erkezett adatcsomagok"
ita "Ricevuti pacchetti non in ordine"
+ jpn "ä¸æ­£ãªé †åºã®ãƒ‘ケットをå—ä¿¡ã—ã¾ã—ãŸã€‚"
kor "순서가 맞지않는 íŒ¨í‚·ì„ ë°›ì•˜ìŠµë‹ˆë‹¤."
por "Obteve pacotes fora de ordem"
rum "Packets care nu sint ordonati au fost gasiti"
@@ -3561,7 +3456,7 @@ ER_NET_PACKETS_OUT_OF_ORDER 08S01
swe "Kommunikationspaketen kom i fel ordning"
ukr "Отримано пакети у неналежному порÑдку"
ER_NET_UNCOMPRESS_ERROR 08S01
- cze "Nemohu rozkomprimovat komunika-BÄní packet"
+ cze "Nemohu rozkomprimovat komunikaÄní packet"
dan "Kunne ikke dekomprimere kommunikations-pakke (communication packet)"
nla "Communicatiepakket kon niet worden gedecomprimeerd"
eng "Couldn't uncompress communication packet"
@@ -3570,6 +3465,7 @@ ER_NET_UNCOMPRESS_ERROR 08S01
ger "Kommunikationspaket lässt sich nicht entpacken"
hun "A kommunikacios adatcsomagok nem tomorithetok ki"
ita "Impossibile scompattare i pacchetti di comunicazione"
+ jpn "圧縮パケットã®å±•é–‹ãŒã§ãã¾ã›ã‚“ã§ã—ãŸã€‚"
kor "통신 íŒ¨í‚·ì˜ ì••ì¶•í•´ì œë¥¼ í•  수 없었습니다."
por "Não conseguiu descomprimir pacote de comunicação"
rum "Nu s-a putut decompresa pachetul de comunicatie (communication packet)"
@@ -3579,7 +3475,7 @@ ER_NET_UNCOMPRESS_ERROR 08S01
swe "Kunde inte packa up kommunikationspaketet"
ukr "Ðе можу декомпреÑувати комунікаційний пакет"
ER_NET_READ_ERROR 08S01
- cze "Zji-BÅ¡tÄ›na chyba pÅ™i Ätení komunikaÄního packetu"
+ cze "ZjiÅ¡tÄ›na chyba pÅ™i Ätení komunikaÄního packetu"
dan "Fik fejlmeddelelse ved læsning af kommunikations-pakker (communication packets)"
nla "Fout bij het lezen van communicatiepakketten"
eng "Got an error reading communication packets"
@@ -3588,6 +3484,7 @@ ER_NET_READ_ERROR 08S01
ger "Fehler beim Lesen eines Kommunikationspakets"
hun "HIba a kommunikacios adatcsomagok olvasasa soran"
ita "Rilevato un errore ricevendo i pacchetti di comunicazione"
+ jpn "パケットã®å—ä¿¡ã§ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
kor "통신 íŒ¨í‚·ì„ ì½ëŠ” 중 오류가 ë°œìƒí•˜ì˜€ìŠµë‹ˆë‹¤."
por "Obteve um erro na leitura de pacotes de comunicação"
rum "Eroare obtinuta citind pachetele de comunicatie (communication packets)"
@@ -3597,7 +3494,7 @@ ER_NET_READ_ERROR 08S01
swe "Fick ett fel vid läsning från klienten"
ukr "Отримано помилку Ñ‡Ð¸Ñ‚Ð°Ð½Ð½Ñ ÐºÐ¾Ð¼ÑƒÐ½Ñ–ÐºÐ°Ñ†Ñ–Ð¹Ð½Ð¸Ñ… пакетів"
ER_NET_READ_INTERRUPTED 08S01
- cze "Zji-BÅ¡tÄ›n timeout pÅ™i Ätení komunikaÄního packetu"
+ cze "ZjiÅ¡tÄ›n timeout pÅ™i Ätení komunikaÄního packetu"
dan "Timeout-fejl ved læsning af kommunukations-pakker (communication packets)"
nla "Timeout bij het lezen van communicatiepakketten"
eng "Got timeout reading communication packets"
@@ -3606,6 +3503,7 @@ ER_NET_READ_INTERRUPTED 08S01
ger "Zeitüberschreitung beim Lesen eines Kommunikationspakets"
hun "Idotullepes a kommunikacios adatcsomagok olvasasa soran"
ita "Rilevato un timeout ricevendo i pacchetti di comunicazione"
+ jpn "パケットã®å—ä¿¡ã§ã‚¿ã‚¤ãƒ ã‚¢ã‚¦ãƒˆãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
kor "통신 íŒ¨í‚·ì„ ì½ëŠ” 중 timeoutì´ ë°œìƒí•˜ì˜€ìŠµë‹ˆë‹¤."
por "Obteve expiração de tempo (timeout) na leitura de pacotes de comunicação"
rum "Timeout obtinut citind pachetele de comunicatie (communication packets)"
@@ -3615,7 +3513,7 @@ ER_NET_READ_INTERRUPTED 08S01
swe "Fick 'timeout' vid läsning från klienten"
ukr "Отримано затримку Ñ‡Ð¸Ñ‚Ð°Ð½Ð½Ñ ÐºÐ¾Ð¼ÑƒÐ½Ñ–ÐºÐ°Ñ†Ñ–Ð¹Ð½Ð¸Ñ… пакетів"
ER_NET_ERROR_ON_WRITE 08S01
- cze "Zji-BÅ¡tÄ›na chyba pÅ™i zápisu komunikaÄního packetu"
+ cze "ZjiÅ¡tÄ›na chyba pÅ™i zápisu komunikaÄního packetu"
dan "Fik fejlmeddelelse ved skrivning af kommunukations-pakker (communication packets)"
nla "Fout bij het schrijven van communicatiepakketten"
eng "Got an error writing communication packets"
@@ -3624,6 +3522,7 @@ ER_NET_ERROR_ON_WRITE 08S01
ger "Fehler beim Schreiben eines Kommunikationspakets"
hun "Hiba a kommunikacios csomagok irasa soran"
ita "Rilevato un errore inviando i pacchetti di comunicazione"
+ jpn "パケットã®é€ä¿¡ã§ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
kor "통신 íŒ¨í‚·ì„ ê¸°ë¡í•˜ëŠ” 중 오류가 ë°œìƒí•˜ì˜€ìŠµë‹ˆë‹¤."
por "Obteve um erro na escrita de pacotes de comunicação"
rum "Eroare in scrierea pachetelor de comunicatie (communication packets)"
@@ -3633,7 +3532,7 @@ ER_NET_ERROR_ON_WRITE 08S01
swe "Fick ett fel vid skrivning till klienten"
ukr "Отримано помилку запиÑу комунікаційних пакетів"
ER_NET_WRITE_INTERRUPTED 08S01
- cze "Zji-BÅ¡tÄ›n timeout pÅ™i zápisu komunikaÄního packetu"
+ cze "ZjiÅ¡tÄ›n timeout pÅ™i zápisu komunikaÄního packetu"
dan "Timeout-fejl ved skrivning af kommunukations-pakker (communication packets)"
nla "Timeout bij het schrijven van communicatiepakketten"
eng "Got timeout writing communication packets"
@@ -3642,6 +3541,7 @@ ER_NET_WRITE_INTERRUPTED 08S01
ger "Zeitüberschreitung beim Schreiben eines Kommunikationspakets"
hun "Idotullepes a kommunikacios csomagok irasa soran"
ita "Rilevato un timeout inviando i pacchetti di comunicazione"
+ jpn "パケットã®é€ä¿¡ã§ã‚¿ã‚¤ãƒ ã‚¢ã‚¦ãƒˆãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
kor "통신 íŒ¨íŒƒì„ ê¸°ë¡í•˜ëŠ” 중 timeoutì´ ë°œìƒí•˜ì˜€ìŠµë‹ˆë‹¤."
por "Obteve expiração de tempo ('timeout') na escrita de pacotes de comunicação"
rum "Timeout obtinut scriind pachetele de comunicatie (communication packets)"
@@ -3651,7 +3551,7 @@ ER_NET_WRITE_INTERRUPTED 08S01
swe "Fick 'timeout' vid skrivning till klienten"
ukr "Отримано затримку запиÑу комунікаційних пакетів"
ER_TOO_LONG_STRING 42000
- cze "V-Býsledný řetězec je delší než 'max_allowed_packet'"
+ cze "Výsledný řetězec je delší než 'max_allowed_packet'"
dan "Strengen med resultater er større end 'max_allowed_packet'"
nla "Resultaat string is langer dan 'max_allowed_packet'"
eng "Result string is longer than 'max_allowed_packet' bytes"
@@ -3660,6 +3560,7 @@ ER_TOO_LONG_STRING 42000
ger "Ergebnis-String ist länger als 'max_allowed_packet' Bytes"
hun "Ez eredmeny sztring nagyobb, mint a lehetseges maximum: 'max_allowed_packet'"
ita "La stringa di risposta e` piu` lunga di 'max_allowed_packet'"
+ jpn "çµæžœã®æ–‡å­—列㌠'max_allowed_packet' よりも大ãã„ã§ã™ã€‚"
por "'String' resultante é mais longa do que 'max_allowed_packet'"
rum "Sirul rezultat este mai lung decit 'max_allowed_packet'"
rus "Ð ÐµÐ·ÑƒÐ»ÑŒÑ‚Ð¸Ñ€ÑƒÑŽÑ‰Ð°Ñ Ñтрока больше, чем 'max_allowed_packet'"
@@ -3668,41 +3569,41 @@ ER_TOO_LONG_STRING 42000
swe "Resultatsträngen är längre än max_allowed_packet"
ukr "Строка результату довша ніж max_allowed_packet"
ER_TABLE_CANT_HANDLE_BLOB 42000
- cze "Typ pou-Bžité tabulky nepodporuje BLOB/TEXT sloupce"
- dan "Denne tabeltype understøtter ikke brug af BLOB og TEXT kolonner"
- nla "Het gebruikte tabel type ondersteunt geen BLOB/TEXT kolommen"
- eng "The used table type doesn't support BLOB/TEXT columns"
- est "Valitud tabelitüüp ei toeta BLOB/TEXT tüüpi välju"
- fre "Ce type de table ne supporte pas les colonnes BLOB/TEXT"
- ger "Der verwendete Tabellentyp unterstützt keine BLOB- und TEXT-Felder"
- hun "A hasznalt tabla tipus nem tamogatja a BLOB/TEXT mezoket"
- ita "Il tipo di tabella usata non supporta colonne di tipo BLOB/TEXT"
- por "Tipo de tabela usado não permite colunas BLOB/TEXT"
- rum "Tipul de tabela folosit nu suporta coloane de tip BLOB/TEXT"
- rus "ИÑÐ¿Ð¾Ð»ÑŒÐ·ÑƒÐµÐ¼Ð°Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ð° не поддерживает типы BLOB/TEXT"
- serbian "Iskorišteni tip tabele ne podržava kolone tipa 'BLOB' odnosno 'TEXT'"
- spa "El tipo de tabla usada no permite soporte para columnas BLOB/TEXT"
- swe "Den använda tabelltypen kan inte hantera BLOB/TEXT-kolumner"
- ukr "ВикориÑтаний тип таблиці не підтримує BLOB/TEXT Ñтовбці"
+ cze "Typ použité tabulky (%s) nepodporuje BLOB/TEXT sloupce"
+ dan "Denne tabeltype (%s) understøtter ikke brug af BLOB og TEXT kolonner"
+ nla "Het gebruikte tabel type (%s) ondersteunt geen BLOB/TEXT kolommen"
+ eng "Storage engine %s doesn't support BLOB/TEXT columns"
+ est "Valitud tabelitüüp (%s) ei toeta BLOB/TEXT tüüpi välju"
+ fre "Ce type de table (%s) ne supporte pas les colonnes BLOB/TEXT"
+ ger "Der verwendete Tabellentyp (%s) unterstützt keine BLOB- und TEXT-Felder"
+ hun "A hasznalt tabla tipus (%s) nem tamogatja a BLOB/TEXT mezoket"
+ ita "Il tipo di tabella usata (%s) non supporta colonne di tipo BLOB/TEXT"
+ por "Tipo de tabela usado (%s) não permite colunas BLOB/TEXT"
+ rum "Tipul de tabela folosit (%s) nu suporta coloane de tip BLOB/TEXT"
+ rus "%s таблицы не поддерживают типы BLOB/TEXT"
+ serbian "Iskorišteni tip tabele (%s) ne podržava kolone tipa 'BLOB' odnosno 'TEXT'"
+ spa "El tipo de tabla usada (%s) no permite soporte para columnas BLOB/TEXT"
+ swe "Den använda tabelltypen (%s) kan inte hantera BLOB/TEXT-kolumner"
+ ukr "%s таблиці не підтримують BLOB/TEXT Ñтовбці"
ER_TABLE_CANT_HANDLE_AUTO_INCREMENT 42000
- cze "Typ pou-Bžité tabulky nepodporuje AUTO_INCREMENT sloupce"
- dan "Denne tabeltype understøtter ikke brug af AUTO_INCREMENT kolonner"
- nla "Het gebruikte tabel type ondersteunt geen AUTO_INCREMENT kolommen"
- eng "The used table type doesn't support AUTO_INCREMENT columns"
- est "Valitud tabelitüüp ei toeta AUTO_INCREMENT tüüpi välju"
- fre "Ce type de table ne supporte pas les colonnes AUTO_INCREMENT"
- ger "Der verwendete Tabellentyp unterstützt keine AUTO_INCREMENT-Felder"
- hun "A hasznalt tabla tipus nem tamogatja az AUTO_INCREMENT tipusu mezoket"
- ita "Il tipo di tabella usata non supporta colonne di tipo AUTO_INCREMENT"
- por "Tipo de tabela usado não permite colunas AUTO_INCREMENT"
- rum "Tipul de tabela folosit nu suporta coloane de tip AUTO_INCREMENT"
- rus "ИÑÐ¿Ð¾Ð»ÑŒÐ·ÑƒÐµÐ¼Ð°Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ð° не поддерживает автоинкрементные Ñтолбцы"
- serbian "Iskorišteni tip tabele ne podržava kolone tipa 'AUTO_INCREMENT'"
- spa "El tipo de tabla usada no permite soporte para columnas AUTO_INCREMENT"
- swe "Den använda tabelltypen kan inte hantera AUTO_INCREMENT-kolumner"
- ukr "ВикориÑтаний тип таблиці не підтримує AUTO_INCREMENT Ñтовбці"
+ cze "Typ použité tabulky (%s) nepodporuje AUTO_INCREMENT sloupce"
+ dan "Denne tabeltype understøtter (%s) ikke brug af AUTO_INCREMENT kolonner"
+ nla "Het gebruikte tabel type (%s) ondersteunt geen AUTO_INCREMENT kolommen"
+ eng "Storage engine %s doesn't support AUTO_INCREMENT columns"
+ est "Valitud tabelitüüp (%s) ei toeta AUTO_INCREMENT tüüpi välju"
+ fre "Ce type de table (%s) ne supporte pas les colonnes AUTO_INCREMENT"
+ ger "Der verwendete Tabellentyp (%s) unterstützt keine AUTO_INCREMENT-Felder"
+ hun "A hasznalt tabla tipus (%s) nem tamogatja az AUTO_INCREMENT tipusu mezoket"
+ ita "Il tipo di tabella usata (%s) non supporta colonne di tipo AUTO_INCREMENT"
+ por "Tipo de tabela usado (%s) não permite colunas AUTO_INCREMENT"
+ rum "Tipul de tabela folosit (%s) nu suporta coloane de tip AUTO_INCREMENT"
+ rus "%s таблицы не поддерживают автоинкрементные Ñтолбцы"
+ serbian "Iskorišteni tip tabele (%s) ne podržava kolone tipa 'AUTO_INCREMENT'"
+ spa "El tipo de tabla usada (%s) no permite soporte para columnas AUTO_INCREMENT"
+ swe "Den använda tabelltypen (%s) kan inte hantera AUTO_INCREMENT-kolumner"
+ ukr "%s таблиці не підтримують AUTO_INCREMENT Ñтовбці"
ER_DELAYED_INSERT_TABLE_LOCKED
- cze "INSERT DELAYED nen-Bí možno s tabulkou '%-.192s' použít, protože je zamÄená pomocí LOCK TABLES"
+ cze "INSERT DELAYED není možno s tabulkou '%-.192s' použít, protože je zamÄená pomocí LOCK TABLES"
dan "INSERT DELAYED kan ikke bruges med tabellen '%-.192s', fordi tabellen er låst med LOCK TABLES"
nla "INSERT DELAYED kan niet worden gebruikt bij table '%-.192s', vanwege een 'lock met LOCK TABLES"
eng "INSERT DELAYED can't be used with table '%-.192s' because it is locked with LOCK TABLES"
@@ -3712,7 +3613,7 @@ ER_DELAYED_INSERT_TABLE_LOCKED
greek "INSERT DELAYED can't be used with table '%-.192s', because it is locked with LOCK TABLES"
hun "Az INSERT DELAYED nem hasznalhato a '%-.192s' tablahoz, mert a tabla zarolt (LOCK TABLES)"
ita "L'inserimento ritardato (INSERT DELAYED) non puo` essere usato con la tabella '%-.192s', perche` soggetta a lock da 'LOCK TABLES'"
- jpn "INSERT DELAYED can't be used with table '%-.192s', because it is locked with LOCK TABLES"
+ jpn "表 '%-.192s' ã¯LOCK TABLESã§ãƒ­ãƒƒã‚¯ã•ã‚Œã¦ã„ã‚‹ãŸã‚ã€INSERT DELAYEDを使用ã§ãã¾ã›ã‚“。"
kor "INSERT DELAYED can't be used with table '%-.192s', because it is locked with LOCK TABLES"
nor "INSERT DELAYED can't be used with table '%-.192s', because it is locked with LOCK TABLES"
norwegian-ny "INSERT DELAYED can't be used with table '%-.192s', because it is locked with LOCK TABLES"
@@ -3726,7 +3627,7 @@ ER_DELAYED_INSERT_TABLE_LOCKED
swe "INSERT DELAYED kan inte användas med tabell '%-.192s', emedan den är låst med LOCK TABLES"
ukr "INSERT DELAYED не може бути викориÑтано з таблицею '%-.192s', тому що Ñ—Ñ— заблоковано з LOCK TABLES"
ER_WRONG_COLUMN_NAME 42000
- cze "Nespr-Bávné jméno sloupce '%-.100s'"
+ cze "Nesprávné jméno sloupce '%-.100s'"
dan "Forkert kolonnenavn '%-.100s'"
nla "Incorrecte kolom naam '%-.100s'"
eng "Incorrect column name '%-.100s'"
@@ -3735,6 +3636,7 @@ ER_WRONG_COLUMN_NAME 42000
ger "Falscher Spaltenname '%-.100s'"
hun "Ervenytelen mezonev: '%-.100s'"
ita "Nome colonna '%-.100s' non corretto"
+ jpn "列å '%-.100s' ã¯ä¸æ­£ã§ã™ã€‚"
por "Nome de coluna '%-.100s' incorreto"
rum "Nume increct de coloana '%-.100s'"
rus "Ðеверное Ð¸Ð¼Ñ Ñтолбца '%-.100s'"
@@ -3743,31 +3645,12 @@ ER_WRONG_COLUMN_NAME 42000
swe "Felaktigt kolumnnamn '%-.100s'"
ukr "Ðевірне ім'Ñ ÑÑ‚Ð¾Ð²Ð±Ñ†Ñ '%-.100s'"
ER_WRONG_KEY_COLUMN 42000
- cze "Handler pou-Bžité tabulky neumí indexovat sloupce '%-.192s'"
- dan "Den brugte tabeltype kan ikke indeksere kolonnen '%-.192s'"
- nla "De gebruikte tabel 'handler' kan kolom '%-.192s' niet indexeren"
- eng "The used storage engine can't index column '%-.192s'"
- est "Tabelihandler ei oska indekseerida tulpa '%-.192s'"
- fre "Le handler de la table ne peut indexé la colonne '%-.192s'"
- ger "Die verwendete Speicher-Engine kann die Spalte '%-.192s' nicht indizieren"
- greek "The used table handler can't index column '%-.192s'"
- hun "A hasznalt tablakezelo nem tudja a '%-.192s' mezot indexelni"
- ita "Il gestore delle tabelle non puo` indicizzare la colonna '%-.192s'"
- jpn "The used table handler can't index column '%-.192s'"
- kor "The used table handler can't index column '%-.192s'"
- nor "The used table handler can't index column '%-.192s'"
- norwegian-ny "The used table handler can't index column '%-.192s'"
- pol "The used table handler can't index column '%-.192s'"
- por "O manipulador de tabela usado não pode indexar a coluna '%-.192s'"
- rum "Handler-ul tabelei folosite nu poate indexa coloana '%-.192s'"
- rus "ИÑпользованный обработчик таблицы не может проиндекÑировать Ñтолбец '%-.192s'"
- serbian "Handler tabele ne može da indeksira kolonu '%-.192s'"
- slo "The used table handler can't index column '%-.192s'"
- spa "El manipulador de tabla usado no puede indexar columna '%-.192s'"
- swe "Den använda tabelltypen kan inte indexera kolumn '%-.192s'"
- ukr "ВикориÑтаний вказівник таблиці не може індекÑувати Ñтовбець '%-.192s'"
+ eng "The storage engine %s can't index column %`s"
+ ger "Die Speicher-Engine %s kann die Spalte %`s nicht indizieren"
+ rus "Обработчик таблиц %s не может проиндекÑировать Ñтолбец %`s"
+ ukr "Вказівник таблиц %s не може індекÑувати Ñтовбець %`s"
ER_WRONG_MRG_TABLE
- cze "V-Bšechny tabulky v MERGE tabulce nejsou definovány stejně"
+ cze "Všechny tabulky v MERGE tabulce nejsou definovány stejně"
dan "Tabellerne i MERGE er ikke defineret ens"
nla "Niet alle tabellen in de MERGE tabel hebben identieke gedefinities"
eng "Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exist"
@@ -3776,7 +3659,7 @@ ER_WRONG_MRG_TABLE
ger "Nicht alle Tabellen in der MERGE-Tabelle sind gleich definiert"
hun "A MERGE tablaban talalhato tablak definicioja nem azonos"
ita "Non tutte le tabelle nella tabella di MERGE sono definite in maniera identica"
- jpn "All tables in the MERGE table are not defined identically"
+ jpn "MERGE表ã®æ§‹æˆè¡¨ãŒã‚ªãƒ¼ãƒ—ンã§ãã¾ã›ã‚“。列定義ãŒç•°ãªã‚‹ã‹ã€MyISAM表ã§ã¯ãªã„ã‹ã€å­˜åœ¨ã—ã¾ã›ã‚“。"
kor "All tables in the MERGE table are not defined identically"
nor "All tables in the MERGE table are not defined identically"
norwegian-ny "All tables in the MERGE table are not defined identically"
@@ -3790,7 +3673,7 @@ ER_WRONG_MRG_TABLE
swe "Tabellerna i MERGE-tabellen är inte identiskt definierade"
ukr "Таблиці у MERGE TABLE мають різну Ñтруктуру"
ER_DUP_UNIQUE 23000
- cze "Kv-Bůli unique constraintu nemozu zapsat do tabulky '%-.192s'"
+ cze "Kvůli unique constraintu nemozu zapsat do tabulky '%-.192s'"
dan "Kan ikke skrive til tabellen '%-.192s' fordi det vil bryde CONSTRAINT regler"
nla "Kan niet opslaan naar table '%-.192s' vanwege 'unique' beperking"
eng "Can't write, because of unique constraint, to table '%-.192s'"
@@ -3798,6 +3681,7 @@ ER_DUP_UNIQUE 23000
fre "Écriture impossible à cause d'un index UNIQUE sur la table '%-.192s'"
ger "Schreiben in Tabelle '%-.192s' nicht möglich wegen einer Eindeutigkeitsbeschränkung (unique constraint)"
hun "A '%-.192s' nem irhato, az egyedi mezok miatt"
+ jpn "一æ„性制約é•åã®ãŸã‚ã€è¡¨ '%-.192s' ã«æ›¸ãè¾¼ã‚ã¾ã›ã‚“。"
ita "Impossibile scrivere nella tabella '%-.192s' per limitazione di unicita`"
por "Não pode gravar, devido à restrição UNIQUE, na tabela '%-.192s'"
rum "Nu pot scrie pe hard-drive, din cauza constraintului unic (unique constraint) pentru tabela '%-.192s'"
@@ -3807,7 +3691,7 @@ ER_DUP_UNIQUE 23000
swe "Kan inte skriva till tabell '%-.192s'; UNIQUE-test"
ukr "Ðе можу запиÑати до таблиці '%-.192s', з причини вимог унікальноÑÑ‚Ñ–"
ER_BLOB_KEY_WITHOUT_LENGTH 42000
- cze "BLOB sloupec '%-.192s' je pou-Bžit ve specifikaci klíÄe bez délky"
+ cze "BLOB sloupec '%-.192s' je použit ve specifikaci klíÄe bez délky"
dan "BLOB kolonnen '%-.192s' brugt i nøglespecifikation uden nøglelængde"
nla "BLOB kolom '%-.192s' gebruikt in zoeksleutel specificatie zonder zoeksleutel lengte"
eng "BLOB/TEXT column '%-.192s' used in key specification without a key length"
@@ -3817,7 +3701,7 @@ ER_BLOB_KEY_WITHOUT_LENGTH 42000
greek "BLOB column '%-.192s' used in key specification without a key length"
hun "BLOB mezo '%-.192s' hasznalt a mezo specifikacioban, a mezohossz megadasa nelkul"
ita "La colonna '%-.192s' di tipo BLOB e` usata in una chiave senza specificarne la lunghezza"
- jpn "BLOB column '%-.192s' used in key specification without a key length"
+ jpn "BLOB列 '%-.192s' をキーã«ä½¿ç”¨ã™ã‚‹ã«ã¯é•·ã•æŒ‡å®šãŒå¿…è¦ã§ã™ã€‚"
kor "BLOB column '%-.192s' used in key specification without a key length"
nor "BLOB column '%-.192s' used in key specification without a key length"
norwegian-ny "BLOB column '%-.192s' used in key specification without a key length"
@@ -3831,7 +3715,7 @@ ER_BLOB_KEY_WITHOUT_LENGTH 42000
swe "Du har inte angett någon nyckellängd för BLOB '%-.192s'"
ukr "Стовбець BLOB '%-.192s' викориÑтано у визначенні ключа без Ð²ÐºÐ°Ð·Ð°Ð½Ð½Ñ Ð´Ð¾Ð²Ð¶Ð¸Ð½Ð¸ ключа"
ER_PRIMARY_CANT_HAVE_NULL 42000
- cze "V-BÅ¡echny Äásti primárního klíÄe musejí být NOT NULL; pokud potÅ™ebujete NULL, použijte UNIQUE"
+ cze "VÅ¡echny Äásti primárního klíÄe musejí být NOT NULL; pokud potÅ™ebujete NULL, použijte UNIQUE"
dan "Alle dele af en PRIMARY KEY skal være NOT NULL; Hvis du skal bruge NULL i nøglen, brug UNIQUE istedet"
nla "Alle delen van een PRIMARY KEY moeten NOT NULL zijn; Indien u NULL in een zoeksleutel nodig heeft kunt u UNIQUE gebruiken"
eng "All parts of a PRIMARY KEY must be NOT NULL; if you need NULL in a key, use UNIQUE instead"
@@ -3840,6 +3724,7 @@ ER_PRIMARY_CANT_HAVE_NULL 42000
ger "Alle Teile eines PRIMARY KEY müssen als NOT NULL definiert sein. Wenn NULL in einem Schlüssel benötigt wird, muss ein UNIQUE-Schlüssel verwendet werden"
hun "Az elsodleges kulcs teljes egeszeben csak NOT NULL tipusu lehet; Ha NULL mezot szeretne a kulcskent, hasznalja inkabb a UNIQUE-ot"
ita "Tutte le parti di una chiave primaria devono essere dichiarate NOT NULL; se necessitano valori NULL nelle chiavi utilizzare UNIQUE"
+ jpn "PRIMARY KEYã®åˆ—ã¯å…¨ã¦NOT NULLã§ãªã‘ã‚Œã°ã„ã‘ã¾ã›ã‚“。UNIQUE索引ã§ã‚ã‚Œã°NULLã‚’å«ã‚€ã“ã¨ãŒå¯èƒ½ã§ã™ã€‚"
por "Todas as partes de uma chave primária devem ser não-nulas. Se você precisou usar um valor nulo (NULL) em uma chave, use a cláusula UNIQUE em seu lugar"
rum "Toate partile unei chei primare (PRIMARY KEY) trebuie sa fie NOT NULL; Daca aveti nevoie de NULL in vreo cheie, folositi UNIQUE in schimb"
rus "Ð’Ñе чаÑти первичного ключа (PRIMARY KEY) должны быть определены как NOT NULL; ЕÑли вам нужна поддержка величин NULL в ключе, воÑпользуйтеÑÑŒ индекÑом UNIQUE"
@@ -3848,7 +3733,7 @@ ER_PRIMARY_CANT_HAVE_NULL 42000
swe "Alla delar av en PRIMARY KEY måste vara NOT NULL; Om du vill ha en nyckel med NULL, använd UNIQUE istället"
ukr "УÑÑ– чаÑтини PRIMARY KEY повинні бути NOT NULL; Якщо ви потребуєте NULL у ключі, ÑкориÑтайтеÑÑ UNIQUE"
ER_TOO_MANY_ROWS 42000
- cze "V-Býsledek obsahuje více než jeden řádek"
+ cze "Výsledek obsahuje více než jeden řádek"
dan "Resultatet bestod af mere end een række"
nla "Resultaat bevatte meer dan een rij"
eng "Result consisted of more than one row"
@@ -3857,6 +3742,7 @@ ER_TOO_MANY_ROWS 42000
ger "Ergebnis besteht aus mehr als einer Zeile"
hun "Az eredmeny tobb, mint egy sort tartalmaz"
ita "Il risultato consiste di piu` di una riga"
+ jpn "çµæžœãŒ2行以上ã§ã™ã€‚"
por "O resultado consistiu em mais do que uma linha"
rum "Resultatul constista din mai multe linii"
rus "Ð’ результате возвращена более чем одна Ñтрока"
@@ -3865,7 +3751,7 @@ ER_TOO_MANY_ROWS 42000
swe "Resultet bestod av mera än en rad"
ukr "Результат знаходитьÑÑ Ñƒ більше ніж одній Ñтроці"
ER_REQUIRES_PRIMARY_KEY 42000
- cze "Tento typ tabulky vy-Bžaduje primární klíÄ"
+ cze "Tento typ tabulky vyžaduje primární klíÄ"
dan "Denne tabeltype kræver en primærnøgle"
nla "Dit tabel type heeft een primaire zoeksleutel nodig"
eng "This table type requires a primary key"
@@ -3874,6 +3760,7 @@ ER_REQUIRES_PRIMARY_KEY 42000
ger "Dieser Tabellentyp benötigt einen Primärschlüssel (PRIMARY KEY)"
hun "Az adott tablatipushoz elsodleges kulcs hasznalata kotelezo"
ita "Questo tipo di tabella richiede una chiave primaria"
+ jpn "使用ã®ã‚¹ãƒˆãƒ¬ãƒ¼ã‚¸ã‚¨ãƒ³ã‚¸ãƒ³ã§ã¯ã€PRIMARY KEYãŒå¿…è¦ã§ã™ã€‚"
por "Este tipo de tabela requer uma chave primária"
rum "Aceast tip de tabela are nevoie de o cheie primara"
rus "Этот тип таблицы требует Ð¾Ð¿Ñ€ÐµÐ´ÐµÐ»ÐµÐ½Ð¸Ñ Ð¿ÐµÑ€Ð²Ð¸Ñ‡Ð½Ð¾Ð³Ð¾ ключа"
@@ -3882,7 +3769,7 @@ ER_REQUIRES_PRIMARY_KEY 42000
swe "Denna tabelltyp kräver en PRIMARY KEY"
ukr "Цей тип таблиці потребує первинного ключа"
ER_NO_RAID_COMPILED
- cze "Tato verze MariaDB nen-Bí zkompilována s podporou RAID"
+ cze "Tato verze MySQL není zkompilována s podporou RAID"
dan "Denne udgave af MariaDB er ikke oversat med understøttelse af RAID"
nla "Deze versie van MariaDB is niet gecompileerd met RAID ondersteuning"
eng "This version of MariaDB is not compiled with RAID support"
@@ -3891,6 +3778,7 @@ ER_NO_RAID_COMPILED
ger "Diese MariaDB-Version ist nicht mit RAID-Unterstützung kompiliert"
hun "Ezen leforditott MariaDB verzio nem tartalmaz RAID support-ot"
ita "Questa versione di MYSQL non e` compilata con il supporto RAID"
+ jpn "ã“ã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã®MySQLã¯RAIDサãƒãƒ¼ãƒˆã‚’å«ã‚ã¦ã‚³ãƒ³ãƒ‘イルã•ã‚Œã¦ã„ã¾ã›ã‚“。"
por "Esta versão do MariaDB não foi compilada com suporte a RAID"
rum "Aceasta versiune de MariaDB, nu a fost compilata cu suport pentru RAID"
rus "Эта верÑÐ¸Ñ MariaDB Ñкомпилирована без поддержки RAID"
@@ -3899,7 +3787,7 @@ ER_NO_RAID_COMPILED
swe "Denna version av MariaDB är inte kompilerad med RAID"
ukr "Ð¦Ñ Ð²ÐµÑ€ÑÑ–Ñ MariaDB не зкомпільована з підтримкою RAID"
ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE
- cze "Update tabulky bez WHERE s kl-BíÄem není v módu bezpeÄných update dovoleno"
+ cze "Update tabulky bez WHERE s klíÄem není v módu bezpeÄných update dovoleno"
dan "Du bruger sikker opdaterings modus ('safe update mode') og du forsøgte at opdatere en tabel uden en WHERE klausul, der gør brug af et KEY felt"
nla "U gebruikt 'safe update mode' en u probeerde een tabel te updaten zonder een WHERE met een KEY kolom"
eng "You are using safe update mode and you tried to update a table without a WHERE that uses a KEY column"
@@ -3908,6 +3796,7 @@ ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE
ger "MariaDB läuft im sicheren Aktualisierungsmodus (safe update mode). Sie haben versucht, eine Tabelle zu aktualisieren, ohne in der WHERE-Klausel ein KEY-Feld anzugeben"
hun "On a biztonsagos update modot hasznalja, es WHERE that uses a KEY column"
ita "In modalita` 'safe update' si e` cercato di aggiornare una tabella senza clausola WHERE su una chiave"
+ jpn "'safe update mode'ã§ã€ç´¢å¼•ã‚’利用ã™ã‚‹WHEREå¥ã®ç„¡ã„更新処ç†ã‚’実行ã—よã†ã¨ã—ã¾ã—ãŸã€‚"
por "Você está usando modo de atualização seguro e tentou atualizar uma tabela sem uma cláusula WHERE que use uma coluna chave"
rus "Ð’Ñ‹ работаете в режиме безопаÑных обновлений (safe update mode) и попробовали изменить таблицу без иÑÐ¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ ÐºÐ»ÑŽÑ‡ÐµÐ²Ð¾Ð³Ð¾ Ñтолбца в чаÑти WHERE"
serbian "Vi koristite safe update mod servera, a probali ste da promenite podatke bez 'WHERE' komande koja koristi kolonu kljuÄa"
@@ -3915,7 +3804,7 @@ ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE
swe "Du använder 'säker uppdateringsmod' och försökte uppdatera en tabell utan en WHERE-sats som använder sig av en nyckel"
ukr "Ви у режимі безпечного Ð¾Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ñ‚Ð° намагаєтеÑÑŒ оновити таблицю без оператора WHERE, що викориÑтовує KEY Ñтовбець"
ER_KEY_DOES_NOT_EXITS 42000 S1009
- cze "Kl-BÃ­Ä '%-.192s' v tabulce '%-.192s' neexistuje"
+ cze "KlÃ­Ä '%-.192s' v tabulce '%-.192s' neexistuje"
dan "Nøglen '%-.192s' eksisterer ikke i tabellen '%-.192s'"
nla "Zoeksleutel '%-.192s' bestaat niet in tabel '%-.192s'"
eng "Key '%-.192s' doesn't exist in table '%-.192s'"
@@ -3924,6 +3813,7 @@ ER_KEY_DOES_NOT_EXITS 42000 S1009
ger "Schlüssel '%-.192s' existiert in der Tabelle '%-.192s' nicht"
hun "A '%-.192s' kulcs nem letezik a '%-.192s' tablaban"
ita "La chiave '%-.192s' non esiste nella tabella '%-.192s'"
+ jpn "索引 '%-.192s' ã¯è¡¨ '%-.192s' ã«ã¯å­˜åœ¨ã—ã¾ã›ã‚“。"
por "Chave '%-.192s' não existe na tabela '%-.192s'"
rus "Ключ '%-.192s' не ÑущеÑтвует в таблице '%-.192s'"
serbian "KljuÄ '%-.192s' ne postoji u tabeli '%-.192s'"
@@ -3931,7 +3821,7 @@ ER_KEY_DOES_NOT_EXITS 42000 S1009
swe "Nyckel '%-.192s' finns inte in tabell '%-.192s'"
ukr "Ключ '%-.192s' не Ñ–Ñнує в таблиці '%-.192s'"
ER_CHECK_NO_SUCH_TABLE 42000
- cze "Nemohu otev-Břít tabulku"
+ cze "Nemohu otevřít tabulku"
dan "Kan ikke åbne tabellen"
nla "Kan tabel niet openen"
eng "Can't open table"
@@ -3940,6 +3830,7 @@ ER_CHECK_NO_SUCH_TABLE 42000
ger "Kann Tabelle nicht öffnen"
hun "Nem tudom megnyitni a tablat"
ita "Impossibile aprire la tabella"
+ jpn "表をオープンã§ãã¾ã›ã‚“。"
por "Não pode abrir a tabela"
rus "Ðевозможно открыть таблицу"
serbian "Ne mogu da otvorim tabelu"
@@ -3957,7 +3848,7 @@ ER_CHECK_NOT_IMPLEMENTED 42000
greek "The handler for the table doesn't support %s"
hun "A tabla kezeloje (handler) nem tamogatja az %s"
ita "Il gestore per la tabella non supporta il %s"
- jpn "The handler for the table doesn't support %s"
+ jpn "ã“ã®è¡¨ã®ã‚¹ãƒˆãƒ¬ãƒ¼ã‚¸ã‚¨ãƒ³ã‚¸ãƒ³ã¯ '%s' を利用ã§ãã¾ã›ã‚“。"
kor "The handler for the table doesn't support %s"
nor "The handler for the table doesn't support %s"
norwegian-ny "The handler for the table doesn't support %s"
@@ -3971,7 +3862,7 @@ ER_CHECK_NOT_IMPLEMENTED 42000
swe "Tabellhanteraren för denna tabell kan inte göra %s"
ukr "Вказівник таблиці не підтримуе %s"
ER_CANT_DO_THIS_DURING_AN_TRANSACTION 25000
- cze "Proveden-Bí tohoto příkazu není v transakci dovoleno"
+ cze "Provedení tohoto příkazu není v transakci dovoleno"
dan "Du må ikke bruge denne kommando i en transaktion"
nla "Het is u niet toegestaan dit commando uit te voeren binnen een transactie"
eng "You are not allowed to execute this command in a transaction"
@@ -3980,6 +3871,7 @@ ER_CANT_DO_THIS_DURING_AN_TRANSACTION 25000
ger "Sie dürfen diesen Befehl nicht in einer Transaktion ausführen"
hun "Az On szamara nem engedelyezett a parancs vegrehajtasa a tranzakcioban"
ita "Non puoi eseguire questo comando in una transazione"
+ jpn "ã“ã®ã‚³ãƒžãƒ³ãƒ‰ã¯ãƒˆãƒ©ãƒ³ã‚¶ã‚¯ã‚·ãƒ§ãƒ³å†…ã§å®Ÿè¡Œã§ãã¾ã›ã‚“。"
por "Não lhe é permitido executar este comando em uma transação"
rus "Вам не разрешено выполнÑÑ‚ÑŒ Ñту команду в транзакции"
serbian "Nije Vam dozvoljeno da izvršite ovu komandu u transakciji"
@@ -3987,71 +3879,75 @@ ER_CANT_DO_THIS_DURING_AN_TRANSACTION 25000
swe "Du får inte utföra detta kommando i en transaktion"
ukr "Вам не дозволено виконувати цю команду в транзакції"
ER_ERROR_DURING_COMMIT
- cze "Chyba %d p-Bři COMMIT"
- dan "Modtog fejl %d mens kommandoen COMMIT blev udført"
- nla "Kreeg fout %d tijdens COMMIT"
- eng "Got error %d during COMMIT"
- est "Viga %d käsu COMMIT täitmisel"
- fre "Erreur %d lors du COMMIT"
- ger "Fehler %d beim COMMIT"
- hun "%d hiba a COMMIT vegrehajtasa soran"
- ita "Rilevato l'errore %d durante il COMMIT"
- por "Obteve erro %d durante COMMIT"
- rus "Получена ошибка %d в процеÑÑе COMMIT"
- serbian "Greška %d za vreme izvršavanja komande 'COMMIT'"
- spa "Obtenido error %d durante COMMIT"
- swe "Fick fel %d vid COMMIT"
- ukr "Отримано помилку %d під Ñ‡Ð°Ñ COMMIT"
+ cze "Chyba %M při COMMIT"
+ dan "Modtog fejl %M mens kommandoen COMMIT blev udført"
+ nla "Kreeg fout %M tijdens COMMIT"
+ eng "Got error %M during COMMIT"
+ est "Viga %M käsu COMMIT täitmisel"
+ fre "Erreur %M lors du COMMIT"
+ ger "Fehler %M beim COMMIT"
+ hun "%M hiba a COMMIT vegrehajtasa soran"
+ ita "Rilevato l'errore %M durante il COMMIT"
+ jpn "COMMIT中ã«ã‚¨ãƒ©ãƒ¼ %M ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
+ por "Obteve erro %M durante COMMIT"
+ rus "Получена ошибка %M в процеÑÑе COMMIT"
+ serbian "Greška %M za vreme izvršavanja komande 'COMMIT'"
+ spa "Obtenido error %M durante COMMIT"
+ swe "Fick fel %M vid COMMIT"
+ ukr "Отримано помилку %M під Ñ‡Ð°Ñ COMMIT"
ER_ERROR_DURING_ROLLBACK
- cze "Chyba %d p-Bři ROLLBACK"
- dan "Modtog fejl %d mens kommandoen ROLLBACK blev udført"
- nla "Kreeg fout %d tijdens ROLLBACK"
- eng "Got error %d during ROLLBACK"
- est "Viga %d käsu ROLLBACK täitmisel"
- fre "Erreur %d lors du ROLLBACK"
- ger "Fehler %d beim ROLLBACK"
- hun "%d hiba a ROLLBACK vegrehajtasa soran"
- ita "Rilevato l'errore %d durante il ROLLBACK"
- por "Obteve erro %d durante ROLLBACK"
- rus "Получена ошибка %d в процеÑÑе ROLLBACK"
- serbian "Greška %d za vreme izvršavanja komande 'ROLLBACK'"
- spa "Obtenido error %d durante ROLLBACK"
- swe "Fick fel %d vid ROLLBACK"
- ukr "Отримано помилку %d під Ñ‡Ð°Ñ ROLLBACK"
+ cze "Chyba %M při ROLLBACK"
+ dan "Modtog fejl %M mens kommandoen ROLLBACK blev udført"
+ nla "Kreeg fout %M tijdens ROLLBACK"
+ eng "Got error %M during ROLLBACK"
+ est "Viga %M käsu ROLLBACK täitmisel"
+ fre "Erreur %M lors du ROLLBACK"
+ ger "Fehler %M beim ROLLBACK"
+ hun "%M hiba a ROLLBACK vegrehajtasa soran"
+ ita "Rilevato l'errore %M durante il ROLLBACK"
+ jpn "ROLLBACK中ã«ã‚¨ãƒ©ãƒ¼ %M ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
+ por "Obteve erro %M durante ROLLBACK"
+ rus "Получена ошибка %M в процеÑÑе ROLLBACK"
+ serbian "Greška %M za vreme izvršavanja komande 'ROLLBACK'"
+ spa "Obtenido error %M durante ROLLBACK"
+ swe "Fick fel %M vid ROLLBACK"
+ ukr "Отримано помилку %M під Ñ‡Ð°Ñ ROLLBACK"
ER_ERROR_DURING_FLUSH_LOGS
- cze "Chyba %d p-Bři FLUSH_LOGS"
- dan "Modtog fejl %d mens kommandoen FLUSH_LOGS blev udført"
- nla "Kreeg fout %d tijdens FLUSH_LOGS"
- eng "Got error %d during FLUSH_LOGS"
- est "Viga %d käsu FLUSH_LOGS täitmisel"
- fre "Erreur %d lors du FLUSH_LOGS"
- ger "Fehler %d bei FLUSH_LOGS"
- hun "%d hiba a FLUSH_LOGS vegrehajtasa soran"
- ita "Rilevato l'errore %d durante il FLUSH_LOGS"
- por "Obteve erro %d durante FLUSH_LOGS"
- rus "Получена ошибка %d в процеÑÑе FLUSH_LOGS"
- serbian "Greška %d za vreme izvršavanja komande 'FLUSH_LOGS'"
- spa "Obtenido error %d durante FLUSH_LOGS"
- swe "Fick fel %d vid FLUSH_LOGS"
- ukr "Отримано помилку %d під Ñ‡Ð°Ñ FLUSH_LOGS"
+ cze "Chyba %M při FLUSH_LOGS"
+ dan "Modtog fejl %M mens kommandoen FLUSH_LOGS blev udført"
+ nla "Kreeg fout %M tijdens FLUSH_LOGS"
+ eng "Got error %M during FLUSH_LOGS"
+ est "Viga %M käsu FLUSH_LOGS täitmisel"
+ fre "Erreur %M lors du FLUSH_LOGS"
+ ger "Fehler %M bei FLUSH_LOGS"
+ hun "%M hiba a FLUSH_LOGS vegrehajtasa soran"
+ ita "Rilevato l'errore %M durante il FLUSH_LOGS"
+ jpn "FLUSH_LOGS中ã«ã‚¨ãƒ©ãƒ¼ %M ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
+ por "Obteve erro %M durante FLUSH_LOGS"
+ rus "Получена ошибка %M в процеÑÑе FLUSH_LOGS"
+ serbian "Greška %M za vreme izvršavanja komande 'FLUSH_LOGS'"
+ spa "Obtenido error %M durante FLUSH_LOGS"
+ swe "Fick fel %M vid FLUSH_LOGS"
+ ukr "Отримано помилку %M під Ñ‡Ð°Ñ FLUSH_LOGS"
ER_ERROR_DURING_CHECKPOINT
- cze "Chyba %d p-Bři CHECKPOINT"
- dan "Modtog fejl %d mens kommandoen CHECKPOINT blev udført"
- nla "Kreeg fout %d tijdens CHECKPOINT"
- eng "Got error %d during CHECKPOINT"
- est "Viga %d käsu CHECKPOINT täitmisel"
- fre "Erreur %d lors du CHECKPOINT"
- ger "Fehler %d bei CHECKPOINT"
- hun "%d hiba a CHECKPOINT vegrehajtasa soran"
- ita "Rilevato l'errore %d durante il CHECKPOINT"
- por "Obteve erro %d durante CHECKPOINT"
- rus "Получена ошибка %d в процеÑÑе CHECKPOINT"
- serbian "Greška %d za vreme izvršavanja komande 'CHECKPOINT'"
- spa "Obtenido error %d durante CHECKPOINT"
- swe "Fick fel %d vid CHECKPOINT"
- ukr "Отримано помилку %d під Ñ‡Ð°Ñ CHECKPOINT"
+ cze "Chyba %M při CHECKPOINT"
+ dan "Modtog fejl %M mens kommandoen CHECKPOINT blev udført"
+ nla "Kreeg fout %M tijdens CHECKPOINT"
+ eng "Got error %M during CHECKPOINT"
+ est "Viga %M käsu CHECKPOINT täitmisel"
+ fre "Erreur %M lors du CHECKPOINT"
+ ger "Fehler %M bei CHECKPOINT"
+ hun "%M hiba a CHECKPOINT vegrehajtasa soran"
+ ita "Rilevato l'errore %M durante il CHECKPOINT"
+ jpn "CHECKPOINT中ã«ã‚¨ãƒ©ãƒ¼ %M ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
+ por "Obteve erro %M durante CHECKPOINT"
+ rus "Получена ошибка %M в процеÑÑе CHECKPOINT"
+ serbian "Greška %M za vreme izvršavanja komande 'CHECKPOINT'"
+ spa "Obtenido error %M durante CHECKPOINT"
+ swe "Fick fel %M vid CHECKPOINT"
+ ukr "Отримано помилку %M під Ñ‡Ð°Ñ CHECKPOINT"
ER_NEW_ABORTING_CONNECTION 08S01
- cze "Spojen-Bí %ld do databáze: '%-.192s' uživatel: '%-.48s' stroj: '%-.64s' (%-.64s) bylo přerušeno"
+ cze "Spojení %ld do databáze: '%-.192s' uživatel: '%-.48s' stroj: '%-.64s' (%-.64s) bylo přerušeno"
dan "Afbrød forbindelsen %ld til databasen '%-.192s' bruger: '%-.48s' vært: '%-.64s' (%-.64s)"
nla "Afgebroken verbinding %ld naar db: '%-.192s' gebruiker: '%-.48s' host: '%-.64s' (%-.64s)"
eng "Aborted connection %ld to db: '%-.192s' user: '%-.48s' host: '%-.64s' (%-.64s)"
@@ -4059,37 +3955,25 @@ ER_NEW_ABORTING_CONNECTION 08S01
fre "Connection %ld avortée vers la bd: '%-.192s' utilisateur: '%-.48s' hôte: '%-.64s' (%-.64s)"
ger "Abbruch der Verbindung %ld zur Datenbank '%-.192s'. Benutzer: '%-.48s', Host: '%-.64s' (%-.64s)"
ita "Interrotta la connessione %ld al db: ''%-.192s' utente: '%-.48s' host: '%-.64s' (%-.64s)"
+ jpn "接続 %ld ãŒä¸­æ–­ã•ã‚Œã¾ã—ãŸã€‚データベース: '%-.192s' ユーザー: '%-.48s' ホスト: '%-.64s' (%-.64s)"
por "Conexão %ld abortada para banco de dados '%-.192s' - usuário '%-.48s' - 'host' '%-.64s' ('%-.64s')"
rus "Прервано Ñоединение %ld к базе данных '%-.192s' Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ '%-.48s' Ñ Ñ…Ð¾Ñта '%-.64s' (%-.64s)"
serbian "Prekinuta konekcija broj %ld ka bazi: '%-.192s' korisnik je bio: '%-.48s' a host: '%-.64s' (%-.64s)"
spa "Abortada conexión %ld para db: '%-.192s' usuario: '%-.48s' servidor: '%-.64s' (%-.64s)"
swe "Avbröt länken för tråd %ld till db '%-.192s', användare '%-.48s', host '%-.64s' (%-.64s)"
ukr "Перервано з'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ %ld до бази данних: '%-.192s' кориÑтувач: '%-.48s' хоÑÑ‚: '%-.64s' (%-.64s)"
-ER_DUMP_NOT_IMPLEMENTED
- cze "Handler tabulky nepodporuje bin-Bární dump"
- dan "Denne tabeltype unserstøtter ikke binært tabeldump"
- nla "De 'handler' voor de tabel ondersteund geen binaire tabel dump"
- eng "The storage engine for the table does not support binary table dump"
- fre "Ce type de table ne supporte pas les copies binaires"
- ger "Die Speicher-Engine für die Tabelle unterstützt keinen binären Tabellen-Dump"
- ita "Il gestore per la tabella non supporta il dump binario"
- jpn "The handler for the table does not support binary table dump"
- por "O manipulador de tabela não suporta 'dump' binário de tabela"
- rum "The handler for the table does not support binary table dump"
- rus "Обработчик Ñтой таблицы не поддерживает двоичного ÑÐ¾Ñ…Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ Ð¾Ð±Ñ€Ð°Ð·Ð° таблицы (dump)"
- serbian "Handler tabele ne podržava binarni dump tabele"
- spa "El manipulador de tabla no soporta dump para tabla binaria"
- swe "Tabellhanteraren klarar inte en binär kopiering av tabellen"
- ukr "Цей тип таблиці не підтримує бінарну передачу таблиці"
+ER_UNUSED_10
+ eng "You should never see it"
ER_FLUSH_MASTER_BINLOG_CLOSED
eng "Binlog closed, cannot RESET MASTER"
ger "Binlog geschlossen. Kann RESET MASTER nicht ausführen"
+ jpn "ãƒã‚¤ãƒŠãƒªãƒ­ã‚°ãŒã‚¯ãƒ­ãƒ¼ã‚ºã•ã‚Œã¦ã„ã¾ã™ã€‚RESET MASTER を実行ã§ãã¾ã›ã‚“。"
por "Binlog fechado. Não pode fazer RESET MASTER"
rus "Двоичный журнал Ð¾Ð±Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ Ð·Ð°ÐºÑ€Ñ‹Ñ‚, невозможно выполнить RESET MASTER"
serbian "Binarni log file zatvoren, ne mogu da izvršim komandu 'RESET MASTER'"
ukr "Реплікаційний лог закрито, не можу виконати RESET MASTER"
ER_INDEX_REBUILD
- cze "P-Břebudování indexu dumpnuté tabulky '%-.192s' nebylo úspěšné"
+ cze "Přebudování indexu dumpnuté tabulky '%-.192s' nebylo úspěšné"
dan "Kunne ikke genopbygge indekset for den dumpede tabel '%-.192s'"
nla "Gefaald tijdens heropbouw index van gedumpte tabel '%-.192s'"
eng "Failed rebuilding the index of dumped table '%-.192s'"
@@ -4098,6 +3982,7 @@ ER_INDEX_REBUILD
greek "Failed rebuilding the index of dumped table '%-.192s'"
hun "Failed rebuilding the index of dumped table '%-.192s'"
ita "Fallita la ricostruzione dell'indice della tabella copiata '%-.192s'"
+ jpn "ダンプ表 '%-.192s' ã®ç´¢å¼•å†æ§‹ç¯‰ã«å¤±æ•—ã—ã¾ã—ãŸã€‚"
por "Falhou na reconstrução do índice da tabela 'dumped' '%-.192s'"
rus "Ошибка переÑтройки индекÑа Ñохраненной таблицы '%-.192s'"
serbian "Izgradnja indeksa dump-ovane tabele '%-.192s' nije uspela"
@@ -4111,20 +3996,22 @@ ER_MASTER
fre "Erreur reçue du maître: '%-.64s'"
ger "Fehler vom Master: '%-.64s'"
ita "Errore dal master: '%-.64s"
+ jpn "マスターã§ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿ: '%-.64s'"
por "Erro no 'master' '%-.64s'"
rus "Ошибка от головного Ñервера: '%-.64s'"
serbian "Greška iz glavnog servera '%-.64s' u klasteru"
spa "Error del master: '%-.64s'"
- swe "Fick en master: '%-.64s'"
+ swe "Fel från master: '%-.64s'"
ukr "Помилка від головного: '%-.64s'"
ER_MASTER_NET_READ 08S01
- cze "S-Bíťová chyba pÅ™i Ätení z masteru"
+ cze "Síťová chyba pÅ™i Ätení z masteru"
dan "Netværksfejl ved læsning fra master"
nla "Net fout tijdens lezen van master"
eng "Net error reading from master"
fre "Erreur de lecture réseau reçue du maître"
ger "Netzfehler beim Lesen vom Master"
ita "Errore di rete durante la ricezione dal master"
+ jpn "マスターã‹ã‚‰ã®ãƒ‡ãƒ¼ã‚¿å—信中ã®ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã‚¨ãƒ©ãƒ¼"
por "Erro de rede lendo do 'master'"
rus "Возникла ошибка Ñ‡Ñ‚ÐµÐ½Ð¸Ñ Ð² процеÑÑе коммуникации Ñ Ð³Ð¾Ð»Ð¾Ð²Ð½Ñ‹Ð¼ Ñервером"
serbian "Greška u primanju mrežnih paketa sa glavnog servera u klasteru"
@@ -4132,13 +4019,14 @@ ER_MASTER_NET_READ 08S01
swe "Fick nätverksfel vid läsning från master"
ukr "Мережева помилка Ñ‡Ð¸Ñ‚Ð°Ð½Ð½Ñ Ð²Ñ–Ð´ головного"
ER_MASTER_NET_WRITE 08S01
- cze "S-Bíťová chyba při zápisu na master"
+ cze "Síťová chyba při zápisu na master"
dan "Netværksfejl ved skrivning til master"
nla "Net fout tijdens schrijven naar master"
eng "Net error writing to master"
fre "Erreur d'écriture réseau reçue du maître"
ger "Netzfehler beim Schreiben zum Master"
ita "Errore di rete durante l'invio al master"
+ jpn "マスターã¸ã®ãƒ‡ãƒ¼ã‚¿é€ä¿¡ä¸­ã®ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã‚¨ãƒ©ãƒ¼"
por "Erro de rede gravando no 'master'"
rus "Возникла ошибка запиÑи в процеÑÑе коммуникации Ñ Ð³Ð¾Ð»Ð¾Ð²Ð½Ñ‹Ð¼ Ñервером"
serbian "Greška u slanju mrežnih paketa na glavni server u klasteru"
@@ -4146,7 +4034,7 @@ ER_MASTER_NET_WRITE 08S01
swe "Fick nätverksfel vid skrivning till master"
ukr "Мережева помилка запиÑу до головного"
ER_FT_MATCHING_KEY_NOT_FOUND
- cze "-BŽádný sloupec nemá vytvořen fulltextový index"
+ cze "Žádný sloupec nemá vytvořen fulltextový index"
dan "Kan ikke finde en FULLTEXT nøgle som svarer til kolonne listen"
nla "Kan geen FULLTEXT index vinden passend bij de kolom lijst"
eng "Can't find FULLTEXT index matching the column list"
@@ -4154,6 +4042,7 @@ ER_FT_MATCHING_KEY_NOT_FOUND
fre "Impossible de trouver un index FULLTEXT correspondant à cette liste de colonnes"
ger "Kann keinen FULLTEXT-Index finden, der der Feldliste entspricht"
ita "Impossibile trovare un indice FULLTEXT che corrisponda all'elenco delle colonne"
+ jpn "列リストã«å¯¾å¿œã™ã‚‹å…¨æ–‡ç´¢å¼•(FULLTEXT)ãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“。"
por "Não pode encontrar um índice para o texto todo que combine com a lista de colunas"
rus "Ðевозможно отыÑкать полнотекÑтовый (FULLTEXT) индекÑ, ÑоответÑтвующий ÑпиÑку Ñтолбцов"
serbian "Ne mogu da pronađem 'FULLTEXT' indeks koli odgovara listi kolona"
@@ -4161,7 +4050,7 @@ ER_FT_MATCHING_KEY_NOT_FOUND
swe "Hittar inte ett FULLTEXT-index i kolumnlistan"
ukr "Ðе можу знайти FULLTEXT індекÑ, що відповідає переліку Ñтовбців"
ER_LOCK_OR_ACTIVE_TRANSACTION
- cze "Nemohu prov-Bést zadaný příkaz, protože existují aktivní zamÄené tabulky nebo aktivní transakce"
+ cze "Nemohu provést zadaný příkaz, protože existují aktivní zamÄené tabulky nebo aktivní transakce"
dan "Kan ikke udføre den givne kommando fordi der findes aktive, låste tabeller eller fordi der udføres en transaktion"
nla "Kan het gegeven commando niet uitvoeren, want u heeft actieve gelockte tabellen of een actieve transactie"
eng "Can't execute the given command because you have active locked tables or an active transaction"
@@ -4169,6 +4058,7 @@ ER_LOCK_OR_ACTIVE_TRANSACTION
fre "Impossible d'exécuter la commande car vous avez des tables verrouillées ou une transaction active"
ger "Kann den angegebenen Befehl wegen einer aktiven Tabellensperre oder einer aktiven Transaktion nicht ausführen"
ita "Impossibile eseguire il comando richiesto: tabelle sotto lock o transazione in atto"
+ jpn "ã™ã§ã«ã‚¢ã‚¯ãƒ†ã‚£ãƒ–ãªè¡¨ãƒ­ãƒƒã‚¯ã‚„トランザクションãŒã‚ã‚‹ãŸã‚ã€ã‚³ãƒžãƒ³ãƒ‰ã‚’実行ã§ãã¾ã›ã‚“。"
por "Não pode executar o comando dado porque você tem tabelas ativas travadas ou uma transação ativa"
rus "Ðевозможно выполнить указанную команду, поÑкольку у Ð²Ð°Ñ Ð¿Ñ€Ð¸ÑутÑтвуют активно заблокированные таблица или Ð¾Ñ‚ÐºÑ€Ñ‹Ñ‚Ð°Ñ Ñ‚Ñ€Ð°Ð½Ð·Ð°ÐºÑ†Ð¸Ñ"
serbian "Ne mogu da izvrÅ¡im datu komandu zbog toga Å¡to su tabele zakljuÄane ili je transakcija u toku"
@@ -4176,7 +4066,7 @@ ER_LOCK_OR_ACTIVE_TRANSACTION
swe "Kan inte utföra kommandot emedan du har en låst tabell eller an aktiv transaktion"
ukr "Ðе можу виконати подану команду тому, що Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ñ Ð·Ð°Ð±Ð»Ð¾ÐºÐ¾Ð²Ð°Ð½Ð° або виконуєтьÑÑ Ñ‚Ñ€Ð°Ð½Ð·Ð°ÐºÑ†Ñ–Ñ"
ER_UNKNOWN_SYSTEM_VARIABLE
- cze "Nezn-Bámá systémová proměnná '%-.64s'"
+ cze "Neznámá systémová proměnná '%-.64s'"
dan "Ukendt systemvariabel '%-.64s'"
nla "Onbekende systeem variabele '%-.64s'"
eng "Unknown system variable '%-.64s'"
@@ -4184,6 +4074,7 @@ ER_UNKNOWN_SYSTEM_VARIABLE
fre "Variable système '%-.64s' inconnue"
ger "Unbekannte Systemvariable '%-.64s'"
ita "Variabile di sistema '%-.64s' sconosciuta"
+ jpn "'%-.64s' ã¯ä¸æ˜Žãªã‚·ã‚¹ãƒ†ãƒ å¤‰æ•°ã§ã™ã€‚"
por "Variável de sistema '%-.64s' desconhecida"
rus "ÐеизвеÑÑ‚Ð½Ð°Ñ ÑиÑÑ‚ÐµÐ¼Ð½Ð°Ñ Ð¿ÐµÑ€ÐµÐ¼ÐµÐ½Ð½Ð°Ñ '%-.64s'"
serbian "Nepoznata sistemska promenljiva '%-.64s'"
@@ -4191,7 +4082,7 @@ ER_UNKNOWN_SYSTEM_VARIABLE
swe "Okänd systemvariabel: '%-.64s'"
ukr "Ðевідома ÑиÑтемна змінна '%-.64s'"
ER_CRASHED_ON_USAGE
- cze "Tabulka '%-.192s' je ozna-BÄena jako poruÅ¡ená a mÄ›la by být opravena"
+ cze "Tabulka '%-.192s' je oznaÄena jako poruÅ¡ená a mÄ›la by být opravena"
dan "Tabellen '%-.192s' er markeret med fejl og bør repareres"
nla "Tabel '%-.192s' staat als gecrashed gemarkeerd en dient te worden gerepareerd"
eng "Table '%-.192s' is marked as crashed and should be repaired"
@@ -4199,6 +4090,7 @@ ER_CRASHED_ON_USAGE
fre "La table '%-.192s' est marquée 'crashed' et devrait être réparée"
ger "Tabelle '%-.192s' ist als defekt markiert und sollte repariert werden"
ita "La tabella '%-.192s' e` segnalata come corrotta e deve essere riparata"
+ jpn "表 '%-.192s' ã¯å£Šã‚Œã¦ã„ã¾ã™ã€‚修復ãŒå¿…è¦ã§ã™ã€‚"
por "Tabela '%-.192s' está marcada como danificada e deve ser reparada"
rus "Таблица '%-.192s' помечена как иÑÐ¿Ð¾Ñ€Ñ‡ÐµÐ½Ð½Ð°Ñ Ð¸ должна пройти проверку и ремонт"
serbian "Tabela '%-.192s' je markirana kao oštećena i trebala bi biti popravljena"
@@ -4206,7 +4098,7 @@ ER_CRASHED_ON_USAGE
swe "Tabell '%-.192s' är trasig och bör repareras med REPAIR TABLE"
ukr "Таблицю '%-.192s' марковано Ñк зіпÑовану та Ñ—Ñ— потрібно відновити"
ER_CRASHED_ON_REPAIR
- cze "Tabulka '%-.192s' je ozna-BÄena jako poruÅ¡ená a poslední (automatická?) oprava se nezdaÅ™ila"
+ cze "Tabulka '%-.192s' je oznaÄena jako poruÅ¡ená a poslední (automatická?) oprava se nezdaÅ™ila"
dan "Tabellen '%-.192s' er markeret med fejl og sidste (automatiske?) REPAIR fejlede"
nla "Tabel '%-.192s' staat als gecrashed gemarkeerd en de laatste (automatische?) reparatie poging mislukte"
eng "Table '%-.192s' is marked as crashed and last (automatic?) repair failed"
@@ -4214,6 +4106,7 @@ ER_CRASHED_ON_REPAIR
fre "La table '%-.192s' est marquée 'crashed' et le dernier 'repair' a échoué"
ger "Tabelle '%-.192s' ist als defekt markiert und der letzte (automatische?) Reparaturversuch schlug fehl"
ita "La tabella '%-.192s' e` segnalata come corrotta e l'ultima ricostruzione (automatica?) e` fallita"
+ jpn "表 '%-.192s' ã¯å£Šã‚Œã¦ã„ã¾ã™ã€‚修復(自動?)ã«ã‚‚失敗ã—ã¦ã„ã¾ã™ã€‚"
por "Tabela '%-.192s' está marcada como danificada e a última reparação (automática?) falhou"
rus "Таблица '%-.192s' помечена как иÑÐ¿Ð¾Ñ€Ñ‡ÐµÐ½Ð½Ð°Ñ Ð¸ поÑледний (автоматичеÑкий?) ремонт не был уÑпешным"
serbian "Tabela '%-.192s' je markirana kao oštećena, a zadnja (automatska?) popravka je bila neuspela"
@@ -4228,6 +4121,7 @@ ER_WARNING_NOT_COMPLETE_ROLLBACK
fre "Attention: certaines tables ne supportant pas les transactions ont été changées et elles ne pourront pas être restituées"
ger "Änderungen an einigen nicht transaktionalen Tabellen konnten nicht zurückgerollt werden"
ita "Attenzione: Alcune delle modifiche alle tabelle non transazionali non possono essere ripristinate (roll back impossibile)"
+ jpn "トランザクション対応ã§ã¯ãªã„表ã¸ã®å¤‰æ›´ã¯ãƒ­ãƒ¼ãƒ«ãƒãƒƒã‚¯ã•ã‚Œã¾ã›ã‚“。"
por "Aviso: Algumas tabelas não-transacionais alteradas não puderam ser reconstituídas (rolled back)"
rus "Внимание: по некоторым измененным нетранзакционным таблицам невозможно будет произвеÑти откат транзакции"
serbian "Upozorenje: Neke izmenjene tabele ne podržavaju komandu 'ROLLBACK'"
@@ -4242,24 +4136,25 @@ ER_TRANS_CACHE_FULL
fre "Cette transaction à commandes multiples nécessite plus de 'max_binlog_cache_size' octets de stockage, augmentez cette variable de mysqld et réessayez"
ger "Transaktionen, die aus mehreren Befehlen bestehen, benötigten mehr als 'max_binlog_cache_size' Bytes an Speicher. Btte vergrössern Sie diese Server-Variable versuchen Sie es noch einmal"
ita "La transazione a comandi multipli (multi-statement) ha richiesto piu` di 'max_binlog_cache_size' bytes di disco: aumentare questa variabile di mysqld e riprovare"
+ jpn "複数ステートメントã‹ã‚‰æˆã‚‹ãƒˆãƒ©ãƒ³ã‚¶ã‚¯ã‚·ãƒ§ãƒ³ãŒ 'max_binlog_cache_size' 以上ã®å®¹é‡ã‚’å¿…è¦ã¨ã—ã¾ã—ãŸã€‚ã“ã®ã‚·ã‚¹ãƒ†ãƒ å¤‰æ•°ã‚’増加ã—ã¦ã€å†è©¦è¡Œã—ã¦ãã ã•ã„。"
por "Transações multi-declaradas (multi-statement transactions) requeriram mais do que o valor limite (max_binlog_cache_size) de bytes para armazenagem. Aumente o valor desta variável do mysqld e tente novamente"
rus "Транзакции, включающей большое количеÑтво команд, потребовалоÑÑŒ более чем 'max_binlog_cache_size' байт. Увеличьте Ñту переменную Ñервера mysqld и попробуйте еще раз"
spa "Multipla transición necesita mas que 'max_binlog_cache_size' bytes de almacenamiento. Aumente esta variable mysqld y tente de nuevo"
swe "Transaktionen krävde mera än 'max_binlog_cache_size' minne. Öka denna mysqld-variabel och försök på nytt"
ukr "Ð¢Ñ€Ð°Ð½Ð·Ð°ÐºÑ†Ñ–Ñ Ð· багатьма виразами вимагає більше ніж 'max_binlog_cache_size' байтів Ð´Ð»Ñ Ð·Ð±ÐµÑ€Ñ–Ð³Ð°Ð½Ð½Ñ. Збільште цю змінну mysqld та Ñпробуйте знову"
ER_SLAVE_MUST_STOP
- dan "Denne handling kunne ikke udføres med kørende slave, brug først kommandoen STOP SLAVE"
- nla "Deze operatie kan niet worden uitgevoerd met een actieve slave, doe eerst STOP SLAVE"
- eng "This operation cannot be performed with a running slave; run STOP SLAVE first"
- fre "Cette opération ne peut être réalisée avec un esclave actif, faites STOP SLAVE d'abord"
- ger "Diese Operation kann bei einem aktiven Slave nicht durchgeführt werden. Bitte zuerst STOP SLAVE ausführen"
- ita "Questa operazione non puo' essere eseguita con un database 'slave' che gira, lanciare prima STOP SLAVE"
- por "Esta operação não pode ser realizada com um 'slave' em execução. Execute STOP SLAVE primeiro"
- rus "Эту операцию невозможно выполнить при работающем потоке подчиненного Ñервера. Сначала выполните STOP SLAVE"
- serbian "Ova operacija ne može biti izvršena dok je aktivan podređeni server. Zadajte prvo komandu 'STOP SLAVE' da zaustavite podređeni server."
- spa "Esta operación no puede ser hecha con el esclavo funcionando, primero use STOP SLAVE"
- swe "Denna operation kan inte göras under replikering; Gör STOP SLAVE först"
- ukr "ÐžÐ¿ÐµÑ€Ð°Ñ†Ñ–Ñ Ð½Ðµ може бути виконана з запущеним підлеглим, Ñпочатку виконайте STOP SLAVE"
+ dan "Denne handling kunne ikke udføres med kørende slave '%2$*1$s', brug først kommandoen STOP SLAVE '%2$*1$s'"
+ nla "Deze operatie kan niet worden uitgevoerd met een actieve slave '%2$*1$s', doe eerst STOP SLAVE '%2$*1$s'"
+ eng "This operation cannot be performed as you have a running slave '%2$*1$s'; run STOP SLAVE '%2$*1$s' first"
+ fre "Cette opération ne peut être réalisée avec un esclave '%2$*1$s' actif, faites STOP SLAVE '%2$*1$s' d'abord"
+ ger "Diese Operation kann bei einem aktiven Slave '%2$*1$s' nicht durchgeführt werden. Bitte zuerst STOP SLAVE '%2$*1$s' ausführen"
+ ita "Questa operazione non puo' essere eseguita con un database 'slave' '%2$*1$s' che gira, lanciare prima STOP SLAVE '%2$*1$s'"
+ por "Esta operação não pode ser realizada com um 'slave' '%2$*1$s' em execução. Execute STOP SLAVE '%2$*1$s' primeiro"
+ rus "Эту операцию невозможно выполнить при работающем потоке подчиненного Ñервера %2$*1$s. Сначала выполните STOP SLAVE '%2$*1$s'"
+ serbian "Ova operacija ne može biti izvršena dok je aktivan podređeni '%2$*1$s' server. Zadajte prvo komandu 'STOP SLAVE '%2$*1$s'' da zaustavite podređeni server."
+ spa "Esta operación no puede ser hecha con el esclavo '%2$*1$s' funcionando, primero use STOP SLAVE '%2$*1$s'"
+ swe "Denna operation kan inte göras under replikering; Du har en aktiv förbindelse till '%2$*1$s'. Gör STOP SLAVE '%2$*1$s' först"
+ ukr "ÐžÐ¿ÐµÑ€Ð°Ñ†Ñ–Ñ Ð½Ðµ може бути виконана з запущеним підлеглим '%2$*1$s', Ñпочатку виконайте STOP SLAVE '%2$*1$s'"
ER_SLAVE_NOT_RUNNING
dan "Denne handling kræver en kørende slave. Konfigurer en slave og brug kommandoen START SLAVE"
nla "Deze operatie vereist een actieve slave, configureer slave en doe dan START SLAVE"
@@ -4267,6 +4162,7 @@ ER_SLAVE_NOT_RUNNING
fre "Cette opération nécessite un esclave actif, configurez les esclaves et faites START SLAVE"
ger "Diese Operation benötigt einen aktiven Slave. Bitte Slave konfigurieren und mittels START SLAVE aktivieren"
ita "Questa operaione richiede un database 'slave', configurarlo ed eseguire START SLAVE"
+ jpn "ã“ã®å‡¦ç†ã¯ã€ç¨¼åƒä¸­ã®ã‚¹ãƒ¬ãƒ¼ãƒ–ã§ãªã‘ã‚Œã°å®Ÿè¡Œã§ãã¾ã›ã‚“。スレーブã®è¨­å®šã‚’ã—ã¦START SLAVEコマンドを実行ã—ã¦ãã ã•ã„。"
por "Esta operação requer um 'slave' em execução. Configure o 'slave' e execute START SLAVE"
rus "Ð”Ð»Ñ Ñтой операции требуетÑÑ Ñ€Ð°Ð±Ð¾Ñ‚Ð°ÑŽÑ‰Ð¸Ð¹ подчиненный Ñервер. Сначала выполните START SLAVE"
serbian "Ova operacija zahteva da je aktivan podređeni server. Konfigurišite prvo podređeni server i onda izvršite komandu 'START SLAVE'"
@@ -4280,6 +4176,7 @@ ER_BAD_SLAVE
fre "Le server n'est pas configuré comme un esclave, changez le fichier de configuration ou utilisez CHANGE MASTER TO"
ger "Der Server ist nicht als Slave konfiguriert. Bitte in der Konfigurationsdatei oder mittels CHANGE MASTER TO beheben"
ita "Il server non e' configurato come 'slave', correggere il file di configurazione cambiando CHANGE MASTER TO"
+ jpn "ã“ã®ã‚µãƒ¼ãƒãƒ¼ã¯ã‚¹ãƒ¬ãƒ¼ãƒ–ã¨ã—ã¦è¨­å®šã•ã‚Œã¦ã„ã¾ã›ã‚“。コンフィグファイルã‹CHANGE MASTER TOコマンドã§è¨­å®šã—ã¦ä¸‹ã•ã„。"
por "O servidor não está configurado como 'slave'. Acerte o arquivo de configuração ou use CHANGE MASTER TO"
rus "Этот Ñервер не наÑтроен как подчиненный. ВнеÑите иÑÐ¿Ñ€Ð°Ð²Ð»ÐµÐ½Ð¸Ñ Ð² конфигурационном файле или Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ CHANGE MASTER TO"
serbian "Server nije konfigurisan kao podređeni server, ispravite konfiguracioni file ili na njemu izvršite komandu 'CHANGE MASTER TO'"
@@ -4287,18 +4184,20 @@ ER_BAD_SLAVE
swe "Servern är inte konfigurerade som en replikationsslav. Ändra konfigurationsfilen eller gör CHANGE MASTER TO"
ukr "Сервер не зконфігуровано Ñк підлеглий, виправте це у файлі конфігурації або з CHANGE MASTER TO"
ER_MASTER_INFO
- eng "Could not initialize master info structure; more error messages can be found in the MariaDB error log"
- fre "Impossible d'initialiser les structures d'information de maître, vous trouverez des messages d'erreur supplémentaires dans le journal des erreurs de MariaDB"
- ger "Konnte Master-Info-Struktur nicht initialisieren. Weitere Fehlermeldungen können im MariaDB-Error-Log eingesehen werden"
- serbian "Nisam mogao da inicijalizujem informacionu strukturu glavnog servera, proverite da li imam privilegije potrebne za pristup file-u 'master.info'"
- swe "Kunde inte initialisera replikationsstrukturerna. See MariaDB fel fil för mera information"
-ER_SLAVE_THREAD
+ eng "Could not initialize master info structure for '%.*s'; more error messages can be found in the MariaDB error log"
+ fre "Impossible d'initialiser les structures d'information de maître '%.*s', vous trouverez des messages d'erreur supplémentaires dans le journal des erreurs de MariaDB"
+ ger "Konnte Master-Info-Struktur '%.*s' nicht initialisieren. Weitere Fehlermeldungen können im MariaDB-Error-Log eingesehen werden"
+ jpn "'master info '%.*s''構造体ã®åˆæœŸåŒ–ãŒã§ãã¾ã›ã‚“ã§ã—ãŸã€‚MariaDBエラーログã§ã‚¨ãƒ©ãƒ¼ãƒ¡ãƒƒã‚»ãƒ¼ã‚¸ã‚’確èªã—ã¦ãã ã•ã„。"
+ serbian "Nisam mogao da inicijalizujem informacionu strukturu glavnog servera, proverite da li imam privilegije potrebne za pristup file-u 'master.info' '%.*s'"
+ swe "Kunde inte initialisera replikationsstrukturerna för '%.*s'. See MariaDB fel fil för mera information"
+ER_SLAVE_THREAD
dan "Kunne ikke danne en slave-tråd; check systemressourcerne"
nla "Kon slave thread niet aanmaken, controleer systeem resources"
eng "Could not create slave thread; check system resources"
fre "Impossible de créer une tâche esclave, vérifiez les ressources système"
ger "Konnte Slave-Thread nicht starten. Bitte System-Ressourcen überprüfen"
ita "Impossibile creare il thread 'slave', controllare le risorse di sistema"
+ jpn "スレーブスレッドを作æˆã§ãã¾ã›ã‚“。システムリソースを確èªã—ã¦ãã ã•ã„。"
por "Não conseguiu criar 'thread' de 'slave'. Verifique os recursos do sistema"
rus "Ðевозможно Ñоздать поток подчиненного Ñервера. Проверьте ÑиÑтемные реÑурÑÑ‹"
serbian "Nisam mogao da startujem thread za podređeni server, proverite sistemske resurse"
@@ -4313,6 +4212,7 @@ ER_TOO_MANY_USER_CONNECTIONS 42000
fre "L'utilisateur %-.64s possède déjà plus de 'max_user_connections' connexions actives"
ger "Benutzer '%-.64s' hat mehr als 'max_user_connections' aktive Verbindungen"
ita "L'utente %-.64s ha gia' piu' di 'max_user_connections' connessioni attive"
+ jpn "ユーザー '%-.64s' ã¯ã™ã§ã« 'max_user_connections' 以上ã®ã‚¢ã‚¯ãƒ†ã‚£ãƒ–ãªæŽ¥ç¶šã‚’è¡Œã£ã¦ã„ã¾ã™ã€‚"
por "Usuário '%-.64s' já possui mais que o valor máximo de conexões (max_user_connections) ativas"
rus "У Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ %-.64s уже больше чем 'max_user_connections' активных Ñоединений"
serbian "Korisnik %-.64s već ima više aktivnih konekcija nego što je to određeno 'max_user_connections' promenljivom"
@@ -4322,13 +4222,14 @@ ER_TOO_MANY_USER_CONNECTIONS 42000
ER_SET_CONSTANTS_ONLY
dan "Du må kun bruge konstantudtryk med SET"
nla "U mag alleen constante expressies gebruiken bij SET"
- eng "You may only use constant expressions with SET"
+ eng "You may only use constant expressions in this statement"
est "Ainult konstantsed suurused on lubatud SET klauslis"
fre "Seules les expressions constantes sont autorisées avec SET"
- ger "Bei SET dürfen nur konstante Ausdrücke verwendet werden"
+ ger "Bei diesem Befehl dürfen nur konstante Ausdrücke verwendet werden"
ita "Si possono usare solo espressioni costanti con SET"
+ jpn "SET処ç†ãŒå¤±æ•—ã—ã¾ã—ãŸã€‚"
por "Você pode usar apenas expressões constantes com SET"
- rus "Ð’Ñ‹ можете иÑпользовать в SET только конÑтантные выражениÑ"
+ rus "С Ñтой командой вы можете иÑпользовать только конÑтантные выражениÑ"
serbian "Možete upotrebiti samo konstantan iskaz sa komandom 'SET'"
spa "Tu solo debes usar expresiones constantes con SET"
swe "Man kan endast använda konstantuttryck med SET"
@@ -4341,6 +4242,7 @@ ER_LOCK_WAIT_TIMEOUT
fre "Timeout sur l'obtention du verrou"
ger "Beim Warten auf eine Sperre wurde die zulässige Wartezeit überschritten. Bitte versuchen Sie, die Transaktion neu zu starten"
ita "E' scaduto il timeout per l'attesa del lock"
+ jpn "ロック待ã¡ãŒã‚¿ã‚¤ãƒ ã‚¢ã‚¦ãƒˆã—ã¾ã—ãŸã€‚トランザクションをå†è©¦è¡Œã—ã¦ãã ã•ã„。"
por "Tempo de espera (timeout) de travamento excedido. Tente reiniciar a transação."
rus "Таймаут Ð¾Ð¶Ð¸Ð´Ð°Ð½Ð¸Ñ Ð±Ð»Ð¾ÐºÐ¸Ñ€Ð¾Ð²ÐºÐ¸ иÑтек; попробуйте перезапуÑтить транзакцию"
serbian "Vremenski limit za zakljuÄavanje tabele je istekao; Probajte da ponovo startujete transakciju"
@@ -4355,6 +4257,7 @@ ER_LOCK_TABLE_FULL
fre "Le nombre total de verrou dépasse la taille de la table des verrous"
ger "Die Gesamtzahl der Sperren überschreitet die Größe der Sperrtabelle"
ita "Il numero totale di lock e' maggiore della grandezza della tabella di lock"
+ jpn "ロックã®æ•°ãŒå¤šã™ãŽã¾ã™ã€‚"
por "O número total de travamentos excede o tamanho da tabela de travamentos"
rus "Общее количеÑтво блокировок превыÑило размеры таблицы блокировок"
serbian "Broj totalnih zakljuÄavanja tabele premaÅ¡uje veliÄinu tabele zakljuÄavanja"
@@ -4369,6 +4272,7 @@ ER_READ_ONLY_TRANSACTION 25000
fre "Un verrou en update ne peut être acquit pendant une transaction READ UNCOMMITTED"
ger "Während einer READ-UNCOMMITTED-Transaktion können keine UPDATE-Sperren angefordert werden"
ita "I lock di aggiornamento non possono essere acquisiti durante una transazione 'READ UNCOMMITTED'"
+ jpn "読ã¿è¾¼ã¿å°‚用トランザクションã§ã™ã€‚"
por "Travamentos de atualização não podem ser obtidos durante uma transação de tipo READ UNCOMMITTED"
rus "Блокировки обновлений Ð½ÐµÐ»ÑŒÐ·Ñ Ð¿Ð¾Ð»ÑƒÑ‡Ð¸Ñ‚ÑŒ в процеÑÑе Ñ‡Ñ‚ÐµÐ½Ð¸Ñ Ð½Ðµ принÑтой (в режиме READ UNCOMMITTED) транзакции"
serbian "ZakljuÄavanja izmena ne mogu biti realizovana sve dok traje 'READ UNCOMMITTED' transakcija"
@@ -4383,6 +4287,7 @@ ER_DROP_DB_WITH_READ_LOCK
fre "DROP DATABASE n'est pas autorisée pendant qu'une tâche possède un verrou global en lecture"
ger "DROP DATABASE ist nicht erlaubt, solange der Thread eine globale Lesesperre hält"
ita "DROP DATABASE non e' permesso mentre il thread ha un lock globale di lettura"
+ jpn "グローãƒãƒ«ãƒªãƒ¼ãƒ‰ãƒ­ãƒƒã‚¯ã‚’ä¿æŒã—ã¦ã„ã‚‹é–“ã¯ã€DROP DATABASE を実行ã§ãã¾ã›ã‚“。"
por "DROP DATABASE não permitido enquanto uma 'thread' está mantendo um travamento global de leitura"
rus "Ðе допуÑкаетÑÑ DROP DATABASE, пока поток держит глобальную блокировку чтениÑ"
serbian "Komanda 'DROP DATABASE' nije dozvoljena dok thread globalno zakljuÄava Äitanje podataka"
@@ -4397,6 +4302,7 @@ ER_CREATE_DB_WITH_READ_LOCK
fre "CREATE DATABASE n'est pas autorisée pendant qu'une tâche possède un verrou global en lecture"
ger "CREATE DATABASE ist nicht erlaubt, solange der Thread eine globale Lesesperre hält"
ita "CREATE DATABASE non e' permesso mentre il thread ha un lock globale di lettura"
+ jpn "グローãƒãƒ«ãƒªãƒ¼ãƒ‰ãƒ­ãƒƒã‚¯ã‚’ä¿æŒã—ã¦ã„ã‚‹é–“ã¯ã€CREATE DATABASE を実行ã§ãã¾ã›ã‚“。"
por "CREATE DATABASE não permitido enquanto uma 'thread' está mantendo um travamento global de leitura"
rus "Ðе допуÑкаетÑÑ CREATE DATABASE, пока поток держит глобальную блокировку чтениÑ"
serbian "Komanda 'CREATE DATABASE' nije dozvoljena dok thread globalno zakljuÄava Äitanje podataka"
@@ -4410,6 +4316,7 @@ ER_WRONG_ARGUMENTS
fre "Mauvais arguments à %s"
ger "Falsche Argumente für %s"
ita "Argomenti errati a %s"
+ jpn "%s ã®å¼•æ•°ãŒä¸æ­£ã§ã™"
por "Argumentos errados para %s"
rus "Ðеверные параметры Ð´Ð»Ñ %s"
serbian "Pogrešni argumenti prosleđeni na %s"
@@ -4436,6 +4343,7 @@ ER_UNION_TABLES_IN_DIFFERENT_DIR
fre "Définition de table incorrecte; toutes les tables MERGE doivent être dans la même base de donnée"
ger "Falsche Tabellendefinition. Alle MERGE-Tabellen müssen sich in derselben Datenbank befinden"
ita "Definizione della tabella errata; tutte le tabelle di tipo MERGE devono essere nello stesso database"
+ jpn "ä¸æ­£ãªè¡¨å®šç¾©ã§ã™ã€‚MERGE表ã®æ§‹æˆè¡¨ã¯ã™ã¹ã¦åŒã˜ãƒ‡ãƒ¼ã‚¿ãƒ™ãƒ¼ã‚¹å†…ã«ãªã‘ã‚Œã°ãªã‚Šã¾ã›ã‚“。"
por "Definição incorreta da tabela. Todas as tabelas contidas na junção devem estar no mesmo banco de dados."
rus "Ðеверное определение таблицы; Ð’Ñе таблицы в MERGE должны принадлежать одной и той же базе данных"
serbian "Pogrešna definicija tabele; sve 'MERGE' tabele moraju biti u istoj bazi podataka"
@@ -4448,30 +4356,32 @@ ER_LOCK_DEADLOCK 40001
fre "Deadlock découvert en essayant d'obtenir les verrous : essayez de redémarrer la transaction"
ger "Beim Versuch, eine Sperre anzufordern, ist ein Deadlock aufgetreten. Versuchen Sie, die Transaktion neu zu starten"
ita "Trovato deadlock durante il lock; Provare a far ripartire la transazione"
+ jpn "ロックå–得中ã«ãƒ‡ãƒƒãƒ‰ãƒ­ãƒƒã‚¯ãŒæ¤œå‡ºã•ã‚Œã¾ã—ãŸã€‚トランザクションをå†è©¦è¡Œã—ã¦ãã ã•ã„。"
por "Encontrado um travamento fatal (deadlock) quando tentava obter uma trava. Tente reiniciar a transação."
rus "Возникла Ñ‚ÑƒÐ¿Ð¸ÐºÐ¾Ð²Ð°Ñ ÑÐ¸Ñ‚ÑƒÐ°Ñ†Ð¸Ñ Ð² процеÑÑе Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ñ Ð±Ð»Ð¾ÐºÐ¸Ñ€Ð¾Ð²ÐºÐ¸; Попробуйте перезапуÑтить транзакцию"
serbian "Unakrsno zakljuÄavanje pronaÄ‘eno kada sam pokuÅ¡ao da dobijem pravo na zakljuÄavanje; Probajte da restartujete transakciju"
spa "Encontrado deadlock cuando tentando obtener el bloqueo; Tente recomenzar la transición"
swe "Fick 'DEADLOCK' vid låsförsök av block/rad. Försök att starta om transaktionen"
ER_TABLE_CANT_HANDLE_FT
- nla "Het gebruikte tabel type ondersteund geen FULLTEXT indexen"
- eng "The used table type doesn't support FULLTEXT indexes"
- est "Antud tabelitüüp ei toeta FULLTEXT indekseid"
- fre "Le type de table utilisé ne supporte pas les index FULLTEXT"
- ger "Der verwendete Tabellentyp unterstützt keine FULLTEXT-Indizes"
- ita "La tabella usata non supporta gli indici FULLTEXT"
- por "O tipo de tabela utilizado não suporta índices de texto completo (fulltext indexes)"
- rus "ИÑпользуемый тип таблиц не поддерживает полнотекÑтовых индекÑов"
- serbian "Upotrebljeni tip tabele ne podržava 'FULLTEXT' indekse"
- spa "El tipo de tabla usada no soporta índices FULLTEXT"
- swe "Tabelltypen har inte hantering av FULLTEXT-index"
- ukr "ВикориÑтаний тип таблиці не підтримує FULLTEXT індекÑів"
+ nla "Het gebruikte tabel type (%s) ondersteund geen FULLTEXT indexen"
+ eng "The storage engine %s doesn't support FULLTEXT indexes"
+ est "Antud tabelitüüp (%s) ei toeta FULLTEXT indekseid"
+ fre "Le type de table utilisé (%s) ne supporte pas les index FULLTEXT"
+ ger "Der verwendete Tabellentyp (%s) unterstützt keine FULLTEXT-Indizes"
+ ita "La tabella usata (%s) non supporta gli indici FULLTEXT"
+ por "O tipo de tabela utilizado (%s) não suporta índices de texto completo (fulltext indexes)"
+ rus "ИÑпользуемый тип таблиц (%s) не поддерживает полнотекÑтовых индекÑов"
+ serbian "Upotrebljeni tip tabele (%s) ne podržava 'FULLTEXT' indekse"
+ spa "El tipo de tabla usada (%s) no soporta índices FULLTEXT"
+ swe "Tabelltypen (%s) har inte hantering av FULLTEXT-index"
+ ukr "ВикориÑтаний тип таблиці (%s) не підтримує FULLTEXT індекÑів"
ER_CANNOT_ADD_FOREIGN
nla "Kan foreign key beperking niet toevoegen"
eng "Cannot add foreign key constraint"
fre "Impossible d'ajouter des contraintes d'index externe"
ger "Fremdschlüssel-Beschränkung kann nicht hinzugefügt werden"
ita "Impossibile aggiungere il vincolo di integrita' referenziale (foreign key constraint)"
+ jpn "外部キー制約を追加ã§ãã¾ã›ã‚“。"
por "Não pode acrescentar uma restrição de chave estrangeira"
rus "Ðевозможно добавить Ð¾Ð³Ñ€Ð°Ð½Ð¸Ñ‡ÐµÐ½Ð¸Ñ Ð²Ð½ÐµÑˆÐ½ÐµÐ³Ð¾ ключа"
serbian "Ne mogu da dodam proveru spoljnog kljuÄa"
@@ -4485,6 +4395,7 @@ ER_NO_REFERENCED_ROW 23000
greek "Cannot add a child row: a foreign key constraint fails"
hun "Cannot add a child row: a foreign key constraint fails"
ita "Impossibile aggiungere la riga: un vincolo d'integrita' referenziale non e' soddisfatto"
+ jpn "親キーãŒã‚ã‚Šã¾ã›ã‚“。外部キー制約é•åã§ã™ã€‚"
norwegian-ny "Cannot add a child row: a foreign key constraint fails"
por "Não pode acrescentar uma linha filha: uma restrição de chave estrangeira falhou"
rus "Ðевозможно добавить или обновить дочернюю Ñтроку: проверка ограничений внешнего ключа не выполнÑетÑÑ"
@@ -4497,6 +4408,7 @@ ER_ROW_IS_REFERENCED 23000
greek "Cannot delete a parent row: a foreign key constraint fails"
hun "Cannot delete a parent row: a foreign key constraint fails"
ita "Impossibile cancellare la riga: un vincolo d'integrita' referenziale non e' soddisfatto"
+ jpn "å­ãƒ¬ã‚³ãƒ¼ãƒ‰ãŒã‚ã‚Šã¾ã™ã€‚外部キー制約é•åã§ã™ã€‚"
por "Não pode apagar uma linha pai: uma restrição de chave estrangeira falhou"
rus "Ðевозможно удалить или обновить родительÑкую Ñтроку: проверка ограничений внешнего ключа не выполнÑетÑÑ"
serbian "Ne mogu da izbriÅ¡em roditeljski slog: provera spoljnog kljuÄa je neuspela"
@@ -4507,6 +4419,7 @@ ER_CONNECT_TO_MASTER 08S01
eng "Error connecting to master: %-.128s"
ger "Fehler bei der Verbindung zum Master: %-.128s"
ita "Errore durante la connessione al master: %-.128s"
+ jpn "マスターã¸ã®æŽ¥ç¶šã‚¨ãƒ©ãƒ¼: %-.128s"
por "Erro conectando com o master: %-.128s"
rus "Ошибка ÑÐ¾ÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ñ Ñ Ð³Ð¾Ð»Ð¾Ð²Ð½Ñ‹Ð¼ Ñервером: %-.128s"
spa "Error de coneccion a master: %-.128s"
@@ -4516,6 +4429,7 @@ ER_QUERY_ON_MASTER
eng "Error running query on master: %-.128s"
ger "Beim Ausführen einer Abfrage auf dem Master trat ein Fehler auf: %-.128s"
ita "Errore eseguendo una query sul master: %-.128s"
+ jpn "マスターã§ã®ã‚¯ã‚¨ãƒªå®Ÿè¡Œã‚¨ãƒ©ãƒ¼: %-.128s"
por "Erro rodando consulta no master: %-.128s"
rus "Ошибка Ð²Ñ‹Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ Ð·Ð°Ð¿Ñ€Ð¾Ñа на головном Ñервере: %-.128s"
spa "Error executando el query en master: %-.128s"
@@ -4526,6 +4440,7 @@ ER_ERROR_WHEN_EXECUTING_COMMAND
est "Viga käsu %s täitmisel: %-.128s"
ger "Fehler beim Ausführen des Befehls %s: %-.128s"
ita "Errore durante l'esecuzione del comando %s: %-.128s"
+ jpn "%s コマンドã®å®Ÿè¡Œã‚¨ãƒ©ãƒ¼: %-.128s"
por "Erro quando executando comando %s: %-.128s"
rus "Ошибка при выполнении команды %s: %-.128s"
serbian "Greška pri izvršavanju komande %s: %-.128s"
@@ -4537,6 +4452,7 @@ ER_WRONG_USAGE
est "Vigane %s ja %s kasutus"
ger "Falsche Verwendung von %s und %s"
ita "Uso errato di %s e %s"
+ jpn "%s ã® %s ã«é–¢ã™ã‚‹ä¸æ­£ãªä½¿ç”¨æ³•ã§ã™ã€‚"
por "Uso errado de %s e %s"
rus "Ðеверное иÑпользование %s и %s"
serbian "Pogrešna upotreba %s i %s"
@@ -4549,6 +4465,7 @@ ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT 21000
est "Tulpade arv kasutatud SELECT lausetes ei kattu"
ger "Die verwendeten SELECT-Befehle liefern unterschiedliche Anzahlen von Feldern zurück"
ita "La SELECT utilizzata ha un numero di colonne differente"
+ jpn "使用ã®SELECTæ–‡ãŒè¿”ã™åˆ—æ•°ãŒé•ã„ã¾ã™ã€‚"
por "Os comandos SELECT usados têm diferente número de colunas"
rus "ИÑпользованные операторы выборки (SELECT) дают разное количеÑтво Ñтолбцов"
serbian "Upotrebljene 'SELECT' komande adresiraju razliÄit broj kolona"
@@ -4560,6 +4477,7 @@ ER_CANT_UPDATE_WITH_READLOCK
est "Ei suuda täita päringut konfliktse luku tõttu"
ger "Augrund eines READ-LOCK-Konflikts kann die Abfrage nicht ausgeführt werden"
ita "Impossibile eseguire la query perche' c'e' un conflitto con in lock di lettura"
+ jpn "競åˆã™ã‚‹ãƒªãƒ¼ãƒ‰ãƒ­ãƒƒã‚¯ã‚’ä¿æŒã—ã¦ã„ã‚‹ã®ã§ã€ã‚¯ã‚¨ãƒªã‚’実行ã§ãã¾ã›ã‚“。"
por "Não posso executar a consulta porque você tem um conflito de travamento de leitura"
rus "Ðевозможно иÑполнить запроÑ, поÑкольку у Ð²Ð°Ñ ÑƒÑтановлены конфликтующие блокировки чтениÑ"
serbian "Ne mogu da izvrÅ¡im upit zbog toga Å¡to imate zakljuÄavanja Äitanja podataka u konfliktu"
@@ -4571,6 +4489,7 @@ ER_MIXING_NOT_ALLOWED
est "Transaktsioone toetavate ning mittetoetavate tabelite kooskasutamine ei ole lubatud"
ger "Die gleichzeitige Verwendung von Tabellen mit und ohne Transaktionsunterstützung ist deaktiviert"
ita "E' disabilitata la possibilita' di mischiare tabelle transazionali e non-transazionali"
+ jpn "トランザクション対応ã®è¡¨ã¨éžå¯¾å¿œã®è¡¨ã®åŒæ™‚使用ã¯ç„¡åŠ¹åŒ–ã•ã‚Œã¦ã„ã¾ã™ã€‚"
por "Mistura de tabelas transacional e não-transacional está desabilitada"
rus "ИÑпользование транзакционных таблиц нарÑду Ñ Ð½ÐµÑ‚Ñ€Ð°Ð½Ð·Ð°ÐºÑ†Ð¸Ð¾Ð½Ð½Ñ‹Ð¼Ð¸ запрещено"
serbian "MeÅ¡anje tabela koje podržavaju transakcije i onih koje ne podržavaju transakcije je iskljuÄeno"
@@ -4582,6 +4501,7 @@ ER_DUP_ARGUMENT
est "Määrangut '%s' on lauses kasutatud topelt"
ger "Option '%s' wird im Befehl zweimal verwendet"
ita "L'opzione '%s' e' stata usata due volte nel comando"
+ jpn "オプション '%s' ãŒ2度使用ã•ã‚Œã¦ã„ã¾ã™ã€‚"
por "Opção '%s' usada duas vezes no comando"
rus "ÐžÐ¿Ñ†Ð¸Ñ '%s' дважды иÑпользована в выражении"
spa "Opción '%s' usada dos veces en el comando"
@@ -4591,6 +4511,7 @@ ER_USER_LIMIT_REACHED 42000
eng "User '%-.64s' has exceeded the '%s' resource (current value: %ld)"
ger "Benutzer '%-.64s' hat die Ressourcenbeschränkung '%s' überschritten (aktueller Wert: %ld)"
ita "L'utente '%-.64s' ha ecceduto la risorsa '%s' (valore corrente: %ld)"
+ jpn "ユーザー '%-.64s' ã¯ãƒªã‚½ãƒ¼ã‚¹ã®ä¸Šé™ '%s' ã«é”ã—ã¾ã—ãŸã€‚(ç¾åœ¨å€¤: %ld)"
por "Usuário '%-.64s' tem excedido o '%s' recurso (atual valor: %ld)"
rus "Пользователь '%-.64s' превыÑил иÑпользование реÑурÑа '%s' (текущее значение: %ld)"
spa "Usuario '%-.64s' ha excedido el recurso '%s' (actual valor: %ld)"
@@ -4600,6 +4521,7 @@ ER_SPECIFIC_ACCESS_DENIED_ERROR 42000
eng "Access denied; you need (at least one of) the %-.128s privilege(s) for this operation"
ger "Kein Zugriff. Hierfür wird die Berechtigung %-.128s benötigt"
ita "Accesso non consentito. Serve il privilegio %-.128s per questa operazione"
+ jpn "アクセスã¯æ‹’å¦ã•ã‚Œã¾ã—ãŸã€‚ã“ã®æ“作ã«ã¯ %-.128s 権é™ãŒ(複数ã®å ´åˆã¯ã©ã‚Œã‹1ã¤)å¿…è¦ã§ã™ã€‚"
por "Acesso negado. Você precisa o privilégio %-.128s para essa operação"
rus "Ð’ доÑтупе отказано. Вам нужны привилегии %-.128s Ð´Ð»Ñ Ñтой операции"
spa "Acceso negado. Usted necesita el privilegio %-.128s para esta operación"
@@ -4610,6 +4532,7 @@ ER_LOCAL_VARIABLE
eng "Variable '%-.64s' is a SESSION variable and can't be used with SET GLOBAL"
ger "Variable '%-.64s' ist eine lokale Variable und kann nicht mit SET GLOBAL verändert werden"
ita "La variabile '%-.64s' e' una variabile locale ( SESSION ) e non puo' essere cambiata usando SET GLOBAL"
+ jpn "変数 '%-.64s' ã¯ã‚»ãƒƒã‚·ãƒ§ãƒ³å¤‰æ•°ã§ã™ã€‚SET GLOBALã§ã¯ä½¿ç”¨ã§ãã¾ã›ã‚“。"
por "Variável '%-.64s' é uma SESSION variável e não pode ser usada com SET GLOBAL"
rus "ÐŸÐµÑ€ÐµÐ¼ÐµÐ½Ð½Ð°Ñ '%-.64s' ÑвлÑетÑÑ Ð¿Ð¾Ñ‚Ð¾ÐºÐ¾Ð²Ð¾Ð¹ (SESSION) переменной и не может быть изменена Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ SET GLOBAL"
spa "Variable '%-.64s' es una SESSION variable y no puede ser usada con SET GLOBAL"
@@ -4619,6 +4542,7 @@ ER_GLOBAL_VARIABLE
eng "Variable '%-.64s' is a GLOBAL variable and should be set with SET GLOBAL"
ger "Variable '%-.64s' ist eine globale Variable und muss mit SET GLOBAL verändert werden"
ita "La variabile '%-.64s' e' una variabile globale ( GLOBAL ) e deve essere cambiata usando SET GLOBAL"
+ jpn "変数 '%-.64s' ã¯ã‚°ãƒ­ãƒ¼ãƒãƒ«å¤‰æ•°ã§ã™ã€‚SET GLOBALを使用ã—ã¦ãã ã•ã„。"
por "Variável '%-.64s' é uma GLOBAL variável e deve ser configurada com SET GLOBAL"
rus "ÐŸÐµÑ€ÐµÐ¼ÐµÐ½Ð½Ð°Ñ '%-.64s' ÑвлÑетÑÑ Ð³Ð»Ð¾Ð±Ð°Ð»ÑŒÐ½Ð¾Ð¹ (GLOBAL) переменной, и ее Ñледует изменÑÑ‚ÑŒ Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ SET GLOBAL"
spa "Variable '%-.64s' es una GLOBAL variable y no puede ser configurada con SET GLOBAL"
@@ -4628,6 +4552,7 @@ ER_NO_DEFAULT 42000
eng "Variable '%-.64s' doesn't have a default value"
ger "Variable '%-.64s' hat keinen Vorgabewert"
ita "La variabile '%-.64s' non ha un valore di default"
+ jpn "変数 '%-.64s' ã«ã¯ãƒ‡ãƒ•ã‚©ãƒ«ãƒˆå€¤ãŒã‚ã‚Šã¾ã›ã‚“。"
por "Variável '%-.64s' não tem um valor padrão"
rus "ÐŸÐµÑ€ÐµÐ¼ÐµÐ½Ð½Ð°Ñ '%-.64s' не имеет Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ Ð¿Ð¾ умолчанию"
spa "Variable '%-.64s' no tiene un valor patrón"
@@ -4637,6 +4562,7 @@ ER_WRONG_VALUE_FOR_VAR 42000
eng "Variable '%-.64s' can't be set to the value of '%-.200s'"
ger "Variable '%-.64s' kann nicht auf '%-.200s' gesetzt werden"
ita "Alla variabile '%-.64s' non puo' essere assegato il valore '%-.200s'"
+ jpn "変数 '%-.64s' ã«å€¤ '%-.200s' を設定ã§ãã¾ã›ã‚“。"
por "Variável '%-.64s' não pode ser configurada para o valor de '%-.200s'"
rus "ÐŸÐµÑ€ÐµÐ¼ÐµÐ½Ð½Ð°Ñ '%-.64s' не может быть уÑтановлена в значение '%-.200s'"
spa "Variable '%-.64s' no puede ser configurada para el valor de '%-.200s'"
@@ -4646,6 +4572,7 @@ ER_WRONG_TYPE_FOR_VAR 42000
eng "Incorrect argument type to variable '%-.64s'"
ger "Falscher Argumenttyp für Variable '%-.64s'"
ita "Tipo di valore errato per la variabile '%-.64s'"
+ jpn "変数 '%-.64s' ã¸ã®å€¤ã®åž‹ãŒä¸æ­£ã§ã™ã€‚"
por "Tipo errado de argumento para variável '%-.64s'"
rus "Ðеверный тип аргумента Ð´Ð»Ñ Ð¿ÐµÑ€ÐµÐ¼ÐµÐ½Ð½Ð¾Ð¹ '%-.64s'"
spa "Tipo de argumento equivocado para variable '%-.64s'"
@@ -4655,6 +4582,7 @@ ER_VAR_CANT_BE_READ
eng "Variable '%-.64s' can only be set, not read"
ger "Variable '%-.64s' kann nur verändert, nicht gelesen werden"
ita "Alla variabile '%-.64s' e' di sola scrittura quindi puo' essere solo assegnato un valore, non letto"
+ jpn "変数 '%-.64s' ã¯æ›¸ãè¾¼ã¿å°‚用ã§ã™ã€‚読ã¿è¾¼ã¿ã¯ã§ãã¾ã›ã‚“。"
por "Variável '%-.64s' somente pode ser configurada, não lida"
rus "ÐŸÐµÑ€ÐµÐ¼ÐµÐ½Ð½Ð°Ñ '%-.64s' может быть только уÑтановлена, но не Ñчитана"
spa "Variable '%-.64s' solamente puede ser configurada, no leída"
@@ -4664,6 +4592,7 @@ ER_CANT_USE_OPTION_HERE 42000
eng "Incorrect usage/placement of '%s'"
ger "Falsche Verwendung oder Platzierung von '%s'"
ita "Uso/posizione di '%s' sbagliato"
+ jpn "'%s' ã®ä½¿ç”¨æ³•ã¾ãŸã¯å ´æ‰€ãŒä¸æ­£ã§ã™ã€‚"
por "Errado uso/colocação de '%s'"
rus "Ðеверное иÑпользование или в неверном меÑте указан '%s'"
spa "Equivocado uso/colocación de '%s'"
@@ -4673,6 +4602,7 @@ ER_NOT_SUPPORTED_YET 42000
eng "This version of MariaDB doesn't yet support '%s'"
ger "Diese MariaDB-Version unterstützt '%s' nicht"
ita "Questa versione di MariaDB non supporta ancora '%s'"
+ jpn "ã“ã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã®MariaDBã§ã¯ã€ã¾ã  '%s' を利用ã§ãã¾ã›ã‚“。"
por "Esta versão de MariaDB não suporta ainda '%s'"
rus "Эта верÑÐ¸Ñ MariaDB пока еще не поддерживает '%s'"
spa "Esta versión de MariaDB no soporta todavia '%s'"
@@ -4682,6 +4612,7 @@ ER_MASTER_FATAL_ERROR_READING_BINLOG
eng "Got fatal error %d from master when reading data from binary log: '%-.320s'"
ger "Schwerer Fehler %d: '%-.320s vom Master beim Lesen des binären Logs"
ita "Errore fatale %d: '%-.320s' dal master leggendo i dati dal log binario"
+ jpn "致命的ãªã‚¨ãƒ©ãƒ¼ %d: '%-.320s' ãŒãƒžã‚¹ã‚¿ãƒ¼ã§ãƒã‚¤ãƒŠãƒªãƒ­ã‚°èª­ã¿è¾¼ã¿ä¸­ã«ç™ºç”Ÿã—ã¾ã—ãŸã€‚"
por "Obteve fatal erro %d: '%-.320s' do master quando lendo dados do binary log"
rus "Получена неиÑÐ¿Ñ€Ð°Ð²Ð¸Ð¼Ð°Ñ Ð¾ÑˆÐ¸Ð±ÐºÐ° %d: '%-.320s' от головного Ñервера в процеÑÑе выборки данных из двоичного журнала"
spa "Recibió fatal error %d: '%-.320s' del master cuando leyendo datos del binary log"
@@ -4689,6 +4620,7 @@ ER_MASTER_FATAL_ERROR_READING_BINLOG
ER_SLAVE_IGNORED_TABLE
eng "Slave SQL thread ignored the query because of replicate-*-table rules"
ger "Slave-SQL-Thread hat die Abfrage aufgrund von replicate-*-table-Regeln ignoriert"
+ jpn "replicate-*-table ルールã«å¾“ã£ã¦ã€ã‚¹ãƒ¬ãƒ¼ãƒ–SQLスレッドã¯ã‚¯ã‚¨ãƒªã‚’無視ã—ã¾ã—ãŸã€‚"
nla "Slave SQL thread negeerde de query vanwege replicate-*-table opties"
por "Slave SQL thread ignorado a consulta devido às normas de replicação-*-tabela"
spa "Slave SQL thread ignorado el query debido a las reglas de replicación-*-tabla"
@@ -4697,12 +4629,14 @@ ER_INCORRECT_GLOBAL_LOCAL_VAR
eng "Variable '%-.192s' is a %s variable"
serbian "Promenljiva '%-.192s' je %s promenljiva"
ger "Variable '%-.192s' ist eine %s-Variable"
+ jpn "変数 '%-.192s' 㯠%s 変数ã§ã™ã€‚"
nla "Variabele '%-.192s' is geen %s variabele"
spa "Variable '%-.192s' es una %s variable"
swe "Variabel '%-.192s' är av typ %s"
ER_WRONG_FK_DEF 42000
eng "Incorrect foreign key definition for '%-.192s': %s"
ger "Falsche Fremdschlüssel-Definition für '%-.192s': %s"
+ jpn "外部キー '%-.192s' ã®å®šç¾©ã®ä¸æ­£: %s"
nla "Incorrecte foreign key definitie voor '%-.192s': %s"
por "Definição errada da chave estrangeira para '%-.192s': %s"
spa "Equivocada definición de llave extranjera para '%-.192s': %s"
@@ -4710,6 +4644,7 @@ ER_WRONG_FK_DEF 42000
ER_KEY_REF_DO_NOT_MATCH_TABLE_REF
eng "Key reference and table reference don't match"
ger "Schlüssel- und Tabellenverweis passen nicht zusammen"
+ jpn "外部キーã®å‚照表ã¨å®šç¾©ãŒä¸€è‡´ã—ã¾ã›ã‚“。"
nla "Sleutel- en tabelreferentie komen niet overeen"
por "Referência da chave e referência da tabela não coincidem"
spa "Referencia de llave y referencia de tabla no coinciden"
@@ -4717,6 +4652,7 @@ ER_KEY_REF_DO_NOT_MATCH_TABLE_REF
ER_OPERAND_COLUMNS 21000
eng "Operand should contain %d column(s)"
ger "Operand sollte %d Spalte(n) enthalten"
+ jpn "オペランド㫠%d 個ã®åˆ—ãŒå¿…è¦ã§ã™ã€‚"
nla "Operand behoort %d kolommen te bevatten"
rus "Операнд должен Ñодержать %d колонок"
spa "Operando debe tener %d columna(s)"
@@ -4724,6 +4660,7 @@ ER_OPERAND_COLUMNS 21000
ER_SUBQUERY_NO_1_ROW 21000
eng "Subquery returns more than 1 row"
ger "Unterabfrage lieferte mehr als einen Datensatz zurück"
+ jpn "サブクエリãŒ2行以上ã®çµæžœã‚’è¿”ã—ã¾ã™ã€‚"
nla "Subquery retourneert meer dan 1 rij"
por "Subconsulta retorna mais que 1 registro"
rus "ÐŸÐ¾Ð´Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð²Ð¾Ð·Ð²Ñ€Ð°Ñ‰Ð°ÐµÑ‚ более одной запиÑи"
@@ -4734,6 +4671,7 @@ ER_UNKNOWN_STMT_HANDLER
dan "Unknown prepared statement handler (%.*s) given to %s"
eng "Unknown prepared statement handler (%.*s) given to %s"
ger "Unbekannter Prepared-Statement-Handler (%.*s) für %s angegeben"
+ jpn "'%.*s' ã¯ãƒ—リペアードステートメントã®ä¸æ˜Žãªãƒãƒ³ãƒ‰ãƒ«ã§ã™ã€‚(%s ã§æŒ‡å®šã•ã‚Œã¾ã—ãŸ)"
nla "Onebekende prepared statement handler (%.*s) voor %s aangegeven"
por "Desconhecido manipulador de declaração preparado (%.*s) determinado para %s"
spa "Desconocido preparado comando handler (%.*s) dado para %s"
@@ -4742,6 +4680,7 @@ ER_UNKNOWN_STMT_HANDLER
ER_CORRUPT_HELP_DB
eng "Help database is corrupt or does not exist"
ger "Die Hilfe-Datenbank ist beschädigt oder existiert nicht"
+ jpn "ヘルプデータベースã¯å£Šã‚Œã¦ã„ã‚‹ã‹å­˜åœ¨ã—ã¾ã›ã‚“。"
nla "Help database is beschadigd of bestaat niet"
por "Banco de dado de ajuda corrupto ou não existente"
spa "Base de datos Help está corrupto o no existe"
@@ -4749,6 +4688,7 @@ ER_CORRUPT_HELP_DB
ER_CYCLIC_REFERENCE
eng "Cyclic reference on subqueries"
ger "Zyklischer Verweis in Unterabfragen"
+ jpn "サブクエリã®å‚ç…§ãŒãƒ«ãƒ¼ãƒ—ã—ã¦ã„ã¾ã™ã€‚"
nla "Cyclische verwijzing in subqueries"
por "Referência cíclica em subconsultas"
rus "ЦикличеÑÐºÐ°Ñ ÑÑылка на подзапроÑ"
@@ -4758,6 +4698,7 @@ ER_CYCLIC_REFERENCE
ER_AUTO_CONVERT
eng "Converting column '%s' from %s to %s"
ger "Feld '%s' wird von %s nach %s umgewandelt"
+ jpn "列 '%s' ã‚’ %s ã‹ã‚‰ %s ã¸å¤‰æ›ã—ã¾ã™ã€‚"
nla "Veld '%s' wordt van %s naar %s geconverteerd"
por "Convertendo coluna '%s' de %s para %s"
rus "Преобразование Ð¿Ð¾Ð»Ñ '%s' из %s в %s"
@@ -4767,6 +4708,7 @@ ER_AUTO_CONVERT
ER_ILLEGAL_REFERENCE 42S22
eng "Reference '%-.64s' not supported (%s)"
ger "Verweis '%-.64s' wird nicht unterstützt (%s)"
+ jpn "'%-.64s' ã®å‚ç…§ã¯ã§ãã¾ã›ã‚“。(%s)"
nla "Verwijzing '%-.64s' niet ondersteund (%s)"
por "Referência '%-.64s' não suportada (%s)"
rus "СÑылка '%-.64s' не поддерживаетÑÑ (%s)"
@@ -4776,6 +4718,7 @@ ER_ILLEGAL_REFERENCE 42S22
ER_DERIVED_MUST_HAVE_ALIAS 42000
eng "Every derived table must have its own alias"
ger "Für jede abgeleitete Tabelle muss ein eigener Alias angegeben werden"
+ jpn "導出表ã«ã¯åˆ¥åãŒå¿…é ˆã§ã™ã€‚"
nla "Voor elke afgeleide tabel moet een unieke alias worden gebruikt"
por "Cada tabela derivada deve ter seu próprio alias"
spa "Cada tabla derivada debe tener su propio alias"
@@ -4783,6 +4726,7 @@ ER_DERIVED_MUST_HAVE_ALIAS 42000
ER_SELECT_REDUCED 01000
eng "Select %u was reduced during optimization"
ger "Select %u wurde während der Optimierung reduziert"
+ jpn "Select %u ã¯æœ€é©åŒ–ã«ã‚ˆã£ã¦æ¸›ã‚‰ã•ã‚Œã¾ã—ãŸã€‚"
nla "Select %u werd geredureerd tijdens optimtalisatie"
por "Select %u foi reduzido durante otimização"
rus "Select %u был упразднен в процеÑÑе оптимизации"
@@ -4792,6 +4736,7 @@ ER_SELECT_REDUCED 01000
ER_TABLENAME_NOT_ALLOWED_HERE 42000
eng "Table '%-.192s' from one of the SELECTs cannot be used in %-.32s"
ger "Tabelle '%-.192s', die in einem der SELECT-Befehle verwendet wurde, kann nicht in %-.32s verwendet werden"
+ jpn "特定ã®SELECTã®ã¿ã§ä½¿ç”¨ã®è¡¨ '%-.192s' 㯠%-.32s ã§ã¯ä½¿ç”¨ã§ãã¾ã›ã‚“。"
nla "Tabel '%-.192s' uit een van de SELECTS kan niet in %-.32s gebruikt worden"
por "Tabela '%-.192s' de um dos SELECTs não pode ser usada em %-.32s"
spa "Tabla '%-.192s' de uno de los SELECT no puede ser usada en %-.32s"
@@ -4799,6 +4744,7 @@ ER_TABLENAME_NOT_ALLOWED_HERE 42000
ER_NOT_SUPPORTED_AUTH_MODE 08004
eng "Client does not support authentication protocol requested by server; consider upgrading MariaDB client"
ger "Client unterstützt das vom Server erwartete Authentifizierungsprotokoll nicht. Bitte aktualisieren Sie Ihren MariaDB-Client"
+ jpn "クライアントã¯ã‚µãƒ¼ãƒãƒ¼ãŒè¦æ±‚ã™ã‚‹èªè¨¼ãƒ—ロトコルã«å¯¾å¿œã§ãã¾ã›ã‚“。MariaDBクライアントã®ã‚¢ãƒƒãƒ—グレードを検討ã—ã¦ãã ã•ã„。"
nla "Client ondersteunt het door de server verwachtte authenticatieprotocol niet. Overweeg een nieuwere MariaDB client te gebruiken"
por "Cliente não suporta o protocolo de autenticação exigido pelo servidor; considere a atualização do cliente MariaDB"
spa "Cliente no soporta protocolo de autenticación solicitado por el servidor; considere actualizar el cliente MariaDB"
@@ -4806,6 +4752,7 @@ ER_NOT_SUPPORTED_AUTH_MODE 08004
ER_SPATIAL_CANT_HAVE_NULL 42000
eng "All parts of a SPATIAL index must be NOT NULL"
ger "Alle Teile eines SPATIAL-Index müssen als NOT NULL deklariert sein"
+ jpn "空間索引ã®ã‚­ãƒ¼åˆ—㯠NOT NULL ã§ãªã‘ã‚Œã°ã„ã‘ã¾ã›ã‚“。"
nla "Alle delete van een SPATIAL index dienen als NOT NULL gedeclareerd te worden"
por "Todas as partes de uma SPATIAL index devem ser NOT NULL"
spa "Todas las partes de una SPATIAL index deben ser NOT NULL"
@@ -4813,6 +4760,7 @@ ER_SPATIAL_CANT_HAVE_NULL 42000
ER_COLLATION_CHARSET_MISMATCH 42000
eng "COLLATION '%s' is not valid for CHARACTER SET '%s'"
ger "COLLATION '%s' ist für CHARACTER SET '%s' ungültig"
+ jpn "COLLATION '%s' 㯠CHARACTER SET '%s' ã«é©ç”¨ã§ãã¾ã›ã‚“。"
nla "COLLATION '%s' is niet geldig voor CHARACTER SET '%s'"
por "COLLATION '%s' não é válida para CHARACTER SET '%s'"
spa "COLLATION '%s' no es válido para CHARACTER SET '%s'"
@@ -4820,6 +4768,7 @@ ER_COLLATION_CHARSET_MISMATCH 42000
ER_SLAVE_WAS_RUNNING
eng "Slave is already running"
ger "Slave läuft bereits"
+ jpn "スレーブã¯ã™ã§ã«ç¨¼åƒä¸­ã§ã™ã€‚"
nla "Slave is reeds actief"
por "O slave já está rodando"
spa "Slave ya está funcionando"
@@ -4827,6 +4776,7 @@ ER_SLAVE_WAS_RUNNING
ER_SLAVE_WAS_NOT_RUNNING
eng "Slave already has been stopped"
ger "Slave wurde bereits angehalten"
+ jpn "スレーブã¯ã™ã§ã«åœæ­¢ã—ã¦ã„ã¾ã™ã€‚"
nla "Slave is reeds gestopt"
por "O slave já está parado"
spa "Slave ya fué parado"
@@ -4834,24 +4784,28 @@ ER_SLAVE_WAS_NOT_RUNNING
ER_TOO_BIG_FOR_UNCOMPRESS
eng "Uncompressed data size too large; the maximum size is %d (probably, length of uncompressed data was corrupted)"
ger "Unkomprimierte Daten sind zu groß. Die maximale Größe beträgt %d (wahrscheinlich wurde die Länge der unkomprimierten Daten beschädigt)"
+ jpn "展開後ã®ãƒ‡ãƒ¼ã‚¿ãŒå¤§ãã™ãŽã¾ã™ã€‚最大サイズ㯠%d ã§ã™ã€‚(展開後データã®é•·ã•æƒ…å ±ãŒå£Šã‚Œã¦ã„ã‚‹å¯èƒ½æ€§ã‚‚ã‚ã‚Šã¾ã™ã€‚)"
nla "Ongecomprimeerder data is te groot; de maximum lengte is %d (waarschijnlijk, de lengte van de gecomprimeerde data was beschadigd)"
por "Tamanho muito grande dos dados des comprimidos. O máximo tamanho é %d. (provavelmente, o comprimento dos dados descomprimidos está corrupto)"
spa "Tamaño demasiado grande para datos descomprimidos. El máximo tamaño es %d. (probablemente, extensión de datos descomprimidos fué corrompida)"
ER_ZLIB_Z_MEM_ERROR
eng "ZLIB: Not enough memory"
ger "ZLIB: Nicht genug Speicher"
+ jpn "ZLIB: メモリä¸è¶³ã§ã™ã€‚"
nla "ZLIB: Onvoldoende geheugen"
por "ZLIB: Não suficiente memória disponível"
spa "Z_MEM_ERROR: No suficiente memoria para zlib"
ER_ZLIB_Z_BUF_ERROR
eng "ZLIB: Not enough room in the output buffer (probably, length of uncompressed data was corrupted)"
ger "ZLIB: Im Ausgabepuffer ist nicht genug Platz vorhanden (wahrscheinlich wurde die Länge der unkomprimierten Daten beschädigt)"
+ jpn "ZLIB: 出力ãƒãƒƒãƒ•ã‚¡ã«å分ãªç©ºããŒã‚ã‚Šã¾ã›ã‚“。(展開後データã®é•·ã•æƒ…å ±ãŒå£Šã‚Œã¦ã„ã‚‹å¯èƒ½æ€§ã‚‚ã‚ã‚Šã¾ã™ã€‚)"
nla "ZLIB: Onvoldoende ruimte in uitgaande buffer (waarschijnlijk, de lengte van de ongecomprimeerde data was beschadigd)"
por "ZLIB: Não suficiente espaço no buffer emissor (provavelmente, o comprimento dos dados descomprimidos está corrupto)"
spa "Z_BUF_ERROR: No suficiente espacio en el búfer de salida para zlib (probablemente, extensión de datos descomprimidos fué corrompida)"
ER_ZLIB_Z_DATA_ERROR
eng "ZLIB: Input data corrupted"
ger "ZLIB: Eingabedaten beschädigt"
+ jpn "ZLIB: 入力データãŒå£Šã‚Œã¦ã„ã¾ã™ã€‚"
nla "ZLIB: Invoer data beschadigd"
por "ZLIB: Dados de entrada está corrupto"
spa "ZLIB: Dato de entrada fué corrompido para zlib"
@@ -4860,18 +4814,21 @@ ER_CUT_VALUE_GROUP_CONCAT
ER_WARN_TOO_FEW_RECORDS 01000
eng "Row %lu doesn't contain data for all columns"
ger "Zeile %lu enthält nicht für alle Felder Daten"
+ jpn "è¡Œ %lu ã¯ã™ã¹ã¦ã®åˆ—ã¸ã®ãƒ‡ãƒ¼ã‚¿ã‚’å«ã‚“ã§ã„ã¾ã›ã‚“。"
nla "Rij %lu bevat niet de data voor alle kolommen"
por "Conta de registro é menor que a conta de coluna na linha %lu"
spa "Línea %lu no contiene datos para todas las columnas"
ER_WARN_TOO_MANY_RECORDS 01000
eng "Row %lu was truncated; it contained more data than there were input columns"
ger "Zeile %lu gekürzt, die Zeile enthielt mehr Daten, als es Eingabefelder gibt"
+ jpn "è¡Œ %lu ã¯ãƒ‡ãƒ¼ã‚¿ã‚’切りæ¨ã¦ã‚‰ã‚Œã¾ã—ãŸã€‚列よりも多ã„データをå«ã‚“ã§ã„ã¾ã—ãŸã€‚"
nla "Regel %lu ingekort, bevatte meer data dan invoer kolommen"
por "Conta de registro é maior que a conta de coluna na linha %lu"
spa "Línea %lu fué truncada; La misma contine mas datos que las que existen en las columnas de entrada"
ER_WARN_NULL_TO_NOTNULL 22004
eng "Column set to default value; NULL supplied to NOT NULL column '%s' at row %lu"
ger "Feld auf Vorgabewert gesetzt, da NULL für NOT-NULL-Feld '%s' in Zeile %lu angegeben"
+ jpn "列ã«ãƒ‡ãƒ•ã‚©ãƒ«ãƒˆå€¤ãŒè¨­å®šã•ã‚Œã¾ã—ãŸã€‚NOT NULLã®åˆ— '%s' ã« è¡Œ %lu 㧠NULL ãŒä¸Žãˆã‚‰ã‚Œã¾ã—ãŸã€‚"
por "Dado truncado, NULL fornecido para NOT NULL coluna '%s' na linha %lu"
spa "Datos truncado, NULL suministrado para NOT NULL columna '%s' en la línea %lu"
ER_WARN_DATA_OUT_OF_RANGE 22003
@@ -4879,17 +4836,20 @@ ER_WARN_DATA_OUT_OF_RANGE 22003
WARN_DATA_TRUNCATED 01000
eng "Data truncated for column '%s' at row %lu"
ger "Daten abgeschnitten für Feld '%s' in Zeile %lu"
+ jpn "列 '%s' ã® è¡Œ %lu ã§ãƒ‡ãƒ¼ã‚¿ãŒåˆ‡ã‚Šæ¨ã¦ã‚‰ã‚Œã¾ã—ãŸã€‚"
por "Dado truncado para coluna '%s' na linha %lu"
spa "Datos truncados para columna '%s' en la línea %lu"
ER_WARN_USING_OTHER_HANDLER
eng "Using storage engine %s for table '%s'"
ger "Für Tabelle '%s' wird Speicher-Engine %s benutzt"
+ jpn "ストレージエンジン %s ãŒè¡¨ '%s' ã«åˆ©ç”¨ã•ã‚Œã¦ã„ã¾ã™ã€‚"
por "Usando engine de armazenamento %s para tabela '%s'"
spa "Usando motor de almacenamiento %s para tabla '%s'"
swe "Använder handler %s för tabell '%s'"
ER_CANT_AGGREGATE_2COLLATIONS
eng "Illegal mix of collations (%s,%s) and (%s,%s) for operation '%s'"
ger "Unerlaubte Mischung von Sortierreihenfolgen (%s, %s) und (%s, %s) für Operation '%s'"
+ jpn "ç…§åˆé †åº (%s,%s) 㨠(%s,%s) ã®æ··åœ¨ã¯æ“作 '%s' ã§ã¯ä¸æ­£ã§ã™ã€‚"
por "Combinação ilegal de collations (%s,%s) e (%s,%s) para operação '%s'"
spa "Ilegal mezcla de collations (%s,%s) y (%s,%s) para operación '%s'"
ER_DROP_USER
@@ -4898,42 +4858,50 @@ ER_DROP_USER
ER_REVOKE_GRANTS
eng "Can't revoke all privileges for one or more of the requested users"
ger "Kann nicht alle Berechtigungen widerrufen, die für einen oder mehrere Benutzer gewährt wurden"
+ jpn "指定ã•ã‚ŒãŸãƒ¦ãƒ¼ã‚¶ãƒ¼ã‹ã‚‰æŒ‡å®šã•ã‚ŒãŸå…¨ã¦ã®æ¨©é™ã‚’剥奪ã™ã‚‹ã“ã¨ãŒã§ãã¾ã›ã‚“ã§ã—ãŸã€‚"
por "Não pode revocar todos os privilégios, grant para um ou mais dos usuários pedidos"
spa "No puede revocar todos los privilegios, derecho para uno o mas de los usuarios solicitados"
ER_CANT_AGGREGATE_3COLLATIONS
eng "Illegal mix of collations (%s,%s), (%s,%s), (%s,%s) for operation '%s'"
ger "Unerlaubte Mischung von Sortierreihenfolgen (%s, %s), (%s, %s), (%s, %s) für Operation '%s'"
+ jpn "ç…§åˆé †åº (%s,%s), (%s,%s), (%s,%s) ã®æ··åœ¨ã¯æ“作 '%s' ã§ã¯ä¸æ­£ã§ã™ã€‚"
por "Ilegal combinação de collations (%s,%s), (%s,%s), (%s,%s) para operação '%s'"
spa "Ilegal mezcla de collations (%s,%s), (%s,%s), (%s,%s) para operación '%s'"
ER_CANT_AGGREGATE_NCOLLATIONS
eng "Illegal mix of collations for operation '%s'"
ger "Unerlaubte Mischung von Sortierreihenfolgen für Operation '%s'"
+ jpn "æ“作 '%s' ã§ã¯ä¸æ­£ãªç…§åˆé †åºã®æ··åœ¨ã§ã™ã€‚"
por "Ilegal combinação de collations para operação '%s'"
spa "Ilegal mezcla de collations para operación '%s'"
ER_VARIABLE_IS_NOT_STRUCT
eng "Variable '%-.64s' is not a variable component (can't be used as XXXX.variable_name)"
ger "Variable '%-.64s' ist keine Variablen-Komponente (kann nicht als XXXX.variablen_name verwendet werden)"
+ jpn "変数 '%-.64s' ã¯æ§‹é€ å¤‰æ•°ã®æ§‹æˆè¦ç´ ã§ã¯ã‚ã‚Šã¾ã›ã‚“。(XXXX.変数å ã¨ã„ã†æŒ‡å®šã¯ã§ãã¾ã›ã‚“。)"
por "Variável '%-.64s' não é uma variável componente (Não pode ser usada como XXXX.variável_nome)"
spa "Variable '%-.64s' no es una variable componente (No puede ser usada como XXXX.variable_name)"
ER_UNKNOWN_COLLATION
eng "Unknown collation: '%-.64s'"
ger "Unbekannte Sortierreihenfolge: '%-.64s'"
+ jpn "ä¸æ˜Žãªç…§åˆé †åº: '%-.64s'"
por "Collation desconhecida: '%-.64s'"
spa "Collation desconocida: '%-.64s'"
ER_SLAVE_IGNORED_SSL_PARAMS
eng "SSL parameters in CHANGE MASTER are ignored because this MariaDB slave was compiled without SSL support; they can be used later if MariaDB slave with SSL is started"
ger "SSL-Parameter in CHANGE MASTER werden ignoriert, weil dieser MariaDB-Slave ohne SSL-Unterstützung kompiliert wurde. Sie können aber später verwendet werden, wenn ein MariaDB-Slave mit SSL gestartet wird"
+ jpn "ã“ã®MySQLスレーブã¯SSLサãƒãƒ¼ãƒˆã‚’å«ã‚ã¦ã‚³ãƒ³ãƒ‘イルã•ã‚Œã¦ã„ãªã„ã®ã§ã€CHANGE MASTER ã®SSLパラメータã¯ç„¡è¦–ã•ã‚Œã¾ã—ãŸã€‚今後SSLサãƒãƒ¼ãƒˆã‚’æŒã¤MySQLスレーブを起動ã™ã‚‹éš›ã«åˆ©ç”¨ã•ã‚Œã¾ã™ã€‚"
por "SSL parâmetros em CHANGE MASTER são ignorados porque este escravo MariaDB foi compilado sem o SSL suporte. Os mesmos podem ser usados mais tarde quando o escravo MariaDB com SSL seja iniciado."
spa "Parametros SSL en CHANGE MASTER son ignorados porque este slave MariaDB fue compilado sin soporte SSL; pueden ser usados despues cuando el slave MariaDB con SSL sea inicializado"
ER_SERVER_IS_IN_SECURE_AUTH_MODE
eng "Server is running in --secure-auth mode, but '%s'@'%s' has a password in the old format; please change the password to the new format"
ger "Server läuft im Modus --secure-auth, aber '%s'@'%s' hat ein Passwort im alten Format. Bitte Passwort ins neue Format ändern"
+ jpn "サーãƒãƒ¼ã¯ --secure-auth モードã§ç¨¼åƒã—ã¦ã„ã¾ã™ã€‚ã—ã‹ã— '%s'@'%s' ã¯å¤ã„å½¢å¼ã®ãƒ‘スワードを使用ã—ã¦ã„ã¾ã™ã€‚æ–°ã—ã„å½¢å¼ã®ãƒ‘スワードã«å¤‰æ›´ã—ã¦ãã ã•ã„。"
por "Servidor está rodando em --secure-auth modo, porêm '%s'@'%s' tem senha no formato antigo; por favor troque a senha para o novo formato"
rus "Сервер запущен в режиме --secure-auth (безопаÑной авторизации), но Ð´Ð»Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ '%s'@'%s' пароль Ñохранён в Ñтаром формате; необходимо обновить формат паролÑ"
spa "Servidor está rodando en modo --secure-auth, pero '%s'@'%s' tiene clave en el antiguo formato; por favor cambie la clave para el nuevo formato"
ER_WARN_FIELD_RESOLVED
eng "Field or reference '%-.192s%s%-.192s%s%-.192s' of SELECT #%d was resolved in SELECT #%d"
ger "Feld oder Verweis '%-.192s%s%-.192s%s%-.192s' im SELECT-Befehl Nr. %d wurde im SELECT-Befehl Nr. %d aufgelöst"
+ jpn "フィールドã¾ãŸã¯å‚ç…§ '%-.192s%s%-.192s%s%-.192s' 㯠SELECT #%d ã§ã¯ãªãã€SELECT #%d ã§è§£æ±ºã•ã‚Œã¾ã—ãŸã€‚"
por "Campo ou referência '%-.192s%s%-.192s%s%-.192s' de SELECT #%d foi resolvido em SELECT #%d"
rus "Поле или ÑÑылка '%-.192s%s%-.192s%s%-.192s' из SELECTа #%d была найдена в SELECTе #%d"
spa "Campo o referencia '%-.192s%s%-.192s%s%-.192s' de SELECT #%d fue resolvido en SELECT #%d"
@@ -4941,27 +4909,32 @@ ER_WARN_FIELD_RESOLVED
ER_BAD_SLAVE_UNTIL_COND
eng "Incorrect parameter or combination of parameters for START SLAVE UNTIL"
ger "Falscher Parameter oder falsche Kombination von Parametern für START SLAVE UNTIL"
+ jpn "START SLAVE UNTIL ã¸ã®ãƒ‘ラメータã¾ãŸã¯ãã®çµ„ã¿åˆã‚ã›ãŒä¸æ­£ã§ã™ã€‚"
por "Parâmetro ou combinação de parâmetros errado para START SLAVE UNTIL"
spa "Parametro equivocado o combinación de parametros para START SLAVE UNTIL"
ER_MISSING_SKIP_SLAVE
eng "It is recommended to use --skip-slave-start when doing step-by-step replication with START SLAVE UNTIL; otherwise, you will get problems if you get an unexpected slave's mysqld restart"
ger "Es wird empfohlen, mit --skip-slave-start zu starten, wenn mit START SLAVE UNTIL eine Schritt-für-Schritt-Replikation ausgeführt wird. Ansonsten gibt es Probleme, wenn ein Slave-Server unerwartet neu startet"
+ jpn "START SLAVE UNTIL ã§æ®µéšŽçš„ã«ãƒ¬ãƒ—リケーションを行ã†éš›ã«ã¯ã€--skip-slave-start オプションを使ã†ã“ã¨ã‚’推奨ã—ã¾ã™ã€‚使ã‚ãªã„å ´åˆã€ã‚¹ãƒ¬ãƒ¼ãƒ–ã®mysqldãŒä¸æ…®ã®å†èµ·å‹•ã‚’ã™ã‚‹ã¨å•é¡ŒãŒç™ºç”Ÿã—ã¾ã™ã€‚"
por "É recomendado para rodar com --skip-slave-start quando fazendo replicação passo-por-passo com START SLAVE UNTIL, de outra forma você não está seguro em caso de inesperada reinicialição do mysqld escravo"
spa "Es recomendado rodar con --skip-slave-start cuando haciendo replicación step-by-step con START SLAVE UNTIL, a menos que usted no esté seguro en caso de inesperada reinicialización del mysqld slave"
ER_UNTIL_COND_IGNORED
eng "SQL thread is not to be started so UNTIL options are ignored"
ger "SQL-Thread soll nicht gestartet werden. Daher werden UNTIL-Optionen ignoriert"
+ jpn "スレーブSQLスレッドãŒé–‹å§‹ã•ã‚Œãªã„ãŸã‚ã€UNTILオプションã¯ç„¡è¦–ã•ã‚Œã¾ã—ãŸã€‚"
por "Thread SQL não pode ser inicializado tal que opções UNTIL são ignoradas"
spa "SQL thread no es inicializado tal que opciones UNTIL son ignoradas"
ER_WRONG_NAME_FOR_INDEX 42000
eng "Incorrect index name '%-.100s'"
ger "Falscher Indexname '%-.100s'"
+ jpn "索引å '%-.100s' ã¯ä¸æ­£ã§ã™ã€‚"
por "Incorreto nome de índice '%-.100s'"
spa "Nombre de índice incorrecto '%-.100s'"
swe "Felaktigt index namn '%-.100s'"
ER_WRONG_NAME_FOR_CATALOG 42000
eng "Incorrect catalog name '%-.100s'"
ger "Falscher Katalogname '%-.100s'"
+ jpn "カタログå '%-.100s' ã¯ä¸æ­£ã§ã™ã€‚"
por "Incorreto nome de catálogo '%-.100s'"
spa "Nombre de catalog incorrecto '%-.100s'"
swe "Felaktigt katalog namn '%-.100s'"
@@ -4976,33 +4949,39 @@ ER_WARN_QC_RESIZE
ER_BAD_FT_COLUMN
eng "Column '%-.192s' cannot be part of FULLTEXT index"
ger "Feld '%-.192s' kann nicht Teil eines FULLTEXT-Index sein"
+ jpn "列 '%-.192s' ã¯å…¨æ–‡ç´¢å¼•ã®ã‚­ãƒ¼ã«ã¯ã§ãã¾ã›ã‚“。"
por "Coluna '%-.192s' não pode ser parte de índice FULLTEXT"
spa "Columna '%-.192s' no puede ser parte de FULLTEXT index"
swe "Kolumn '%-.192s' kan inte vara del av ett FULLTEXT index"
ER_UNKNOWN_KEY_CACHE
eng "Unknown key cache '%-.100s'"
ger "Unbekannter Schlüssel-Cache '%-.100s'"
+ jpn "'%-.100s' ã¯ä¸æ˜Žãªã‚­ãƒ¼ã‚­ãƒ£ãƒƒã‚·ãƒ¥ã§ã™ã€‚"
por "Key cache desconhecida '%-.100s'"
spa "Desconocida key cache '%-.100s'"
swe "Okänd nyckel cache '%-.100s'"
ER_WARN_HOSTNAME_WONT_WORK
eng "MariaDB is started in --skip-name-resolve mode; you must restart it without this switch for this grant to work"
ger "MariaDB wurde mit --skip-name-resolve gestartet. Diese Option darf nicht verwendet werden, damit diese Rechtevergabe möglich ist"
+ jpn "MariaDB㯠--skip-name-resolve モードã§èµ·å‹•ã—ã¦ã„ã¾ã™ã€‚ã“ã®ã‚ªãƒ—ションを外ã—ã¦å†èµ·å‹•ã—ãªã‘ã‚Œã°ã€ã“ã®æ¨©é™æ“作ã¯æ©Ÿèƒ½ã—ã¾ã›ã‚“。"
por "MariaDB foi inicializado em modo --skip-name-resolve. Você necesita reincializá-lo sem esta opção para este grant funcionar"
spa "MariaDB esta inicializado en modo --skip-name-resolve. Usted necesita reinicializarlo sin esta opción para este derecho funcionar"
ER_UNKNOWN_STORAGE_ENGINE 42000
eng "Unknown storage engine '%s'"
ger "Unbekannte Speicher-Engine '%s'"
+ jpn "'%s' ã¯ä¸æ˜Žãªã‚¹ãƒˆãƒ¬ãƒ¼ã‚¸ã‚¨ãƒ³ã‚¸ãƒ³ã§ã™ã€‚"
por "Motor de tabela desconhecido '%s'"
spa "Desconocido motor de tabla '%s'"
ER_WARN_DEPRECATED_SYNTAX
eng "'%s' is deprecated and will be removed in a future release. Please use %s instead"
ger "'%s' ist veraltet. Bitte benutzen Sie '%s'"
+ jpn "'%s' ã¯å°†æ¥ã®ãƒªãƒªãƒ¼ã‚¹ã§å»ƒæ­¢äºˆå®šã§ã™ã€‚代ã‚ã‚Šã« %s を使用ã—ã¦ãã ã•ã„。"
por "'%s' é desatualizado. Use '%s' em seu lugar"
spa "'%s' está desaprobado, use '%s' en su lugar"
ER_NON_UPDATABLE_TABLE
eng "The target table %-.100s of the %s is not updatable"
ger "Die Zieltabelle %-.100s von %s ist nicht aktualisierbar"
+ jpn "対象表 %-.100s ã¯æ›´æ–°å¯èƒ½ã§ã¯ãªã„ã®ã§ã€%s ã‚’è¡Œãˆã¾ã›ã‚“。"
por "A tabela destino %-.100s do %s não é atualizável"
rus "Таблица %-.100s в %s не может изменÑÑ‚ÑÑ"
spa "La tabla destino %-.100s del %s no es actualizable"
@@ -5011,33 +4990,39 @@ ER_NON_UPDATABLE_TABLE
ER_FEATURE_DISABLED
eng "The '%s' feature is disabled; you need MariaDB built with '%s' to have it working"
ger "Das Feature '%s' ist ausgeschaltet, Sie müssen MariaDB mit '%s' übersetzen, damit es verfügbar ist"
+ jpn "機能 '%s' ã¯ç„¡åŠ¹ã§ã™ã€‚利用ã™ã‚‹ãŸã‚ã«ã¯ '%s' ã‚’å«ã‚ã¦ãƒ“ルドã—ãŸMariaDBãŒå¿…è¦ã§ã™ã€‚"
por "O recurso '%s' foi desativado; você necessita MariaDB construído com '%s' para ter isto funcionando"
spa "El recurso '%s' fue deshabilitado; usted necesita construir MariaDB con '%s' para tener eso funcionando"
swe "'%s' är inte aktiverad; För att aktivera detta måste du bygga om MariaDB med '%s' definierad"
ER_OPTION_PREVENTS_STATEMENT
eng "The MariaDB server is running with the %s option so it cannot execute this statement"
ger "Der MariaDB-Server läuft mit der Option %s und kann diese Anweisung deswegen nicht ausführen"
+ jpn "MariaDBサーãƒãƒ¼ãŒ %s オプションã§å®Ÿè¡Œã•ã‚Œã¦ã„ã‚‹ã®ã§ã€ã“ã®ã‚¹ãƒ†ãƒ¼ãƒˆãƒ¡ãƒ³ãƒˆã¯å®Ÿè¡Œã§ãã¾ã›ã‚“。"
por "O servidor MariaDB está rodando com a opção %s razão pela qual não pode executar esse commando"
spa "El servidor MariaDB está rodando con la opción %s tal que no puede ejecutar este comando"
swe "MariaDB är startad med %s. Pga av detta kan du inte använda detta kommando"
ER_DUPLICATED_VALUE_IN_TYPE
eng "Column '%-.100s' has duplicated value '%-.64s' in %s"
ger "Feld '%-.100s' hat doppelten Wert '%-.64s' in %s"
+ jpn "列 '%-.100s' ã§ã€é‡è¤‡ã™ã‚‹å€¤ '%-.64s' ㌠%s ã«æŒ‡å®šã•ã‚Œã¦ã„ã¾ã™ã€‚"
por "Coluna '%-.100s' tem valor duplicado '%-.64s' em %s"
spa "Columna '%-.100s' tiene valor doblado '%-.64s' en %s"
ER_TRUNCATED_WRONG_VALUE 22007
eng "Truncated incorrect %-.32s value: '%-.128s'"
ger "Falscher %-.32s-Wert gekürzt: '%-.128s'"
+ jpn "ä¸æ­£ãª %-.32s ã®å€¤ãŒåˆ‡ã‚Šæ¨ã¦ã‚‰ã‚Œã¾ã—ãŸã€‚: '%-.128s'"
por "Truncado errado %-.32s valor: '%-.128s'"
spa "Equivocado truncado %-.32s valor: '%-.128s'"
ER_TOO_MUCH_AUTO_TIMESTAMP_COLS
eng "Incorrect table definition; there can be only one TIMESTAMP column with CURRENT_TIMESTAMP in DEFAULT or ON UPDATE clause"
ger "Fehlerhafte Tabellendefinition. Es kann nur eine einzige TIMESTAMP-Spalte mit CURRENT_TIMESTAMP als DEFAULT oder in einer ON-UPDATE-Klausel geben"
+ jpn "ä¸æ­£ãªè¡¨å®šç¾©ã§ã™ã€‚DEFAULTå¥ã¾ãŸã¯ON UPDATEå¥ã« CURRENT_TIMESTAMP ã‚’ã¨ã‚‚ãªã†TIMESTAMPåž‹ã®åˆ—ã¯1ã¤ã¾ã§ã§ã™ã€‚"
por "Incorreta definição de tabela; Pode ter somente uma coluna TIMESTAMP com CURRENT_TIMESTAMP em DEFAULT ou ON UPDATE cláusula"
spa "Incorrecta definición de tabla; Solamente debe haber una columna TIMESTAMP con CURRENT_TIMESTAMP en DEFAULT o ON UPDATE cláusula"
ER_INVALID_ON_UPDATE
eng "Invalid ON UPDATE clause for '%-.192s' column"
ger "Ungültige ON-UPDATE-Klausel für Spalte '%-.192s'"
+ jpn "列 '%-.192s' ã« ON UPDATEå¥ã¯ç„¡åŠ¹ã§ã™ã€‚"
por "Inválida cláusula ON UPDATE para campo '%-.192s'"
spa "Inválido ON UPDATE cláusula para campo '%-.192s'"
ER_UNSUPPORTED_PS
@@ -5047,11 +5032,13 @@ ER_GET_ERRMSG
dan "Modtog fejl %d '%-.200s' fra %s"
eng "Got error %d '%-.200s' from %s"
ger "Fehler %d '%-.200s' von %s"
+ jpn "エラー %d '%-.200s' ㌠%s ã‹ã‚‰è¿”ã•ã‚Œã¾ã—ãŸã€‚"
nor "Mottok feil %d '%-.200s' fa %s"
norwegian-ny "Mottok feil %d '%-.200s' fra %s"
ER_GET_TEMPORARY_ERRMSG
dan "Modtog temporary fejl %d '%-.200s' fra %s"
eng "Got temporary error %d '%-.200s' from %s"
+ jpn "一時エラー %d '%-.200s' ㌠%s ã‹ã‚‰è¿”ã•ã‚Œã¾ã—ãŸã€‚"
ger "Temporärer Fehler %d '%-.200s' von %s"
nor "Mottok temporary feil %d '%-.200s' fra %s"
norwegian-ny "Mottok temporary feil %d '%-.200s' fra %s"
@@ -5209,8 +5196,8 @@ ER_FPARSER_EOF_IN_UNKNOWN_PARAMETER
ER_VIEW_NO_EXPLAIN
eng "EXPLAIN/SHOW can not be issued; lacking privileges for underlying table"
ger "EXPLAIN/SHOW kann nicht verlangt werden. Rechte für zugrunde liegende Tabelle fehlen"
- rus "EXPLAIN/SHOW не может быть выполненно; недоÑтаточно прав на таблицы запроÑа"
- ukr "EXPLAIN/SHOW не може бути виконано; немає прав на тиблиці запиту"
+ rus "EXPLAIN/SHOW не может быть выполнено; недоÑтаточно прав на таблицы запроÑа"
+ ukr "EXPLAIN/SHOW не може бути виконано; немає прав на таблиці запиту"
ER_FRM_UNKNOWN_TYPE
eng "File '%-.192s' has unknown type '%-.64s' in its header"
ger "Datei '%-.192s' hat unbekannten Typ '%-.64s' im Header"
@@ -5307,8 +5294,8 @@ ER_VIEW_CHECK_FAILED
rus "проверка CHECK OPTION Ð´Ð»Ñ VIEW '%-.192s.%-.192s' провалилаÑÑŒ"
ukr "Перевірка CHECK OPTION Ð´Ð»Ñ VIEW '%-.192s.%-.192s' не пройшла"
ER_PROCACCESS_DENIED_ERROR 42000
- eng "%-.16s command denied to user '%s'@'%s' for routine '%-.192s'"
- ger "Befehl %-.16s nicht zulässig für Benutzer '%s'@'%s' in Routine '%-.192s'"
+ eng "%-.32s command denied to user '%s'@'%s' for routine '%-.192s'"
+ ger "Befehl %-.32s nicht zulässig für Benutzer '%s'@'%s' in Routine '%-.192s'"
ER_RELAY_LOG_FAIL
eng "Failed purging old relay logs: %s"
ger "Bereinigen alter Relais-Logs fehlgeschlagen: %s"
@@ -5361,8 +5348,8 @@ ER_LOGGING_PROHIBIT_CHANGING_OF
eng "Binary logging and replication forbid changing the global server %s"
ger "Binärlogs und Replikation verhindern Wechsel des globalen Servers %s"
ER_NO_FILE_MAPPING
- eng "Can't map file: %-.200s, errno: %d"
- ger "Kann Datei nicht abbilden: %-.200s, Fehler: %d"
+ eng "Can't map file: %-.200s, errno: %M"
+ ger "Kann Datei nicht abbilden: %-.200s, Fehler: %M"
ER_WRONG_MAGIC
eng "Wrong magic in %-.64s"
ger "Falsche magische Zahlen in %-.64s"
@@ -5517,6 +5504,7 @@ ER_TRG_IN_WRONG_SCHEMA
ER_STACK_OVERRUN_NEED_MORE
eng "Thread stack overrun: %ld bytes used of a %ld byte stack, and %ld bytes needed. Use 'mysqld --thread_stack=#' to specify a bigger stack."
ger "Thread-Stack-Überlauf: %ld Bytes eines %ld-Byte-Stacks in Verwendung, und %ld Bytes benötigt. Verwenden Sie 'mysqld --thread_stack=#', um einen größeren Stack anzugeben"
+ jpn "スレッドスタックä¸è¶³ã§ã™(使用: %ld ; サイズ: %ld ; è¦æ±‚: %ld)。より大ãã„値㧠'mysqld --thread_stack=#' ã®æŒ‡å®šã‚’ã—ã¦ãã ã•ã„。"
ER_TOO_LONG_BODY 42000 S1009
eng "Routine body for '%-.100s' is too long"
ger "Routinen-Body für '%-.100s' ist zu lang"
@@ -5525,7 +5513,7 @@ ER_WARN_CANT_DROP_DEFAULT_KEYCACHE
ger "Der vorgabemäßige Schlüssel-Cache kann nicht gelöscht werden"
ER_TOO_BIG_DISPLAYWIDTH 42000 S1009
eng "Display width out of range for '%-.192s' (max = %lu)"
- ger "Anzeigebreite außerhalb des zulässigen Bereichs für '%-.192s' (Maximum: %lu)"
+ ger "Anzeigebreite außerhalb des zulässigen Bereichs für '%-.192s' (Maximum = %lu)"
ER_XAER_DUPID XAE08
eng "XAER_DUPID: The XID already exists"
ger "XAER_DUPID: Die XID existiert bereits"
@@ -5544,9 +5532,8 @@ ER_PS_NO_RECURSION
ER_SP_CANT_SET_AUTOCOMMIT
eng "Not allowed to set autocommit from a stored function or trigger"
ger "Es ist nicht erlaubt, innerhalb einer gespeicherten Funktion oder eines Triggers AUTOCOMMIT zu setzen"
-ER_MALFORMED_DEFINER
- eng "Definer is not fully qualified"
- ger "Definierer des View ist nicht vollständig spezifiziert"
+ER_MALFORMED_DEFINER 0L000
+ eng "Invalid definer"
ER_VIEW_FRM_NO_USER
eng "View '%-.192s'.'%-.192s' has no definer information (old table format). Current user is used as definer. Please recreate the view!"
ger "View '%-.192s'.'%-.192s' hat keine Definierer-Information (altes Tabellenformat). Der aktuelle Benutzer wird als Definierer verwendet. Bitte erstellen Sie den View neu"
@@ -5599,8 +5586,8 @@ ER_NON_GROUPING_FIELD_USED 42000
eng "Non-grouping field '%-.192s' is used in %-.64s clause"
ger "In der %-.192s-Klausel wird das die Nicht-Gruppierungsspalte '%-.64s' verwendet"
ER_TABLE_CANT_HANDLE_SPKEYS
- eng "The used table type doesn't support SPATIAL indexes"
- ger "Der verwendete Tabellentyp unterstützt keine SPATIAL-Indizes"
+ eng "The storage engine %s doesn't support SPATIAL indexes"
+ ger "Der verwendete Tabellentyp (%s) unterstützt keine SPATIAL-Indizes"
ER_NO_TRIGGERS_ON_SYSTEM_SCHEMA
eng "Triggers can not be created on system tables"
ger "Trigger können nicht auf Systemtabellen erzeugt werden"
@@ -5622,6 +5609,7 @@ ER_WRONG_STRING_LENGTH
ER_NON_INSERTABLE_TABLE
eng "The target table %-.100s of the %s is not insertable-into"
ger "Die Zieltabelle %-.100s von %s ist nicht einfügbar"
+ jpn "対象表 %-.100s ã¯æŒ¿å…¥å¯èƒ½ã§ã¯ãªã„ã®ã§ã€%s ã‚’è¡Œãˆã¾ã›ã‚“。"
ER_ADMIN_WRONG_MRG_TABLE
eng "Table '%-.64s' is differently defined or of non-MyISAM type or doesn't exist"
ger "Tabelle '%-.64s' ist unterschiedlich definiert, nicht vom Typ MyISAM oder existiert nicht"
@@ -5863,8 +5851,8 @@ ER_EVENT_ALREADY_EXISTS
eng "Event '%-.192s' already exists"
ger "Event '%-.192s' existiert bereits"
ER_EVENT_STORE_FAILED
- eng "Failed to store event %s. Error code %d from storage engine."
- ger "Speichern von Event %s fehlgeschlagen. Fehlercode der Speicher-Engine: %d"
+ eng "Failed to store event %s. Error code %M from storage engine."
+ ger "Speichern von Event %s fehlgeschlagen. Fehlercode der Speicher-Engine: %M"
ER_EVENT_DOES_NOT_EXIST
eng "Unknown event '%-.192s'"
ger "Unbekanntes Event '%-.192s'"
@@ -5889,12 +5877,11 @@ ER_EVENT_OPEN_TABLE_FAILED
ER_EVENT_NEITHER_M_EXPR_NOR_M_AT
eng "No datetime expression provided"
ger "Kein DATETIME-Ausdruck angegeben"
-ER_COL_COUNT_DOESNT_MATCH_CORRUPTED
- eng "Column count of mysql.%s is wrong. Expected %d, found %d. The table is probably corrupted"
- ger "Spaltenanzahl von mysql.%s falsch. %d erwartet, aber %d gefunden. Tabelle ist wahrscheinlich beschädigt"
-ER_CANNOT_LOAD_FROM_TABLE
- eng "Cannot load from mysql.%s. The table is probably corrupted"
- ger "Kann mysql.%s nicht einlesen. Tabelle ist wahrscheinlich beschädigt"
+
+ER_UNUSED_2
+ eng "You should never see it"
+ER_UNUSED_3
+ eng "You should never see it"
ER_EVENT_CANNOT_DELETE
eng "Failed to delete the event from mysql.event"
ger "Löschen des Events aus mysql.event fehlgeschlagen"
@@ -5921,9 +5908,8 @@ ER_CANT_WRITE_LOCK_LOG_TABLE
ER_CANT_LOCK_LOG_TABLE
eng "You can't use locks with log tables."
ger "Log-Tabellen können nicht gesperrt werden."
-ER_FOREIGN_DUPLICATE_KEY 23000 S1009
- eng "Upholding foreign key constraints for table '%.192s', entry '%-.192s', key %d would lead to a duplicate entry"
- ger "Aufrechterhalten der Fremdschlüssel-Beschränkungen für Tabelle '%.192s', Eintrag '%-.192s', Schlüssel %d würde zu einem doppelten Eintrag führen"
+ER_UNUSED_4
+ eng "You should never see it"
ER_COL_COUNT_DOESNT_MATCH_PLEASE_UPDATE
eng "Column count of mysql.%s is wrong. Expected %d, found %d. Created with MariaDB %d, now running %d. Please use mysql_upgrade to fix this error."
ger "Spaltenanzahl von mysql.%s falsch. %d erwartet, aber %d erhalten. Erzeugt mit MariaDB %d, jetzt unter %d. Bitte benutzen Sie mysql_upgrade, um den Fehler zu beheben"
@@ -5958,9 +5944,8 @@ ER_WRONG_PARTITION_NAME
eng "Incorrect partition name"
ger "Falscher Partitionsname"
swe "Felaktigt partitionsnamn"
-ER_CANT_CHANGE_TX_ISOLATION 25001
- eng "Transaction isolation level can't be changed while a transaction is in progress"
- ger "Transaktionsisolationsebene kann während einer laufenden Transaktion nicht geändert werden"
+ER_CANT_CHANGE_TX_CHARACTERISTICS 25001
+ eng "Transaction characteristics can't be changed while a transaction is in progress"
ER_DUP_ENTRY_AUTOINCREMENT_CASE
eng "ALTER TABLE causes auto_increment resequencing, resulting in duplicate entry '%-.192s' for key '%-.192s'"
ger "ALTER TABLE führt zur Neusequenzierung von auto_increment, wodurch der doppelte Eintrag '%-.192s' für Schlüssel '%-.192s' auftritt"
@@ -5968,8 +5953,8 @@ ER_EVENT_MODIFY_QUEUE_ERROR
eng "Internal scheduler error %d"
ger "Interner Scheduler-Fehler %d"
ER_EVENT_SET_VAR_ERROR
- eng "Error during starting/stopping of the scheduler. Error code %u"
- ger "Fehler während des Startens oder Anhalten des Schedulers. Fehlercode %u"
+ eng "Error during starting/stopping of the scheduler. Error code %M"
+ ger "Fehler während des Startens oder Anhalten des Schedulers. Fehlercode %M"
ER_PARTITION_MERGE_ERROR
eng "Engine cannot be used in partitioned tables"
ger "Engine kann in partitionierten Tabellen nicht verwendet werden"
@@ -5994,8 +5979,8 @@ ER_ONLY_INTEGERS_ALLOWED
eng "Only integers allowed as number here"
ger "An dieser Stelle sind nur Ganzzahlen zulässig"
ER_UNSUPORTED_LOG_ENGINE
- eng "This storage engine cannot be used for log tables"
- ger "Diese Speicher-Engine kann für Logtabellen nicht verwendet werden"
+ eng "Storage engine %s cannot be used for log tables"
+ ger "Speicher-Engine %s kann für Logtabellen nicht verwendet werden"
ER_BAD_LOG_STATEMENT
eng "You cannot '%s' a log table if logging is enabled"
ger "Sie können eine Logtabelle nicht '%s', wenn Loggen angeschaltet ist"
@@ -6017,29 +6002,28 @@ ER_NATIVE_FCT_NAME_COLLISION
# When using this error message, use the ER_DUP_ENTRY error code. See, for
# example, code in handler.cc.
ER_DUP_ENTRY_WITH_KEY_NAME 23000 S1009
- cze "Zvojen-Bý klÃ­Ä '%-.64s' (Äíslo klíÄe '%-.192s')"
+ cze "Zvojený klÃ­Ä '%-.64s' (Äíslo klíÄe '%-.192s')"
dan "Ens værdier '%-.64s' for indeks '%-.192s'"
nla "Dubbele ingang '%-.64s' voor zoeksleutel '%-.192s'"
eng "Duplicate entry '%-.64s' for key '%-.192s'"
- jps "'%-.64s' 㯠key '%-.192s' ã«ãŠã„ã¦é‡è¤‡ã—ã¦ã„ã¾ã™",
est "Kattuv väärtus '%-.64s' võtmele '%-.192s'"
fre "Duplicata du champ '%-.64s' pour la clef '%-.192s'"
ger "Doppelter Eintrag '%-.64s' für Schlüssel '%-.192s'"
greek "Διπλή εγγÏαφή '%-.64s' για το κλειδί '%-.192s'"
hun "Duplikalt bejegyzes '%-.64s' a '%-.192s' kulcs szerint."
ita "Valore duplicato '%-.64s' per la chiave '%-.192s'"
- jpn "'%-.64s' 㯠key '%-.192s' ã«ãŠã„ã¦é‡è¤‡ã—ã¦ã„ã¾ã™"
+ jpn "'%-.64s' ã¯ç´¢å¼• '%-.192s' ã§é‡è¤‡ã—ã¦ã„ã¾ã™ã€‚"
kor "ì¤‘ë³µëœ ìž…ë ¥ ê°’ '%-.64s': key '%-.192s'"
nor "Like verdier '%-.64s' for nøkkel '%-.192s'"
norwegian-ny "Like verdiar '%-.64s' for nykkel '%-.192s'"
- pol "Powtórzone wyst?pienie '%-.64s' dla klucza '%-.192s'"
+ pol "Powtórzone wystąpienie '%-.64s' dla klucza '%-.192s'"
por "Entrada '%-.64s' duplicada para a chave '%-.192s'"
rum "Cimpul '%-.64s' e duplicat pentru cheia '%-.192s'"
rus "ДублирующаÑÑÑ Ð·Ð°Ð¿Ð¸ÑÑŒ '%-.64s' по ключу '%-.192s'"
serbian "Dupliran unos '%-.64s' za kljuÄ '%-.192s'"
slo "Opakovaný kÄ¾ÃºÄ '%-.64s' (Äíslo kľúÄa '%-.192s')"
spa "Entrada duplicada '%-.64s' para la clave '%-.192s'"
- swe "Dubbel nyckel '%-.64s' för nyckel '%-.192s'"
+ swe "Dublett '%-.64s' för nyckel '%-.192s'"
ukr "Дублюючий Ð·Ð°Ð¿Ð¸Ñ '%-.64s' Ð´Ð»Ñ ÐºÐ»ÑŽÑ‡Ð° '%-.192s'"
ER_BINLOG_PURGE_EMFILE
eng "Too many files opened, please execute the command again"
@@ -6048,8 +6032,8 @@ ER_EVENT_CANNOT_CREATE_IN_THE_PAST
eng "Event execution time is in the past and ON COMPLETION NOT PRESERVE is set. The event was dropped immediately after creation."
ger "Ausführungszeit des Events liegt in der Vergangenheit, und es wurde ON COMPLETION NOT PRESERVE gesetzt. Das Event wurde unmittelbar nach Erzeugung gelöscht."
ER_EVENT_CANNOT_ALTER_IN_THE_PAST
- eng "Event execution time is in the past and ON COMPLETION NOT PRESERVE is set. The event was dropped immediately after creation."
- ger "Ausführungszeit des Events liegt in der Vergangenheit, und es wurde ON COMPLETION NOT PRESERVE gesetzt. Das Event wurde unmittelbar nach Erzeugung gelöscht."
+ eng "Event execution time is in the past and ON COMPLETION NOT PRESERVE is set. The event was not changed. Specify a time in the future."
+ ger "Execution Zeitpunkt des Ereignisses in der Vergangenheit liegt, und es war NACH ABSCHLUSS Set nicht erhalten. Die Veranstaltung wurde nicht verändert. Geben Sie einen Zeitpunkt in der Zukunft."
ER_SLAVE_INCIDENT
eng "The incident %s occured on the master. Message: %-.64s"
ger "Der Vorfall %s passierte auf dem Master. Meldung: %-.64s"
@@ -6105,9 +6089,8 @@ ER_TRG_CANT_OPEN_TABLE
ER_CANT_CREATE_SROUTINE
eng "Cannot create stored routine `%-.64s`. Check warnings"
ger "Kann gespeicherte Routine `%-.64s` nicht erzeugen. Beachten Sie die Warnungen"
-ER_NEVER_USED
- eng "Ambiguous slave modes combination. %s"
- ger "Mehrdeutige Kombination von Slave-Modi. %s"
+ER_UNUSED_11
+ eng "You should never see it"
ER_NO_FORMAT_DESCRIPTION_EVENT_BEFORE_BINLOG_STATEMENT
eng "The BINLOG statement of type `%s` was not preceded by a format description BINLOG statement."
ger "Der BINLOG-Anweisung vom Typ `%s` ging keine BINLOG-Anweisung zur Formatbeschreibung voran."
@@ -6133,8 +6116,8 @@ ER_DELAYED_NOT_SUPPORTED
eng "DELAYED option not supported for table '%-.192s'"
ger "Die DELAYED-Option wird für Tabelle '%-.192s' nicht unterstützt"
WARN_NO_MASTER_INFO
- eng "The master info structure does not exist"
- ger "Die Master-Info-Struktur existiert nicht"
+ eng "There is no master connection '%.*s'"
+ ger "Die Master-Info-Struktur existiert nicht '%.*s'"
WARN_OPTION_IGNORED
eng "<%-.64s> option ignored"
ger "Option <%-.64s> ignoriert"
@@ -6165,13 +6148,13 @@ ER_EXCEPTIONS_WRITE_ERROR
eng "Write to exceptions table failed. Message: %-.128s""
ger "Schreiben in Ausnahme-Tabelle fehlgeschlagen. Meldung: %-.128s""
ER_TOO_LONG_TABLE_COMMENT
- eng "Comment for table '%-.64s' is too long (max = %lu)"
- por "Comentário para a tabela '%-.64s' é longo demais (max = %lu)"
- ger "Kommentar für Tabelle '%-.64s' ist zu lang (max = %lu)"
+ eng "Comment for table '%-.64s' is too long (max = %u)"
+ por "Comentário para a tabela '%-.64s' é longo demais (max = %u)"
+ ger "Kommentar für Tabelle '%-.64s' ist zu lang (max = %u)"
ER_TOO_LONG_FIELD_COMMENT
- eng "Comment for field '%-.64s' is too long (max = %lu)"
- por "Comentário para o campo '%-.64s' é longo demais (max = %lu)"
- ger "Kommentar für Feld '%-.64s' ist zu lang (max = %lu)"
+ eng "Comment for field '%-.64s' is too long (max = %u)"
+ por "Comentário para o campo '%-.64s' é longo demais (max = %u)"
+ ger "Kommentar für Feld '%-.64s' ist zu lang (max = %u)"
ER_FUNC_INEXISTENT_NAME_COLLISION 42000
eng "FUNCTION %s does not exist. Check the 'Function Name Parsing and Resolution' section in the Reference Manual"
ger "FUNCTION %s existiert nicht. Erläuterungen im Abschnitt 'Function Name Parsing and Resolution' im Referenzhandbuch"
@@ -6370,7 +6353,7 @@ ER_VALUES_IS_NOT_INT_TYPE_ERROR
swe "Värden i VALUES för partition '%-.64s' måste ha typen INT"
ER_ACCESS_DENIED_NO_PASSWORD_ERROR 28000
- cze "P-Břístup pro uživatele '%s'@'%s'"
+ cze "Přístup pro uživatele '%s'@'%s'"
dan "Adgang nægtet bruger: '%s'@'%s'"
nla "Toegang geweigerd voor gebruiker: '%s'@'%s'"
eng "Access denied for user '%s'@'%s'"
@@ -6457,11 +6440,11 @@ ER_BINLOG_UNSAFE_CREATE_REPLACE_SELECT
ER_BINLOG_UNSAFE_UPDATE_IGNORE
eng "UPDATE IGNORE is unsafe because the order in which rows are updated determines which (if any) rows are ignored. This order cannot be predicted and may differ on master and the slave."
-ER_PLUGIN_NO_UNINSTALL
- eng "Plugin '%s' is marked as not dynamically uninstallable. You have to stop the server to uninstall it."
+ER_UNUSED_13
+ eng "You should never see it"
-ER_PLUGIN_NO_INSTALL
- eng "Plugin '%s' is marked as not dynamically installable. You have to stop the server to install it."
+ER_UNUSED_14
+ eng "You should never see it"
ER_BINLOG_UNSAFE_WRITE_AUTOINC_SELECT
eng "Statements writing to a table with an auto-increment column after selecting from another table are unsafe because the order in which rows are retrieved determines what (if any) rows will be written. This order cannot be predicted and may differ on master and the slave."
@@ -6485,6 +6468,492 @@ ER_BINLOG_UNSAFE_AUTOINC_NOT_FIRST
# End of 5.5 error messages.
#
+ER_CANNOT_LOAD_FROM_TABLE_V2
+ eng "Cannot load from %s.%s. The table is probably corrupted"
+ ger "Kann %s.%s nicht einlesen. Tabelle ist wahrscheinlich beschädigt"
+
+ER_MASTER_DELAY_VALUE_OUT_OF_RANGE
+ eng "The requested value %u for the master delay exceeds the maximum %u"
+ER_ONLY_FD_AND_RBR_EVENTS_ALLOWED_IN_BINLOG_STATEMENT
+ eng "Only Format_description_log_event and row events are allowed in BINLOG statements (but %s was provided)"
+
+ER_PARTITION_EXCHANGE_DIFFERENT_OPTION
+ eng "Non matching attribute '%-.64s' between partition and table"
+ swe "Attributet '%-.64s' är olika mellan partition och tabell"
+ER_PARTITION_EXCHANGE_PART_TABLE
+ eng "Table to exchange with partition is partitioned: '%-.64s'"
+ swe "Tabellen att byta ut mot partition är partitionerad: '%-.64s'"
+ER_PARTITION_EXCHANGE_TEMP_TABLE
+ eng "Table to exchange with partition is temporary: '%-.64s'"
+ swe "Tabellen att byta ut mot partition är temporär: '%-.64s'"
+ER_PARTITION_INSTEAD_OF_SUBPARTITION
+ eng "Subpartitioned table, use subpartition instead of partition"
+ swe "Subpartitionerad tabell, använd subpartition istället för partition"
+ER_UNKNOWN_PARTITION
+ eng "Unknown partition '%-.64s' in table '%-.64s'"
+ swe "Okänd partition '%-.64s' i tabell '%-.64s'"
+ER_TABLES_DIFFERENT_METADATA
+ eng "Tables have different definitions"
+ swe "Tabellerna har olika definitioner"
+ER_ROW_DOES_NOT_MATCH_PARTITION
+ eng "Found a row that does not match the partition"
+ swe "Hittade en rad som inte passar i partitionen"
+ER_BINLOG_CACHE_SIZE_GREATER_THAN_MAX
+ eng "Option binlog_cache_size (%lu) is greater than max_binlog_cache_size (%lu); setting binlog_cache_size equal to max_binlog_cache_size."
+ER_WARN_INDEX_NOT_APPLICABLE
+ eng "Cannot use %-.64s access on index '%-.64s' due to type or collation conversion on field '%-.64s'"
+
+ER_PARTITION_EXCHANGE_FOREIGN_KEY
+ eng "Table to exchange with partition has foreign key references: '%-.64s'"
+ swe "Tabellen att byta ut mot partition har foreign key referenser: '%-.64s'"
+ER_NO_SUCH_KEY_VALUE
+ eng "Key value '%-.192s' was not found in table '%-.192s.%-.192s'"
+ER_RPL_INFO_DATA_TOO_LONG
+ eng "Data for column '%s' too long"
+ER_NETWORK_READ_EVENT_CHECKSUM_FAILURE
+ eng "Replication event checksum verification failed while reading from network."
+ER_BINLOG_READ_EVENT_CHECKSUM_FAILURE
+ eng "Replication event checksum verification failed while reading from a log file."
+
+ER_BINLOG_STMT_CACHE_SIZE_GREATER_THAN_MAX
+ eng "Option binlog_stmt_cache_size (%lu) is greater than max_binlog_stmt_cache_size (%lu); setting binlog_stmt_cache_size equal to max_binlog_stmt_cache_size."
+ER_CANT_UPDATE_TABLE_IN_CREATE_TABLE_SELECT
+ eng "Can't update table '%-.192s' while '%-.192s' is being created."
+
+ER_PARTITION_CLAUSE_ON_NONPARTITIONED
+ eng "PARTITION () clause on non partitioned table"
+ swe "PARTITION () klausul för en icke partitionerad tabell"
+ER_ROW_DOES_NOT_MATCH_GIVEN_PARTITION_SET
+ eng "Found a row not matching the given partition set"
+ swe "Hittade en rad som inte passar i någon given partition"
+
+ER_UNUSED_5
+ eng "You should never see it"
+
+ER_CHANGE_RPL_INFO_REPOSITORY_FAILURE
+ eng "Failure while changing the type of replication repository: %s."
+
+ER_WARNING_NOT_COMPLETE_ROLLBACK_WITH_CREATED_TEMP_TABLE
+ eng "The creation of some temporary tables could not be rolled back."
+ER_WARNING_NOT_COMPLETE_ROLLBACK_WITH_DROPPED_TEMP_TABLE
+ eng "Some temporary tables were dropped, but these operations could not be rolled back."
+
+ER_MTS_FEATURE_IS_NOT_SUPPORTED
+ eng "%s is not supported in multi-threaded slave mode. %s"
+ER_MTS_UPDATED_DBS_GREATER_MAX
+ eng "The number of modified databases exceeds the maximum %d; the database names will not be included in the replication event metadata."
+ER_MTS_CANT_PARALLEL
+ eng "Cannot execute the current event group in the parallel mode. Encountered event %s, relay-log name %s, position %s which prevents execution of this event group in parallel mode. Reason: %s."
+ER_MTS_INCONSISTENT_DATA
+ eng "%s"
+
+ER_FULLTEXT_NOT_SUPPORTED_WITH_PARTITIONING
+ eng "FULLTEXT index is not supported for partitioned tables."
+ swe "FULLTEXT index stöds ej för partitionerade tabeller."
+
+ER_DA_INVALID_CONDITION_NUMBER 35000
+ eng "Invalid condition number"
+ por "Número de condição inválido"
+
+ER_INSECURE_PLAIN_TEXT
+ eng "Sending passwords in plain text without SSL/TLS is extremely insecure."
+
+ER_INSECURE_CHANGE_MASTER
+ eng "Storing MySQL user name or password information in the master.info repository is not secure and is therefore not recommended. Please see the MySQL Manual for more about this issue and possible alternatives."
+
+ER_FOREIGN_DUPLICATE_KEY_WITH_CHILD_INFO 23000 S1009
+ eng "Foreign key constraint for table '%.192s', record '%-.192s' would lead to a duplicate entry in table '%.192s', key '%.192s'"
+ ger "Fremdschlüssel-Beschränkung für Tabelle '%.192s', Datensatz '%-.192s' würde zu einem doppelten Eintrag in Tabelle '%.192s', Schlüssel '%.192s' führen"
+ swe "FOREIGN KEY constraint för tabell '%.192s', posten '%-.192s' kan inte uppdatera barntabell '%.192s' på grund av nyckel '%.192s'"
+
+ER_FOREIGN_DUPLICATE_KEY_WITHOUT_CHILD_INFO 23000 S1009
+ eng "Foreign key constraint for table '%.192s', record '%-.192s' would lead to a duplicate entry in a child table"
+ ger "Fremdschlüssel-Beschränkung für Tabelle '%.192s', Datensatz '%-.192s' würde zu einem doppelten Eintrag in einer Kind-Tabelle führen"
+ swe "FOREIGN KEY constraint för tabell '%.192s', posten '%-.192s' kan inte uppdatera en barntabell på grund av UNIQUE-test"
+
+ER_SQLTHREAD_WITH_SECURE_SLAVE
+ eng "Setting authentication options is not possible when only the Slave SQL Thread is being started."
+
+ER_TABLE_HAS_NO_FT
+ eng "The table does not have FULLTEXT index to support this query"
+
+ER_VARIABLE_NOT_SETTABLE_IN_SF_OR_TRIGGER
+ eng "The system variable %.200s cannot be set in stored functions or triggers."
+
+ER_VARIABLE_NOT_SETTABLE_IN_TRANSACTION
+ eng "The system variable %.200s cannot be set when there is an ongoing transaction."
+
+ER_GTID_NEXT_IS_NOT_IN_GTID_NEXT_LIST
+ eng "The system variable @@SESSION.GTID_NEXT has the value %.200s, which is not listed in @@SESSION.GTID_NEXT_LIST."
+
+ER_CANT_CHANGE_GTID_NEXT_IN_TRANSACTION_WHEN_GTID_NEXT_LIST_IS_NULL
+ eng "When @@SESSION.GTID_NEXT_LIST == NULL, the system variable @@SESSION.GTID_NEXT cannot change inside a transaction."
+
+ER_SET_STATEMENT_CANNOT_INVOKE_FUNCTION
+ eng "The statement 'SET %.200s' cannot invoke a stored function."
+
+ER_GTID_NEXT_CANT_BE_AUTOMATIC_IF_GTID_NEXT_LIST_IS_NON_NULL
+ eng "The system variable @@SESSION.GTID_NEXT cannot be 'AUTOMATIC' when @@SESSION.GTID_NEXT_LIST is non-NULL."
+
+ER_SKIPPING_LOGGED_TRANSACTION
+ eng "Skipping transaction %.200s because it has already been executed and logged."
+
+ER_MALFORMED_GTID_SET_SPECIFICATION
+ eng "Malformed GTID set specification '%.200s'."
+
+ER_MALFORMED_GTID_SET_ENCODING
+ eng "Malformed GTID set encoding."
+
+ER_MALFORMED_GTID_SPECIFICATION
+ eng "Malformed GTID specification '%.200s'."
+
+ER_GNO_EXHAUSTED
+ eng "Impossible to generate Global Transaction Identifier: the integer component reached the maximal value. Restart the server with a new server_uuid."
+
+ER_BAD_SLAVE_AUTO_POSITION
+ eng "Parameters MASTER_LOG_FILE, MASTER_LOG_POS, RELAY_LOG_FILE and RELAY_LOG_POS cannot be set when MASTER_AUTO_POSITION is active."
+
+ER_AUTO_POSITION_REQUIRES_GTID_MODE_ON
+ eng "CHANGE MASTER TO MASTER_AUTO_POSITION = 1 can only be executed when GTID_MODE = ON."
+
+ER_CANT_DO_IMPLICIT_COMMIT_IN_TRX_WHEN_GTID_NEXT_IS_SET
+ eng "Cannot execute statements with implicit commit inside a transaction when GTID_NEXT != AUTOMATIC or GTID_NEXT_LIST != NULL."
+
+ER_GTID_MODE_2_OR_3_REQUIRES_ENFORCE_GTID_CONSISTENCY_ON
+ eng "GTID_MODE = ON or GTID_MODE = UPGRADE_STEP_2 requires ENFORCE_GTID_CONSISTENCY = 1."
+
+ER_GTID_MODE_REQUIRES_BINLOG
+ eng "GTID_MODE = ON or UPGRADE_STEP_1 or UPGRADE_STEP_2 requires --log-bin and --log-slave-updates."
+
+ER_CANT_SET_GTID_NEXT_TO_GTID_WHEN_GTID_MODE_IS_OFF
+ eng "GTID_NEXT cannot be set to UUID:NUMBER when GTID_MODE = OFF."
+
+ER_CANT_SET_GTID_NEXT_TO_ANONYMOUS_WHEN_GTID_MODE_IS_ON
+ eng "GTID_NEXT cannot be set to ANONYMOUS when GTID_MODE = ON."
+
+ER_CANT_SET_GTID_NEXT_LIST_TO_NON_NULL_WHEN_GTID_MODE_IS_OFF
+ eng "GTID_NEXT_LIST cannot be set to a non-NULL value when GTID_MODE = OFF."
+
+ER_FOUND_GTID_EVENT_WHEN_GTID_MODE_IS_OFF
+ eng "Found a Gtid_log_event or Previous_gtids_log_event when GTID_MODE = OFF."
+
+ER_GTID_UNSAFE_NON_TRANSACTIONAL_TABLE
+ eng "When ENFORCE_GTID_CONSISTENCY = 1, updates to non-transactional tables can only be done in either autocommitted statements or single-statement transactions, and never in the same statement as updates to transactional tables."
+
+ER_GTID_UNSAFE_CREATE_SELECT
+ eng "CREATE TABLE ... SELECT is forbidden when ENFORCE_GTID_CONSISTENCY = 1."
+
+ER_GTID_UNSAFE_CREATE_DROP_TEMPORARY_TABLE_IN_TRANSACTION
+ eng "When ENFORCE_GTID_CONSISTENCY = 1, the statements CREATE TEMPORARY TABLE and DROP TEMPORARY TABLE can be executed in a non-transactional context only, and require that AUTOCOMMIT = 1."
+
+ER_GTID_MODE_CAN_ONLY_CHANGE_ONE_STEP_AT_A_TIME
+ eng "The value of GTID_MODE can only change one step at a time: OFF <-> UPGRADE_STEP_1 <-> UPGRADE_STEP_2 <-> ON. Also note that this value must be stepped up or down simultaneously on all servers; see the Manual for instructions."
+
+ER_MASTER_HAS_PURGED_REQUIRED_GTIDS
+ eng "The slave is connecting using CHANGE MASTER TO MASTER_AUTO_POSITION = 1, but the master has purged binary logs containing GTIDs that the slave requires."
+
+ER_CANT_SET_GTID_NEXT_WHEN_OWNING_GTID
+ eng "GTID_NEXT cannot be changed by a client that owns a GTID. The client owns %s. Ownership is released on COMMIT or ROLLBACK."
+
+ER_UNKNOWN_EXPLAIN_FORMAT
+ eng "Unknown EXPLAIN format name: '%s'"
+ rus "ÐеизвеÑтное Ð¸Ð¼Ñ Ñ„Ð¾Ñ€Ð¼Ð°Ñ‚Ð° команды EXPLAIN: '%s'"
+
+ER_CANT_EXECUTE_IN_READ_ONLY_TRANSACTION 25006
+ eng "Cannot execute statement in a READ ONLY transaction."
+
+ER_TOO_LONG_TABLE_PARTITION_COMMENT
+ eng "Comment for table partition '%-.64s' is too long (max = %lu)"
+
+ER_SLAVE_CONFIGURATION
+ eng "Slave is not configured or failed to initialize properly. You must at least set --server-id to enable either a master or a slave. Additional error messages can be found in the MySQL error log."
+
+ER_INNODB_FT_LIMIT
+ eng "InnoDB presently supports one FULLTEXT index creation at a time"
+
+ER_INNODB_NO_FT_TEMP_TABLE
+ eng "Cannot create FULLTEXT index on temporary InnoDB table"
+
+ER_INNODB_FT_WRONG_DOCID_COLUMN
+ eng "Column '%-.192s' is of wrong type for an InnoDB FULLTEXT index"
+
+ER_INNODB_FT_WRONG_DOCID_INDEX
+ eng "Index '%-.192s' is of wrong type for an InnoDB FULLTEXT index"
+
+ER_INNODB_ONLINE_LOG_TOO_BIG
+ eng "Creating index '%-.192s' required more than 'innodb_online_alter_log_max_size' bytes of modification log. Please try again."
+
+ER_UNKNOWN_ALTER_ALGORITHM
+ eng "Unknown ALGORITHM '%s'"
+
+ER_UNKNOWN_ALTER_LOCK
+ eng "Unknown LOCK type '%s'"
+
+ER_MTS_CHANGE_MASTER_CANT_RUN_WITH_GAPS
+ eng "CHANGE MASTER cannot be executed when the slave was stopped with an error or killed in MTS mode. Consider using RESET SLAVE or START SLAVE UNTIL."
+
+ER_MTS_RECOVERY_FAILURE
+ eng "Cannot recover after SLAVE errored out in parallel execution mode. Additional error messages can be found in the MySQL error log."
+
+ER_MTS_RESET_WORKERS
+ eng "Cannot clean up worker info tables. Additional error messages can be found in the MySQL error log."
+
+ER_COL_COUNT_DOESNT_MATCH_CORRUPTED_V2
+ eng "Column count of %s.%s is wrong. Expected %d, found %d. The table is probably corrupted"
+ ger "Spaltenanzahl von %s.%s falsch. %d erwartet, aber %d gefunden. Tabelle ist wahrscheinlich beschädigt"
+
+ER_SLAVE_SILENT_RETRY_TRANSACTION
+ eng "Slave must silently retry current transaction"
+
+ER_DISCARD_FK_CHECKS_RUNNING
+ eng "There is a foreign key check running on table '%-.192s'. Cannot discard the table."
+
+ER_TABLE_SCHEMA_MISMATCH
+ eng "Schema mismatch (%s)"
+
+ER_TABLE_IN_SYSTEM_TABLESPACE
+ eng "Table '%-.192s' in system tablespace"
+
+ER_IO_READ_ERROR
+ eng "IO Read error: (%lu, %s) %s"
+
+ER_IO_WRITE_ERROR
+ eng "IO Write error: (%lu, %s) %s"
+
+ER_TABLESPACE_MISSING
+ eng "Tablespace is missing for table '%-.192s'"
+
+ER_TABLESPACE_EXISTS
+ eng "Tablespace for table '%-.192s' exists. Please DISCARD the tablespace before IMPORT."
+
+ER_TABLESPACE_DISCARDED
+ eng "Tablespace has been discarded for table '%-.192s'"
+
+ER_INTERNAL_ERROR
+ eng "Internal error: %-.192s"
+
+ER_INNODB_IMPORT_ERROR
+ eng "ALTER TABLE '%-.192s' IMPORT TABLESPACE failed with error %lu : '%s'"
+
+ER_INNODB_INDEX_CORRUPT
+ eng "Index corrupt: %s"
+
+ER_INVALID_YEAR_COLUMN_LENGTH
+ eng "YEAR(%lu) column type is deprecated. Creating YEAR(4) column instead."
+ rus "Тип YEAR(%lu) более не поддерживаетÑÑ, вмеÑто него будет Ñоздана колонка Ñ Ñ‚Ð¸Ð¿Ð¾Ð¼ YEAR(4)."
+
+ER_NOT_VALID_PASSWORD
+ eng "Your password does not satisfy the current policy requirements"
+
+ER_MUST_CHANGE_PASSWORD
+ eng "You must SET PASSWORD before executing this statement"
+ bgn "ТрÑбва първо да Ñи Ñмените паролата ÑÑŠÑ SET PASSWORD за да можете да изпълните тази команда"
+
+ER_FK_NO_INDEX_CHILD
+ eng "Failed to add the foreign key constaint. Missing index for constraint '%s' in the foreign table '%s'"
+
+ER_FK_NO_INDEX_PARENT
+ eng "Failed to add the foreign key constaint. Missing index for constraint '%s' in the referenced table '%s'"
+
+ER_FK_FAIL_ADD_SYSTEM
+ eng "Failed to add the foreign key constraint '%s' to system tables"
+
+ER_FK_CANNOT_OPEN_PARENT
+ eng "Failed to open the referenced table '%s'"
+
+ER_FK_INCORRECT_OPTION
+ eng "Failed to add the foreign key constraint on table '%s'. Incorrect options in FOREIGN KEY constraint '%s'"
+
+ER_FK_DUP_NAME
+ eng "Duplicate foreign key constraint name '%s'"
+
+ER_PASSWORD_FORMAT
+ eng "The password hash doesn't have the expected format. Check if the correct password algorithm is being used with the PASSWORD() function."
+
+ER_FK_COLUMN_CANNOT_DROP
+ eng "Cannot drop column '%-.192s': needed in a foreign key constraint '%-.192s'"
+ ger "Kann Spalte '%-.192s' nicht löschen: wird für eine Fremdschlüsselbeschränkung '%-.192s' benötigt"
+
+ER_FK_COLUMN_CANNOT_DROP_CHILD
+ eng "Cannot drop column '%-.192s': needed in a foreign key constraint '%-.192s' of table '%-.192s'"
+ ger "Kann Spalte '%-.192s' nicht löschen: wird für eine Fremdschlüsselbeschränkung '%-.192s' der Tabelle '%-.192s' benötigt"
+
+ER_FK_COLUMN_NOT_NULL
+ eng "Column '%-.192s' cannot be NOT NULL: needed in a foreign key constraint '%-.192s' SET NULL"
+ ger "Spalte '%-.192s' kann nicht NOT NULL sein: wird für eine Fremdschlüsselbeschränkung '%-.192s' SET NULL benötigt"
+
+ER_DUP_INDEX
+ eng "Duplicate index '%-.64s' defined on the table '%-.64s.%-.64s'. This is deprecated and will be disallowed in a future release."
+
+ER_FK_COLUMN_CANNOT_CHANGE
+ eng "Cannot change column '%-.192s': used in a foreign key constraint '%-.192s'"
+
+ER_FK_COLUMN_CANNOT_CHANGE_CHILD
+ eng "Cannot change column '%-.192s': used in a foreign key constraint '%-.192s' of table '%-.192s'"
+
+ER_FK_CANNOT_DELETE_PARENT
+ eng "Cannot delete rows from table which is parent in a foreign key constraint '%-.192s' of table '%-.192s'"
+
+ER_MALFORMED_PACKET
+ eng "Malformed communication packet."
+
+ER_READ_ONLY_MODE
+ eng "Running in read-only mode"
+
+ER_GTID_NEXT_TYPE_UNDEFINED_GROUP
+ eng "When GTID_NEXT is set to a GTID, you must explicitly set it again after a COMMIT or ROLLBACK. If you see this error message in the slave SQL thread, it means that a table in the current transaction is transactional on the master and non-transactional on the slave. In a client connection, it means that you executed SET GTID_NEXT before a transaction and forgot to set GTID_NEXT to a different identifier or to 'AUTOMATIC' after COMMIT or ROLLBACK. Current GTID_NEXT is '%s'."
+
+ER_VARIABLE_NOT_SETTABLE_IN_SP
+ eng "The system variable %.200s cannot be set in stored procedures."
+
+ER_CANT_SET_GTID_PURGED_WHEN_GTID_MODE_IS_OFF
+ eng "GTID_PURGED can only be set when GTID_MODE = ON."
+
+ER_CANT_SET_GTID_PURGED_WHEN_GTID_EXECUTED_IS_NOT_EMPTY
+ eng "GTID_PURGED can only be set when GTID_EXECUTED is empty."
+
+ER_CANT_SET_GTID_PURGED_WHEN_OWNED_GTIDS_IS_NOT_EMPTY
+ eng "GTID_PURGED can only be set when there are no ongoing transactions (not even in other clients)."
+
+ER_GTID_PURGED_WAS_CHANGED
+ eng "GTID_PURGED was changed from '%s' to '%s'."
+
+ER_GTID_EXECUTED_WAS_CHANGED
+ eng "GTID_EXECUTED was changed from '%s' to '%s'."
+
+ER_BINLOG_STMT_MODE_AND_NO_REPL_TABLES
+ eng "Cannot execute statement: impossible to write to binary log since BINLOG_FORMAT = STATEMENT, and both replicated and non replicated tables are written to."
+
+ER_ALTER_OPERATION_NOT_SUPPORTED 0A000
+ eng "%s is not supported for this operation. Try %s."
+
+ER_ALTER_OPERATION_NOT_SUPPORTED_REASON 0A000
+ eng "%s is not supported. Reason: %s. Try %s."
+
+ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_COPY
+ eng "COPY algorithm requires a lock"
+
+ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_PARTITION
+ eng "Partition specific operations do not yet support LOCK/ALGORITHM"
+
+ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_FK_RENAME
+ eng "Columns participating in a foreign key are renamed"
+
+ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_COLUMN_TYPE
+ eng "Cannot change column type INPLACE"
+
+ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_FK_CHECK
+ eng "Adding foreign keys needs foreign_key_checks=OFF"
+
+ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_IGNORE
+ eng "Creating unique indexes with IGNORE requires COPY algorithm to remove duplicate rows"
+
+ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_NOPK
+ eng "Dropping a primary key is not allowed without also adding a new primary key"
+
+ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_AUTOINC
+ eng "Adding an auto-increment column requires a lock"
+
+ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_HIDDEN_FTS
+ eng "Cannot replace hidden FTS_DOC_ID with a user-visible one"
+
+ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_CHANGE_FTS
+ eng "Cannot drop or rename FTS_DOC_ID"
+
+ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_FTS
+ eng "Fulltext index creation requires a lock"
+
+ER_SQL_SLAVE_SKIP_COUNTER_NOT_SETTABLE_IN_GTID_MODE
+ eng "sql_slave_skip_counter can not be set when the server is running with GTID_MODE = ON. Instead, for each transaction that you want to skip, generate an empty transaction with the same GTID as the transaction"
+
+ER_DUP_UNKNOWN_IN_INDEX 23000
+ cze "Zdvojený klÃ­Ä (Äíslo klíÄe '%-.192s')"
+ dan "Flere ens nøgler for indeks '%-.192s'"
+ nla "Dubbele ingang voor zoeksleutel '%-.192s'"
+ eng "Duplicate entry for key '%-.192s'"
+ est "Kattuv väärtus võtmele '%-.192s'"
+ fre "Duplicata du champ pour la clef '%-.192s'"
+ ger "Doppelter Eintrag für Schlüssel '%-.192s'"
+ greek "Διπλή εγγÏαφή για το κλειδί '%-.192s'"
+ hun "Duplikalt bejegyzes a '%-.192s' kulcs szerint."
+ ita "Valore duplicato per la chiave '%-.192s'"
+ jpn "ã¯ç´¢å¼• '%-.192s' ã§é‡è¤‡ã—ã¦ã„ã¾ã™ã€‚"
+ kor "ì¤‘ë³µëœ ìž…ë ¥ ê°’: key '%-.192s'"
+ nor "Like verdier for nøkkel '%-.192s'"
+ norwegian-ny "Like verdiar for nykkel '%-.192s'"
+ pol "Powtórzone wystąpienie dla klucza '%-.192s'"
+ por "Entrada duplicada para a chave '%-.192s'"
+ rum "Cimpul e duplicat pentru cheia '%-.192s'"
+ rus "ДублирующаÑÑÑ Ð·Ð°Ð¿Ð¸ÑÑŒ по ключу '%-.192s'"
+ serbian "Dupliran unos za kljuÄ '%-.192s'"
+ slo "Opakovaný kÄ¾ÃºÄ (Äíslo kľúÄa '%-.192s')"
+ spa "Entrada duplicada para la clave '%-.192s'"
+ swe "Dublett för nyckel '%-.192s'"
+ ukr "Дублюючий Ð·Ð°Ð¿Ð¸Ñ Ð´Ð»Ñ ÐºÐ»ÑŽÑ‡Ð° '%-.192s'"
+
+ER_IDENT_CAUSES_TOO_LONG_PATH
+ eng "Long database name and identifier for object resulted in path length exceeding %d characters. Path: '%s'."
+
+ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_NOT_NULL
+ eng "cannot silently convert NULL values, as required in this SQL_MODE"
+
+ER_MUST_CHANGE_PASSWORD_LOGIN
+ eng "Your password has expired. To log in you must change it using a client that supports expired passwords."
+ bgn "Паролата ви е изтекла. За да влезете Ñ‚Ñ€Ñбва да Ñ Ñмените използвайки клиент който поддрържа такива пароли."
+
+ER_ROW_IN_WRONG_PARTITION
+ eng "Found a row in wrong partition %s"
+ swe "Hittade en rad i fel partition %s"
+
+ER_MTS_EVENT_BIGGER_PENDING_JOBS_SIZE_MAX
+ eng "Cannot schedule event %s, relay-log name %s, position %s to Worker thread because its size %lu exceeds %lu of slave_pending_jobs_size_max."
+
+ER_INNODB_NO_FT_USES_PARSER
+ eng "Cannot CREATE FULLTEXT INDEX WITH PARSER on InnoDB table"
+ER_BINLOG_LOGICAL_CORRUPTION
+ eng "The binary log file '%s' is logically corrupted: %s"
+
+ER_WARN_PURGE_LOG_IN_USE
+ eng "file %s was not purged because it was being read by %d thread(s), purged only %d out of %d files."
+
+ER_WARN_PURGE_LOG_IS_ACTIVE
+ eng "file %s was not purged because it is the active log file."
+
+ER_AUTO_INCREMENT_CONFLICT
+ eng "Auto-increment value in UPDATE conflicts with internally generated values"
+
+WARN_ON_BLOCKHOLE_IN_RBR
+ eng "Row events are not logged for %s statements that modify BLACKHOLE tables in row format. Table(s): '%-.192s'"
+
+ER_SLAVE_MI_INIT_REPOSITORY
+ eng "Slave failed to initialize master info structure from the repository"
+
+ER_SLAVE_RLI_INIT_REPOSITORY
+ eng "Slave failed to initialize relay log info structure from the repository"
+
+ER_ACCESS_DENIED_CHANGE_USER_ERROR 28000
+ eng "Access denied trying to change to user '%-.48s'@'%-.64s' (using password: %s). Disconnecting."
+ bgn "Отказан доÑтъп при опит за ÑмÑна към потребител %-.48s'@'%-.64s' (използвана парола: %s). ЗатварÑне на връзката."
+
+ER_INNODB_READ_ONLY
+ eng "InnoDB is in read only mode."
+
+ER_STOP_SLAVE_SQL_THREAD_TIMEOUT
+ eng "STOP SLAVE command execution is incomplete: Slave SQL thread got the stop signal, thread is busy, SQL thread will stop once the current task is complete."
+
+ER_STOP_SLAVE_IO_THREAD_TIMEOUT
+ eng "STOP SLAVE command execution is incomplete: Slave IO thread got the stop signal, thread is busy, IO thread will stop once the current task is complete."
+
+ER_TABLE_CORRUPT
+ eng "Operation cannot be performed. The table '%-.64s.%-.64s' is missing, corrupt or contains bad data."
+
+ER_TEMP_FILE_WRITE_FAILURE
+ eng "Temporary file write failure."
+
+ER_INNODB_FT_AUX_NOT_HEX_ID
+ eng "Upgrade index name failed, please use create index(alter table) algorithm copy to rebuild index."
+
+
#
# MariaDB error messages section starts here
#
@@ -6524,12 +6993,12 @@ ER_UNKNOWN_OPTION
eng "Unknown option '%-.64s'"
ER_BAD_OPTION_VALUE
eng "Incorrect value '%-.64s' for option '%-.64s'"
-ER_NETWORK_READ_EVENT_CHECKSUM_FAILURE
- eng "Replication event checksum verification failed while reading from network."
-ER_BINLOG_READ_EVENT_CHECKSUM_FAILURE
- eng "Replication event checksum verification failed while reading from a log file."
-ER_CANT_DO_ONLINE
- eng "Can't execute the given '%s' command as online"
+ER_UNUSED_6
+ eng "You should never see it"
+ER_UNUSED_7
+ eng "You should never see it"
+ER_UNUSED_8
+ eng "You should never see it"
ER_DATA_OVERFLOW 22003
eng "Got overflow when converting '%-.128s' to %-.32s. Value truncated."
ER_DATA_TRUNCATED 22003
@@ -6554,8 +7023,8 @@ ER_VIEW_ORDERBY_IGNORED
eng "View '%-.192s'.'%-.192s' ORDER BY clause ignored because there is other ORDER BY clause already."
ER_CONNECTION_KILLED 70100
eng "Connection was killed"
-ER_INTERNAL_ERROR
- eng "Internal error: '%-.192s'"
+ER_UNUSED_12
+ eng "You should never see it"
ER_INSIDE_TRANSACTION_PREVENTS_SWITCH_SKIP_REPLICATION
eng "Cannot modify @@session.skip_replication inside a transaction"
ER_STORED_FUNCTION_PREVENTS_SWITCH_SKIP_REPLICATION
@@ -6565,3 +7034,83 @@ ER_QUERY_EXCEEDED_ROWS_EXAMINED_LIMIT
ER_NO_SUCH_TABLE_IN_ENGINE 42S02
eng "Table '%-.192s.%-.192s' doesn't exist in engine"
swe "Det finns ingen tabell som heter '%-.192s.%-.192s' i handlern"
+ER_TARGET_NOT_EXPLAINABLE
+ eng "Target is not running an EXPLAINable command"
+ER_CONNECTION_ALREADY_EXISTS
+ eng "Connection '%.*s' conflicts with existing connection '%.*s'"
+ER_MASTER_LOG_PREFIX
+ eng "Master '%.*s': "
+ER_CANT_START_STOP_SLAVE
+ eng "Can't %s SLAVE '%.*s'"
+ER_SLAVE_STARTED
+ eng "SLAVE '%.*s' started"
+ER_SLAVE_STOPPED
+ eng "SLAVE '%.*s' stopped"
+ER_SQL_DISCOVER_ERROR
+ eng "Engine %s failed to discover table %`-.192s.%`-.192s with '%s'"
+ER_FAILED_GTID_STATE_INIT
+ eng "Failed initializing replication GTID state"
+ER_INCORRECT_GTID_STATE
+ eng "Could not parse GTID list"
+ER_CANNOT_UPDATE_GTID_STATE
+ eng "Could not update replication slave gtid state"
+ER_DUPLICATE_GTID_DOMAIN
+ eng "GTID %u-%u-%llu and %u-%u-%llu conflict (duplicate domain id %u)"
+ER_GTID_OPEN_TABLE_FAILED
+ eng "Failed to open %s.%s"
+ ger "Öffnen von %s.%s fehlgeschlagen"
+ER_GTID_POSITION_NOT_FOUND_IN_BINLOG
+ eng "Connecting slave requested to start from GTID %u-%u-%llu, which is not in the master's binlog"
+ER_CANNOT_LOAD_SLAVE_GTID_STATE
+ eng "Failed to load replication slave GTID position from table %s.%s"
+ER_MASTER_GTID_POS_CONFLICTS_WITH_BINLOG
+ eng "Specified GTID %u-%u-%llu conflicts with the binary log which contains a more recent GTID %u-%u-%llu. If MASTER_GTID_POS=CURRENT_POS is used, the binlog position will override the new value of @@gtid_slave_pos."
+ER_MASTER_GTID_POS_MISSING_DOMAIN
+ eng "Specified value for @@gtid_slave_pos contains no value for replication domain %u. This conflicts with the binary log which contains GTID %u-%u-%llu. If MASTER_GTID_POS=CURRENT_POS is used, the binlog position will override the new value of @@gtid_slave_pos."
+ER_UNTIL_REQUIRES_USING_GTID
+ eng "START SLAVE UNTIL master_gtid_pos requires that slave is using GTID"
+ER_GTID_STRICT_OUT_OF_ORDER
+ eng "An attempt was made to binlog GTID %u-%u-%llu which would create an out-of-order sequence number with existing GTID %u-%u-%llu, and gtid strict mode is enabled."
+ER_GTID_START_FROM_BINLOG_HOLE
+ eng "The binlog on the master is missing the GTID %u-%u-%llu requested by the slave (even though a subsequent sequence number does exist), and GTID strict mode is enabled"
+ER_SLAVE_UNEXPECTED_MASTER_SWITCH
+ eng "Unexpected GTID received from master after reconnect. This normally indicates that the master server was replaced without restarting the slave threads. %s"
+ER_INSIDE_TRANSACTION_PREVENTS_SWITCH_GTID_DOMAIN_ID_SEQ_NO
+ eng "Cannot modify @@session.gtid_domain_id or @@session.gtid_seq_no inside a transaction"
+ER_STORED_FUNCTION_PREVENTS_SWITCH_GTID_DOMAIN_ID_SEQ_NO
+ eng "Cannot modify @@session.gtid_domain_id or @@session.gtid_seq_no inside a stored function or trigger"
+ER_GTID_POSITION_NOT_FOUND_IN_BINLOG2
+ eng "Connecting slave requested to start from GTID %u-%u-%llu, which is not in the master's binlog. Since the master's binlog contains GTIDs with higher sequence numbers, it probably means that the slave has diverged due to executing extra erroneous transactions"
+ER_BINLOG_MUST_BE_EMPTY
+ eng "This operation is not allowed if any GTID has been logged to the binary log. Run RESET MASTER first to erase the log"
+ER_NO_SUCH_QUERY
+ eng "Unknown query id: %lld"
+ ger "Unbekannte Abfrage-ID: %lld"
+ rus "ÐеизвеÑтный номер запроÑа: %lld"
+ER_BAD_BASE64_DATA
+ eng "Bad base64 data as position %u"
+ER_INVALID_ROLE OP000
+ eng "Invalid role specification %`s."
+ rum "Rolul %`s este invalid."
+ER_INVALID_CURRENT_USER 0L000
+ eng "The current user is invalid."
+ rum "Utilizatorul curent este invalid."
+ER_CANNOT_GRANT_ROLE
+ eng "Cannot grant role '%s' to: %s."
+ rum "Rolul '%s' nu poate fi acordat catre: %s."
+ER_CANNOT_REVOKE_ROLE
+ eng "Cannot revoke role '%s' from: %s."
+ rum "Rolul '%s' nu poate fi revocat de la: %s."
+ER_CHANGE_SLAVE_PARALLEL_THREADS_ACTIVE
+ eng "Cannot change @@slave_parallel_threads while another change is in progress"
+ER_PRIOR_COMMIT_FAILED
+ eng "Commit failed due to failure of an earlier commit on which this one depends"
+ER_IT_IS_A_VIEW 42S02
+ eng "'%-.192s' is a view"
+ER_SLAVE_SKIP_NOT_IN_GTID
+ eng "When using parallel replication and GTID with multiple replication domains, @@sql_slave_skip_counter can not be used. Instead, setting @@gtid_slave_pos explicitly can be used to skip to after a given GTID position."
+ER_TABLE_DEFINITION_TOO_BIG
+ eng "The definition for table %`s is too big"
+ER_PLUGIN_INSTALLED
+ eng "Plugin '%-.192s' already installed"
+ rus "Плагин '%-.192s' уже уÑтановлен"
diff --git a/sql/signal_handler.cc b/sql/signal_handler.cc
index b0c67fbfe32..3fadbcd088f 100644
--- a/sql/signal_handler.cc
+++ b/sql/signal_handler.cc
@@ -190,7 +190,7 @@ extern "C" sig_handler handle_fatal_signal(int sig)
"Some pointers may be invalid and cause the dump to abort.\n");
my_safe_printf_stderr("Query (%p): ", thd->query());
- my_safe_print_str(thd->query(), min(65536U, thd->query_length()));
+ my_safe_print_str(thd->query(), MY_MIN(65536U, thd->query_length()));
my_safe_printf_stderr("\nConnection ID (thread ID): %lu\n",
(ulong) thd->thread_id);
my_safe_printf_stderr("Status: %s\n\n", kreason);
diff --git a/sql/slave.cc b/sql/slave.cc
index 4636cb330e5..625ae7b0b4b 100644
--- a/sql/slave.cc
+++ b/sql/slave.cc
@@ -1,5 +1,5 @@
/* Copyright (c) 2000, 2015, Oracle and/or its affiliates.
- Copyright (c) 2008, 2015, SkySQL Ab.
+ Copyright (c) 2008, 2015, MariaDB
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
@@ -25,8 +25,8 @@
replication slave.
*/
+#include <my_global.h>
#include "sql_priv.h"
-#include "my_global.h"
#include "slave.h"
#include "sql_parse.h" // execute_init_command
#include "sql_table.h" // mysql_rm_table
@@ -60,6 +60,8 @@
#include "rpl_tblmap.h"
#include "debug_sync.h"
+#include "rpl_parallel.h"
+
#define FLAGSTR(V,F) ((V)&(F)?#F" ":"")
@@ -74,8 +76,10 @@ char slave_skip_error_names[SHOW_VAR_FUNC_BUFF_SIZE];
char* slave_load_tmpdir = 0;
Master_info *active_mi= 0;
+Master_info_index *master_info_index;
my_bool replicate_same_server_id;
ulonglong relay_log_space_limit = 0;
+LEX_STRING default_master_connection_name= { (char*) "", 0 };
/*
When slave thread exits, we need to remember the temporary tables so we
@@ -111,11 +115,11 @@ static const char *reconnect_messages[SLAVE_RECON_ACT_MAX][SLAVE_RECON_MSG_MAX]=
{
{
"Waiting to reconnect after a failed registration on master",
- "Slave I/O thread killed while waitnig to reconnect after a failed \
+ "Slave I/O thread killed while waiting to reconnect after a failed \
registration on master",
"Reconnecting after a failed registration on master",
"failed registering on master, reconnecting to try again, \
-log '%s' at position %s",
+log '%s' at position %s%s",
"COM_REGISTER_SLAVE",
"Slave I/O thread killed during or after reconnect"
},
@@ -123,7 +127,7 @@ log '%s' at position %s",
"Waiting to reconnect after a failed binlog dump request",
"Slave I/O thread killed while retrying master dump",
"Reconnecting after a failed binlog dump request",
- "failed dump request, reconnecting to try again, log '%s' at position %s",
+ "failed dump request, reconnecting to try again, log '%s' at position %s%s",
"COM_BINLOG_DUMP",
"Slave I/O thread killed during or after reconnect"
},
@@ -132,7 +136,7 @@ log '%s' at position %s",
"Slave I/O thread killed while waiting to reconnect after a failed read",
"Reconnecting after a failed master event read",
"Slave I/O thread: Failed reading log event, reconnecting to retry, \
-log '%s' at position %s",
+log '%s' at position %s%s",
"",
"Slave I/O thread killed during or after a reconnect done to recover from \
failed read"
@@ -145,23 +149,20 @@ typedef enum { SLAVE_THD_IO, SLAVE_THD_SQL} SLAVE_THD_TYPE;
static int process_io_rotate(Master_info* mi, Rotate_log_event* rev);
static int process_io_create_file(Master_info* mi, Create_file_log_event* cev);
static bool wait_for_relay_log_space(Relay_log_info* rli);
-static inline bool io_slave_killed(THD* thd,Master_info* mi);
-static inline bool sql_slave_killed(THD* thd,Relay_log_info* rli);
-static int init_slave_thread(THD* thd, SLAVE_THD_TYPE thd_type);
+static bool io_slave_killed(Master_info* mi);
+static bool sql_slave_killed(rpl_group_info *rgi);
+static int init_slave_thread(THD*, Master_info *, SLAVE_THD_TYPE);
static void print_slave_skip_errors(void);
static int safe_connect(THD* thd, MYSQL* mysql, Master_info* mi);
-static int safe_reconnect(THD* thd, MYSQL* mysql, Master_info* mi,
- bool suppress_warnings);
-static int connect_to_master(THD* thd, MYSQL* mysql, Master_info* mi,
- bool reconnect, bool suppress_warnings);
-static Log_event* next_event(Relay_log_info* rli);
+static int safe_reconnect(THD*, MYSQL*, Master_info*, bool);
+static int connect_to_master(THD*, MYSQL*, Master_info*, bool, bool);
+static Log_event* next_event(rpl_group_info* rgi, ulonglong *event_size);
static int queue_event(Master_info* mi,const char* buf,ulong event_len);
-static int terminate_slave_thread(THD *thd,
- mysql_mutex_t *term_lock,
- mysql_cond_t *term_cond,
- volatile uint *slave_running,
- bool skip_lock);
-static bool check_io_slave_killed(THD *thd, Master_info *mi, const char *info);
+static int terminate_slave_thread(THD *, mysql_mutex_t *, mysql_cond_t *,
+ volatile uint *, bool);
+static bool check_io_slave_killed(Master_info *mi, const char *info);
+static bool send_show_master_info_header(THD *, bool, size_t);
+static bool send_show_master_info_data(THD *, Master_info *, bool, String *);
/*
Function to set the slave's max_allowed_packet based on the value
of slave_max_allowed_packet.
@@ -280,6 +281,80 @@ static void init_slave_psi_keys(void)
}
#endif /* HAVE_PSI_INTERFACE */
+
+static bool slave_init_thread_running;
+
+
+pthread_handler_t
+handle_slave_init(void *arg __attribute__((unused)))
+{
+ THD *thd;
+
+ my_thread_init();
+ thd= new THD;
+ thd->thread_stack= (char*) &thd; /* Set approximate stack start */
+ mysql_mutex_lock(&LOCK_thread_count);
+ thd->thread_id= thread_id++;
+ mysql_mutex_unlock(&LOCK_thread_count);
+ thd->system_thread = SYSTEM_THREAD_SLAVE_INIT;
+ thread_safe_increment32(&service_thread_count, &thread_count_lock);
+ thd->store_globals();
+ thd->security_ctx->skip_grants();
+ thd->set_command(COM_DAEMON);
+
+ thd_proc_info(thd, "Loading slave GTID position from table");
+ if (rpl_load_gtid_slave_state(thd))
+ sql_print_warning("Failed to load slave replication state from table "
+ "%s.%s: %u: %s", "mysql",
+ rpl_gtid_slave_state_table_name.str,
+ thd->get_stmt_da()->sql_errno(),
+ thd->get_stmt_da()->message());
+
+ mysql_mutex_lock(&LOCK_thread_count);
+ delete thd;
+ mysql_mutex_unlock(&LOCK_thread_count);
+ thread_safe_decrement32(&service_thread_count, &thread_count_lock);
+ signal_thd_deleted();
+ my_thread_end();
+
+ mysql_mutex_lock(&LOCK_slave_init);
+ slave_init_thread_running= false;
+ mysql_cond_broadcast(&COND_slave_init);
+ mysql_mutex_unlock(&LOCK_slave_init);
+
+ return 0;
+}
+
+
+/*
+ Start the slave init thread.
+
+ This thread is used to load the GTID state from mysql.gtid_slave_pos at
+ server start; reading from table requires valid THD, which is otherwise not
+ available during server init.
+*/
+static int
+run_slave_init_thread()
+{
+ pthread_t th;
+
+ slave_init_thread_running= true;
+ if (mysql_thread_create(key_thread_slave_init, &th, &connection_attrib,
+ handle_slave_init, NULL))
+ {
+ sql_print_error("Failed to create thread while initialising slave");
+ return 1;
+ }
+
+ mysql_mutex_lock(&LOCK_slave_init);
+ while (slave_init_thread_running)
+ mysql_cond_wait(&COND_slave_init, &LOCK_slave_init);
+ mysql_mutex_unlock(&LOCK_slave_init);
+
+ return 0;
+}
+
+
/* Initialize slave structures */
int init_slave()
@@ -291,21 +366,45 @@ int init_slave()
init_slave_psi_keys();
#endif
+ if (run_slave_init_thread())
+ return 1;
+
+ if (global_rpl_thread_pool.init(opt_slave_parallel_threads))
+ return 1;
+
/*
This is called when mysqld starts. Before client connections are
accepted. However bootstrap may conflict with us if it does START SLAVE.
So it's safer to take the lock.
*/
mysql_mutex_lock(&LOCK_active_mi);
- /*
- TODO: re-write this to interate through the list of files
- for multi-master
- */
- active_mi= new Master_info(relay_log_recovery);
if (pthread_key_create(&RPL_MASTER_INFO, NULL))
goto err;
+ master_info_index= new Master_info_index;
+ if (!master_info_index || master_info_index->init_all_master_info())
+ {
+ sql_print_error("Failed to initialize multi master structures");
+ mysql_mutex_unlock(&LOCK_active_mi);
+ DBUG_RETURN(1);
+ }
+ if (!(active_mi= new Master_info(&default_master_connection_name,
+ relay_log_recovery)) ||
+ active_mi->error())
+ {
+ delete active_mi;
+ active_mi= 0;
+ goto err;
+ }
+
+ if (master_info_index->add_master_info(active_mi, FALSE))
+ {
+ delete active_mi;
+ active_mi= 0;
+ goto err;
+ }
+
/*
If --slave-skip-errors=... was not used, the string value for the
system variable has not been set up yet. Do it now.
@@ -320,18 +419,11 @@ int init_slave()
If master_host is specified, create the master_info file if it doesn't
exists.
*/
- if (!active_mi)
- {
- sql_print_error("Failed to allocate memory for the master info structure");
- error= 1;
- goto err;
- }
if (init_master_info(active_mi,master_info_file,relay_log_info_file,
1, (SLAVE_IO | SLAVE_SQL)))
{
sql_print_error("Failed to initialize the master info structure");
- error= 1;
goto err;
}
@@ -347,14 +439,18 @@ int init_slave()
SLAVE_IO | SLAVE_SQL))
{
sql_print_error("Failed to create slave threads");
- error= 1;
goto err;
}
}
-err:
+end:
mysql_mutex_unlock(&LOCK_active_mi);
DBUG_RETURN(error);
+
+err:
+ sql_print_error("Failed to allocate memory for the Master Info structure");
+ error= 1;
+ goto end;
}
/*
@@ -391,7 +487,7 @@ int init_recovery(Master_info* mi, const char** errmsg)
Relay_log_info *rli= &mi->rli;
if (rli->group_master_log_name[0])
{
- mi->master_log_pos= max(BIN_LOG_HEADER_SIZE,
+ mi->master_log_pos= MY_MAX(BIN_LOG_HEADER_SIZE,
rli->group_master_log_pos);
strmake_buf(mi->master_log_name, rli->group_master_log_name);
@@ -406,6 +502,7 @@ int init_recovery(Master_info* mi, const char** errmsg)
DBUG_RETURN(0);
}
+
/**
Convert slave skip errors bitmap into a printable string.
@@ -484,7 +581,7 @@ void init_slave_skip_errors(const char* arg)
const char *p;
DBUG_ENTER("init_slave_skip_errors");
- if (bitmap_init(&slave_error_mask,0,MAX_SLAVE_ERROR,0))
+ if (my_bitmap_init(&slave_error_mask,0,MAX_SLAVE_ERROR,0))
{
fprintf(stderr, "Badly out of memory, please check your system status\n");
exit(1);
@@ -513,14 +610,6 @@ void init_slave_skip_errors(const char* arg)
DBUG_VOID_RETURN;
}
-static void set_thd_in_use_temporary_tables(Relay_log_info *rli)
-{
- TABLE *table;
-
- for (table= rli->save_temporary_tables ; table ; table= table->next)
- table->in_use= rli->sql_thd;
-}
-
int terminate_slave_threads(Master_info* mi,int thread_mask,bool skip_lock)
{
DBUG_ENTER("terminate_slave_threads");
@@ -534,8 +623,15 @@ int terminate_slave_threads(Master_info* mi,int thread_mask,bool skip_lock)
if (thread_mask & (SLAVE_SQL|SLAVE_FORCE_ALL))
{
DBUG_PRINT("info",("Terminating SQL thread"));
- mi->rli.abort_slave=1;
- if ((error=terminate_slave_thread(mi->rli.sql_thd, sql_lock,
+ if (opt_slave_parallel_threads > 0 &&
+ mi->rli.abort_slave && mi->rli.stop_for_until)
+ {
+ mi->rli.stop_for_until= false;
+ mi->rli.parallel.stop_during_until();
+ }
+ else
+ mi->rli.abort_slave=1;
+ if ((error=terminate_slave_thread(mi->rli.sql_driver_thd, sql_lock,
&mi->rli.stop_cond,
&mi->rli.slave_running,
skip_lock)) &&
@@ -546,7 +642,7 @@ int terminate_slave_threads(Master_info* mi,int thread_mask,bool skip_lock)
DBUG_PRINT("info",("Flushing relay-log info file."));
if (current_thd)
- thd_proc_info(current_thd, "Flushing relay-log info file.");
+ THD_STAGE_INFO(current_thd, stage_flushing_relay_log_info_file);
if (flush_relay_log_info(&mi->rli))
DBUG_RETURN(ER_ERROR_DURING_FLUSH_LOGS);
@@ -555,6 +651,9 @@ int terminate_slave_threads(Master_info* mi,int thread_mask,bool skip_lock)
mysql_mutex_unlock(log_lock);
}
+ if (opt_slave_parallel_threads > 0 &&
+ !master_info_index->any_slave_sql_running())
+ rpl_parallel_inactivate_pool(&global_rpl_thread_pool);
if (thread_mask & (SLAVE_IO|SLAVE_FORCE_ALL))
{
DBUG_PRINT("info",("Terminating IO thread"));
@@ -570,7 +669,7 @@ int terminate_slave_threads(Master_info* mi,int thread_mask,bool skip_lock)
DBUG_PRINT("info",("Flushing relay log and master info file."));
if (current_thd)
- thd_proc_info(current_thd, "Flushing relay log and master info files.");
+ THD_STAGE_INFO(current_thd, stage_flushing_relay_log_and_master_info_repository);
if (flush_master_info(mi, TRUE, FALSE))
DBUG_RETURN(ER_ERROR_DURING_FLUSH_LOGS);
@@ -718,7 +817,7 @@ int start_slave_thread(
if (start_lock)
mysql_mutex_lock(start_lock);
- if (!server_id)
+ if (!global_system_variables.server_id)
{
if (start_cond)
mysql_cond_broadcast(start_cond);
@@ -752,8 +851,10 @@ int start_slave_thread(
while (start_id == *slave_run_id)
{
DBUG_PRINT("sleep",("Waiting for slave thread to start"));
- const char *old_msg= thd->enter_cond(start_cond, cond_lock,
- "Waiting for slave thread to start");
+ PSI_stage_info saved_stage= {0, "", 0};
+ thd->ENTER_COND(start_cond, cond_lock,
+ & stage_waiting_for_slave_thread_to_start,
+ & saved_stage);
/*
It is not sufficient to test this at loop bottom. We must test
it after registering the mutex in enter_cond(). If the kill
@@ -763,7 +864,7 @@ int start_slave_thread(
*/
if (!thd->killed)
mysql_cond_wait(start_cond, cond_lock);
- thd->exit_cond(old_msg);
+ thd->EXIT_COND(& saved_stage);
mysql_mutex_lock(cond_lock); // re-acquire it as exit_cond() released
if (thd->killed)
{
@@ -795,6 +896,7 @@ int start_slave_threads(bool need_slave_mutex, bool wait_for_start,
mysql_mutex_t *lock_io=0, *lock_sql=0, *lock_cond_io=0, *lock_cond_sql=0;
mysql_cond_t* cond_io=0, *cond_sql=0;
int error=0;
+ const char *errmsg;
DBUG_ENTER("start_slave_threads");
if (need_slave_mutex)
@@ -810,7 +912,43 @@ int start_slave_threads(bool need_slave_mutex, bool wait_for_start,
lock_cond_sql = &mi->rli.run_lock;
}
- if (thread_mask & SLAVE_IO)
+ /*
+ If we are using GTID and both SQL and IO threads are stopped, then get
+ rid of all relay logs.
+
+ Relay logs are not very useful when using GTID, except as a buffer
+ between the fetch in the IO thread and the apply in SQL thread. However
+ while one of the threads is running, they are in use and cannot be
+ removed.
+ */
+ if (mi->using_gtid != Master_info::USE_GTID_NO &&
+ !mi->slave_running && !mi->rli.slave_running)
+ {
+ /*
+ purge_relay_logs() clears the mi->rli.group_master_log_pos.
+ So save and restore them, like we do in CHANGE MASTER.
+ (We are not going to use them for GTID, but it might be worth to
+ keep them in case connection with GTID fails and user wants to go
+ back and continue with previous old-style replication coordinates).
+ */
+ mi->master_log_pos = MY_MAX(BIN_LOG_HEADER_SIZE,
+ mi->rli.group_master_log_pos);
+ strmake(mi->master_log_name, mi->rli.group_master_log_name,
+ sizeof(mi->master_log_name)-1);
+ purge_relay_logs(&mi->rli, NULL, 0, &errmsg);
+ mi->rli.group_master_log_pos= mi->master_log_pos;
+ strmake(mi->rli.group_master_log_name, mi->master_log_name,
+ sizeof(mi->rli.group_master_log_name)-1);
+
+ error= rpl_load_gtid_state(&mi->gtid_current_pos, mi->using_gtid ==
+ Master_info::USE_GTID_CURRENT_POS);
+ mi->events_queued_since_last_gtid= 0;
+ mi->gtid_reconnect_event_skip_count= 0;
+
+ mi->rli.restart_gtid_pos.reset();
+ }
+
+ if (!error && (thread_mask & SLAVE_IO))
error= start_slave_thread(
#ifdef HAVE_PSI_INTERFACE
key_thread_slave_io,
@@ -821,7 +959,10 @@ int start_slave_threads(bool need_slave_mutex, bool wait_for_start,
mi);
if (!error && (thread_mask & SLAVE_SQL))
{
- error= start_slave_thread(
+ if (opt_slave_parallel_threads > 0)
+ error= rpl_parallel_activate_pool(&global_rpl_thread_pool);
+ if (!error)
+ error= start_slave_thread(
#ifdef HAVE_PSI_INTERFACE
key_thread_slave_sql,
#endif
@@ -853,49 +994,27 @@ void end_slave()
running presently. If a START SLAVE was in progress, the mutex lock below
will make us wait until slave threads have started, and START SLAVE
returns, then we terminate them here.
+
+ We can also be called by cleanup(), which only happens if some
+ startup parameter to the server was wrong.
*/
mysql_mutex_lock(&LOCK_active_mi);
- if (active_mi)
- {
- /*
- TODO: replace the line below with
- list_walk(&master_list, (list_walk_action)end_slave_on_walk,0);
- once multi-master code is ready.
- */
- terminate_slave_threads(active_mi,SLAVE_FORCE_ALL);
- }
+ /* This will call terminate_slave_threads() on all connections */
+ delete master_info_index;
+ master_info_index= 0;
+ active_mi= 0;
mysql_mutex_unlock(&LOCK_active_mi);
+ global_rpl_thread_pool.destroy();
+ free_all_rpl_filters();
DBUG_VOID_RETURN;
}
-/**
- Free all resources used by slave threads at time of executing shutdown.
- The routine must be called after all possible users of @c active_mi
- have left.
-
- SYNOPSIS
- close_active_mi()
-
-*/
-void close_active_mi()
-{
- mysql_mutex_lock(&LOCK_active_mi);
- if (active_mi)
- {
- end_master_info(active_mi);
- delete active_mi;
- active_mi= 0;
- }
- mysql_mutex_unlock(&LOCK_active_mi);
-}
-
-static bool io_slave_killed(THD* thd, Master_info* mi)
+static bool io_slave_killed(Master_info* mi)
{
DBUG_ENTER("io_slave_killed");
- DBUG_ASSERT(mi->io_thd == thd);
DBUG_ASSERT(mi->slave_running); // tracking buffer overrun
- DBUG_RETURN(mi->abort_slave || abort_loop || thd->killed);
+ DBUG_RETURN(mi->abort_slave || abort_loop || mi->io_thd->killed);
}
/**
@@ -905,32 +1024,41 @@ static bool io_slave_killed(THD* thd, Master_info* mi)
In the event of deffering decision @rli->last_event_start_time waiting
timer is set to force the killed status be accepted upon its expiration.
- @param thd pointer to a THD instance
- @param rli pointer to Relay_log_info instance
+ @param rgi pointer to relay_group_info instance
@return TRUE the killed status is recognized, FALSE a possible killed
status is deferred.
*/
-static bool sql_slave_killed(THD* thd, Relay_log_info* rli)
+static bool sql_slave_killed(rpl_group_info *rgi)
{
bool ret= FALSE;
+ Relay_log_info *rli= rgi->rli;
+ THD *thd= rgi->thd;
DBUG_ENTER("sql_slave_killed");
- DBUG_ASSERT(rli->sql_thd == thd);
+ DBUG_ASSERT(rli->sql_driver_thd == thd);
DBUG_ASSERT(rli->slave_running == 1);// tracking buffer overrun
- if (abort_loop || thd->killed || rli->abort_slave)
+ if (abort_loop || rli->sql_driver_thd->killed || rli->abort_slave)
{
/*
- The transaction should always be binlogged if OPTION_KEEP_LOG is set
- (it implies that something can not be rolled back). And such case
- should be regarded similarly as modifing a non-transactional table
- because retrying of the transaction will lead to an error or inconsistency
- as well.
- Example: OPTION_KEEP_LOG is set if a temporary table is created or dropped.
+ The transaction should always be binlogged if OPTION_KEEP_LOG is
+ set (it implies that something can not be rolled back). And such
+ case should be regarded similarly as modifing a
+ non-transactional table because retrying of the transaction will
+ lead to an error or inconsistency as well.
+
+ Example: OPTION_KEEP_LOG is set if a temporary table is created
+ or dropped.
+
+ Note that transaction.all.modified_non_trans_table may be 1
+ if last statement was a single row transaction without begin/end.
+ Testing this flag must always be done in connection with
+ rli->is_in_group().
*/
+
if ((thd->transaction.all.modified_non_trans_table ||
- (thd->variables.option_bits & OPTION_KEEP_LOG))
- && rli->is_in_group())
+ (thd->variables.option_bits & OPTION_KEEP_LOG)) &&
+ rli->is_in_group())
{
char msg_stopped[]=
"... Slave SQL Thread stopped with incomplete event group "
@@ -940,25 +1068,34 @@ static bool sql_slave_killed(THD* thd, Relay_log_info* rli)
"ignores duplicate key, key not found, and similar errors (see "
"documentation for details).";
+ DBUG_PRINT("info", ("modified_non_trans_table: %d OPTION_BEGIN: %d "
+ "OPTION_KEEP_LOG: %d is_in_group: %d",
+ thd->transaction.all.modified_non_trans_table,
+ MY_TEST(thd->variables.option_bits & OPTION_BEGIN),
+ MY_TEST(thd->variables.option_bits & OPTION_KEEP_LOG),
+ rli->is_in_group()));
+
if (rli->abort_slave)
{
- DBUG_PRINT("info", ("Request to stop slave SQL Thread received while "
- "applying a group that has non-transactional "
- "changes; waiting for completion of the group ... "));
+ DBUG_PRINT("info",
+ ("Request to stop slave SQL Thread received while "
+ "applying a group that has non-transactional "
+ "changes; waiting for completion of the group ... "));
/*
- Slave sql thread shutdown in face of unfinished group modified
- Non-trans table is handled via a timer. The slave may eventually
- give out to complete the current group and in that case there
- might be issues at consequent slave restart, see the error message.
- WL#2975 offers a robust solution requiring to store the last exectuted
- event's coordinates along with the group's coordianates
- instead of waiting with @c last_event_start_time the timer.
+ Slave sql thread shutdown in face of unfinished group
+ modified Non-trans table is handled via a timer. The slave
+ may eventually give out to complete the current group and in
+ that case there might be issues at consequent slave restart,
+ see the error message. WL#2975 offers a robust solution
+ requiring to store the last exectuted event's coordinates
+ along with the group's coordianates instead of waiting with
+ @c last_event_start_time the timer.
*/
- if (rli->last_event_start_time == 0)
- rli->last_event_start_time= my_time(0);
- ret= difftime(my_time(0), rli->last_event_start_time) <=
+ if (rgi->last_event_start_time == 0)
+ rgi->last_event_start_time= my_time(0);
+ ret= difftime(my_time(0), rgi->last_event_start_time) <=
SLAVE_WAIT_GROUP_DONE ? FALSE : TRUE;
DBUG_EXECUTE_IF("stop_slave_middle_group",
@@ -967,21 +1104,22 @@ static bool sql_slave_killed(THD* thd, Relay_log_info* rli)
if (ret == 0)
{
- rli->report(WARNING_LEVEL, 0,
+ rli->report(WARNING_LEVEL, 0, rgi->gtid_info(),
"Request to stop slave SQL Thread received while "
"applying a group that has non-transactional "
"changes; waiting for completion of the group ... ");
}
else
{
- rli->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR,
+ rli->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR, rgi->gtid_info(),
ER(ER_SLAVE_FATAL_ERROR), msg_stopped);
}
}
else
{
ret= TRUE;
- rli->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR, ER(ER_SLAVE_FATAL_ERROR),
+ rli->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR, rgi->gtid_info(),
+ ER(ER_SLAVE_FATAL_ERROR),
msg_stopped);
}
}
@@ -991,7 +1129,7 @@ static bool sql_slave_killed(THD* thd, Relay_log_info* rli)
}
}
if (ret)
- rli->last_event_start_time= 0;
+ rgi->last_event_start_time= 0;
DBUG_RETURN(ret);
}
@@ -1245,7 +1383,7 @@ bool is_network_error(uint errorno)
static int get_master_version_and_clock(MYSQL* mysql, Master_info* mi)
{
- char err_buff[MAX_SLAVE_ERRMSG];
+ char err_buff[MAX_SLAVE_ERRMSG], err_buff2[MAX_SLAVE_ERRMSG];
const char* errmsg= 0;
int err_code= 0;
MYSQL_RES *master_res= 0;
@@ -1262,23 +1400,28 @@ static int get_master_version_and_clock(MYSQL* mysql, Master_info* mi)
if (!my_isdigit(&my_charset_bin,*mysql->server_version))
{
- errmsg = "Master reported unrecognized MySQL version";
+ errmsg= err_buff2;
+ snprintf(err_buff2, sizeof(err_buff2),
+ "Master reported unrecognized MySQL version: %s",
+ mysql->server_version);
err_code= ER_SLAVE_FATAL_ERROR;
- sprintf(err_buff, ER(err_code), errmsg);
+ sprintf(err_buff, ER(err_code), err_buff2);
}
else
{
/*
Note the following switch will bug when we have MySQL branch 30 ;)
*/
- switch (version)
- {
+ switch (version) {
case 0:
case 1:
case 2:
- errmsg = "Master reported unrecognized MySQL version";
+ errmsg= err_buff2;
+ snprintf(err_buff2, sizeof(err_buff2),
+ "Master reported unrecognized MySQL version: %s",
+ mysql->server_version);
err_code= ER_SLAVE_FATAL_ERROR;
- sprintf(err_buff, ER(err_code), errmsg);
+ sprintf(err_buff, ER(err_code), err_buff2);
break;
case 3:
mi->rli.relay_log.description_event_for_queue= new
@@ -1389,11 +1532,11 @@ static int get_master_version_and_clock(MYSQL* mysql, Master_info* mi)
mi->clock_diff_with_master=
(long) (time((time_t*) 0) - strtoul(master_row[0], 0, 10));
}
- else if (check_io_slave_killed(mi->io_thd, mi, NULL))
+ else if (check_io_slave_killed(mi, NULL))
goto slave_killed_err;
else if (is_network_error(mysql_errno(mysql)))
{
- mi->report(WARNING_LEVEL, mysql_errno(mysql),
+ mi->report(WARNING_LEVEL, mysql_errno(mysql), NULL,
"Get master clock failed with error: %s", mysql_error(mysql));
goto network_err;
}
@@ -1439,7 +1582,8 @@ static int get_master_version_and_clock(MYSQL* mysql, Master_info* mi)
(master_res= mysql_store_result(mysql)) &&
(master_row= mysql_fetch_row(master_res)))
{
- if ((::server_id == (mi->master_id= strtoul(master_row[1], 0, 10))) &&
+ if ((global_system_variables.server_id ==
+ (mi->master_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 \
@@ -1453,11 +1597,11 @@ not always make sense; please check the manual before using it).";
}
else if (mysql_errno(mysql))
{
- if (check_io_slave_killed(mi->io_thd, mi, NULL))
+ if (check_io_slave_killed(mi, NULL))
goto slave_killed_err;
else if (is_network_error(mysql_errno(mysql)))
{
- mi->report(WARNING_LEVEL, mysql_errno(mysql),
+ mi->report(WARNING_LEVEL, mysql_errno(mysql), NULL,
"Get master SERVER_ID failed with error: %s", mysql_error(mysql));
goto network_err;
}
@@ -1470,7 +1614,7 @@ when it try to get the value of SERVER_ID variable from master.";
}
else if (!master_row && master_res)
{
- mi->report(WARNING_LEVEL, ER_UNKNOWN_SYSTEM_VARIABLE,
+ mi->report(WARNING_LEVEL, ER_UNKNOWN_SYSTEM_VARIABLE, NULL,
"Unknown system variable 'SERVER_ID' on master, \
maybe it is a *VERY OLD MASTER*.");
}
@@ -1526,11 +1670,11 @@ be equal for the Statement-format replication to work";
goto err;
}
}
- else if (check_io_slave_killed(mi->io_thd, mi, NULL))
+ else if (check_io_slave_killed(mi, NULL))
goto slave_killed_err;
else if (is_network_error(mysql_errno(mysql)))
{
- mi->report(WARNING_LEVEL, mysql_errno(mysql),
+ mi->report(WARNING_LEVEL, mysql_errno(mysql), NULL,
"Get master COLLATION_SERVER failed with error: %s", mysql_error(mysql));
goto network_err;
}
@@ -1544,7 +1688,7 @@ when it try to get the value of COLLATION_SERVER global variable from master.";
goto err;
}
else
- mi->report(WARNING_LEVEL, ER_UNKNOWN_SYSTEM_VARIABLE,
+ mi->report(WARNING_LEVEL, ER_UNKNOWN_SYSTEM_VARIABLE, NULL,
"Unknown system variable 'COLLATION_SERVER' on master, \
maybe it is a *VERY OLD MASTER*. *NOTE*: slave may experience \
inconsistency if replicated data deals with collation.");
@@ -1589,11 +1733,11 @@ be equal for the Statement-format replication to work";
goto err;
}
}
- else if (check_io_slave_killed(mi->io_thd, mi, NULL))
+ else if (check_io_slave_killed(mi, NULL))
goto slave_killed_err;
else if (is_network_error(err_code= mysql_errno(mysql)))
{
- mi->report(ERROR_LEVEL, err_code,
+ mi->report(ERROR_LEVEL, err_code, NULL,
"Get master TIME_ZONE failed with error: %s",
mysql_error(mysql));
goto network_err;
@@ -1601,7 +1745,7 @@ be equal for the Statement-format replication to work";
else if (err_code == ER_UNKNOWN_SYSTEM_VARIABLE)
{
/* We use ERROR_LEVEL to get the error logged to file */
- mi->report(ERROR_LEVEL, err_code,
+ mi->report(ERROR_LEVEL, err_code, NULL,
"MySQL master doesn't have a TIME_ZONE variable. Note that"
"if your timezone is not same between master and slave, your "
@@ -1640,13 +1784,13 @@ when it try to get the value of TIME_ZONE global variable from master.";
});
if (mysql_real_query(mysql, query, strlen(query)))
{
- if (check_io_slave_killed(mi->io_thd, mi, NULL))
+ if (check_io_slave_killed(mi, NULL))
goto slave_killed_err;
if (is_network_error(mysql_errno(mysql)))
{
IF_DBUG(heartbeat_network_error: , )
- mi->report(WARNING_LEVEL, mysql_errno(mysql),
+ mi->report(WARNING_LEVEL, mysql_errno(mysql), NULL,
"SET @master_heartbeat_period to master failed with error: %s",
mysql_error(mysql));
mysql_free_result(mysql_store_result(mysql));
@@ -1689,7 +1833,7 @@ when it try to get the value of TIME_ZONE global variable from master.";
rc= mysql_real_query(mysql, query, strlen(query));
if (rc != 0)
{
- if (check_io_slave_killed(mi->io_thd, mi, NULL))
+ if (check_io_slave_killed(mi, NULL))
goto slave_killed_err;
if (mysql_errno(mysql) == ER_UNKNOWN_SYSTEM_VARIABLE)
@@ -1698,7 +1842,7 @@ when it try to get the value of TIME_ZONE global variable from master.";
if (global_system_variables.log_warnings > 1)
{
// this is tolerable as OM -> NS is supported
- mi->report(WARNING_LEVEL, mysql_errno(mysql),
+ mi->report(WARNING_LEVEL, mysql_errno(mysql), NULL,
"Notifying master by %s failed with "
"error: %s", query, mysql_error(mysql));
}
@@ -1707,7 +1851,7 @@ when it try to get the value of TIME_ZONE global variable from master.";
{
if (is_network_error(mysql_errno(mysql)))
{
- mi->report(WARNING_LEVEL, mysql_errno(mysql),
+ mi->report(WARNING_LEVEL, mysql_errno(mysql), NULL,
"Notifying master by %s failed with "
"error: %s", query, mysql_error(mysql));
mysql_free_result(mysql_store_result(mysql));
@@ -1739,11 +1883,11 @@ when it try to get the value of TIME_ZONE global variable from master.";
DBUG_ASSERT(mi->checksum_alg_before_fd == BINLOG_CHECKSUM_ALG_OFF ||
mi->checksum_alg_before_fd == BINLOG_CHECKSUM_ALG_CRC32);
}
- else if (check_io_slave_killed(mi->io_thd, mi, NULL))
+ else if (check_io_slave_killed(mi, NULL))
goto slave_killed_err;
else if (is_network_error(mysql_errno(mysql)))
{
- mi->report(WARNING_LEVEL, mysql_errno(mysql),
+ mi->report(WARNING_LEVEL, mysql_errno(mysql), NULL,
"Get master BINLOG_CHECKSUM failed with error: %s", mysql_error(mysql));
goto network_err;
}
@@ -1780,7 +1924,7 @@ past_checksum:
err_code= mysql_errno(mysql);
if (is_network_error(err_code))
{
- mi->report(ERROR_LEVEL, err_code,
+ mi->report(ERROR_LEVEL, err_code, NULL,
"Setting master-side filtering of @@skip_replication failed "
"with error: %s", mysql_error(mysql));
goto network_err;
@@ -1810,13 +1954,277 @@ past_checksum:
}
}
}
+
+ /* Announce MariaDB slave capabilities. */
+ DBUG_EXECUTE_IF("simulate_slave_capability_none", goto after_set_capability;);
+ {
+ int rc= DBUG_EVALUATE_IF("simulate_slave_capability_old_53",
+ mysql_real_query(mysql, STRING_WITH_LEN("SET @mariadb_slave_capability="
+ STRINGIFY_ARG(MARIA_SLAVE_CAPABILITY_ANNOTATE))),
+ mysql_real_query(mysql, STRING_WITH_LEN("SET @mariadb_slave_capability="
+ STRINGIFY_ARG(MARIA_SLAVE_CAPABILITY_MINE))));
+ if (rc)
+ {
+ err_code= mysql_errno(mysql);
+ if (is_network_error(err_code))
+ {
+ mi->report(ERROR_LEVEL, err_code, NULL,
+ "Setting @mariadb_slave_capability failed with error: %s",
+ mysql_error(mysql));
+ goto network_err;
+ }
+ else
+ {
+ /* Fatal error */
+ errmsg= "The slave I/O thread stops because a fatal error is "
+ "encountered when it tries to set @mariadb_slave_capability.";
+ sprintf(err_buff, "%s Error: %s", errmsg, mysql_error(mysql));
+ goto err;
+ }
+ }
+ }
+#ifndef DBUG_OFF
+after_set_capability:
+#endif
+
+ if (mi->using_gtid != Master_info::USE_GTID_NO)
+ {
+ /* Request dump to start from slave replication GTID state. */
+ int rc;
+ char str_buf[256];
+ String query_str(str_buf, sizeof(str_buf), system_charset_info);
+ query_str.length(0);
+
+ /*
+ Read the master @@GLOBAL.gtid_domain_id variable.
+ This is mostly to check that master is GTID aware, but we could later
+ perhaps use it to check that different multi-source masters are correctly
+ configured with distinct domain_id.
+ */
+ if (mysql_real_query(mysql,
+ STRING_WITH_LEN("SELECT @@GLOBAL.gtid_domain_id")) ||
+ !(master_res= mysql_store_result(mysql)) ||
+ !(master_row= mysql_fetch_row(master_res)))
+ {
+ err_code= mysql_errno(mysql);
+ if (is_network_error(err_code))
+ {
+ mi->report(ERROR_LEVEL, err_code, NULL,
+ "Get master @@GLOBAL.gtid_domain_id failed with error: %s",
+ mysql_error(mysql));
+ goto network_err;
+ }
+ else
+ {
+ errmsg= "The slave I/O thread stops because master does not support "
+ "MariaDB global transaction id. A fatal error is encountered when "
+ "it tries to SELECT @@GLOBAL.gtid_domain_id.";
+ sprintf(err_buff, "%s Error: %s", errmsg, mysql_error(mysql));
+ goto err;
+ }
+ }
+ mysql_free_result(master_res);
+ master_res= NULL;
+
+ query_str.append(STRING_WITH_LEN("SET @slave_connect_state='"),
+ system_charset_info);
+ if (mi->gtid_current_pos.append_to_string(&query_str))
+ {
+ err_code= ER_OUTOFMEMORY;
+ errmsg= "The slave I/O thread stops because a fatal out-of-memory "
+ "error is encountered when it tries to compute @slave_connect_state.";
+ sprintf(err_buff, "%s Error: Out of memory", errmsg);
+ goto err;
+ }
+ query_str.append(STRING_WITH_LEN("'"), system_charset_info);
+
+ rc= mysql_real_query(mysql, query_str.ptr(), query_str.length());
+ if (rc)
+ {
+ err_code= mysql_errno(mysql);
+ if (is_network_error(err_code))
+ {
+ mi->report(ERROR_LEVEL, err_code, NULL,
+ "Setting @slave_connect_state failed with error: %s",
+ mysql_error(mysql));
+ goto network_err;
+ }
+ else
+ {
+ /* Fatal error */
+ errmsg= "The slave I/O thread stops because a fatal error is "
+ "encountered when it tries to set @slave_connect_state.";
+ sprintf(err_buff, "%s Error: %s", errmsg, mysql_error(mysql));
+ goto err;
+ }
+ }
+
+ query_str.length(0);
+ if (query_str.append(STRING_WITH_LEN("SET @slave_gtid_strict_mode="),
+ system_charset_info) ||
+ query_str.append_ulonglong(opt_gtid_strict_mode != false))
+ {
+ err_code= ER_OUTOFMEMORY;
+ errmsg= "The slave I/O thread stops because a fatal out-of-memory "
+ "error is encountered when it tries to set @slave_gtid_strict_mode.";
+ sprintf(err_buff, "%s Error: Out of memory", errmsg);
+ goto err;
+ }
+
+ rc= mysql_real_query(mysql, query_str.ptr(), query_str.length());
+ if (rc)
+ {
+ err_code= mysql_errno(mysql);
+ if (is_network_error(err_code))
+ {
+ mi->report(ERROR_LEVEL, err_code, NULL,
+ "Setting @slave_gtid_strict_mode failed with error: %s",
+ mysql_error(mysql));
+ goto network_err;
+ }
+ else
+ {
+ /* Fatal error */
+ errmsg= "The slave I/O thread stops because a fatal error is "
+ "encountered when it tries to set @slave_gtid_strict_mode.";
+ sprintf(err_buff, "%s Error: %s", errmsg, mysql_error(mysql));
+ goto err;
+ }
+ }
+
+ query_str.length(0);
+ if (query_str.append(STRING_WITH_LEN("SET @slave_gtid_ignore_duplicates="),
+ system_charset_info) ||
+ query_str.append_ulonglong(opt_gtid_ignore_duplicates != false))
+ {
+ err_code= ER_OUTOFMEMORY;
+ errmsg= "The slave I/O thread stops because a fatal out-of-memory error "
+ "is encountered when it tries to set @slave_gtid_ignore_duplicates.";
+ sprintf(err_buff, "%s Error: Out of memory", errmsg);
+ goto err;
+ }
+
+ rc= mysql_real_query(mysql, query_str.ptr(), query_str.length());
+ if (rc)
+ {
+ err_code= mysql_errno(mysql);
+ if (is_network_error(err_code))
+ {
+ mi->report(ERROR_LEVEL, err_code, NULL,
+ "Setting @slave_gtid_ignore_duplicates failed with "
+ "error: %s", mysql_error(mysql));
+ goto network_err;
+ }
+ else
+ {
+ /* Fatal error */
+ errmsg= "The slave I/O thread stops because a fatal error is "
+ "encountered when it tries to set @slave_gtid_ignore_duplicates.";
+ sprintf(err_buff, "%s Error: %s", errmsg, mysql_error(mysql));
+ goto err;
+ }
+ }
+
+ if (mi->rli.until_condition == Relay_log_info::UNTIL_GTID)
+ {
+ query_str.length(0);
+ query_str.append(STRING_WITH_LEN("SET @slave_until_gtid='"),
+ system_charset_info);
+ if (mi->rli.until_gtid_pos.append_to_string(&query_str))
+ {
+ err_code= ER_OUTOFMEMORY;
+ errmsg= "The slave I/O thread stops because a fatal out-of-memory "
+ "error is encountered when it tries to compute @slave_until_gtid.";
+ sprintf(err_buff, "%s Error: Out of memory", errmsg);
+ goto err;
+ }
+ query_str.append(STRING_WITH_LEN("'"), system_charset_info);
+
+ rc= mysql_real_query(mysql, query_str.ptr(), query_str.length());
+ if (rc)
+ {
+ err_code= mysql_errno(mysql);
+ if (is_network_error(err_code))
+ {
+ mi->report(ERROR_LEVEL, err_code, NULL,
+ "Setting @slave_until_gtid failed with error: %s",
+ mysql_error(mysql));
+ goto network_err;
+ }
+ else
+ {
+ /* Fatal error */
+ errmsg= "The slave I/O thread stops because a fatal error is "
+ "encountered when it tries to set @slave_until_gtid.";
+ sprintf(err_buff, "%s Error: %s", errmsg, mysql_error(mysql));
+ goto err;
+ }
+ }
+ }
+ }
+ else
+ {
+ /*
+ If we are not using GTID to connect this time, then instead request
+ the corresponding GTID position from the master, so that the user
+ can reconnect the next time using MASTER_GTID_POS=AUTO.
+ */
+ char quote_buf[2*sizeof(mi->master_log_name)+1];
+ char str_buf[28+2*sizeof(mi->master_log_name)+10];
+ String query(str_buf, sizeof(str_buf), system_charset_info);
+ query.length(0);
+
+ query.append("SELECT binlog_gtid_pos('");
+ escape_quotes_for_mysql(&my_charset_bin, quote_buf, sizeof(quote_buf),
+ mi->master_log_name, strlen(mi->master_log_name));
+ query.append(quote_buf);
+ query.append("',");
+ query.append_ulonglong(mi->master_log_pos);
+ query.append(")");
+
+ if (!mysql_real_query(mysql, query.c_ptr_safe(), query.length()) &&
+ (master_res= mysql_store_result(mysql)) &&
+ (master_row= mysql_fetch_row(master_res)) &&
+ (master_row[0] != NULL))
+ {
+ rpl_global_gtid_slave_state->load(mi->io_thd, master_row[0],
+ strlen(master_row[0]), false, false);
+ }
+ else if (check_io_slave_killed(mi, NULL))
+ goto slave_killed_err;
+ else if (is_network_error(mysql_errno(mysql)))
+ {
+ mi->report(WARNING_LEVEL, mysql_errno(mysql), NULL,
+ "Get master GTID position failed with error: %s", mysql_error(mysql));
+ goto network_err;
+ }
+ else
+ {
+ /*
+ ToDo: If the master does not have the binlog_gtid_pos() function, it
+ just means that it is an old master with no GTID support, so we should
+ do nothing.
+
+ However, if binlog_gtid_pos() exists, but fails or returns NULL, then
+ it means that the requested position is not valid. We could use this
+ to catch attempts to replicate from within the middle of an event,
+ avoiding strange failures or possible corruption.
+ */
+ }
+ if (master_res)
+ {
+ mysql_free_result(master_res);
+ master_res= NULL;
+ }
+ }
+
err:
if (errmsg)
{
if (master_res)
mysql_free_result(master_res);
DBUG_ASSERT(err_code != 0);
- mi->report(ERROR_LEVEL, err_code, "%s", err_buff);
+ mi->report(ERROR_LEVEL, err_code, NULL, "%s", err_buff);
DBUG_RETURN(1);
}
@@ -1837,21 +2245,27 @@ slave_killed_err:
static bool wait_for_relay_log_space(Relay_log_info* rli)
{
bool slave_killed=0;
+ bool ignore_log_space_limit;
Master_info* mi = rli->mi;
- const char *save_proc_info;
+ PSI_stage_info old_stage;
THD* thd = mi->io_thd;
DBUG_ENTER("wait_for_relay_log_space");
mysql_mutex_lock(&rli->log_space_lock);
- save_proc_info= thd->enter_cond(&rli->log_space_cond,
- &rli->log_space_lock,
- "\
-Waiting for the slave SQL thread to free enough relay log space");
+ thd->ENTER_COND(&rli->log_space_cond,
+ &rli->log_space_lock,
+ &stage_waiting_for_relay_log_space,
+ &old_stage);
while (rli->log_space_limit < rli->log_space_total &&
- !(slave_killed=io_slave_killed(thd,mi)) &&
+ !(slave_killed=io_slave_killed(mi)) &&
!rli->ignore_log_space_limit)
mysql_cond_wait(&rli->log_space_cond, &rli->log_space_lock);
+ ignore_log_space_limit= rli->ignore_log_space_limit;
+ rli->ignore_log_space_limit= 0;
+
+ thd->EXIT_COND(&old_stage);
+
/*
Makes the IO thread read only one event at a time
until the SQL thread is able to purge the relay
@@ -1875,7 +2289,8 @@ Waiting for the slave SQL thread to free enough relay log space");
thread sleeps waiting for events.
*/
- if (rli->ignore_log_space_limit)
+
+ if (ignore_log_space_limit)
{
#ifndef DBUG_OFF
{
@@ -1892,16 +2307,13 @@ Waiting for the slave SQL thread to free enough relay log space");
#endif
if (rli->sql_force_rotate_relay)
{
- mysql_mutex_lock(&active_mi->data_lock);
+ mysql_mutex_lock(&mi->data_lock);
rotate_relay_log(rli->mi);
- mysql_mutex_unlock(&active_mi->data_lock);
+ mysql_mutex_unlock(&mi->data_lock);
rli->sql_force_rotate_relay= false;
}
-
- rli->ignore_log_space_limit= false;
}
- thd->exit_cond(save_proc_info);
DBUG_RETURN(slave_killed);
}
@@ -1927,34 +2339,66 @@ static void write_ignored_events_info_to_relay_log(THD *thd, Master_info *mi)
DBUG_ASSERT(thd == mi->io_thd);
mysql_mutex_lock(log_lock);
- if (rli->ign_master_log_name_end[0])
- {
- DBUG_PRINT("info",("writing a Rotate event to track down ignored events"));
- Rotate_log_event *ev= new Rotate_log_event(rli->ign_master_log_name_end,
- 0, rli->ign_master_log_pos_end,
- Rotate_log_event::DUP_NAME);
- rli->ign_master_log_name_end[0]= 0;
- /* can unlock before writing as slave SQL thd will soon see our Rotate */
+ if (rli->ign_master_log_name_end[0] || rli->ign_gtids.count())
+ {
+ Rotate_log_event *rev= NULL;
+ Gtid_list_log_event *glev= NULL;
+ if (rli->ign_master_log_name_end[0])
+ {
+ rev= new Rotate_log_event(rli->ign_master_log_name_end,
+ 0, rli->ign_master_log_pos_end,
+ Rotate_log_event::DUP_NAME);
+ rli->ign_master_log_name_end[0]= 0;
+ if (unlikely(!(bool)rev))
+ mi->report(ERROR_LEVEL, ER_SLAVE_CREATE_EVENT_FAILURE, NULL,
+ ER(ER_SLAVE_CREATE_EVENT_FAILURE),
+ "Rotate_event (out of memory?),"
+ " SHOW SLAVE STATUS may be inaccurate");
+ }
+ if (rli->ign_gtids.count())
+ {
+ glev= new Gtid_list_log_event(&rli->ign_gtids,
+ Gtid_list_log_event::FLAG_IGN_GTIDS);
+ rli->ign_gtids.reset();
+ if (unlikely(!(bool)glev))
+ mi->report(ERROR_LEVEL, ER_SLAVE_CREATE_EVENT_FAILURE, NULL,
+ ER(ER_SLAVE_CREATE_EVENT_FAILURE),
+ "Gtid_list_event (out of memory?),"
+ " gtid_slave_pos may be inaccurate");
+ }
+
+ /* Can unlock before writing as slave SQL thd will soon see our event. */
mysql_mutex_unlock(log_lock);
- if (likely((bool)ev))
+ if (rev)
{
- ev->server_id= 0; // don't be ignored by slave SQL thread
- if (unlikely(rli->relay_log.append(ev)))
- mi->report(ERROR_LEVEL, ER_SLAVE_RELAY_LOG_WRITE_FAILURE,
+ DBUG_PRINT("info",("writing a Rotate event to track down ignored events"));
+ rev->server_id= 0; // don't be ignored by slave SQL thread
+ if (unlikely(rli->relay_log.append(rev)))
+ mi->report(ERROR_LEVEL, ER_SLAVE_RELAY_LOG_WRITE_FAILURE, NULL,
ER(ER_SLAVE_RELAY_LOG_WRITE_FAILURE),
"failed to write a Rotate event"
" to the relay log, SHOW SLAVE STATUS may be"
" inaccurate");
+ delete rev;
+ }
+ if (glev)
+ {
+ DBUG_PRINT("info",("writing a Gtid_list event to track down ignored events"));
+ glev->server_id= 0; // don't be ignored by slave SQL thread
+ glev->set_artificial_event(); // Don't mess up Exec_Master_Log_Pos
+ if (unlikely(rli->relay_log.append(glev)))
+ mi->report(ERROR_LEVEL, ER_SLAVE_RELAY_LOG_WRITE_FAILURE, NULL,
+ ER(ER_SLAVE_RELAY_LOG_WRITE_FAILURE),
+ "failed to write a Gtid_list event to the relay log, "
+ "gtid_slave_pos may be inaccurate");
+ delete glev;
+ }
+ if (likely (rev || glev))
+ {
rli->relay_log.harvest_bytes_written(&rli->log_space_total);
if (flush_master_info(mi, TRUE, TRUE))
sql_print_error("Failed to flush master info file");
- delete ev;
}
- else
- mi->report(ERROR_LEVEL, ER_SLAVE_CREATE_EVENT_FAILURE,
- ER(ER_SLAVE_CREATE_EVENT_FAILURE),
- "Rotate_event (out of memory?),"
- " SHOW SLAVE STATUS may be inaccurate");
}
else
mysql_mutex_unlock(log_lock);
@@ -2003,7 +2447,7 @@ int register_slave_on_master(MYSQL* mysql, Master_info *mi,
DBUG_RETURN(0);
}
- int4store(pos, server_id); pos+= 4;
+ int4store(pos, global_system_variables.server_id); pos+= 4;
pos= net_store_data(pos, (uchar*) report_host, report_host_len);
pos= net_store_data(pos, (uchar*) report_user, report_user_len);
pos= net_store_data(pos, (uchar*) report_password, report_password_len);
@@ -2023,12 +2467,12 @@ int register_slave_on_master(MYSQL* mysql, Master_info *mi,
{
*suppress_warnings= TRUE; // Suppress reconnect warning
}
- else if (!check_io_slave_killed(mi->io_thd, mi, NULL))
+ else if (!check_io_slave_killed(mi, NULL))
{
char buf[256];
my_snprintf(buf, sizeof(buf), "%s (Errno: %d)", mysql_error(mysql),
mysql_errno(mysql));
- mi->report(ERROR_LEVEL, ER_SLAVE_MASTER_COM_FAILURE,
+ mi->report(ERROR_LEVEL, ER_SLAVE_MASTER_COM_FAILURE, NULL,
ER(ER_SLAVE_MASTER_COM_FAILURE), "COM_REGISTER_SLAVE", buf);
}
DBUG_RETURN(1);
@@ -2048,15 +2492,40 @@ int register_slave_on_master(MYSQL* mysql, Master_info *mi,
@retval FALSE success
@retval TRUE failure
*/
-bool show_master_info(THD* thd, Master_info* mi)
+
+bool show_master_info(THD *thd, Master_info *mi, bool full)
+{
+ DBUG_ENTER("show_master_info");
+ String gtid_pos;
+
+ if (full && rpl_global_gtid_slave_state->tostring(&gtid_pos, NULL, 0))
+ DBUG_RETURN(TRUE);
+ if (send_show_master_info_header(thd, full, gtid_pos.length()))
+ DBUG_RETURN(TRUE);
+ if (send_show_master_info_data(thd, mi, full, &gtid_pos))
+ DBUG_RETURN(TRUE);
+ my_eof(thd);
+ DBUG_RETURN(FALSE);
+}
+
+static bool send_show_master_info_header(THD *thd, bool full,
+ size_t gtid_pos_length)
{
- // TODO: fix this for multi-master
List<Item> field_list;
Protocol *protocol= thd->protocol;
- DBUG_ENTER("show_master_info");
+ Master_info *mi;
+ DBUG_ENTER("show_master_info_header");
+
+ if (full)
+ {
+ field_list.push_back(new Item_empty_string("Connection_name",
+ MAX_CONNECTION_NAME));
+ field_list.push_back(new Item_empty_string("Slave_SQL_State",
+ 30));
+ }
field_list.push_back(new Item_empty_string("Slave_IO_State",
- 14));
+ 30));
field_list.push_back(new Item_empty_string("Master_Host",
sizeof(mi->host)));
field_list.push_back(new Item_empty_string("Master_User",
@@ -2119,23 +2588,73 @@ bool show_master_info(THD* thd, Master_info* mi)
FN_REFLEN));
field_list.push_back(new Item_return_int("Master_Server_Id", sizeof(ulong),
MYSQL_TYPE_LONG));
+ field_list.push_back(new Item_empty_string("Master_SSL_Crl",
+ sizeof(mi->ssl_crl)));
+ field_list.push_back(new Item_empty_string("Master_SSL_Crlpath",
+ sizeof(mi->ssl_crlpath)));
+ field_list.push_back(new Item_empty_string("Using_Gtid",
+ sizeof("Current_Pos")-1));
+ field_list.push_back(new Item_empty_string("Gtid_IO_Pos", 30));
+ if (full)
+ {
+ field_list.push_back(new Item_return_int("Retried_transactions",
+ 10, MYSQL_TYPE_LONG));
+ field_list.push_back(new Item_return_int("Max_relay_log_size",
+ 10, MYSQL_TYPE_LONGLONG));
+ field_list.push_back(new Item_return_int("Executed_log_entries",
+ 10, MYSQL_TYPE_LONG));
+ field_list.push_back(new Item_return_int("Slave_received_heartbeats",
+ 10, MYSQL_TYPE_LONG));
+ field_list.push_back(new Item_float("Slave_heartbeat_period",
+ 0.0, 3, 10));
+ field_list.push_back(new Item_empty_string("Gtid_Slave_Pos",
+ gtid_pos_length));
+ }
if (protocol->send_result_set_metadata(&field_list,
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(TRUE);
+ DBUG_RETURN(FALSE);
+}
+
+/* Text for Slave_IO_Running */
+static const char *slave_running[]= { "No", "Connecting", "Preparing", "Yes" };
+
+static bool send_show_master_info_data(THD *thd, Master_info *mi, bool full,
+ String *gtid_pos)
+{
+ DBUG_ENTER("send_show_master_info_data");
if (mi->host[0])
{
DBUG_PRINT("info",("host is set: '%s'", mi->host));
String *packet= &thd->packet;
+ Protocol *protocol= thd->protocol;
+ Rpl_filter *rpl_filter= mi->rpl_filter;
+ char buf[256];
+ String tmp(buf, sizeof(buf), &my_charset_bin);
+
protocol->prepare_for_resend();
/*
slave_running can be accessed without run_lock but not other
non-volotile members like mi->io_thd, which is guarded by the mutex.
*/
+ if (full)
+ protocol->store(mi->connection_name.str, mi->connection_name.length,
+ &my_charset_bin);
mysql_mutex_lock(&mi->run_lock);
- protocol->store(mi->io_thd ? mi->io_thd->proc_info : "", &my_charset_bin);
+ if (full)
+ {
+ /*
+ Show what the sql driver replication thread is doing
+ This is only meaningful if there is only one slave thread.
+ */
+ protocol->store(mi->rli.sql_driver_thd ?
+ mi->rli.sql_driver_thd->get_proc_info() : "",
+ &my_charset_bin);
+ }
+ protocol->store(mi->io_thd ? mi->io_thd->get_proc_info() : "", &my_charset_bin);
mysql_mutex_unlock(&mi->run_lock);
mysql_mutex_lock(&mi->data_lock);
@@ -2153,15 +2672,11 @@ bool show_master_info(THD* thd, Master_info* mi)
&my_charset_bin);
protocol->store((ulonglong) mi->rli.group_relay_log_pos);
protocol->store(mi->rli.group_master_log_name, &my_charset_bin);
- protocol->store(mi->slave_running == MYSQL_SLAVE_RUN_CONNECT ?
- "Yes" : (mi->slave_running == MYSQL_SLAVE_RUN_NOT_CONNECT ?
- "Connecting" : "No"), &my_charset_bin);
+ protocol->store(slave_running[mi->slave_running], &my_charset_bin);
protocol->store(mi->rli.slave_running ? "Yes":"No", &my_charset_bin);
protocol->store(rpl_filter->get_do_db());
protocol->store(rpl_filter->get_ignore_db());
- char buf[256];
- String tmp(buf, sizeof(buf), &my_charset_bin);
rpl_filter->get_do_table(&tmp);
protocol->store(&tmp);
rpl_filter->get_ignore_table(&tmp);
@@ -2180,7 +2695,8 @@ bool show_master_info(THD* thd, Master_info* mi)
protocol->store(
mi->rli.until_condition==Relay_log_info::UNTIL_NONE ? "None":
( mi->rli.until_condition==Relay_log_info::UNTIL_MASTER_POS? "Master":
- "Relay"), &my_charset_bin);
+ ( mi->rli.until_condition==Relay_log_info::UNTIL_RELAY_POS? "Relay":
+ "Gtid")), &my_charset_bin);
protocol->store(mi->rli.until_log_name, &my_charset_bin);
protocol->store((ulonglong) mi->rli.until_log_pos);
@@ -2199,11 +2715,27 @@ bool show_master_info(THD* thd, Master_info* mi)
Seconds_Behind_Master: if SQL thread is running and I/O thread is
connected, we can compute it otherwise show NULL (i.e. unknown).
*/
- if ((mi->slave_running == MYSQL_SLAVE_RUN_CONNECT) &&
+ if ((mi->slave_running == MYSQL_SLAVE_RUN_READING) &&
mi->rli.slave_running)
{
- long time_diff= ((long)(time(0) - mi->rli.last_master_timestamp)
- - mi->clock_diff_with_master);
+ long time_diff;
+ bool idle;
+ time_t stamp= mi->rli.last_master_timestamp;
+
+ if (!stamp)
+ idle= true;
+ else
+ {
+ idle= mi->rli.sql_thread_caught_up;
+ if (opt_slave_parallel_threads > 0 && idle &&
+ !mi->rli.parallel.workers_idle())
+ idle= false;
+ }
+ if (idle)
+ time_diff= 0;
+ else
+ {
+ time_diff= ((long)(time(0) - stamp) - mi->clock_diff_with_master);
/*
Apparently on some systems time_diff can be <0. Here are possible
reasons related to MySQL:
@@ -2219,13 +2751,15 @@ bool show_master_info(THD* thd, Master_info* mi)
slave is 2. At SHOW SLAVE STATUS time, assume that the difference
between timestamp of slave and rli->last_master_timestamp is 0
(i.e. they are in the same second), then we get 0-(2-1)=-1 as a result.
- This confuses users, so we don't go below 0: hence the max().
+ This confuses users, so we don't go below 0.
last_master_timestamp == 0 (an "impossible" timestamp 1970) is a
special marker to say "consider we have caught up".
*/
- protocol->store((longlong)(mi->rli.last_master_timestamp ?
- max(0, time_diff) : 0));
+ if (time_diff < 0)
+ time_diff= 0;
+ }
+ protocol->store((longlong)time_diff);
}
else
{
@@ -2267,6 +2801,26 @@ bool show_master_info(THD* thd, Master_info* mi)
}
// Master_Server_id
protocol->store((uint32) mi->master_id);
+ // Master_Ssl_Crl
+ protocol->store(mi->ssl_ca, &my_charset_bin);
+ // Master_Ssl_Crlpath
+ protocol->store(mi->ssl_capath, &my_charset_bin);
+ protocol->store(mi->using_gtid_astext(mi->using_gtid), &my_charset_bin);
+ {
+ char buff[30];
+ String tmp(buff, sizeof(buff), system_charset_info);
+ mi->gtid_current_pos.to_string(&tmp);
+ protocol->store(tmp.ptr(), tmp.length(), &my_charset_bin);
+ }
+ if (full)
+ {
+ protocol->store((uint32) mi->rli.retried_trans);
+ protocol->store((ulonglong) mi->rli.max_relay_log_size);
+ protocol->store((uint32) mi->rli.executed_entries);
+ protocol->store((uint32) mi->received_heartbeats);
+ protocol->store((double) mi->heartbeat_period, 3, &tmp);
+ protocol->store(gtid_pos->ptr(), gtid_pos->length(), &my_charset_bin);
+ }
mysql_mutex_unlock(&mi->rli.err_lock);
mysql_mutex_unlock(&mi->err_lock);
@@ -2276,6 +2830,78 @@ bool show_master_info(THD* thd, Master_info* mi)
if (my_net_write(&thd->net, (uchar*) thd->packet.ptr(), packet->length()))
DBUG_RETURN(TRUE);
}
+ DBUG_RETURN(FALSE);
+}
+
+
+/* Used to sort connections by name */
+
+static int cmp_mi_by_name(const Master_info **arg1,
+ const Master_info **arg2)
+{
+ return my_strcasecmp(system_charset_info, (*arg1)->connection_name.str,
+ (*arg2)->connection_name.str);
+}
+
+
+/**
+ Execute a SHOW FULL SLAVE STATUS statement.
+
+ @param thd Pointer to THD object for the client thread executing the
+ statement.
+
+ Elements are sorted according to the original connection_name.
+
+ @retval FALSE success
+ @retval TRUE failure
+
+ @note
+ master_info_index is protected by LOCK_active_mi.
+*/
+
+bool show_all_master_info(THD* thd)
+{
+ uint i, elements;
+ String gtid_pos;
+ Master_info **tmp;
+ DBUG_ENTER("show_master_info");
+ mysql_mutex_assert_owner(&LOCK_active_mi);
+
+ gtid_pos.length(0);
+ if (rpl_append_gtid_state(&gtid_pos, true))
+ {
+ my_error(ER_OUT_OF_RESOURCES, MYF(0));
+ DBUG_RETURN(TRUE);
+ }
+
+ if (send_show_master_info_header(thd, 1, gtid_pos.length()))
+ DBUG_RETURN(TRUE);
+
+ if (!master_info_index ||
+ !(elements= master_info_index->master_info_hash.records))
+ goto end;
+
+ /*
+ Sort lines to get them into a predicted order
+ (needed for test cases and to not confuse users)
+ */
+ if (!(tmp= (Master_info**) thd->alloc(sizeof(Master_info*) * elements)))
+ DBUG_RETURN(TRUE);
+
+ for (i= 0; i < elements; i++)
+ {
+ tmp[i]= (Master_info *) my_hash_element(&master_info_index->
+ master_info_hash, i);
+ }
+ my_qsort(tmp, elements, sizeof(Master_info*), (qsort_cmp) cmp_mi_by_name);
+
+ for (i= 0; i < elements; i++)
+ {
+ if (send_show_master_info_data(thd, tmp[i], 1, &gtid_pos))
+ DBUG_RETURN(TRUE);
+ }
+
+end:
my_eof(thd);
DBUG_RETURN(FALSE);
}
@@ -2303,7 +2929,7 @@ void set_slave_thread_options(THD* thd)
DBUG_VOID_RETURN;
}
-void set_slave_thread_default_charset(THD* thd, Relay_log_info const *rli)
+void set_slave_thread_default_charset(THD* thd, rpl_group_info *rgi)
{
DBUG_ENTER("set_slave_thread_default_charset");
@@ -2315,13 +2941,7 @@ void set_slave_thread_default_charset(THD* thd, Relay_log_info const *rli)
global_system_variables.collation_server;
thd->update_charset();
- /*
- We use a const cast here since the conceptual (and externally
- visible) behavior of the function is to set the default charset of
- the thread. That the cache has to be invalidated is a secondary
- effect.
- */
- const_cast<Relay_log_info*>(rli)->cached_charset_invalidate();
+ thd->system_thread_info.rpl_sql_info->cached_charset_invalidate();
DBUG_VOID_RETURN;
}
@@ -2329,40 +2949,44 @@ void set_slave_thread_default_charset(THD* thd, Relay_log_info const *rli)
init_slave_thread()
*/
-static int init_slave_thread(THD* thd, SLAVE_THD_TYPE thd_type)
+static int init_slave_thread(THD* thd, Master_info *mi,
+ 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();
- my_net_init(&thd->net, 0);
- thd->slave_thread = 1;
- thd->enable_slow_log= opt_log_slow_slave_statements;
- thd->variables.log_slow_filter= global_system_variables.log_slow_filter;
- set_slave_thread_options(thd);
- thd->client_capabilities = CLIENT_LOCAL_FILES;
- mysql_mutex_lock(&LOCK_thread_count);
- thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;
- mysql_mutex_unlock(&LOCK_thread_count);
-
+ int simulate_error __attribute__((unused))= 0;
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););
+
+ thd->system_thread = (thd_type == SLAVE_THD_SQL) ?
+ SYSTEM_THREAD_SLAVE_SQL : SYSTEM_THREAD_SLAVE_IO;
+ thread_safe_increment32(&service_thread_count, &thread_count_lock);
+
+ /* We must call store_globals() before doing my_net_init() */
if (init_thr_lock() || thd->store_globals() ||
+ my_net_init(&thd->net, 0, MYF(MY_THREAD_SPECIFIC)) ||
IF_DBUG(simulate_error & (1<< thd_type), 0))
{
thd->cleanup();
DBUG_RETURN(-1);
}
+ thd->security_ctx->skip_grants();
+ thd->slave_thread= 1;
+ thd->connection_name= mi->connection_name;
+ thd->enable_slow_log= opt_log_slow_slave_statements;
+ thd->variables.log_slow_filter= global_system_variables.log_slow_filter;
+ set_slave_thread_options(thd);
+ thd->client_capabilities = CLIENT_LOCAL_FILES;
+ mysql_mutex_lock(&LOCK_thread_count);
+ thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;
+ mysql_mutex_unlock(&LOCK_thread_count);
+
if (thd_type == SLAVE_THD_SQL)
- thd_proc_info(thd, "Waiting for the next event in relay log");
+ THD_STAGE_INFO(thd, stage_waiting_for_the_next_event_in_relay_log);
else
- thd_proc_info(thd, "Waiting for master update");
+ THD_STAGE_INFO(thd, stage_waiting_for_master_update);
thd->set_time();
/* Do not use user-supplied timeout value for system threads. */
thd->variables.lock_wait_timeout= LONG_TIMEOUT;
@@ -2380,13 +3004,12 @@ static int init_slave_thread(THD* thd, SLAVE_THD_TYPE thd_type)
@retval True if the thread has been killed, false otherwise.
*/
template <typename killed_func, typename rpl_info>
-static inline bool slave_sleep(THD *thd, time_t seconds,
- killed_func func, rpl_info info)
+static bool slave_sleep(THD *thd, time_t seconds,
+ killed_func func, rpl_info info)
{
bool ret;
struct timespec abstime;
- const char *old_proc_info;
mysql_mutex_t *lock= &info->sleep_lock;
mysql_cond_t *cond= &info->sleep_cond;
@@ -2394,16 +3017,16 @@ static inline bool slave_sleep(THD *thd, time_t seconds,
/* Absolute system time at which the sleep time expires. */
set_timespec(abstime, seconds);
mysql_mutex_lock(lock);
- old_proc_info= thd->enter_cond(cond, lock, thd->proc_info);
+ thd->ENTER_COND(cond, lock, NULL, NULL);
- while (! (ret= func(thd, info)))
+ while (! (ret= func(info)))
{
int error= mysql_cond_timedwait(cond, lock, &abstime);
if (error == ETIMEDOUT || error == ETIME)
break;
}
/* Implicitly unlocks the mutex. */
- thd->exit_cond(old_proc_info);
+ thd->EXIT_COND(NULL);
return ret;
}
@@ -2430,7 +3053,7 @@ static int request_dump(THD *thd, MYSQL* mysql, Master_info* mi,
// TODO if big log files: Change next to int8store()
int4store(buf, (ulong) mi->master_log_pos);
int2store(buf + 4, binlog_flags);
- int4store(buf + 6, server_id);
+ int4store(buf + 6, global_system_variables.server_id);
len = (uint) strlen(logname);
memcpy(buf + 10, logname,len);
if (simple_command(mysql, COM_BINLOG_DUMP, buf, len + 10, 1))
@@ -2524,12 +3147,13 @@ static ulong read_event(MYSQL* mysql, Master_info *mi, bool* suppress_warnings)
that the error is temporary by pushing a warning with the error code
ER_GET_TEMPORARY_ERRMSG, if the originating error is temporary.
*/
-static int has_temporary_error(THD *thd)
+int
+has_temporary_error(THD *thd)
{
DBUG_ENTER("has_temporary_error");
DBUG_EXECUTE_IF("all_errors_are_temporary_errors",
- if (thd->stmt_da->is_error())
+ if (thd->get_stmt_da()->is_error())
{
thd->clear_error();
my_error(ER_LOCK_DEADLOCK, MYF(0));
@@ -2548,16 +3172,16 @@ static int has_temporary_error(THD *thd)
currently, InnoDB deadlock detected by InnoDB or lock
wait timeout (innodb_lock_wait_timeout exceeded
*/
- if (thd->stmt_da->sql_errno() == ER_LOCK_DEADLOCK ||
- thd->stmt_da->sql_errno() == ER_LOCK_WAIT_TIMEOUT)
+ if (thd->get_stmt_da()->sql_errno() == ER_LOCK_DEADLOCK ||
+ thd->get_stmt_da()->sql_errno() == ER_LOCK_WAIT_TIMEOUT)
DBUG_RETURN(1);
#ifdef HAVE_NDB_BINLOG
/*
currently temporary error set in ndbcluster
*/
- List_iterator_fast<MYSQL_ERROR> it(thd->warning_info->warn_list());
- MYSQL_ERROR *err;
+ List_iterator_fast<Sql_condition> it(thd->warning_info->warn_list());
+ Sql_condition *err;
while ((err= it++))
{
DBUG_PRINT("info", ("has condition %d %s", err->get_sql_errno(),
@@ -2601,19 +3225,22 @@ static int has_temporary_error(THD *thd)
@retval 2 No error calling ev->apply_event(), but error calling
ev->update_pos().
*/
-int apply_event_and_update_pos(Log_event* ev, THD* thd, Relay_log_info* rli)
+int apply_event_and_update_pos(Log_event* ev, THD* thd,
+ rpl_group_info *rgi,
+ rpl_parallel_thread *rpt)
{
int exec_res= 0;
-
+ Relay_log_info* rli= rgi->rli;
DBUG_ENTER("apply_event_and_update_pos");
DBUG_PRINT("exec_event",("%s(type_code: %d; server_id: %d)",
ev->get_type_str(), ev->get_type_code(),
ev->server_id));
- DBUG_PRINT("info", ("thd->options: %s%s; rli->last_event_start_time: %lu",
+ DBUG_PRINT("info", ("thd->options: '%s%s%s' rgi->last_event_start_time: %lu",
FLAGSTR(thd->variables.option_bits, OPTION_NOT_AUTOCOMMIT),
FLAGSTR(thd->variables.option_bits, OPTION_BEGIN),
- (ulong) rli->last_event_start_time));
+ FLAGSTR(thd->variables.option_bits, OPTION_GTID_BEGIN),
+ (ulong) rgi->last_event_start_time));
/*
Execute the event to change the database and update the binary
@@ -2639,7 +3266,8 @@ int apply_event_and_update_pos(Log_event* ev, THD* thd, Relay_log_info* rli)
has a Rotate etc).
*/
- thd->server_id = ev->server_id; // use the original server id for logging
+ /* Use the original server id for logging. */
+ thd->variables.server_id = ev->server_id;
thd->set_time(); // time the query
thd->lex->current_select= 0;
if (!ev->when)
@@ -2653,12 +3281,32 @@ int apply_event_and_update_pos(Log_event* ev, THD* thd, Relay_log_info* rli)
(ev->flags & LOG_EVENT_SKIP_REPLICATION_F ? OPTION_SKIP_REPLICATION : 0);
ev->thd = thd; // because up to this point, ev->thd == 0
- int reason= ev->shall_skip(rli);
+ int reason= ev->shall_skip(rgi);
if (reason == Log_event::EVENT_SKIP_COUNT)
- sql_slave_skip_counter= --rli->slave_skip_counter;
+ {
+ DBUG_ASSERT(rli->slave_skip_counter > 0);
+ rli->slave_skip_counter--;
+ }
mysql_mutex_unlock(&rli->data_lock);
+ DBUG_EXECUTE_IF("inject_slave_sql_before_apply_event",
+ {
+ DBUG_ASSERT(!debug_sync_set_action
+ (thd, STRING_WITH_LEN("now WAIT_FOR continue")));
+ DBUG_SET_INITIAL("-d,inject_slave_sql_before_apply_event");
+ };);
if (reason == Log_event::EVENT_SKIP_NOT)
- exec_res= ev->apply_event(rli);
+ exec_res= ev->apply_event(rgi);
+
+#ifdef WITH_WSREP
+ if (exec_res && thd->wsrep_conflict_state != NO_CONFLICT)
+ {
+ WSREP_DEBUG("SQL apply failed, res %d conflict state: %d",
+ exec_res, thd->wsrep_conflict_state);
+ rli->abort_slave= 1;
+ rli->report(ERROR_LEVEL, ER_UNKNOWN_COM_ERROR, rgi->gtid_info(),
+ "Node has dropped from cluster");
+ }
+#endif
#ifndef DBUG_OFF
/*
@@ -2674,9 +3322,10 @@ int apply_event_and_update_pos(Log_event* ev, THD* thd, Relay_log_info* rli)
// EVENT_SKIP_COUNT
"skipped because event skip counter was non-zero"
};
- DBUG_PRINT("info", ("OPTION_BEGIN: %d; IN_STMT: %d",
- test(thd->variables.option_bits & OPTION_BEGIN),
- rli->get_flag(Relay_log_info::IN_STMT)));
+ DBUG_PRINT("info", ("OPTION_BEGIN: %d IN_STMT: %d IN_TRANSACTION: %d",
+ MY_TEST(thd->variables.option_bits & OPTION_BEGIN),
+ rli->get_flag(Relay_log_info::IN_STMT),
+ rli->get_flag(Relay_log_info::IN_TRANSACTION)));
DBUG_PRINT("skip_event", ("%s event was %s",
ev->get_type_str(), explain[reason]));
#endif
@@ -2684,7 +3333,7 @@ int apply_event_and_update_pos(Log_event* ev, THD* thd, Relay_log_info* rli)
DBUG_PRINT("info", ("apply_event error = %d", exec_res));
if (exec_res == 0)
{
- int error= ev->update_pos(rli);
+ int error= ev->update_pos(rgi);
#ifdef HAVE_valgrind
if (!rli->is_fake)
#endif
@@ -2710,7 +3359,7 @@ int apply_event_and_update_pos(Log_event* ev, THD* thd, Relay_log_info* rli)
if (error)
{
char buf[22];
- rli->report(ERROR_LEVEL, ER_UNKNOWN_ERROR,
+ rli->report(ERROR_LEVEL, ER_UNKNOWN_ERROR, rgi->gtid_info(),
"It was not possible to update the positions"
" of the relay log information: the slave may"
" be in an inconsistent state."
@@ -2720,12 +3369,94 @@ int apply_event_and_update_pos(Log_event* ev, THD* thd, Relay_log_info* rli)
DBUG_RETURN(2);
}
}
+ else
+ {
+ /*
+ Make sure we do not erroneously update gtid_slave_pos with a lingering
+ GTID from this failed event group (MDEV-4906).
+ */
+ rgi->gtid_pending= false;
+ }
DBUG_RETURN(exec_res ? 1 : 0);
}
/**
+ Keep the relay log transaction state up to date.
+
+ The state reflects how things are after the given event, that has just been
+ read from the relay log, is executed.
+
+ This is only needed to ensure we:
+ - Don't abort the sql driver thread in the middle of an event group.
+ - Don't rotate the io thread in the middle of a statement or transaction.
+ The mechanism is that the io thread, when it needs to rotate the relay
+ log, will wait until the sql driver has read all the cached events
+ and then continue reading events one by one from the master until
+ the sql threads signals that log doesn't have an active group anymore.
+
+ There are two possible cases. We keep them as 2 separate flags mainly
+ to make debugging easier.
+
+ - IN_STMT is set when we have read an event that should be used
+ together with the next event. This is for example setting a
+ variable that is used when executing the next statement.
+ - IN_TRANSACTION is set when we are inside a BEGIN...COMMIT group
+
+ To test the state one should use the is_in_group() function.
+*/
+
+inline void update_state_of_relay_log(Relay_log_info *rli, Log_event *ev)
+{
+ Log_event_type typ= ev->get_type_code();
+
+ /* check if we are in a multi part event */
+ if (ev->is_part_of_group())
+ rli->set_flag(Relay_log_info::IN_STMT);
+ else if (Log_event::is_group_event(typ))
+ {
+ /*
+ If it was not a is_part_of_group() and not a group event (like
+ rotate) then we can reset the IN_STMT flag. We have the above
+ if only to allow us to have a rotate element anywhere.
+ */
+ rli->clear_flag(Relay_log_info::IN_STMT);
+ }
+
+ /* Check for an event that starts or stops a transaction */
+ if (typ == QUERY_EVENT)
+ {
+ Query_log_event *qev= (Query_log_event*) ev;
+ /*
+ Trivial optimization to avoid the following somewhat expensive
+ checks.
+ */
+ if (qev->q_len <= sizeof("ROLLBACK"))
+ {
+ if (qev->is_begin())
+ rli->set_flag(Relay_log_info::IN_TRANSACTION);
+ if (qev->is_commit() || qev->is_rollback())
+ rli->clear_flag(Relay_log_info::IN_TRANSACTION);
+ }
+ }
+ if (typ == XID_EVENT)
+ rli->clear_flag(Relay_log_info::IN_TRANSACTION);
+ if (typ == GTID_EVENT &&
+ !(((Gtid_log_event*) ev)->flags2 & Gtid_log_event::FL_STANDALONE))
+ {
+ /* This GTID_EVENT will generate a BEGIN event */
+ rli->set_flag(Relay_log_info::IN_TRANSACTION);
+ }
+
+ DBUG_PRINT("info", ("event: %u IN_STMT: %d IN_TRANSACTION: %d",
+ (uint) typ,
+ rli->get_flag(Relay_log_info::IN_STMT),
+ rli->get_flag(Relay_log_info::IN_TRANSACTION)));
+}
+
+
+/**
Top-level function for executing the next event from the relay log.
This function reads the event from the relay log, executes it, and
@@ -2753,22 +3484,23 @@ int apply_event_and_update_pos(Log_event* ev, THD* thd, Relay_log_info* rli)
@retval 1 The event was not applied.
*/
-static int exec_relay_log_event(THD* thd, Relay_log_info* rli)
+
+static int exec_relay_log_event(THD* thd, Relay_log_info* rli,
+ rpl_group_info *serial_rgi)
{
+ ulonglong event_size;
DBUG_ENTER("exec_relay_log_event");
/*
- We acquire this mutex since we need it for all operations except
- event execution. But we will release it in places where we will
- wait for something for example inside of next_event().
- */
+ We acquire this mutex since we need it for all operations except
+ event execution. But we will release it in places where we will
+ wait for something for example inside of next_event().
+ */
mysql_mutex_lock(&rli->data_lock);
- Log_event * ev = next_event(rli);
+ Log_event *ev= next_event(serial_rgi, &event_size);
- DBUG_ASSERT(rli->sql_thd==thd);
-
- if (sql_slave_killed(thd,rli))
+ if (sql_slave_killed(serial_rgi))
{
mysql_mutex_unlock(&rli->data_lock);
delete ev;
@@ -2777,22 +3509,45 @@ static int exec_relay_log_event(THD* thd, Relay_log_info* rli)
if (ev)
{
int exec_res;
+ Log_event_type typ= ev->get_type_code();
+
+ /*
+ Even if we don't execute this event, we keep the master timestamp,
+ so that seconds behind master shows correct delta (there are events
+ that are not replayed, so we keep falling behind).
+
+ If it is an artificial event, or a relay log event (IO thread generated
+ event) or ev->when is set to 0, we don't update the
+ last_master_timestamp.
+
+ In parallel replication, we might queue a large number of events, and
+ the user might be surprised to see a claim that the slave is up to date
+ long before those queued events are actually executed.
+ */
+ if (opt_slave_parallel_threads == 0 &&
+ !(ev->is_artificial_event() || ev->is_relay_log_event() || (ev->when == 0)))
+ {
+ rli->last_master_timestamp= ev->when + (time_t) ev->exec_time;
+ DBUG_ASSERT(rli->last_master_timestamp >= 0);
+ }
/*
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 &&
+ if ((rli->until_condition == Relay_log_info::UNTIL_MASTER_POS ||
+ rli->until_condition == Relay_log_info::UNTIL_RELAY_POS) &&
rli->is_until_satisfied(thd, ev))
{
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.
+ Setting abort_slave flag because we do not want additional
+ message about error in query execution to be printed.
*/
rli->abort_slave= 1;
+ rli->stop_for_until= true;
mysql_mutex_unlock(&rli->data_lock);
delete ev;
DBUG_RETURN(1);
@@ -2805,22 +3560,82 @@ static int exec_relay_log_event(THD* thd, Relay_log_info* rli)
read hanging if the realy log does not have any more events.
*/
DBUG_EXECUTE_IF("incomplete_group_in_relay_log",
- if ((ev->get_type_code() == XID_EVENT) ||
- ((ev->get_type_code() == QUERY_EVENT) &&
+ if ((typ == XID_EVENT) ||
+ ((typ == QUERY_EVENT) &&
strcmp("COMMIT", ((Query_log_event *) ev)->query) == 0))
{
DBUG_ASSERT(thd->transaction.all.modified_non_trans_table);
rli->abort_slave= 1;
mysql_mutex_unlock(&rli->data_lock);
delete ev;
- rli->inc_event_relay_log_pos();
+ serial_rgi->inc_event_relay_log_pos();
DBUG_RETURN(0);
};);
}
- exec_res= apply_event_and_update_pos(ev, thd, rli);
+ update_state_of_relay_log(rli, ev);
- delete_or_keep_event_post_apply(rli, ev->get_type_code(), ev);
+ if (opt_slave_parallel_threads > 0)
+ {
+ int res= rli->parallel.do_event(serial_rgi, ev, event_size);
+ if (res >= 0)
+ DBUG_RETURN(res);
+ /*
+ Else we proceed to execute the event non-parallel.
+ This is the case for pre-10.0 events without GTID, and for handling
+ slave_skip_counter.
+ */
+ }
+
+ if (typ == GTID_EVENT)
+ {
+ Gtid_log_event *gev= static_cast<Gtid_log_event *>(ev);
+
+ /*
+ For GTID, allocate a new sub_id for the given domain_id.
+ The sub_id must be allocated in increasing order of binlog order.
+ */
+ if (event_group_new_gtid(serial_rgi, gev))
+ {
+ sql_print_error("Error reading relay log event: %s", "slave SQL thread "
+ "aborted because of out-of-memory error");
+ mysql_mutex_unlock(&rli->data_lock);
+ delete ev;
+ DBUG_RETURN(1);
+ }
+
+ if (opt_gtid_ignore_duplicates)
+ {
+ int res= rpl_global_gtid_slave_state->check_duplicate_gtid
+ (&serial_rgi->current_gtid, serial_rgi);
+ if (res < 0)
+ {
+ sql_print_error("Error processing GTID event: %s", "slave SQL "
+ "thread aborted because of out-of-memory error");
+ mysql_mutex_unlock(&rli->data_lock);
+ delete ev;
+ DBUG_RETURN(1);
+ }
+ /*
+ If we need to skip this event group (because the GTID was already
+ applied), then do it using the code for slave_skip_counter, which
+ is able to handle skipping until the end of the event group.
+ */
+ if (!res)
+ rli->slave_skip_counter= 1;
+ }
+ }
+
+ serial_rgi->future_event_relay_log_pos= rli->future_event_relay_log_pos;
+ serial_rgi->event_relay_log_name= rli->event_relay_log_name;
+ serial_rgi->event_relay_log_pos= rli->event_relay_log_pos;
+ exec_res= apply_event_and_update_pos(ev, thd, serial_rgi, NULL);
+
+#ifdef WITH_WSREP
+ WSREP_DEBUG("apply_event_and_update_pos() result: %d", exec_res);
+#endif /* WITH_WSREP */
+
+ delete_or_keep_event_post_apply(serial_rgi, typ, ev);
/*
update_log_pos failed: this should not happen, so we don't
@@ -2829,6 +3644,12 @@ static int exec_relay_log_event(THD* thd, Relay_log_info* rli)
if (exec_res == 2)
DBUG_RETURN(1);
+#ifdef WITH_WSREP
+ mysql_mutex_lock(&thd->LOCK_wsrep_thd);
+ if (thd->wsrep_conflict_state == NO_CONFLICT)
+ {
+ mysql_mutex_unlock(&thd->LOCK_wsrep_thd);
+#endif /* WITH_WSREP */
if (slave_trans_retries)
{
int temp_err;
@@ -2836,6 +3657,7 @@ static int exec_relay_log_event(THD* thd, Relay_log_info* rli)
if (exec_res && (temp_err= has_temporary_error(thd)))
{
const char *errmsg;
+ rli->clear_error();
/*
We were in a transaction which has been rolled back because of a
temporary error;
@@ -2843,14 +3665,16 @@ static int exec_relay_log_event(THD* thd, Relay_log_info* rli)
Note, if lock wait timeout (innodb_lock_wait_timeout exceeded)
there is no rollback since 5.0.13 (ref: manual).
We have to not only seek but also
- a) init_master_info(), to seek back to hot relay log's start for later
- (for when we will come back to this hot log after re-processing the
- possibly existing old logs where BEGIN is: check_binlog_magic() will
- then need the cache to be at position 0 (see comments at beginning of
+
+ a) init_master_info(), to seek back to hot relay log's start
+ for later (for when we will come back to this hot log after
+ re-processing the possibly existing old logs where BEGIN is:
+ check_binlog_magic() will then need the cache to be at
+ position 0 (see comments at beginning of
init_master_info()).
b) init_relay_log_pos(), because the BEGIN may be an older relay log.
*/
- if (rli->trans_retries < slave_trans_retries)
+ if (serial_rgi->trans_retries < slave_trans_retries)
{
if (init_master_info(rli->mi, 0, 0, 0, SLAVE_SQL))
sql_print_error("Failed to initialize the master info structure");
@@ -2863,16 +3687,19 @@ static int exec_relay_log_event(THD* thd, Relay_log_info* rli)
else
{
exec_res= 0;
- rli->cleanup_context(thd, 1);
+ serial_rgi->cleanup_context(thd, 1);
/* chance for concurrent connection to get more locks */
- slave_sleep(thd, min(rli->trans_retries, MAX_SLAVE_RETRY_PAUSE),
- sql_slave_killed, rli);
+ slave_sleep(thd, MY_MIN(serial_rgi->trans_retries,
+ MAX_SLAVE_RETRY_PAUSE),
+ sql_slave_killed, serial_rgi);
+ serial_rgi->trans_retries++;
mysql_mutex_lock(&rli->data_lock); // because of SHOW STATUS
- rli->trans_retries++;
rli->retried_trans++;
+ statistic_increment(slave_retried_transactions, LOCK_status);
mysql_mutex_unlock(&rli->data_lock);
DBUG_PRINT("info", ("Slave retries transaction "
- "rli->trans_retries: %lu", rli->trans_retries));
+ "rgi->trans_retries: %lu",
+ serial_rgi->trans_retries));
}
}
else
@@ -2891,15 +3718,23 @@ static int exec_relay_log_event(THD* thd, Relay_log_info* rli)
event, the execution will proceed as usual; in the case of a
non-transient error, the slave will stop with an error.
*/
- rli->trans_retries= 0; // restart from fresh
- DBUG_PRINT("info", ("Resetting retry counter, rli->trans_retries: %lu",
- rli->trans_retries));
+ serial_rgi->trans_retries= 0; // restart from fresh
+ DBUG_PRINT("info", ("Resetting retry counter, rgi->trans_retries: %lu",
+ serial_rgi->trans_retries));
}
}
+#ifdef WITH_WSREP
+ } else {
+ mysql_mutex_unlock(&thd->LOCK_wsrep_thd);
+ }
+#endif /* WITH_WSREP */
+
+ thread_safe_increment64(&rli->executed_entries,
+ &slave_executed_entries_lock);
DBUG_RETURN(exec_res);
}
mysql_mutex_unlock(&rli->data_lock);
- rli->report(ERROR_LEVEL, ER_SLAVE_RELAY_LOG_READ_FAILURE,
+ rli->report(ERROR_LEVEL, ER_SLAVE_RELAY_LOG_READ_FAILURE, NULL,
ER(ER_SLAVE_RELAY_LOG_READ_FAILURE), "\
Could not parse relay log event entry. The possible reasons are: the master's \
binary log is corrupted (you can check this by running 'mysqlbinlog' on the \
@@ -2913,9 +3748,9 @@ on this slave.\
}
-static bool check_io_slave_killed(THD *thd, Master_info *mi, const char *info)
+static bool check_io_slave_killed(Master_info *mi, const char *info)
{
- if (io_slave_killed(thd, mi))
+ if (io_slave_killed(mi))
{
if (info && global_system_variables.log_warnings)
sql_print_information("%s", info);
@@ -2966,21 +3801,35 @@ static int try_to_reconnect(THD *thd, MYSQL *mysql, Master_info *mi,
return 1; // Don't retry forever
slave_sleep(thd, mi->connect_retry, io_slave_killed, mi);
}
- if (check_io_slave_killed(thd, mi, messages[SLAVE_RECON_MSG_KILLED_WAITING]))
+ if (check_io_slave_killed(mi, messages[SLAVE_RECON_MSG_KILLED_WAITING]))
return 1;
thd->proc_info = messages[SLAVE_RECON_MSG_AFTER];
if (!suppress_warnings)
{
char buf[256], llbuff[22];
+ String tmp;
+ if (mi->using_gtid != Master_info::USE_GTID_NO)
+ {
+ tmp.append(STRING_WITH_LEN("; GTID position '"));
+ mi->gtid_current_pos.append_to_string(&tmp);
+ if (mi->events_queued_since_last_gtid == 0)
+ tmp.append(STRING_WITH_LEN("'"));
+ else
+ {
+ tmp.append(STRING_WITH_LEN("', GTID event skip "));
+ tmp.append_ulonglong((ulonglong)mi->events_queued_since_last_gtid);
+ }
+ }
my_snprintf(buf, sizeof(buf), messages[SLAVE_RECON_MSG_FAILED],
- IO_RPL_LOG_NAME, llstr(mi->master_log_pos, llbuff));
+ IO_RPL_LOG_NAME, llstr(mi->master_log_pos, llbuff),
+ tmp.c_ptr_safe());
/*
Raise a warining during registering on master/requesting dump.
Log a message reading event.
*/
if (messages[SLAVE_RECON_MSG_COMMAND][0])
{
- mi->report(WARNING_LEVEL, ER_SLAVE_MASTER_COM_FAILURE,
+ mi->report(WARNING_LEVEL, ER_SLAVE_MASTER_COM_FAILURE, NULL,
ER(ER_SLAVE_MASTER_COM_FAILURE),
messages[SLAVE_RECON_MSG_COMMAND], buf);
}
@@ -2989,7 +3838,7 @@ static int try_to_reconnect(THD *thd, MYSQL *mysql, Master_info *mi,
sql_print_information("%s", buf);
}
}
- if (safe_reconnect(thd, mysql, mi, 1) || io_slave_killed(thd, mi))
+ if (safe_reconnect(thd, mysql, mi, 1) || io_slave_killed(mi))
{
if (global_system_variables.log_warnings)
sql_print_information("%s", messages[SLAVE_RECON_MSG_KILLED_AFTER]);
@@ -3017,6 +3866,7 @@ pthread_handler_t handle_slave_io(void *arg)
uint retry_count;
bool suppress_warnings;
int ret;
+ rpl_io_thread_info io_info;
#ifndef DBUG_OFF
uint retry_count_reg= 0, retry_count_dump= 0, retry_count_event= 0;
#endif
@@ -3044,16 +3894,17 @@ pthread_handler_t handle_slave_io(void *arg)
pthread_detach_this_thread();
thd->thread_stack= (char*) &thd; // remember where our stack is
mi->clear_error();
- if (init_slave_thread(thd, SLAVE_THD_IO))
+ if (init_slave_thread(thd, mi, SLAVE_THD_IO))
{
mysql_cond_broadcast(&mi->start_cond);
sql_print_error("Failed during slave I/O thread initialization");
goto err_during_init;
}
+ thd->system_thread_info.rpl_io_info= &io_info;
mysql_mutex_lock(&LOCK_thread_count);
threads.append(thd);
mysql_mutex_unlock(&LOCK_thread_count);
- mi->slave_running = 1;
+ mi->slave_running = MYSQL_SLAVE_RUN_NOT_CONNECT;
mi->abort_slave = 0;
mysql_mutex_unlock(&mi->run_lock);
mysql_cond_broadcast(&mi->start_cond);
@@ -3065,29 +3916,55 @@ pthread_handler_t handle_slave_io(void *arg)
/* This must be called before run any binlog_relay_io hooks */
my_pthread_setspecific_ptr(RPL_MASTER_INFO, mi);
+ /* Load the set of seen GTIDs, if we did not already. */
+ if (rpl_load_gtid_slave_state(thd))
+ {
+ mi->report(ERROR_LEVEL, thd->get_stmt_da()->sql_errno(), NULL,
+ "Unable to load replication GTID slave state from mysql.%s: %s",
+ rpl_gtid_slave_state_table_name.str,
+ thd->get_stmt_da()->message());
+ /*
+ If we are using old-style replication, we can continue, even though we
+ then will not be able to record the GTIDs we receive. But if using GTID,
+ we must give up.
+ */
+ if (mi->using_gtid != Master_info::USE_GTID_NO || opt_gtid_strict_mode)
+ goto err;
+ }
+
+
if (RUN_HOOK(binlog_relay_io, thread_start, (thd, mi)))
{
- mi->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR,
+ mi->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR, NULL,
ER(ER_SLAVE_FATAL_ERROR), "Failed to run 'thread_start' hook");
goto err;
}
if (!(mi->mysql = mysql = mysql_init(NULL)))
{
- mi->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR,
+ mi->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR, NULL,
ER(ER_SLAVE_FATAL_ERROR), "error in mysql_init()");
goto err;
}
- thd_proc_info(thd, "Connecting to master");
+ THD_STAGE_INFO(thd, stage_connecting_to_master);
// we can get killed during safe_connect
if (!safe_connect(thd, mysql, mi))
{
- sql_print_information("Slave I/O thread: connected to master '%s@%s:%d',"
- "replication started in log '%s' at position %s",
- mi->user, mi->host, mi->port,
- IO_RPL_LOG_NAME,
- llstr(mi->master_log_pos,llbuff));
+ if (mi->using_gtid == Master_info::USE_GTID_NO)
+ sql_print_information("Slave I/O thread: connected to master '%s@%s:%d',"
+ "replication started in log '%s' at position %s",
+ mi->user, mi->host, mi->port,
+ IO_RPL_LOG_NAME,
+ llstr(mi->master_log_pos,llbuff));
+ else
+ {
+ String tmp;
+ mi->gtid_current_pos.to_string(&tmp);
+ sql_print_information("Slave I/O thread: connected to master '%s@%s:%d',"
+ "replication starts at GTID position '%s'",
+ mi->user, mi->host, mi->port, tmp.c_ptr_safe());
+ }
}
else
{
@@ -3097,6 +3974,25 @@ pthread_handler_t handle_slave_io(void *arg)
connected:
+ if (mi->using_gtid != Master_info::USE_GTID_NO)
+ {
+ /*
+ When the IO thread (re)connects to the master using GTID, it will
+ connect at the start of an event group. But the IO thread may have
+ previously logged part of the following event group to the relay
+ log.
+
+ When the IO and SQL thread are started together, we erase any previous
+ relay logs, but this is not possible/desirable while the SQL thread is
+ running. To avoid duplicating partial event groups in the relay logs in
+ this case, we remember the count of events in any partially logged event
+ group before the reconnect, and then here at connect we set up a counter
+ to skip the already-logged part of the group.
+ */
+ mi->gtid_reconnect_event_skip_count= mi->events_queued_since_last_gtid;
+ mi->gtid_event_seen= false;
+ }
+
#ifdef ENABLED_DEBUG_SYNC
DBUG_EXECUTE_IF("dbug.before_get_running_status_yes",
{
@@ -3112,7 +4008,7 @@ connected:
// TODO: the assignment below should be under mutex (5.0)
mi->slave_running= MYSQL_SLAVE_RUN_CONNECT;
thd->slave_net = &mysql->net;
- thd_proc_info(thd, "Checking master version");
+ THD_STAGE_INFO(thd, stage_checking_master_version);
ret= get_master_version_and_clock(mysql, mi);
if (ret == 1)
/* Fatal error */
@@ -3120,11 +4016,14 @@ connected:
if (ret == 2)
{
- if (check_io_slave_killed(mi->io_thd, mi, "Slave I/O thread killed"
+ if (check_io_slave_killed(mi, "Slave I/O thread killed"
"while calling get_master_version_and_clock(...)"))
goto err;
suppress_warnings= FALSE;
- /* Try to reconnect because the error was caused by a transient network problem */
+ /*
+ Try to reconnect because the error was caused by a transient network
+ problem
+ */
if (try_to_reconnect(thd, mysql, mi, &retry_count, suppress_warnings,
reconnect_messages[SLAVE_RECON_ACT_REG]))
goto err;
@@ -3136,10 +4035,10 @@ connected:
/*
Register ourselves with the master.
*/
- thd_proc_info(thd, "Registering slave on master");
+ THD_STAGE_INFO(thd, stage_registering_slave_on_master);
if (register_slave_on_master(mysql, mi, &suppress_warnings))
{
- if (!check_io_slave_killed(thd, mi, "Slave I/O thread killed "
+ if (!check_io_slave_killed(mi, "Slave I/O thread killed "
"while registering slave on master"))
{
sql_print_error("Slave I/O thread couldn't register on master");
@@ -3164,16 +4063,15 @@ connected:
}
DBUG_PRINT("info",("Starting reading binary log from master"));
- while (!io_slave_killed(thd,mi))
+ while (!io_slave_killed(mi))
{
- thd_proc_info(thd, "Requesting binlog dump");
+ THD_STAGE_INFO(thd, stage_requesting_binlog_dump);
if (request_dump(thd, mysql, mi, &suppress_warnings))
{
sql_print_error("Failed on request_dump()");
- if (check_io_slave_killed(thd, mi, "Slave I/O thread killed while \
-requesting master dump") ||
- try_to_reconnect(thd, mysql, mi, &retry_count, suppress_warnings,
- reconnect_messages[SLAVE_RECON_ACT_DUMP]))
+ if (check_io_slave_killed(mi, NullS) ||
+ try_to_reconnect(thd, mysql, mi, &retry_count, suppress_warnings,
+ reconnect_messages[SLAVE_RECON_ACT_DUMP]))
goto err;
goto connected;
}
@@ -3189,8 +4087,9 @@ requesting master dump") ||
});
const char *event_buf;
+ mi->slave_running= MYSQL_SLAVE_RUN_READING;
DBUG_ASSERT(mi->last_error().number == 0);
- while (!io_slave_killed(thd,mi))
+ while (!io_slave_killed(mi))
{
ulong event_len;
/*
@@ -3199,10 +4098,9 @@ requesting master dump") ||
important thing is to not confuse users by saying "reading" whereas
we're in fact receiving nothing.
*/
- thd_proc_info(thd, "Waiting for master to send event");
+ THD_STAGE_INFO(thd, stage_waiting_for_master_to_send_event);
event_len= read_event(mysql, mi, &suppress_warnings);
- if (check_io_slave_killed(thd, mi, "Slave I/O thread killed while \
-reading event"))
+ if (check_io_slave_killed(mi, NullS))
goto err;
DBUG_EXECUTE_IF("FORCE_SLAVE_TO_RECONNECT_EVENT",
if (!retry_count_event)
@@ -3225,18 +4123,18 @@ Log entry on master is longer than slave_max_allowed_packet (%lu) on \
slave. If the entry is correct, restart the server with a higher value of \
slave_max_allowed_packet",
slave_max_allowed_packet);
- mi->report(ERROR_LEVEL, ER_NET_PACKET_TOO_LARGE,
+ mi->report(ERROR_LEVEL, ER_NET_PACKET_TOO_LARGE, NULL,
"%s", "Got a packet bigger than 'slave_max_allowed_packet' bytes");
goto err;
case ER_MASTER_FATAL_ERROR_READING_BINLOG:
- mi->report(ERROR_LEVEL, ER_MASTER_FATAL_ERROR_READING_BINLOG,
+ mi->report(ERROR_LEVEL, ER_MASTER_FATAL_ERROR_READING_BINLOG, NULL,
ER(ER_MASTER_FATAL_ERROR_READING_BINLOG),
mysql_error_number, mysql_error(mysql));
goto err;
case ER_OUT_OF_RESOURCES:
sql_print_error("\
Stopping slave I/O thread due to out-of-memory error from master");
- mi->report(ERROR_LEVEL, ER_OUT_OF_RESOURCES,
+ mi->report(ERROR_LEVEL, ER_OUT_OF_RESOURCES, NULL,
"%s", ER(ER_OUT_OF_RESOURCES));
goto err;
}
@@ -3247,13 +4145,13 @@ Stopping slave I/O thread due to out-of-memory error from master");
} // if (event_len == packet_error)
retry_count=0; // ok event, reset retry counter
- thd_proc_info(thd, "Queueing master event to the relay log");
+ THD_STAGE_INFO(thd, stage_queueing_master_event_to_the_relay_log);
event_buf= (const char*)mysql->net.read_pos + 1;
if (RUN_HOOK(binlog_relay_io, after_read_event,
(thd, mi,(const char*)mysql->net.read_pos + 1,
event_len, &event_buf, &event_len)))
{
- mi->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR,
+ mi->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR, NULL,
ER(ER_SLAVE_FATAL_ERROR),
"Failed to run 'after_read_event' hook");
goto err;
@@ -3264,7 +4162,7 @@ Stopping slave I/O thread due to out-of-memory error from master");
bool synced= 0;
if (queue_event(mi, event_buf, event_len))
{
- mi->report(ERROR_LEVEL, ER_SLAVE_RELAY_LOG_WRITE_FAILURE,
+ mi->report(ERROR_LEVEL, ER_SLAVE_RELAY_LOG_WRITE_FAILURE, NULL,
ER(ER_SLAVE_RELAY_LOG_WRITE_FAILURE),
"could not queue event from master");
goto err;
@@ -3273,13 +4171,14 @@ Stopping slave I/O thread due to out-of-memory error from master");
if (RUN_HOOK(binlog_relay_io, after_queue_event,
(thd, mi, event_buf, event_len, synced)))
{
- mi->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR,
+ mi->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR, NULL,
ER(ER_SLAVE_FATAL_ERROR),
"Failed to run 'after_queue_event' hook");
goto err;
}
- if (flush_master_info(mi, TRUE, TRUE))
+ if (mi->using_gtid == Master_info::USE_GTID_NO &&
+ flush_master_info(mi, TRUE, TRUE))
{
sql_print_error("Failed to flush master info file");
goto err;
@@ -3291,10 +4190,11 @@ Stopping slave I/O thread due to out-of-memory error from master");
- if mi->rli.ignore_log_space_limit is 1 but becomes 0 just after (so
the clean value is 0), then we are reading only one more event as we
should, and we'll block only at the next event. No big deal.
- - if mi->rli.ignore_log_space_limit is 0 but becomes 1 just after (so
- the clean value is 1), then we are going into wait_for_relay_log_space()
- for no reason, but this function will do a clean read, notice the clean
- value and exit immediately.
+ - if mi->rli.ignore_log_space_limit is 0 but becomes 1 just
+ after (so the clean value is 1), then we are going into
+ wait_for_relay_log_space() for no reason, but this function
+ will do a clean read, notice the clean value and exit
+ immediately.
*/
#ifndef DBUG_OFF
{
@@ -3322,8 +4222,19 @@ log space");
// error = 0;
err:
// print the current replication position
- sql_print_information("Slave I/O thread exiting, read up to log '%s', position %s",
- IO_RPL_LOG_NAME, llstr(mi->master_log_pos,llbuff));
+ if (mi->using_gtid == Master_info::USE_GTID_NO)
+ sql_print_information("Slave I/O thread exiting, read up to log '%s', "
+ "position %s",
+ IO_RPL_LOG_NAME, llstr(mi->master_log_pos,llbuff));
+ else
+ {
+ String tmp;
+ mi->gtid_current_pos.to_string(&tmp);
+ sql_print_information("Slave I/O thread exiting, read up to log '%s', "
+ "position %s; GTID position %s",
+ IO_RPL_LOG_NAME, llstr(mi->master_log_pos,llbuff),
+ tmp.c_ptr_safe());
+ }
RUN_HOOK(binlog_relay_io, thread_stop, (thd, mi));
thd->reset_query();
thd->reset_db(NULL, 0);
@@ -3344,7 +4255,9 @@ err:
mi->mysql=0;
}
write_ignored_events_info_to_relay_log(thd, mi);
- thd_proc_info(thd, "Waiting for slave mutex on exit");
+ if (mi->using_gtid != Master_info::USE_GTID_NO)
+ flush_master_info(mi, TRUE, TRUE);
+ THD_STAGE_INFO(thd, stage_waiting_for_slave_mutex_on_exit);
thd->add_status_to_global();
mysql_mutex_lock(&mi->run_lock);
@@ -3354,15 +4267,16 @@ err_during_init:
mi->rli.relay_log.description_event_for_queue= 0;
// TODO: make rpl_status part of Master_info
change_rpl_status(RPL_ACTIVE_SLAVE,RPL_IDLE_SLAVE);
- DBUG_ASSERT(thd->net.buff != 0);
- net_end(&thd->net); // destructor will not free it, because net.vio is 0
+
mysql_mutex_lock(&LOCK_thread_count);
thd->unlink();
mysql_mutex_unlock(&LOCK_thread_count);
- THD_CHECK_SENTRY(thd);
delete thd;
+ thread_safe_decrement32(&service_thread_count, &thread_count_lock);
+ signal_thd_deleted();
+
mi->abort_slave= 0;
- mi->slave_running= 0;
+ mi->slave_running= MYSQL_SLAVE_NOT_RUN;
mi->io_thd= 0;
/*
Note: the order of the two following calls (first broadcast, then unlock)
@@ -3385,17 +4299,33 @@ err_during_init:
/*
Check the temporary directory used by commands like
LOAD DATA INFILE.
+
+ As the directory never changes during a mysqld run, we only
+ test this once and cache the result. This also resolve a race condition
+ when this can be run by multiple threads at the same time.
*/
+
+static bool check_temp_dir_run= 0;
+static int check_temp_dir_result= 0;
+
static
int check_temp_dir(char* tmp_file)
{
- int fd;
+ File fd;
+ int result= 1; // Assume failure
MY_DIR *dirp;
char tmp_dir[FN_REFLEN];
size_t tmp_dir_size;
-
DBUG_ENTER("check_temp_dir");
+ mysql_mutex_lock(&LOCK_thread_count);
+ if (check_temp_dir_run)
+ {
+ result= check_temp_dir_result;
+ goto end;
+ }
+ check_temp_dir_run= 1;
+
/*
Get the directory from the temporary file.
*/
@@ -3405,27 +4335,122 @@ int check_temp_dir(char* tmp_file)
Check if the directory exists.
*/
if (!(dirp=my_dir(tmp_dir,MYF(MY_WME))))
- DBUG_RETURN(1);
+ goto end;
my_dirend(dirp);
/*
- Check permissions to create a file.
+ Check permissions to create a file. We use O_TRUNC to ensure that
+ things works even if we happen to have and old file laying around.
*/
if ((fd= mysql_file_create(key_file_misc,
tmp_file, CREATE_MODE,
- O_WRONLY | O_BINARY | O_EXCL | O_NOFOLLOW,
+ O_WRONLY | O_BINARY | O_TRUNC | O_NOFOLLOW,
MYF(MY_WME))) < 0)
- DBUG_RETURN(1);
+ goto end;
+ result= 0; // Directory name ok
/*
Clean up.
*/
mysql_file_close(fd, MYF(0));
mysql_file_delete(key_file_misc, tmp_file, MYF(0));
- DBUG_RETURN(0);
+end:
+ check_temp_dir_result= result;
+ mysql_mutex_unlock(&LOCK_thread_count);
+ DBUG_RETURN(result);
}
+
+void
+slave_output_error_info(rpl_group_info *rgi, THD *thd)
+{
+ /*
+ retrieve as much info as possible from the thd and, error
+ codes and warnings and print this to the error log as to
+ allow the user to locate the error
+ */
+ Relay_log_info *rli= rgi->rli;
+ uint32 const last_errno= rli->last_error().number;
+ char llbuff[22];
+
+ if (thd->is_error())
+ {
+ char const *const errmsg= thd->get_stmt_da()->message();
+
+ DBUG_PRINT("info",
+ ("thd->get_stmt_da()->sql_errno()=%d; rli->last_error.number=%d",
+ thd->get_stmt_da()->sql_errno(), last_errno));
+ if (last_errno == 0)
+ {
+ /*
+ This function is reporting an error which was not reported
+ while executing exec_relay_log_event().
+ */
+ rli->report(ERROR_LEVEL, thd->get_stmt_da()->sql_errno(),
+ rgi->gtid_info(), "%s", errmsg);
+ }
+ else if (last_errno != thd->get_stmt_da()->sql_errno())
+ {
+ /*
+ * An error was reported while executing exec_relay_log_event()
+ * however the error code differs from what is in the thread.
+ * This function prints out more information to help finding
+ * what caused the problem.
+ */
+ sql_print_error("Slave (additional info): %s Error_code: %d",
+ errmsg, thd->get_stmt_da()->sql_errno());
+ }
+ }
+
+ /* Print any warnings issued */
+ Diagnostics_area::Sql_condition_iterator it=
+ thd->get_stmt_da()->sql_conditions();
+ const Sql_condition *err;
+ /*
+ Added controlled slave thread cancel for replication
+ of user-defined variables.
+ */
+ bool udf_error = false;
+ while ((err= it++))
+ {
+ if (err->get_sql_errno() == ER_CANT_OPEN_LIBRARY)
+ udf_error = true;
+ sql_print_warning("Slave: %s Error_code: %d", err->get_message_text(), err->get_sql_errno());
+ }
+ if (udf_error)
+ {
+ String tmp;
+ if (rli->mi->using_gtid != Master_info::USE_GTID_NO)
+ {
+ tmp.append(STRING_WITH_LEN("; GTID position '"));
+ rpl_append_gtid_state(&tmp, false);
+ tmp.append(STRING_WITH_LEN("'"));
+ }
+ sql_print_error("Error loading user-defined library, slave SQL "
+ "thread aborted. Install the missing library, and restart the "
+ "slave SQL thread with \"SLAVE START\". We stopped at log '%s' "
+ "position %s%s", RPL_LOG_NAME, llstr(rli->group_master_log_pos,
+ llbuff), tmp.c_ptr_safe());
+ }
+ else
+ {
+ String tmp;
+ if (rli->mi->using_gtid != Master_info::USE_GTID_NO)
+ {
+ tmp.append(STRING_WITH_LEN("; GTID position '"));
+ rpl_append_gtid_state(&tmp, false);
+ tmp.append(STRING_WITH_LEN("'"));
+ }
+ sql_print_error("\
+Error running query, slave SQL thread aborted. Fix the problem, and restart \
+the slave SQL thread with \"SLAVE START\". We stopped at log \
+'%s' position %s%s", RPL_LOG_NAME, llstr(rli->group_master_log_pos, llbuff),
+ tmp.c_ptr_safe());
+ }
+}
+
+
/**
Slave SQL thread entry point.
@@ -3442,12 +4467,16 @@ pthread_handler_t handle_slave_sql(void *arg)
char saved_master_log_name[FN_REFLEN];
my_off_t UNINIT_VAR(saved_log_pos);
my_off_t UNINIT_VAR(saved_master_log_pos);
+ String saved_skip_gtid_pos;
my_off_t saved_skip= 0;
+ Master_info *mi= ((Master_info*)arg);
+ Relay_log_info* rli = &mi->rli;
#ifdef WITH_WSREP
my_bool wsrep_node_dropped= FALSE;
#endif /* WITH_WSREP */
- Relay_log_info* rli = &((Master_info*)arg)->rli;
const char *errmsg;
+ rpl_group_info *serial_rgi;
+ rpl_sql_thread_info sql_info(mi->rpl_filter);
// needs to call my_thread_init(), otherwise we get a coredump in DBUG_ stuff
my_thread_init();
@@ -3459,10 +4488,13 @@ pthread_handler_t handle_slave_sql(void *arg)
LINT_INIT(saved_master_log_pos);
LINT_INIT(saved_log_pos);
+ serial_rgi= new rpl_group_info(rli);
thd = new THD; // note that contructor of THD uses DBUG_ !
thd->thread_stack = (char*)&thd; // remember where our stack is
+ thd->system_thread_info.rpl_sql_info= &sql_info;
DBUG_ASSERT(rli->inited);
+ DBUG_ASSERT(rli->mi == mi);
mysql_mutex_lock(&rli->run_lock);
DBUG_ASSERT(!rli->slave_running);
errmsg= 0;
@@ -3470,36 +4502,42 @@ pthread_handler_t handle_slave_sql(void *arg)
rli->events_till_abort = abort_slave_event_count;
#endif
- rli->sql_thd= thd;
+ /*
+ THD for the sql driver thd. In parallel replication this is the thread
+ that reads things from the relay log and calls rpl_parallel::do_event()
+ to execute queries.
+
+ In single thread replication this is the THD for the thread that is
+ executing SQL queries too.
+ */
+ serial_rgi->thd= rli->sql_driver_thd= thd;
/* Inform waiting threads that slave has started */
rli->slave_run_id++;
- rli->slave_running = 1;
+ rli->slave_running= MYSQL_SLAVE_RUN_NOT_CONNECT;
pthread_detach_this_thread();
- if (init_slave_thread(thd, SLAVE_THD_SQL))
+ if (init_slave_thread(thd, mi, SLAVE_THD_SQL))
{
/*
TODO: this is currently broken - slave start and change master
will be stuck if we fail here
*/
mysql_cond_broadcast(&rli->start_cond);
- rli->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR,
+ rli->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR, NULL,
"Failed during slave thread initialization");
goto err_during_init;
}
thd->init_for_queries();
- thd->rli_slave= rli;
- if ((rli->deferred_events_collecting= rpl_filter->is_on()))
+ thd->rgi_slave= serial_rgi;
+ if ((serial_rgi->deferred_events_collecting= mi->rpl_filter->is_on()))
{
- rli->deferred_events= new Deferred_log_events(rli);
+ serial_rgi->deferred_events= new Deferred_log_events(rli);
}
- thd->temporary_tables = rli->save_temporary_tables; // restore temp tables
- set_thd_in_use_temporary_tables(rli); // (re)set sql_thd in use for saved temp tables
/*
binlog_annotate_row_events must be TRUE only after an Annotate_rows event
- has been recieved and only till the last corresponding rbr event has been
+ has been received and only till the last corresponding rbr event has been
applied. In all other cases it must be FALSE.
*/
thd->variables.binlog_annotate_row_events= 0;
@@ -3515,6 +4553,7 @@ pthread_handler_t handle_slave_sql(void *arg)
Seconds_Behind_Master grows. No big deal.
*/
rli->abort_slave = 0;
+ rli->stop_for_until= false;
mysql_mutex_unlock(&rli->run_lock);
mysql_cond_broadcast(&rli->start_cond);
@@ -3529,22 +4568,29 @@ pthread_handler_t handle_slave_sql(void *arg)
But the master timestamp is reset by RESET SLAVE & CHANGE MASTER.
*/
rli->clear_error();
+ rli->parallel.reset();
//tell the I/O thread to take relay_log_space_limit into account from now on
rli->ignore_log_space_limit= 0;
- rli->trans_retries= 0; // start from "no error"
- DBUG_PRINT("info", ("rli->trans_retries: %lu", rli->trans_retries));
+ serial_rgi->gtid_sub_id= 0;
+ serial_rgi->gtid_pending= false;
+ rli->gtid_skip_flag = GTID_SKIP_NOT;
if (init_relay_log_pos(rli,
rli->group_relay_log_name,
rli->group_relay_log_pos,
1 /*need data lock*/, &errmsg,
1 /*look for a description_event*/))
{
- rli->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR,
+ rli->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR, NULL,
"Error initializing relay log position: %s", errmsg);
goto err;
}
+ rli->reset_inuse_relaylog();
+ if (rli->alloc_inuse_relaylog(rli->group_relay_log_name))
+ goto err;
+
+ strcpy(rli->future_event_master_log_name, rli->group_master_log_name);
THD_CHECK_SENTRY(thd);
#ifndef DBUG_OFF
{
@@ -3570,7 +4616,6 @@ pthread_handler_t handle_slave_sql(void *arg)
#endif
}
#endif
- DBUG_ASSERT(rli->sql_thd == thd);
#ifdef WITH_WSREP
thd->wsrep_exec_mode= LOCAL_STATE;
@@ -3581,26 +4626,52 @@ pthread_handler_t handle_slave_sql(void *arg)
rli->group_master_log_name,
llstr(rli->group_master_log_pos,llbuff)));
if (global_system_variables.log_warnings)
+ {
+ String tmp;
+ if (mi->using_gtid != Master_info::USE_GTID_NO)
+ {
+ tmp.append(STRING_WITH_LEN("; GTID position '"));
+ rpl_append_gtid_state(&tmp,
+ mi->using_gtid==Master_info::USE_GTID_CURRENT_POS);
+ tmp.append(STRING_WITH_LEN("'"));
+ }
sql_print_information("Slave SQL thread initialized, starting replication in \
-log '%s' at position %s, relay log '%s' position: %s", RPL_LOG_NAME,
+log '%s' at position %s, relay log '%s' position: %s%s", RPL_LOG_NAME,
llstr(rli->group_master_log_pos,llbuff),rli->group_relay_log_name,
- llstr(rli->group_relay_log_pos,llbuff1));
+ llstr(rli->group_relay_log_pos,llbuff1), tmp.c_ptr_safe());
+ }
if (check_temp_dir(rli->slave_patternload_file))
{
- rli->report(ERROR_LEVEL, thd->stmt_da->sql_errno(),
+ rli->report(ERROR_LEVEL, thd->get_stmt_da()->sql_errno(), NULL,
"Unable to use slave's temporary directory %s - %s",
- slave_load_tmpdir, thd->stmt_da->message());
+ slave_load_tmpdir, thd->get_stmt_da()->message());
goto err;
}
+ /* Load the set of seen GTIDs, if we did not already. */
+ if (rpl_load_gtid_slave_state(thd))
+ {
+ rli->report(ERROR_LEVEL, thd->get_stmt_da()->sql_errno(), NULL,
+ "Unable to load replication GTID slave state from mysql.%s: %s",
+ rpl_gtid_slave_state_table_name.str,
+ thd->get_stmt_da()->message());
+ /*
+ If we are using old-style replication, we can continue, even though we
+ then will not be able to record the GTIDs we receive. But if using GTID,
+ we must give up.
+ */
+ if (mi->using_gtid != Master_info::USE_GTID_NO || opt_gtid_strict_mode)
+ goto err;
+ }
+
/* execute init_slave variable */
if (opt_init_slave.length)
{
execute_init_command(thd, &opt_init_slave, &LOCK_sys_init_slave);
if (thd->is_slave_error)
{
- rli->report(ERROR_LEVEL, thd->stmt_da->sql_errno(),
+ rli->report(ERROR_LEVEL, thd->get_stmt_da()->sql_errno(), NULL,
"Slave SQL thread aborted. Can't execute init_slave query");
goto err;
}
@@ -3617,9 +4688,16 @@ log '%s' at position %s, relay log '%s' position: %s", RPL_LOG_NAME,
strmake_buf(saved_master_log_name, rli->group_master_log_name);
saved_log_pos= rli->group_relay_log_pos;
saved_master_log_pos= rli->group_master_log_pos;
+ if (mi->using_gtid != Master_info::USE_GTID_NO)
+ {
+ saved_skip_gtid_pos.append(STRING_WITH_LEN(", GTID '"));
+ rpl_append_gtid_state(&saved_skip_gtid_pos, false);
+ saved_skip_gtid_pos.append(STRING_WITH_LEN("'; "));
+ }
saved_skip= rli->slave_skip_counter;
}
- if (rli->until_condition != Relay_log_info::UNTIL_NONE &&
+ if ((rli->until_condition == Relay_log_info::UNTIL_MASTER_POS ||
+ rli->until_condition == Relay_log_info::UNTIL_RELAY_POS) &&
rli->is_until_satisfied(thd, NULL))
{
char buf[22];
@@ -3632,118 +4710,100 @@ log '%s' at position %s, relay log '%s' position: %s", RPL_LOG_NAME,
/* Read queries from the IO/THREAD until this thread is killed */
- while (!sql_slave_killed(thd,rli))
+ while (!sql_slave_killed(serial_rgi))
{
- thd_proc_info(thd, "Reading event from the relay log");
- DBUG_ASSERT(rli->sql_thd == thd);
+ THD_STAGE_INFO(thd, stage_reading_event_from_the_relay_log);
THD_CHECK_SENTRY(thd);
if (saved_skip && rli->slave_skip_counter == 0)
{
+ String tmp;
+ if (mi->using_gtid != Master_info::USE_GTID_NO)
+ {
+ tmp.append(STRING_WITH_LEN(", GTID '"));
+ rpl_append_gtid_state(&tmp, false);
+ tmp.append(STRING_WITH_LEN("'; "));
+ }
+
sql_print_information("'SQL_SLAVE_SKIP_COUNTER=%ld' executed at "
"relay_log_file='%s', relay_log_pos='%ld', master_log_name='%s', "
- "master_log_pos='%ld' and new position at "
+ "master_log_pos='%ld'%s and new position at "
"relay_log_file='%s', relay_log_pos='%ld', master_log_name='%s', "
- "master_log_pos='%ld' ",
+ "master_log_pos='%ld'%s ",
(ulong) saved_skip, saved_log_name, (ulong) saved_log_pos,
saved_master_log_name, (ulong) saved_master_log_pos,
+ saved_skip_gtid_pos.c_ptr_safe(),
rli->group_relay_log_name, (ulong) rli->group_relay_log_pos,
- rli->group_master_log_name, (ulong) rli->group_master_log_pos);
+ rli->group_master_log_name, (ulong) rli->group_master_log_pos,
+ tmp.c_ptr_safe());
saved_skip= 0;
+ saved_skip_gtid_pos.free();
}
- if (exec_relay_log_event(thd,rli))
+ if (exec_relay_log_event(thd, rli, serial_rgi))
{
+#ifdef WITH_WSREP
+ if (thd->wsrep_conflict_state != NO_CONFLICT)
+ {
+ wsrep_node_dropped= TRUE;
+ rli->abort_slave= TRUE;
+ }
+#endif /* WITH_WSREP */
+
DBUG_PRINT("info", ("exec_relay_log_event() failed"));
// do not scare the user if SQL thread was simply killed or stopped
- if (!sql_slave_killed(thd,rli))
+ if (!sql_slave_killed(serial_rgi))
{
- /*
- retrieve as much info as possible from the thd and, error
- codes and warnings and print this to the error log as to
- allow the user to locate the error
- */
- uint32 const last_errno= rli->last_error().number;
-
- if (thd->is_error())
- {
- char const *const errmsg= thd->stmt_da->message();
-
- DBUG_PRINT("info",
- ("thd->stmt_da->sql_errno()=%d; rli->last_error.number=%d",
- thd->stmt_da->sql_errno(), last_errno));
- if (last_errno == 0)
- {
- /*
- This function is reporting an error which was not reported
- while executing exec_relay_log_event().
- */
- rli->report(ERROR_LEVEL, thd->stmt_da->sql_errno(), "%s", errmsg);
- }
- else if (last_errno != thd->stmt_da->sql_errno())
- {
- /*
- * An error was reported while executing exec_relay_log_event()
- * however the error code differs from what is in the thread.
- * This function prints out more information to help finding
- * what caused the problem.
- */
- sql_print_error("Slave (additional info): %s Error_code: %d",
- errmsg, thd->stmt_da->sql_errno());
- }
- }
-
- /* Print any warnings issued */
- List_iterator_fast<MYSQL_ERROR> it(thd->warning_info->warn_list());
- MYSQL_ERROR *err;
- /*
- Added controlled slave thread cancel for replication
- of user-defined variables.
- */
- bool udf_error = false;
- while ((err= it++))
- {
- if (err->get_sql_errno() == ER_CANT_OPEN_LIBRARY)
- udf_error = true;
- sql_print_warning("Slave: %s Error_code: %d", err->get_message_text(), err->get_sql_errno());
- }
- if (udf_error)
- sql_print_error("Error loading user-defined library, slave SQL "
- "thread aborted. Install the missing library, and restart the "
- "slave SQL thread with \"SLAVE START\". We stopped at log '%s' "
- "position %s", RPL_LOG_NAME, llstr(rli->group_master_log_pos,
- llbuff));
- else
- sql_print_error("\
-Error running query, slave SQL thread aborted. Fix the problem, and restart \
-the slave SQL thread with \"SLAVE START\". We stopped at log \
-'%s' position %s", RPL_LOG_NAME, llstr(rli->group_master_log_pos, llbuff));
+ slave_output_error_info(serial_rgi, thd);
#ifdef WITH_WSREP
+ uint32 const last_errno= rli->last_error().number;
if (WSREP_ON && last_errno == ER_UNKNOWN_COM_ERROR)
{
- wsrep_node_dropped= TRUE;
- }
+ wsrep_node_dropped= TRUE;
+ }
#endif /* WITH_WSREP */
}
goto err;
}
}
+ if (opt_slave_parallel_threads > 0)
+ rli->parallel.wait_for_done(thd, rli);
+
/* Thread stopped. Print the current replication position to the log */
- sql_print_information("Slave SQL thread exiting, replication stopped in log "
- "'%s' at position %s",
- RPL_LOG_NAME, llstr(rli->group_master_log_pos,llbuff));
+ {
+ String tmp;
+ if (mi->using_gtid != Master_info::USE_GTID_NO)
+ {
+ tmp.append(STRING_WITH_LEN("; GTID position '"));
+ rpl_append_gtid_state(&tmp, false);
+ tmp.append(STRING_WITH_LEN("'"));
+ }
+ sql_print_information("Slave SQL thread exiting, replication stopped in "
+ "log '%s' at position %s%s",
+ RPL_LOG_NAME,
+ llstr(rli->group_master_log_pos,llbuff),
+ tmp.c_ptr_safe());
+ }
err:
/*
+ Once again, in case we aborted with an error and skipped the first one.
+ (We want the first one to be before the printout of stop position to
+ get the correct position printed.)
+ */
+ if (opt_slave_parallel_threads > 0)
+ rli->parallel.wait_for_done(thd, rli);
+
+ /*
Some events set some playgrounds, which won't be cleared because thread
stops. Stopping of this thread may not be known to these events ("stop"
request is detected only by the present function, not by events), so we
must "proactively" clear playgrounds:
*/
thd->clear_error();
- rli->cleanup_context(thd, 1);
+ serial_rgi->cleanup_context(thd, 1);
/*
Some extra safety, which should not been needed (normally, event deletion
should already have done these assignments (each event which sets these
@@ -3752,63 +4812,110 @@ the slave SQL thread with \"SLAVE START\". We stopped at log \
thd->catalog= 0;
thd->reset_query();
thd->reset_db(NULL, 0);
+ if (rli->mi->using_gtid != Master_info::USE_GTID_NO)
+ {
+ ulong domain_count;
+
+ flush_relay_log_info(rli);
+ if (opt_slave_parallel_threads > 0)
+ {
+ /*
+ In parallel replication GTID mode, we may stop with different domains
+ at different positions in the relay log.
+
+ To handle this when we restart the SQL thread, mark the current
+ per-domain position in the Relay_log_info.
+ */
+ mysql_mutex_lock(&rpl_global_gtid_slave_state->LOCK_slave_state);
+ domain_count= rpl_global_gtid_slave_state->count();
+ mysql_mutex_unlock(&rpl_global_gtid_slave_state->LOCK_slave_state);
+ if (domain_count > 1)
+ {
+ inuse_relaylog *ir;
+
+ /*
+ Load the starting GTID position, so that we can skip already applied
+ GTIDs when we restart the SQL thread. And set the start position in
+ the relay log back to a known safe place to start (prior to any not
+ yet applied transaction in any domain).
+ */
+ rli->restart_gtid_pos.load(rpl_global_gtid_slave_state, NULL, 0);
+ if ((ir= rli->inuse_relaylog_list))
+ {
+ rpl_gtid *gtid= ir->relay_log_state;
+ uint32 count= ir->relay_log_state_count;
+ while (count > 0)
+ {
+ process_gtid_for_restart_pos(rli, gtid);
+ ++gtid;
+ --count;
+ }
+ strmake_buf(rli->group_relay_log_name, ir->name);
+ rli->group_relay_log_pos= BIN_LOG_HEADER_SIZE;
+ rli->relay_log_state.load(ir->relay_log_state, ir->relay_log_state_count);
+ }
+ }
+ }
+ }
+ THD_STAGE_INFO(thd, stage_waiting_for_slave_mutex_on_exit);
thd->add_status_to_global();
- thd_proc_info(thd, "Waiting for slave mutex on exit");
mysql_mutex_lock(&rli->run_lock);
err_during_init:
/* We need data_lock, at least to wake up any waiting master_pos_wait() */
mysql_mutex_lock(&rli->data_lock);
- DBUG_ASSERT(rli->slave_running == 1); // tracking buffer overrun
+ DBUG_ASSERT(rli->slave_running == MYSQL_SLAVE_RUN_NOT_CONNECT); // tracking buffer overrun
/* When master_pos_wait() wakes up it will check this and terminate */
- rli->slave_running= 0;
+ rli->slave_running= MYSQL_SLAVE_NOT_RUN;
/* Forget the relay log's format */
delete rli->relay_log.description_event_for_exec;
rli->relay_log.description_event_for_exec= 0;
+ rli->reset_inuse_relaylog();
/* Wake up master_pos_wait() */
mysql_mutex_unlock(&rli->data_lock);
DBUG_PRINT("info",("Signaling possibly waiting master_pos_wait() functions"));
mysql_cond_broadcast(&rli->data_cond);
rli->ignore_log_space_limit= 0; /* don't need any lock */
/* we die so won't remember charset - re-update them on next thread start */
- rli->cached_charset_invalidate();
- rli->save_temporary_tables = thd->temporary_tables;
+ thd->system_thread_info.rpl_sql_info->cached_charset_invalidate();
/*
TODO: see if we can do this conditionally in next_event() instead
to avoid unneeded position re-init
*/
thd->temporary_tables = 0; // remove tempation from destructor to close them
- DBUG_ASSERT(thd->net.buff != 0);
- net_end(&thd->net); // destructor will not free it, because we are weird
- DBUG_ASSERT(rli->sql_thd == thd);
THD_CHECK_SENTRY(thd);
- rli->sql_thd= 0;
- set_thd_in_use_temporary_tables(rli); // (re)set sql_thd in use for saved temp tables
+ rli->sql_driver_thd= 0;
mysql_mutex_lock(&LOCK_thread_count);
- THD_CHECK_SENTRY(thd);
- delete thd;
+ thd->rgi_fake= thd->rgi_slave= NULL;
+ delete serial_rgi;
mysql_mutex_unlock(&LOCK_thread_count);
+
#ifdef WITH_WSREP
- /* if slave stopped due to node going non primary, we set global flag to
- trigger automatic restart of slave when node joins back to cluster
+ /*
+ If slave stopped due to node going non primary, we set global flag to
+ trigger automatic restart of slave when node joins back to cluster.
*/
- if (wsrep_node_dropped && wsrep_restart_slave)
- {
- if (wsrep_ready)
- {
- WSREP_INFO("Slave error due to node temporarily non-primary"
- "SQL slave will continue");
- wsrep_node_dropped= FALSE;
- mysql_mutex_unlock(&rli->run_lock);
- goto wsrep_restart_point;
- } else {
- WSREP_INFO("Slave error due to node going non-primary");
- WSREP_INFO("wsrep_restart_slave was set and therefore slave will be "
- "automatically restarted when node joins back to cluster");
- wsrep_restart_slave_activated= TRUE;
- }
- }
+ if (wsrep_node_dropped && wsrep_restart_slave)
+ {
+ if (wsrep_ready)
+ {
+ WSREP_INFO("Slave error due to node temporarily non-primary"
+ "SQL slave will continue");
+ wsrep_node_dropped= FALSE;
+ mysql_mutex_unlock(&rli->run_lock);
+ WSREP_DEBUG("wsrep_conflict_state now: %d", thd->wsrep_conflict_state);
+ WSREP_INFO("slave restart: %d", thd->wsrep_conflict_state);
+ thd->wsrep_conflict_state= NO_CONFLICT;
+ goto wsrep_restart_point;
+ } else {
+ WSREP_INFO("Slave error due to node going non-primary");
+ WSREP_INFO("wsrep_restart_slave was set and therefore slave will be "
+ "automatically restarted when node joins back to cluster.");
+ wsrep_restart_slave_activated= TRUE;
+ }
+ }
#endif /* WITH_WSREP */
+
/*
Note: the order of the broadcast and unlock calls below (first broadcast, then unlock)
is important. Otherwise a killer_thread can execute between the calls and
@@ -3818,6 +4925,23 @@ err_during_init:
DBUG_EXECUTE_IF("simulate_slave_delay_at_terminate_bug38694", sleep(5););
mysql_mutex_unlock(&rli->run_lock); // tell the world we are done
+ /*
+ Deactivate the parallel replication thread pool, if there are now no more
+ SQL threads running. Do this here, when we have released all locks, but
+ while our THD (and current_thd) is still valid.
+ */
+ mysql_mutex_lock(&LOCK_active_mi);
+ if (opt_slave_parallel_threads > 0 &&
+ !master_info_index->any_slave_sql_running())
+ rpl_parallel_inactivate_pool(&global_rpl_thread_pool);
+ mysql_mutex_unlock(&LOCK_active_mi);
+
+ mysql_mutex_lock(&LOCK_thread_count);
+ delete thd;
+ mysql_mutex_unlock(&LOCK_thread_count);
+ thread_safe_decrement32(&service_thread_count, &thread_count_lock);
+ signal_thd_deleted();
+
DBUG_LEAVE; // Must match DBUG_ENTER()
my_thread_end();
#ifdef HAVE_OPENSSL
@@ -3844,14 +4968,14 @@ static int process_io_create_file(Master_info* mi, Create_file_log_event* cev)
if (unlikely(!cev->is_valid()))
DBUG_RETURN(1);
- if (!rpl_filter->db_ok(cev->db))
+ if (!mi->rpl_filter->db_ok(cev->db))
{
skip_load_data_infile(net);
DBUG_RETURN(0);
}
DBUG_ASSERT(cev->inited_from_old);
thd->file_id = cev->file_id = mi->file_id++;
- thd->server_id = cev->server_id;
+ thd->variables.server_id = cev->server_id;
cev_not_written = 1;
if (unlikely(net_request_file(net,cev->fname)))
@@ -3893,7 +5017,7 @@ static int process_io_create_file(Master_info* mi, Create_file_log_event* cev)
xev.log_pos = cev->log_pos;
if (unlikely(mi->rli.relay_log.append(&xev)))
{
- mi->report(ERROR_LEVEL, ER_SLAVE_RELAY_LOG_WRITE_FAILURE,
+ mi->report(ERROR_LEVEL, ER_SLAVE_RELAY_LOG_WRITE_FAILURE, NULL,
ER(ER_SLAVE_RELAY_LOG_WRITE_FAILURE),
"error writing Exec_load event to relay log");
goto err;
@@ -3907,7 +5031,7 @@ static int process_io_create_file(Master_info* mi, Create_file_log_event* cev)
cev->block_len = num_bytes;
if (unlikely(mi->rli.relay_log.append(cev)))
{
- mi->report(ERROR_LEVEL, ER_SLAVE_RELAY_LOG_WRITE_FAILURE,
+ mi->report(ERROR_LEVEL, ER_SLAVE_RELAY_LOG_WRITE_FAILURE, NULL,
ER(ER_SLAVE_RELAY_LOG_WRITE_FAILURE),
"error writing Create_file event to relay log");
goto err;
@@ -3922,7 +5046,7 @@ static int process_io_create_file(Master_info* mi, Create_file_log_event* cev)
aev.log_pos = cev->log_pos;
if (unlikely(mi->rli.relay_log.append(&aev)))
{
- mi->report(ERROR_LEVEL, ER_SLAVE_RELAY_LOG_WRITE_FAILURE,
+ mi->report(ERROR_LEVEL, ER_SLAVE_RELAY_LOG_WRITE_FAILURE, NULL,
ER(ER_SLAVE_RELAY_LOG_WRITE_FAILURE),
"error writing Append_block event to relay log");
goto err;
@@ -4029,7 +5153,7 @@ static int queue_binlog_ver_1_event(Master_info *mi, const char *buf,
{
if (unlikely(!(tmp_buf=(char*)my_malloc(event_len+1,MYF(MY_WME)))))
{
- mi->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR,
+ mi->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR, NULL,
ER(ER_SLAVE_FATAL_ERROR), "Memory allocation failed");
DBUG_RETURN(1);
}
@@ -4234,6 +5358,10 @@ static int queue_event(Master_info* mi,const char* buf, ulong event_len)
mysql_mutex_t *log_lock= rli->relay_log.get_log_lock();
ulong s_id;
bool unlock_data_lock= TRUE;
+ bool gtid_skip_enqueue= false;
+ bool got_gtid_event= false;
+ rpl_gtid event_gtid;
+
/*
FD_q must have been prepared for the first R_a event
inside get_master_version_and_clock()
@@ -4296,8 +5424,6 @@ static int queue_event(Master_info* mi,const char* buf, ulong event_len)
goto err;
}
- LINT_INIT(inc_pos);
-
if (mi->rli.relay_log.description_event_for_queue->binlog_version<4 &&
(uchar)buf[EVENT_TYPE_OFFSET] != FORMAT_DESCRIPTION_EVENT /* a way to escape */)
DBUG_RETURN(queue_old_event(mi,buf,event_len));
@@ -4325,6 +5451,86 @@ static int queue_event(Master_info* mi,const char* buf, ulong event_len)
event_len - BINLOG_CHECKSUM_LEN : event_len,
mi->rli.relay_log.description_event_for_queue);
+ if (unlikely(mi->gtid_reconnect_event_skip_count) &&
+ unlikely(!mi->gtid_event_seen) &&
+ rev.is_artificial_event() &&
+ (mi->prev_master_id != mi->master_id ||
+ strcmp(rev.new_log_ident, mi->master_log_name) != 0))
+ {
+ /*
+ Artificial Rotate_log_event is the first event we receive at the start
+ of each master binlog file. It gives the name of the new binlog file.
+
+ Normally, we already have this name from the real rotate event at the
+ end of the previous binlog file (unless we are making a new connection
+ using GTID). But if the master server restarted/crashed, there is no
+ rotate event at the end of the prior binlog file, so the name is new.
+
+ We use this fact to handle a special case of master crashing. If the
+ master crashed while writing the binlog, it might end with a partial
+ event group lacking the COMMIT/XID event, which must be rolled
+ back. If the slave IO thread happens to get a disconnect in the middle
+ of exactly this event group, it will try to reconnect at the same GTID
+ and skip already fetched events. However, that GTID did not commit on
+ the master before the crash, so it does not really exist, and the
+ master will connect the slave at the next following GTID starting in
+ the next binlog. This could confuse the slave and make it mix the
+ start of one event group with the end of another.
+
+ But we detect this case here, by noticing the change of binlog name
+ which detects the missing rotate event at the end of the previous
+ binlog file. In this case, we reset the counters to make us not skip
+ the next event group, and queue an artificial Format Description
+ event. The previously fetched incomplete event group will then be
+ rolled back when the Format Description event is executed by the SQL
+ thread.
+
+ A similar case is if the reconnect somehow connects to a different
+ master server (like due to a network proxy or IP address takeover).
+ We detect this case by noticing a change of server_id and in this
+ case likewise rollback the partially received event group.
+ */
+ Format_description_log_event fdle(4);
+
+ if (mi->prev_master_id != mi->master_id)
+ sql_print_warning("The server_id of master server changed in the "
+ "middle of GTID %u-%u-%llu. Assuming a change of "
+ "master server, so rolling back the previously "
+ "received partial transaction. Expected: %lu, "
+ "received: %lu", mi->last_queued_gtid.domain_id,
+ mi->last_queued_gtid.server_id,
+ mi->last_queued_gtid.seq_no,
+ mi->prev_master_id, mi->master_id);
+ else if (strcmp(rev.new_log_ident, mi->master_log_name) != 0)
+ sql_print_warning("Unexpected change of master binlog file name in the "
+ "middle of GTID %u-%u-%llu, assuming that master has "
+ "crashed and rolling back the transaction. Expected: "
+ "'%s', received: '%s'",
+ mi->last_queued_gtid.domain_id,
+ mi->last_queued_gtid.server_id,
+ mi->last_queued_gtid.seq_no,
+ mi->master_log_name, rev.new_log_ident);
+
+ mysql_mutex_lock(log_lock);
+ if (likely(!fdle.write(rli->relay_log.get_log_file()) &&
+ !rli->relay_log.flush_and_sync(NULL)))
+ {
+ rli->relay_log.harvest_bytes_written(&rli->log_space_total);
+ }
+ else
+ {
+ error= ER_SLAVE_RELAY_LOG_WRITE_FAILURE;
+ mysql_mutex_unlock(log_lock);
+ goto err;
+ }
+ rli->relay_log.signal_update();
+ mysql_mutex_unlock(log_lock);
+
+ mi->gtid_reconnect_event_skip_count= 0;
+ mi->events_queued_since_last_gtid= 0;
+ }
+ mi->prev_master_id= mi->master_id;
+
if (unlikely(process_io_rotate(mi, &rev)))
{
error= ER_SLAVE_RELAY_LOG_WRITE_FAILURE;
@@ -4346,7 +5552,7 @@ static int queue_event(Master_info* mi,const char* buf, ulong event_len)
if (uint4korr(&buf[0]) == 0 && checksum_alg == BINLOG_CHECKSUM_ALG_OFF &&
mi->rli.relay_log.relay_log_checksum_alg != BINLOG_CHECKSUM_ALG_OFF)
{
- ha_checksum rot_crc= my_checksum(0L, NULL, 0);
+ ha_checksum rot_crc= 0;
event_len += BINLOG_CHECKSUM_LEN;
memcpy(rot_buf, buf, event_len - BINLOG_CHECKSUM_LEN);
int4store(&rot_buf[EVENT_LEN_OFFSET],
@@ -4421,6 +5627,19 @@ static int queue_event(Master_info* mi,const char* buf, ulong event_len)
mi->rli.relay_log.relay_log_checksum_alg= tmp->checksum_alg;
/*
+ Do not queue any format description event that we receive after a
+ reconnect where we are skipping over a partial event group received
+ before the reconnect.
+
+ (If we queued such an event, and it was the first format_description
+ event after master restart, the slave SQL thread would think that
+ the partial event group before it in the relay log was from a
+ previous master crash and should be rolled back).
+ */
+ if (unlikely(mi->gtid_reconnect_event_skip_count && !mi->gtid_event_seen))
+ gtid_skip_enqueue= true;
+
+ /*
Though this does some conversion to the slave's format, this will
preserve the master's binlog format version, and number of event types.
*/
@@ -4463,16 +5682,18 @@ static int queue_event(Master_info* mi,const char* buf, ulong event_len)
Heartbeat is sent only after an event corresponding to the corrdinates
the heartbeat carries.
- Slave can not have a difference in coordinates except in the only
+ Slave can not have a higher coordinate except in the only
special case when mi->master_log_name, master_log_pos have never
been updated by Rotate event i.e when slave does not have any history
with the master (and thereafter mi->master_log_pos is NULL).
+ Slave can have lower coordinates, if some event from master was omitted.
+
TODO: handling `when' for SHOW SLAVE STATUS' snds behind
*/
if ((memcmp(mi->master_log_name, hb.get_log_ident(), hb.get_ident_len())
&& mi->master_log_name != NULL)
- || mi->master_log_pos != hb.log_pos)
+ || mi->master_log_pos > hb.log_pos)
{
/* missed events of heartbeat from the past */
error= ER_SLAVE_HEARTBEAT_FAILURE;
@@ -4484,11 +5705,160 @@ static int queue_event(Master_info* mi,const char* buf, ulong event_len)
error_msg.append(llbuf, strlen(llbuf));
goto err;
}
+
+ /*
+ Heartbeat events doesn't count in the binlog size, so we don't have to
+ increment mi->master_log_pos
+ */
goto skip_relay_logging;
}
break;
+ case GTID_LIST_EVENT:
+ {
+ const char *errmsg;
+ Gtid_list_log_event *glev;
+ Log_event *tmp;
+ uint32 flags;
+
+ if (!(tmp= Log_event::read_log_event(buf, event_len, &errmsg,
+ mi->rli.relay_log.description_event_for_queue,
+ opt_slave_sql_verify_checksum)))
+ {
+ error= ER_SLAVE_RELAY_LOG_WRITE_FAILURE;
+ goto err;
+ }
+ glev= static_cast<Gtid_list_log_event *>(tmp);
+ event_pos= glev->log_pos;
+ flags= glev->gl_flags;
+ delete glev;
+
+ /*
+ We use fake Gtid_list events to update the old-style position (among
+ other things).
+
+ Early code created fake Gtid_list events with zero log_pos, those should
+ not modify old-style position.
+ */
+ if (event_pos == 0 || event_pos <= mi->master_log_pos)
+ inc_pos= 0;
+ else
+ inc_pos= event_pos - mi->master_log_pos;
+
+ if (mi->rli.until_condition == Relay_log_info::UNTIL_GTID &&
+ flags & Gtid_list_log_event::FLAG_UNTIL_REACHED)
+ {
+ char str_buf[128];
+ String str(str_buf, sizeof(str_buf), system_charset_info);
+ mi->rli.until_gtid_pos.to_string(&str);
+ sql_print_information("Slave I/O thread stops because it reached its"
+ " UNTIL master_gtid_pos %s", str.c_ptr_safe());
+ mi->abort_slave= true;
+ }
+ }
+ break;
+
+ case GTID_EVENT:
+ {
+ uchar gtid_flag;
+
+ if (Gtid_log_event::peek(buf, event_len, checksum_alg,
+ &event_gtid.domain_id, &event_gtid.server_id,
+ &event_gtid.seq_no, &gtid_flag,
+ rli->relay_log.description_event_for_queue))
+ {
+ error= ER_SLAVE_RELAY_LOG_WRITE_FAILURE;
+ goto err;
+ }
+ got_gtid_event= true;
+ if (mi->using_gtid == Master_info::USE_GTID_NO)
+ goto default_action;
+ if (unlikely(!mi->gtid_event_seen))
+ {
+ mi->gtid_event_seen= true;
+ if (mi->gtid_reconnect_event_skip_count)
+ {
+ /*
+ If we are reconnecting, and we need to skip a partial event group
+ already queued to the relay log before the reconnect, then we check
+ that we actually get the same event group (same GTID) as before, so
+ we do not end up with half of one group and half another.
+
+ The only way we should be able to receive a different GTID than what
+ we expect is if the binlog on the master (or more likely the whole
+ master server) was replaced with a different one, on the same IP
+ address, _and_ the new master happens to have domains in a different
+ order so we get the GTID from a different domain first. Still, it is
+ best to protect against this case.
+ */
+ if (event_gtid.domain_id != mi->last_queued_gtid.domain_id ||
+ event_gtid.server_id != mi->last_queued_gtid.server_id ||
+ event_gtid.seq_no != mi->last_queued_gtid.seq_no)
+ {
+ bool first;
+ error= ER_SLAVE_UNEXPECTED_MASTER_SWITCH;
+ error_msg.append(STRING_WITH_LEN("Expected: "));
+ first= true;
+ rpl_slave_state_tostring_helper(&error_msg, &mi->last_queued_gtid,
+ &first);
+ error_msg.append(STRING_WITH_LEN(", received: "));
+ first= true;
+ rpl_slave_state_tostring_helper(&error_msg, &event_gtid, &first);
+ goto err;
+ }
+ }
+ }
+
+ if (unlikely(mi->gtid_reconnect_event_skip_count))
+ {
+ goto default_action;
+ }
+
+ /*
+ We have successfully queued to relay log everything before this GTID, so
+ in case of reconnect we can start from after any previous GTID.
+ (Normally we would have updated gtid_current_pos earlier at the end of
+ the previous event group, but better leave an extra check here for
+ safety).
+ */
+ if (mi->events_queued_since_last_gtid)
+ {
+ mi->gtid_current_pos.update(&mi->last_queued_gtid);
+ mi->events_queued_since_last_gtid= 0;
+ }
+ mi->last_queued_gtid= event_gtid;
+ mi->last_queued_gtid_standalone=
+ (gtid_flag & Gtid_log_event::FL_STANDALONE) != 0;
+ ++mi->events_queued_since_last_gtid;
+ inc_pos= event_len;
+ }
+ break;
+
+#ifndef DBUG_OFF
+ case XID_EVENT:
+ DBUG_EXECUTE_IF("slave_discard_xid_for_gtid_0_x_1000",
+ {
+ /* Inject an event group that is missing its XID commit event. */
+ if (mi->last_queued_gtid.domain_id == 0 &&
+ mi->last_queued_gtid.seq_no == 1000)
+ goto skip_relay_logging;
+ });
+ /* Fall through to default case ... */
+#endif
+
default:
+ default_action:
+ if (mi->using_gtid != Master_info::USE_GTID_NO && mi->gtid_event_seen)
+ {
+ if (unlikely(mi->gtid_reconnect_event_skip_count))
+ {
+ --mi->gtid_reconnect_event_skip_count;
+ gtid_skip_enqueue= true;
+ }
+ else if (mi->events_queued_since_last_gtid)
+ ++mi->events_queued_since_last_gtid;
+ }
+
inc_pos= event_len;
break;
}
@@ -4524,7 +5894,52 @@ static int queue_event(Master_info* mi,const char* buf, ulong event_len)
mysql_mutex_lock(log_lock);
s_id= uint4korr(buf + SERVER_ID_OFFSET);
- if ((s_id == ::server_id && !mi->rli.replicate_same_server_id) ||
+ /*
+ Write the event to the relay log, unless we reconnected in the middle
+ of an event group and now need to skip the initial part of the group that
+ we already wrote before reconnecting.
+ */
+ if (unlikely(gtid_skip_enqueue))
+ {
+ mi->master_log_pos+= inc_pos;
+ if ((uchar)buf[EVENT_TYPE_OFFSET] == FORMAT_DESCRIPTION_EVENT &&
+ s_id == mi->master_id)
+ {
+ /*
+ If we write this master's description event in the middle of an event
+ group due to GTID reconnect, SQL thread will think that master crashed
+ in the middle of the group and roll back the first half, so we must not.
+
+ But we still have to write an artificial copy of the masters description
+ event, to override the initial slave-version description event so that
+ SQL thread has the right information for parsing the events it reads.
+ */
+ rli->relay_log.description_event_for_queue->created= 0;
+ rli->relay_log.description_event_for_queue->set_artificial_event();
+ if (rli->relay_log.append_no_lock
+ (rli->relay_log.description_event_for_queue))
+ error= ER_SLAVE_RELAY_LOG_WRITE_FAILURE;
+ else
+ rli->relay_log.harvest_bytes_written(&rli->log_space_total);
+ }
+ else if (mi->gtid_reconnect_event_skip_count == 0)
+ {
+ /*
+ Add a fake rotate event so that SQL thread can see the old-style
+ position where we re-connected in the middle of a GTID event group.
+ */
+ Rotate_log_event fake_rev(mi->master_log_name, 0, mi->master_log_pos, 0);
+ fake_rev.server_id= mi->master_id;
+ if (rli->relay_log.append_no_lock(&fake_rev))
+ error= ER_SLAVE_RELAY_LOG_WRITE_FAILURE;
+ else
+ rli->relay_log.harvest_bytes_written(&rli->log_space_total);
+ }
+ }
+ else
+ if ((s_id == global_system_variables.server_id &&
+ !mi->rli.replicate_same_server_id) ||
+ event_that_should_be_ignored(buf) ||
/*
the following conjunction deals with IGNORE_SERVER_IDS, if set
If the master is on the ignore list, execution of
@@ -4555,7 +5970,8 @@ static int queue_event(Master_info* mi,const char* buf, ulong event_len)
IGNORE_SERVER_IDS it increments mi->master_log_pos
as well as rli->group_relay_log_pos.
*/
- if (!(s_id == ::server_id && !mi->rli.replicate_same_server_id) ||
+ if (!(s_id == global_system_variables.server_id &&
+ !mi->rli.replicate_same_server_id) ||
(buf[EVENT_TYPE_OFFSET] != FORMAT_DESCRIPTION_EVENT &&
buf[EVENT_TYPE_OFFSET] != ROTATE_EVENT &&
buf[EVENT_TYPE_OFFSET] != STOP_EVENT))
@@ -4564,6 +5980,8 @@ static int queue_event(Master_info* mi,const char* buf, ulong event_len)
memcpy(rli->ign_master_log_name_end, mi->master_log_name, FN_REFLEN);
DBUG_ASSERT(rli->ign_master_log_name_end[0]);
rli->ign_master_log_pos_end= mi->master_log_pos;
+ if (got_gtid_event)
+ rli->ign_gtids.update(&event_gtid);
}
rli->relay_log.signal_update(); // the slave SQL thread needs to re-check
DBUG_PRINT("info", ("master_log_pos: %lu, event originating from %u server, ignored",
@@ -4571,7 +5989,6 @@ static int queue_event(Master_info* mi,const char* buf, ulong event_len)
}
else
{
- /* write the event to the relay log */
if (likely(!(rli->relay_log.appendv(buf,event_len,0))))
{
mi->master_log_pos+= inc_pos;
@@ -4583,11 +6000,33 @@ static int queue_event(Master_info* mi,const char* buf, ulong event_len)
error= ER_SLAVE_RELAY_LOG_WRITE_FAILURE;
}
rli->ign_master_log_name_end[0]= 0; // last event is not ignored
+ if (got_gtid_event)
+ rli->ign_gtids.remove_if_present(&event_gtid);
if (save_buf != NULL)
buf= save_buf;
}
mysql_mutex_unlock(log_lock);
+ if (!error &&
+ mi->using_gtid != Master_info::USE_GTID_NO &&
+ mi->events_queued_since_last_gtid > 0 &&
+ ( (mi->last_queued_gtid_standalone &&
+ !Log_event::is_part_of_group((Log_event_type)(uchar)
+ buf[EVENT_TYPE_OFFSET])) ||
+ (!mi->last_queued_gtid_standalone &&
+ ((uchar)buf[EVENT_TYPE_OFFSET] == XID_EVENT ||
+ ((uchar)buf[EVENT_TYPE_OFFSET] == QUERY_EVENT &&
+ Query_log_event::peek_is_commit_rollback(buf, event_len,
+ checksum_alg))))))
+ {
+ /*
+ The whole of the current event group is queued. So in case of
+ reconnect we can start from after the current GTID.
+ */
+ mi->gtid_current_pos.update(&mi->last_queued_gtid);
+ mi->events_queued_since_last_gtid= 0;
+ }
+
skip_relay_logging:
err:
@@ -4595,7 +6034,7 @@ err:
mysql_mutex_unlock(&mi->data_lock);
DBUG_PRINT("info", ("error: %d", error));
if (error)
- mi->report(ERROR_LEVEL, error, ER(error),
+ mi->report(ERROR_LEVEL, error, NULL, ER(error),
(error == ER_SLAVE_RELAY_LOG_WRITE_FAILURE)?
"could not queue event from master" :
error_msg.ptr());
@@ -4696,17 +6135,20 @@ static int connect_to_master(THD* thd, MYSQL* mysql, Master_info* mi,
int last_errno= -2; // impossible error
ulong err_count=0;
char llbuff[22];
+ my_bool my_true= 1;
DBUG_ENTER("connect_to_master");
set_slave_max_allowed_packet(thd, mysql);
#ifndef DBUG_OFF
mi->events_till_disconnect = disconnect_slave_event_count;
#endif
- ulong client_flag= CLIENT_REMEMBER_OPTIONS;
+ ulong client_flag= 0;
if (opt_slave_compressed_protocol)
client_flag=CLIENT_COMPRESS; /* We will use compression */
mysql_options(mysql, MYSQL_OPT_CONNECT_TIMEOUT, (char *) &slave_net_timeout);
mysql_options(mysql, MYSQL_OPT_READ_TIMEOUT, (char *) &slave_net_timeout);
+ mysql_options(mysql, MYSQL_OPT_USE_THREAD_SPECIFIC_MEMORY,
+ (char*) &my_true);
#ifdef HAVE_OPENSSL
if (mi->ssl)
@@ -4719,6 +6161,10 @@ static int connect_to_master(THD* thd, MYSQL* mysql, Master_info* mi,
mi->ssl_cipher[0]?mi->ssl_cipher:0);
mysql_options(mysql, MYSQL_OPT_SSL_VERIFY_SERVER_CERT,
&mi->ssl_verify_server_cert);
+ mysql_options(mysql, MYSQL_OPT_SSL_CRLPATH,
+ mi->ssl_crlpath[0] ? mi->ssl_crlpath : 0);
+ mysql_options(mysql, MYSQL_OPT_SSL_VERIFY_SERVER_CERT,
+ &mi->ssl_verify_server_cert);
}
#endif
@@ -4749,14 +6195,14 @@ static int connect_to_master(THD* thd, MYSQL* mysql, Master_info* mi,
/* we disallow empty users */
if (mi->user == NULL || mi->user[0] == 0)
{
- mi->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR,
+ mi->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR, NULL,
ER(ER_SLAVE_FATAL_ERROR),
"Invalid (empty) username when attempting to "
"connect to the master server. Connection attempt "
"terminated.");
DBUG_RETURN(1);
}
- while (!(slave_was_killed = io_slave_killed(thd,mi)) &&
+ while (!(slave_was_killed = io_slave_killed(mi)) &&
(reconnect ? mysql_reconnect(mysql) != 0 :
mysql_real_connect(mysql, mi->host, mi->user, mi->password, 0,
mi->port, 0, client_flag) == 0))
@@ -4766,7 +6212,7 @@ static int connect_to_master(THD* thd, MYSQL* mysql, Master_info* mi,
{
last_errno=mysql_errno(mysql);
suppress_warnings= 0;
- mi->report(ERROR_LEVEL, last_errno,
+ mi->report(ERROR_LEVEL, last_errno, NULL,
"error %s to master '%s@%s:%d'"
" - retry-time: %d retries: %lu message: %s",
(reconnect ? "reconnecting" : "connecting"),
@@ -4834,18 +6280,20 @@ static int safe_reconnect(THD* thd, MYSQL* mysql, Master_info* mi,
}
+#ifdef NOT_USED
MYSQL *rpl_connect_master(MYSQL *mysql)
{
- THD *thd= current_thd;
Master_info *mi= my_pthread_getspecific_ptr(Master_info*, RPL_MASTER_INFO);
+ bool allocated= false;
+ my_bool my_true= 1;
+ THD *thd;
+
if (!mi)
{
sql_print_error("'rpl_connect_master' must be called in slave I/O thread context.");
return NULL;
}
-
- bool allocated= false;
-
+ thd= mi->io_thd;
if (!mysql)
{
if(!(mysql= mysql_init(NULL)))
@@ -4865,6 +6313,8 @@ MYSQL *rpl_connect_master(MYSQL *mysql)
*/
mysql_options(mysql, MYSQL_OPT_CONNECT_TIMEOUT, (char *) &slave_net_timeout);
mysql_options(mysql, MYSQL_OPT_READ_TIMEOUT, (char *) &slave_net_timeout);
+ mysql_options(mysql, MYSQL_OPT_USE_THREAD_SPECIFIC_MEMORY,
+ (char*) &my_true);
#ifdef HAVE_OPENSSL
if (mi->ssl)
@@ -4886,11 +6336,11 @@ MYSQL *rpl_connect_master(MYSQL *mysql)
if (mi->user == NULL
|| mi->user[0] == 0
- || io_slave_killed(thd, mi)
+ || io_slave_killed( mi)
|| !mysql_real_connect(mysql, mi->host, mi->user, mi->password, 0,
mi->port, 0, 0))
{
- if (!io_slave_killed(thd, mi))
+ if (!io_slave_killed( mi))
sql_print_error("rpl_connect_master: error connecting to master: %s (server_error: %d)",
mysql_error(mysql), mysql_errno(mysql));
@@ -4900,6 +6350,7 @@ MYSQL *rpl_connect_master(MYSQL *mysql)
}
return mysql;
}
+#endif
/*
Store the file and position where the execute-slave thread are in the
@@ -4990,7 +6441,7 @@ static IO_CACHE *reopen_relay_log(Relay_log_info *rli, const char **errmsg)
relay_log_pos Current log pos
pending Number of bytes already processed from the event
*/
- rli->event_relay_log_pos= max(rli->event_relay_log_pos, BIN_LOG_HEADER_SIZE);
+ rli->event_relay_log_pos= MY_MAX(rli->event_relay_log_pos, BIN_LOG_HEADER_SIZE);
my_b_seek(cur_log,rli->event_relay_log_pos);
DBUG_RETURN(cur_log);
}
@@ -5005,17 +6456,20 @@ static IO_CACHE *reopen_relay_log(Relay_log_info *rli, const char **errmsg)
@return The event read, or NULL on error. If an error occurs, the
error is reported through the sql_print_information() or
sql_print_error() functions.
+
+ The size of the read event (in bytes) is returned in *event_size.
*/
-static Log_event* next_event(Relay_log_info* rli)
+static Log_event* next_event(rpl_group_info *rgi, ulonglong *event_size)
{
Log_event* ev;
+ Relay_log_info *rli= rgi->rli;
IO_CACHE* cur_log = rli->cur_log;
mysql_mutex_t *log_lock = rli->relay_log.get_log_lock();
const char* errmsg=0;
- THD* thd = rli->sql_thd;
DBUG_ENTER("next_event");
- DBUG_ASSERT(thd != 0);
+ DBUG_ASSERT(rgi->thd != 0 && rgi->thd == rli->sql_driver_thd);
+ *event_size= 0;
#ifndef DBUG_OFF
if (abort_slave_event_count && !rli->events_till_abort--)
@@ -5031,7 +6485,7 @@ static Log_event* next_event(Relay_log_info* rli)
*/
mysql_mutex_assert_owner(&rli->data_lock);
- while (!sql_slave_killed(thd,rli))
+ while (!sql_slave_killed(rgi))
{
/*
We can have two kinds of log reading:
@@ -5044,6 +6498,7 @@ static Log_event* next_event(Relay_log_info* rli)
The other case is much simpler:
We just have a read only log that nobody else will be updating.
*/
+ ulonglong old_pos;
bool hot_log;
if ((hot_log = (cur_log != &rli->cache_buf)))
{
@@ -5079,7 +6534,8 @@ static Log_event* next_event(Relay_log_info* rli)
llstr(my_b_tell(cur_log),llbuf1),
llstr(rli->event_relay_log_pos,llbuf2)));
DBUG_ASSERT(my_b_tell(cur_log) >= BIN_LOG_HEADER_SIZE);
- DBUG_ASSERT(my_b_tell(cur_log) == rli->event_relay_log_pos);
+ DBUG_ASSERT(opt_slave_parallel_threads > 0 ||
+ my_b_tell(cur_log) == rli->event_relay_log_pos);
}
#endif
/*
@@ -5094,22 +6550,24 @@ static Log_event* next_event(Relay_log_info* rli)
But if the relay log is created by new_file(): then the solution is:
MYSQL_BIN_LOG::open() will write the buffered description event.
*/
+ old_pos= rli->event_relay_log_pos;
if ((ev= Log_event::read_log_event(cur_log,0,
rli->relay_log.description_event_for_exec,
opt_slave_sql_verify_checksum)))
{
- DBUG_ASSERT(thd==rli->sql_thd);
/*
read it while we have a lock, to avoid a mutex lock in
inc_event_relay_log_pos()
*/
rli->future_event_relay_log_pos= my_b_tell(cur_log);
+ *event_size= rli->future_event_relay_log_pos - old_pos;
+
if (hot_log)
mysql_mutex_unlock(log_lock);
+ rli->sql_thread_caught_up= false;
DBUG_RETURN(ev);
}
- DBUG_ASSERT(thd==rli->sql_thd);
if (opt_reckless_slave) // For mysql-test
cur_log->error = 0;
if (cur_log->error < 0)
@@ -5145,12 +6603,10 @@ static Log_event* next_event(Relay_log_info* rli)
Seconds_Behind_Master would be zero only when master has no
more updates in binlog for slave. The heartbeat can be sent
in a (small) fraction of slave_net_timeout. Until it's done
- rli->last_master_timestamp is temporarely (for time of
- waiting for the following event) reset whenever EOF is
- reached.
+ rli->sql_thread_caught_up is temporarely (for time of waiting for
+ the following event) set whenever EOF is reached.
*/
- time_t save_timestamp= rli->last_master_timestamp;
- rli->last_master_timestamp= 0;
+ rli->sql_thread_caught_up= true;
DBUG_ASSERT(rli->relay_log.get_open_count() ==
rli->cur_log_old_open_count);
@@ -5174,6 +6630,36 @@ static Log_event* next_event(Relay_log_info* rli)
DBUG_RETURN(ev);
}
+ if (rli->ign_gtids.count())
+ {
+ /* We generate and return a Gtid_list, to update gtid_slave_pos. */
+ DBUG_PRINT("info",("seeing ignored end gtids"));
+ ev= new Gtid_list_log_event(&rli->ign_gtids,
+ Gtid_list_log_event::FLAG_IGN_GTIDS);
+ rli->ign_gtids.reset();
+ mysql_mutex_unlock(log_lock);
+ if (unlikely(!ev))
+ {
+ errmsg= "Slave SQL thread failed to create a Gtid_list event "
+ "(out of memory?), gtid_slave_pos may be inaccurate";
+ goto err;
+ }
+ ev->server_id= 0; // don't be ignored by slave SQL thread
+ ev->set_artificial_event(); // Don't mess up Exec_Master_Log_Pos
+ DBUG_RETURN(ev);
+ }
+
+ /*
+ We have to check sql_slave_killed() here an extra time.
+ Otherwise we may miss a wakeup, since last check was done
+ without holding LOCK_log.
+ */
+ if (sql_slave_killed(rgi))
+ {
+ mysql_mutex_unlock(log_lock);
+ break;
+ }
+
/*
We can, and should release data_lock while we are waiting for
update. If we do not, show slave status will block
@@ -5197,14 +6683,15 @@ static Log_event* next_event(Relay_log_info* rli)
and reads one more event and starts honoring log_space_limit again.
If the SQL thread needs more events to be able to rotate the log (it
- might need to finish the current group first), then it can ask for one
- more at a time. Thus we don't outgrow the relay log indefinitely,
+ might need to finish the current group first), then it can ask for
+ one more at a time. Thus we don't outgrow the relay log indefinitely,
but rather in a controlled manner, until the next rotate.
When the SQL thread starts it sets ignore_log_space_limit to false.
We should also reset ignore_log_space_limit to 0 when the user does
- RESET SLAVE, but in fact, no need as RESET SLAVE requires that the slave
- be stopped, and the SQL thread sets ignore_log_space_limit to 0 when
+ RESET SLAVE, but in fact, no need as RESET SLAVE requires that the
+ slave be stopped, and the SQL thread sets ignore_log_space_limit
+ to 0 when
it stops.
*/
mysql_mutex_lock(&rli->log_space_lock);
@@ -5236,10 +6723,10 @@ static Log_event* next_event(Relay_log_info* rli)
mysql_cond_broadcast(&rli->log_space_cond);
mysql_mutex_unlock(&rli->log_space_lock);
// Note that wait_for_update_relay_log unlocks lock_log !
- rli->relay_log.wait_for_update_relay_log(rli->sql_thd);
+ rli->relay_log.wait_for_update_relay_log(rli->sql_driver_thd);
// re-acquire data lock since we released it earlier
mysql_mutex_lock(&rli->data_lock);
- rli->last_master_timestamp= save_timestamp;
+ rli->sql_thread_caught_up= false;
continue;
}
/*
@@ -5251,6 +6738,7 @@ static Log_event* next_event(Relay_log_info* rli)
DBUG_ASSERT(rli->cur_log_fd >= 0);
mysql_file_close(rli->cur_log_fd, MYF(MY_WME));
rli->cur_log_fd = -1;
+ rli->last_inuse_relaylog->completed= true;
if (relay_log_purge)
{
@@ -5308,11 +6796,6 @@ static Log_event* next_event(Relay_log_info* rli)
mysql_mutex_lock(log_lock);
if (rli->relay_log.is_active(rli->linfo.log_file_name))
{
-#ifdef EXTRA_DEBUG
- if (global_system_variables.log_warnings)
- sql_print_information("next log '%s' is currently active",
- rli->linfo.log_file_name);
-#endif
rli->cur_log= cur_log= rli->relay_log.get_log_file();
rli->cur_log_old_open_count= rli->relay_log.get_open_count();
DBUG_ASSERT(rli->cur_log_fd == -1);
@@ -5384,6 +6867,12 @@ static Log_event* next_event(Relay_log_info* rli)
mysql_mutex_unlock(log_lock);
goto err;
}
+ if (rli->alloc_inuse_relaylog(rli->linfo.log_file_name))
+ {
+ if (!hot_log)
+ mysql_mutex_unlock(log_lock);
+ goto err;
+ }
if (!hot_log)
mysql_mutex_unlock(log_lock);
continue;
@@ -5395,15 +6884,12 @@ static Log_event* next_event(Relay_log_info* rli)
ourselves. We are sure that the log is still not hot now (a log can get
from hot to cold, but not from cold to hot). No need for LOCK_log.
*/
-#ifdef EXTRA_DEBUG
- if (global_system_variables.log_warnings)
- sql_print_information("next log '%s' is not active",
- rli->linfo.log_file_name);
-#endif
// open_binlog() will check the magic header
if ((rli->cur_log_fd=open_binlog(cur_log,rli->linfo.log_file_name,
&errmsg)) <0)
goto err;
+ if (rli->alloc_inuse_relaylog(rli->linfo.log_file_name))
+ goto err;
}
else
{
@@ -5542,7 +7028,7 @@ bool rpl_master_has_bug(const Relay_log_info *rli, uint bug_id, bool report,
" so slave stops; check error log on slave"
" for more info", MYF(0), bug_id);
// a verbose message for the error log
- rli->report(ERROR_LEVEL, ER_UNKNOWN_ERROR,
+ rli->report(ERROR_LEVEL, ER_UNKNOWN_ERROR, NULL,
"According to the master's version ('%s'),"
" it is probable that master suffers from this bug:"
" http://bugs.mysql.com/bug.php?id=%u"
@@ -5579,20 +7065,14 @@ bool rpl_master_has_bug(const Relay_log_info *rli, uint bug_id, bool report,
*/
bool rpl_master_erroneous_autoinc(THD *thd)
{
- if (active_mi && active_mi->rli.sql_thd == thd)
+ if (thd->rgi_slave)
{
- Relay_log_info *rli= &active_mi->rli;
DBUG_EXECUTE_IF("simulate_bug33029", return TRUE;);
- return rpl_master_has_bug(rli, 33029, FALSE, NULL, NULL);
+ return rpl_master_has_bug(thd->rgi_slave->rli, 33029, FALSE, NULL, NULL);
}
return FALSE;
}
-#ifdef HAVE_EXPLICIT_TEMPLATE_INSTANTIATION
-template class I_List_iterator<i_string>;
-template class I_List_iterator<i_string_pair>;
-#endif
-
/**
@} (end of group Replication)
*/
diff --git a/sql/slave.h b/sql/slave.h
index c220f881619..5cc02c8a10b 100644
--- a/sql/slave.h
+++ b/sql/slave.h
@@ -45,9 +45,14 @@
#define MAX_SLAVE_ERROR 2000
+#define MAX_REPLICATION_THREAD 64
+
// Forward declarations
class Relay_log_info;
class Master_info;
+class Master_info_index;
+struct rpl_group_info;
+struct rpl_parallel_thread;
int init_intvar_from_file(int* var, IO_CACHE* f, int default_val);
int init_strvar_from_file(char *var, int max_size, IO_CACHE *f,
@@ -127,11 +132,11 @@ extern my_bool opt_replicate_annotate_row_events;
extern ulonglong relay_log_space_limit;
/*
- 3 possible values for Master_info::slave_running and
+ 4 possible values for Master_info::slave_running and
Relay_log_info::slave_running.
- The values 0,1,2 are very important: to keep the diff small, I didn't
- substitute places where we use 0/1 with the newly defined symbols. So don't change
- these values.
+ The values 0,1,2,3 are very important: to keep the diff small, I didn't
+ substitute places where we use 0/1 with the newly defined symbols.
+ So don't change these values.
The same way, code is assuming that in Relay_log_info we use only values
0/1.
I started with using an enum, but
@@ -140,6 +145,7 @@ extern ulonglong relay_log_space_limit;
#define MYSQL_SLAVE_NOT_RUN 0
#define MYSQL_SLAVE_RUN_NOT_CONNECT 1
#define MYSQL_SLAVE_RUN_CONNECT 2
+#define MYSQL_SLAVE_RUN_READING 3
#define RPL_LOG_NAME (rli->group_master_log_name[0] ? rli->group_master_log_name :\
"FIRST")
@@ -197,7 +203,8 @@ int mysql_table_dump(THD* thd, const char* db,
int fetch_master_table(THD* thd, const char* db_name, const char* table_name,
Master_info* mi, MYSQL* mysql, bool overwrite);
-bool show_master_info(THD* thd, Master_info* mi);
+bool show_master_info(THD* thd, Master_info* mi, bool full);
+bool show_all_master_info(THD* thd);
bool show_binlog_info(THD* thd);
bool rpl_master_has_bug(const Relay_log_info *rli, uint bug_id, bool report,
bool (*pred)(const void *), const void *param);
@@ -214,6 +221,10 @@ void end_relay_log_info(Relay_log_info* rli);
void lock_slave_threads(Master_info* mi);
void unlock_slave_threads(Master_info* mi);
void init_thread_mask(int* mask,Master_info* mi,bool inverse);
+Format_description_log_event *
+read_relay_log_description_event(IO_CACHE *cur_log, ulonglong start_pos,
+ const char **errmsg);
+
int init_relay_log_pos(Relay_log_info* rli,const char* log,ulonglong pos,
bool need_data_lock, const char** errmsg,
bool look_for_description_event);
@@ -221,16 +232,23 @@ int init_relay_log_pos(Relay_log_info* rli,const char* log,ulonglong pos,
int purge_relay_logs(Relay_log_info* rli, THD *thd, bool just_reset,
const char** errmsg);
void set_slave_thread_options(THD* thd);
-void set_slave_thread_default_charset(THD *thd, Relay_log_info const *rli);
+void set_slave_thread_default_charset(THD *thd, rpl_group_info *rgi);
int rotate_relay_log(Master_info* mi);
-int apply_event_and_update_pos(Log_event* ev, THD* thd, Relay_log_info* rli);
+int has_temporary_error(THD *thd);
+int apply_event_and_update_pos(Log_event* ev, THD* thd,
+ struct rpl_group_info *rgi,
+ rpl_parallel_thread *rpt);
pthread_handler_t handle_slave_io(void *arg);
+void slave_output_error_info(rpl_group_info *rgi, THD *thd);
pthread_handler_t handle_slave_sql(void *arg);
bool net_request_file(NET* net, const char* fname);
extern bool volatile abort_loop;
extern Master_info *active_mi; /* active_mi for multi-master */
+extern Master_info *default_master_info; /* To replace active_mi */
+extern Master_info_index *master_info_index;
+extern LEX_STRING default_master_connection_name;
extern my_bool replicate_same_server_id;
extern int disconnect_slave_event_count, abort_slave_event_count ;
diff --git a/sql/sp.cc b/sql/sp.cc
index 9ae0c84b36c..c247101ca7f 100644
--- a/sql/sp.cc
+++ b/sql/sp.cc
@@ -15,6 +15,7 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+#include <my_global.h>
#include "sql_priv.h"
#include "unireg.h"
#include "sp.h"
@@ -51,7 +52,8 @@ db_load_routine(THD *thd, stored_procedure_type type, sp_name *name,
sp_head **sphp,
ulonglong sql_mode, const char *params, const char *returns,
const char *body, st_sp_chistics &chistics,
- const char *definer, longlong created, longlong modified,
+ LEX_STRING *definer_user_name, LEX_STRING *definer_host_name,
+ longlong created, longlong modified,
Stored_program_creation_ctx *creation_ctx);
static const
@@ -169,7 +171,7 @@ TABLE_FIELD_TYPE proc_table_fields[MYSQL_PROC_FIELD_COUNT] =
};
static const TABLE_FIELD_DEF
- proc_table_def= {MYSQL_PROC_FIELD_COUNT, proc_table_fields};
+proc_table_def= {MYSQL_PROC_FIELD_COUNT, proc_table_fields, 0, (uint*) 0 };
/*************************************************************************/
@@ -326,7 +328,7 @@ Stored_routine_creation_ctx::load_from_db(THD *thd,
if (invalid_creation_ctx)
{
push_warning_printf(thd,
- MYSQL_ERROR::WARN_LEVEL_WARN,
+ Sql_condition::WARN_LEVEL_WARN,
ER_SR_INVALID_CREATION_CTX,
ER(ER_SR_INVALID_CREATION_CTX),
(const char *) db_name,
@@ -378,7 +380,7 @@ void Proc_table_intact::report_error(uint code, const char *fmt, ...)
if (code)
my_message(code, buf, MYF(0));
else
- my_error(ER_CANNOT_LOAD_FROM_TABLE, MYF(0), "proc");
+ my_error(ER_CANNOT_LOAD_FROM_TABLE_V2, MYF(0), "mysql", "proc");
if (m_print_once)
{
@@ -549,6 +551,10 @@ db_find_routine(THD *thd, stored_procedure_type type, sp_name *name,
ulonglong sql_mode, saved_mode= thd->variables.sql_mode;
Open_tables_backup open_tables_state_backup;
Stored_program_creation_ctx *creation_ctx;
+ char definer_user_name_holder[USERNAME_LENGTH + 1];
+ LEX_STRING definer_user_name= { definer_user_name_holder, USERNAME_LENGTH };
+ char definer_host_name_holder[HOSTNAME_LENGTH + 1];
+ LEX_STRING definer_host_name= { definer_host_name_holder, HOSTNAME_LENGTH };
DBUG_ENTER("db_find_routine");
DBUG_PRINT("enter", ("type: %d name: %.*s",
@@ -658,9 +664,19 @@ db_find_routine(THD *thd, stored_procedure_type type, sp_name *name,
close_system_tables(thd, &open_tables_state_backup);
table= 0;
+ if (parse_user(definer, strlen(definer),
+ definer_user_name.str, &definer_user_name.length,
+ definer_host_name.str, &definer_host_name.length) &&
+ definer_user_name.length && !definer_host_name.length)
+ {
+ // 'user@' -> 'user@%'
+ definer_host_name= host_not_specified;
+ }
+
ret= db_load_routine(thd, type, name, sphp,
sql_mode, params, returns, body, chistics,
- definer, created, modified, creation_ctx);
+ &definer_user_name, &definer_host_name,
+ created, modified, creation_ctx);
done:
/*
Restore the time zone flag as the timezone usage in proc table
@@ -684,9 +700,9 @@ public:
virtual bool handle_condition(THD *thd,
uint sql_errno,
const char* sqlstate,
- MYSQL_ERROR::enum_warning_level level,
+ Sql_condition::enum_warning_level level,
const char* msg,
- MYSQL_ERROR ** cond_hdl);
+ Sql_condition ** cond_hdl);
};
bool
@@ -694,13 +710,13 @@ Silence_deprecated_warning::handle_condition(
THD *,
uint sql_errno,
const char*,
- MYSQL_ERROR::enum_warning_level level,
+ Sql_condition::enum_warning_level level,
const char*,
- MYSQL_ERROR ** cond_hdl)
+ Sql_condition ** cond_hdl)
{
*cond_hdl= NULL;
if (sql_errno == ER_WARN_DEPRECATED_SYNTAX &&
- level == MYSQL_ERROR::WARN_LEVEL_WARN)
+ level == Sql_condition::WARN_LEVEL_WARN)
return TRUE;
return FALSE;
@@ -773,9 +789,9 @@ public:
virtual bool handle_condition(THD *thd,
uint sql_errno,
const char* sqlstate,
- MYSQL_ERROR::enum_warning_level level,
+ Sql_condition::enum_warning_level level,
const char* message,
- MYSQL_ERROR ** cond_hdl);
+ Sql_condition ** cond_hdl);
bool error_caught() const { return m_error_caught; }
@@ -787,9 +803,9 @@ bool
Bad_db_error_handler::handle_condition(THD *thd,
uint sql_errno,
const char* sqlstate,
- MYSQL_ERROR::enum_warning_level level,
+ Sql_condition::enum_warning_level level,
const char* message,
- MYSQL_ERROR ** cond_hdl)
+ Sql_condition ** cond_hdl)
{
if (sql_errno == ER_BAD_DB_ERROR)
{
@@ -805,7 +821,8 @@ db_load_routine(THD *thd, stored_procedure_type type,
sp_name *name, sp_head **sphp,
ulonglong sql_mode, const char *params, const char *returns,
const char *body, st_sp_chistics &chistics,
- const char *definer, longlong created, longlong modified,
+ LEX_STRING *definer_user_name, LEX_STRING *definer_host_name,
+ longlong created, longlong modified,
Stored_program_creation_ctx *creation_ctx)
{
LEX *old_lex= thd->lex, newlex;
@@ -815,22 +832,12 @@ db_load_routine(THD *thd, stored_procedure_type type,
{ saved_cur_db_name_buf, sizeof(saved_cur_db_name_buf) };
bool cur_db_changed;
Bad_db_error_handler db_not_exists_handler;
- char definer_user_name_holder[USERNAME_LENGTH + 1];
- LEX_STRING definer_user_name= { definer_user_name_holder,
- USERNAME_LENGTH };
-
- char definer_host_name_holder[HOSTNAME_LENGTH + 1];
- LEX_STRING definer_host_name= { definer_host_name_holder, HOSTNAME_LENGTH };
int ret= 0;
thd->lex= &newlex;
newlex.current_select= NULL;
- parse_user(definer, strlen(definer),
- definer_user_name.str, &definer_user_name.length,
- definer_host_name.str, &definer_host_name.length);
-
defstr.set_charset(creation_ctx->get_client_cs());
/*
@@ -846,7 +853,7 @@ db_load_routine(THD *thd, stored_procedure_type type,
params, strlen(params),
returns, strlen(returns),
body, strlen(body),
- &chistics, &definer_user_name, &definer_host_name,
+ &chistics, definer_user_name, definer_host_name,
sql_mode))
{
ret= SP_INTERNAL_ERROR;
@@ -896,7 +903,7 @@ db_load_routine(THD *thd, stored_procedure_type type,
goto end;
}
- (*sphp)->set_definer(&definer_user_name, &definer_host_name);
+ (*sphp)->set_definer(definer_user_name, definer_host_name);
(*sphp)->set_info(created, modified, &chistics, sql_mode);
(*sphp)->set_creation_ctx(creation_ctx);
(*sphp)->optimize();
@@ -975,7 +982,8 @@ sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp)
{
int ret;
TABLE *table;
- char definer[USER_HOST_BUFF_SIZE];
+ char definer_buf[USER_HOST_BUFF_SIZE];
+ LEX_STRING definer;
ulonglong saved_mode= thd->variables.sql_mode;
MDL_key::enum_mdl_namespace mdl_type= type == TYPE_ENUM_FUNCTION ?
MDL_key::FUNCTION : MDL_key::PROCEDURE;
@@ -985,9 +993,6 @@ sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp)
enum_check_fields saved_count_cuted_fields;
bool store_failed= FALSE;
-
- bool save_binlog_row_based;
-
DBUG_ENTER("sp_create_routine");
DBUG_PRINT("enter", ("type: %d name: %.*s", (int) type,
(int) sp->m_name.length,
@@ -1005,14 +1010,6 @@ sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp)
/* 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
- statement.
- */
- if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
- thd->clear_current_stmt_binlog_format_row();
-
saved_count_cuted_fields= thd->count_cuted_fields;
thd->count_cuted_fields= CHECK_FIELD_WARN;
@@ -1023,8 +1020,7 @@ sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp)
restore_record(table, s->default_values); // Get default values for fields
/* NOTE: all needed privilege checks have been already done. */
- strxnmov(definer, sizeof(definer)-1, thd->lex->definer->user.str, "@",
- thd->lex->definer->host.str, NullS);
+ thd->lex->definer->set_lex_string(&definer, definer_buf);
if (table->s->fields < MYSQL_PROC_FIELD_COUNT)
{
@@ -1099,7 +1095,7 @@ sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp)
store_failed= store_failed ||
table->field[MYSQL_PROC_FIELD_DEFINER]->
- store(definer, (uint)strlen(definer), system_charset_info);
+ store(definer.str, definer.length, system_charset_info);
((Field_timestamp *)table->field[MYSQL_PROC_FIELD_CREATED])->set_time();
((Field_timestamp *)table->field[MYSQL_PROC_FIELD_MODIFIED])->set_time();
@@ -1179,6 +1175,9 @@ sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp)
ret= SP_OK;
if (table->file->ha_write_row(table->record[0]))
ret= SP_WRITE_ROW_FAILED;
+ /* Make change permanent and avoid 'table is marked as crashed' errors */
+ table->file->extra(HA_EXTRA_FLUSH);
+
if (ret == SP_OK)
sp_cache_invalidate();
@@ -1218,10 +1217,7 @@ sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp)
done:
thd->count_cuted_fields= saved_count_cuted_fields;
thd->variables.sql_mode= saved_mode;
- /* Restore the state of binlog format */
DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
- if (save_binlog_row_based)
- thd->set_current_stmt_binlog_format_row();
DBUG_RETURN(ret);
}
@@ -1246,7 +1242,6 @@ sp_drop_routine(THD *thd, stored_procedure_type type, sp_name *name)
{
TABLE *table;
int ret;
- bool save_binlog_row_based;
MDL_key::enum_mdl_namespace mdl_type= type == TYPE_ENUM_FUNCTION ?
MDL_key::FUNCTION : MDL_key::PROCEDURE;
DBUG_ENTER("sp_drop_routine");
@@ -1268,13 +1263,12 @@ sp_drop_routine(THD *thd, stored_procedure_type type, sp_name *name)
row-based replication. The flag will be reset at the end of the
statement.
*/
- if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
- thd->clear_current_stmt_binlog_format_row();
-
if ((ret= db_find_routine_aux(thd, type, name, table)) == SP_OK)
{
if (table->file->ha_delete_row(table->record[0]))
ret= SP_DELETE_ROW_FAILED;
+ /* Make change permanent and avoid 'table is marked as crashed' errors */
+ table->file->extra(HA_EXTRA_FLUSH);
}
if (ret == SP_OK)
@@ -1297,10 +1291,7 @@ sp_drop_routine(THD *thd, stored_procedure_type type, sp_name *name)
sp_cache_flush_obsolete(spc, &sp);
}
}
- /* Restore the state of binlog format */
DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
- if (save_binlog_row_based)
- thd->set_current_stmt_binlog_format_row();
DBUG_RETURN(ret);
}
@@ -1328,7 +1319,6 @@ sp_update_routine(THD *thd, stored_procedure_type type, sp_name *name,
{
TABLE *table;
int ret;
- bool save_binlog_row_based;
MDL_key::enum_mdl_namespace mdl_type= type == TYPE_ENUM_FUNCTION ?
MDL_key::FUNCTION : MDL_key::PROCEDURE;
DBUG_ENTER("sp_update_routine");
@@ -1346,14 +1336,6 @@ sp_update_routine(THD *thd, stored_procedure_type type, sp_name *name,
if (!(table= open_proc_table_for_update(thd)))
DBUG_RETURN(SP_OPEN_TABLE_FAILED);
- /*
- This statement will be replicated as a statement, even when using
- row-based replication. The flag will be reset at the end of the
- statement.
- */
- if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
- thd->clear_current_stmt_binlog_format_row();
-
if ((ret= db_find_routine_aux(thd, type, name, table)) == SP_OK)
{
if (type == TYPE_ENUM_FUNCTION && ! trust_function_creators &&
@@ -1381,7 +1363,6 @@ sp_update_routine(THD *thd, stored_procedure_type type, sp_name *name,
}
store_record(table,record[1]);
- table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
((Field_timestamp *)table->field[MYSQL_PROC_FIELD_MODIFIED])->set_time();
if (chistics->suid != SP_IS_DEFAULT_SUID)
table->field[MYSQL_PROC_FIELD_SECURITY_TYPE]->
@@ -1398,6 +1379,8 @@ sp_update_routine(THD *thd, stored_procedure_type type, sp_name *name,
ret= SP_WRITE_ROW_FAILED;
else
ret= 0;
+ /* Make change permanent and avoid 'table is marked as crashed' errors */
+ table->file->extra(HA_EXTRA_FLUSH);
}
if (ret == SP_OK)
@@ -1407,10 +1390,7 @@ sp_update_routine(THD *thd, stored_procedure_type type, sp_name *name,
sp_cache_invalidate();
}
err:
- /* Restore the state of binlog format */
DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
- if (save_binlog_row_based)
- thd->set_current_stmt_binlog_format_row();
DBUG_RETURN(ret);
}
@@ -1425,15 +1405,15 @@ public:
bool handle_condition(THD *thd,
uint sql_errno,
const char* sqlstate,
- MYSQL_ERROR::enum_warning_level level,
+ Sql_condition::enum_warning_level level,
const char* msg,
- MYSQL_ERROR ** cond_hdl)
+ Sql_condition ** cond_hdl)
{
if (sql_errno == ER_NO_SUCH_TABLE ||
sql_errno == ER_NO_SUCH_TABLE_IN_ENGINE ||
- sql_errno == ER_CANNOT_LOAD_FROM_TABLE ||
+ sql_errno == ER_CANNOT_LOAD_FROM_TABLE_V2 ||
sql_errno == ER_COL_COUNT_DOESNT_MATCH_PLEASE_UPDATE ||
- sql_errno == ER_COL_COUNT_DOESNT_MATCH_CORRUPTED)
+ sql_errno == ER_COL_COUNT_DOESNT_MATCH_CORRUPTED_V2)
return true;
return false;
}
@@ -1459,6 +1439,8 @@ bool lock_db_routines(THD *thd, char *db)
uchar keybuf[MAX_KEY_LENGTH];
DBUG_ENTER("lock_db_routines");
+ DBUG_ASSERT(ok_for_lower_case_names(db));
+
/*
mysql.proc will be re-opened during deletion, so we can ignore
errors when opening the table here. The error handler is
@@ -1578,7 +1560,11 @@ sp_drop_db_routines(THD *thd, char *db)
if (nxtres != HA_ERR_END_OF_FILE)
ret= SP_KEY_NOT_FOUND;
if (deleted)
+ {
sp_cache_invalidate();
+ /* Make change permanent and avoid 'table is marked as crashed' errors */
+ table->file->extra(HA_EXTRA_FLUSH);
+ }
}
table->file->ha_index_end();
@@ -1686,7 +1672,6 @@ sp_find_routine(THD *thd, stored_procedure_type type, sp_name *name,
ulong level;
sp_head *new_sp;
const char *returns= "";
- char definer[USER_HOST_BUFF_SIZE];
/*
String buffer for RETURNS data type must have system charset;
@@ -1723,8 +1708,6 @@ sp_find_routine(THD *thd, stored_procedure_type type, sp_name *name,
DBUG_RETURN(0);
}
- strxmov(definer, sp->m_definer_user.str, "@",
- sp->m_definer_host.str, NullS);
if (type == TYPE_ENUM_FUNCTION)
{
sp_returns_type(thd, retstr, sp);
@@ -1732,7 +1715,8 @@ sp_find_routine(THD *thd, stored_procedure_type type, sp_name *name,
}
if (db_load_routine(thd, type, name, &new_sp,
sp->m_sql_mode, sp->m_params.str, returns,
- sp->m_body.str, *sp->m_chistics, definer,
+ sp->m_body.str, *sp->m_chistics,
+ &sp->m_definer_user, &sp->m_definer_host,
sp->m_created, sp->m_modified,
sp->get_creation_ctx()) == SP_OK)
{
@@ -1767,7 +1751,8 @@ sp_find_routine(THD *thd, stored_procedure_type type, sp_name *name,
@param thd Thread handler
@param routines List of needles in the hay stack
- @param any Any of the needles are good enough
+ @param is_proc Indicates whether routines in the list are procedures
+ or functions.
@return
@retval FALSE Found.
@@ -1775,7 +1760,7 @@ sp_find_routine(THD *thd, stored_procedure_type type, sp_name *name,
*/
bool
-sp_exist_routines(THD *thd, TABLE_LIST *routines, bool any)
+sp_exist_routines(THD *thd, TABLE_LIST *routines, bool is_proc)
{
TABLE_LIST *routine;
bool sp_object_found;
@@ -1791,17 +1776,14 @@ sp_exist_routines(THD *thd, TABLE_LIST *routines, bool any)
lex_name.str= thd->strmake(routine->table_name, lex_name.length);
name= new sp_name(lex_db, lex_name, true);
name->init_qname(thd);
- sp_object_found= sp_find_routine(thd, TYPE_ENUM_PROCEDURE, name,
- &thd->sp_proc_cache, FALSE) != NULL ||
- sp_find_routine(thd, TYPE_ENUM_FUNCTION, name,
- &thd->sp_func_cache, FALSE) != NULL;
- thd->warning_info->clear_warning_info(thd->query_id);
- if (sp_object_found)
- {
- if (any)
- break;
- }
- else if (!any)
+ sp_object_found= is_proc ? sp_find_routine(thd, TYPE_ENUM_PROCEDURE,
+ name, &thd->sp_proc_cache,
+ FALSE) != NULL :
+ sp_find_routine(thd, TYPE_ENUM_FUNCTION,
+ name, &thd->sp_func_cache,
+ FALSE) != NULL;
+ thd->get_stmt_da()->clear_warning_info(thd->query_id);
+ if (! sp_object_found)
{
my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "FUNCTION or PROCEDURE",
routine->table_name);
@@ -2320,7 +2302,8 @@ int wsrep_create_sp(THD *thd, uchar** buf, size_t* buf_len)
&(thd->lex->definer->host),
saved_mode))
{
- WSREP_WARN("SP create string failed: %s", thd->query());
+ WSREP_WARN("SP create string failed: schema: %s, query: %s",
+ (thd->db ? thd->db : "(null)"), thd->query());
return 1;
}
return wsrep_to_buf_helper(thd, log_query.ptr(), log_query.length(), buf, buf_len);
diff --git a/sql/sp.h b/sql/sp.h
index 3353132346b..82d4704cf2c 100644
--- a/sql/sp.h
+++ b/sql/sp.h
@@ -121,7 +121,7 @@ sp_cache_routine(THD *thd, stored_procedure_type type, sp_name *name,
bool lookup_only, sp_head **sp);
bool
-sp_exist_routines(THD *thd, TABLE_LIST *procs, bool any);
+sp_exist_routines(THD *thd, TABLE_LIST *procs, bool is_proc);
bool
sp_show_create_routine(THD *thd, stored_procedure_type type, sp_name *name);
diff --git a/sql/sp_cache.cc b/sql/sp_cache.cc
index 14f49ecc077..bafd0f34ab6 100644
--- a/sql/sp_cache.cc
+++ b/sql/sp_cache.cc
@@ -13,6 +13,7 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+#include <my_global.h>
#include "sql_priv.h"
#include "unireg.h"
#ifdef USE_PRAGMA_IMPLEMENTATION
diff --git a/sql/sp_head.cc b/sql/sp_head.cc
index 2cd627a2a32..e181e14611b 100644
--- a/sql/sp_head.cc
+++ b/sql/sp_head.cc
@@ -15,7 +15,7 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
-#include "my_global.h" /* NO_EMBEDDED_ACCESS_CHECKS */
+#include <my_global.h> /* NO_EMBEDDED_ACCESS_CHECKS */
#include "sql_priv.h"
#include "unireg.h"
#include "sql_prepare.h"
@@ -27,7 +27,7 @@
// prepare_create_field
#include "sql_acl.h" // *_ACL
#include "sql_array.h" // Dynamic_array
-#include "log_event.h" // append_query_string, Query_log_event
+#include "log_event.h" // Query_log_event
#include "sql_derived.h" // mysql_handle_derived
#ifdef USE_PRAGMA_IMPLEMENTATION
@@ -42,6 +42,8 @@
#include "sql_parse.h" // cleanup_items
#include "sql_base.h" // close_thread_tables
#include "transaction.h" // trans_commit_stmt
+#include "sql_audit.h"
+#include "debug_sync.h"
/*
Sufficient max length of printed destinations and frame offsets (all uints).
@@ -159,7 +161,8 @@ sp_get_item_value(THD *thd, Item *item, String *str)
buf.append(result->charset()->csname);
if (cs->escape_with_backslash_is_dangerous)
buf.append(' ');
- append_query_string(thd, cs, result, &buf);
+ append_query_string(cs, &buf, result->ptr(), result->length(),
+ thd->variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES);
buf.append(" COLLATE '");
buf.append(item->collation.collation->name);
buf.append('\'');
@@ -219,6 +222,7 @@ sp_get_flags_for_command(LEX *lex)
case SQLCOM_SHOW_CREATE_TRIGGER:
case SQLCOM_SHOW_DATABASES:
case SQLCOM_SHOW_ERRORS:
+ case SQLCOM_SHOW_EXPLAIN:
case SQLCOM_SHOW_FIELDS:
case SQLCOM_SHOW_FUNC_CODE:
case SQLCOM_SHOW_GRANTS:
@@ -259,7 +263,7 @@ sp_get_flags_for_command(LEX *lex)
flags= sp_head::CONTAINS_DYNAMIC_SQL;
break;
case SQLCOM_CREATE_TABLE:
- if (lex->create_info.options & HA_LEX_CREATE_TMP_TABLE)
+ if (lex->create_info.tmp_table())
flags= 0;
else
flags= sp_head::HAS_COMMIT_OR_ROLLBACK;
@@ -281,9 +285,12 @@ sp_get_flags_for_command(LEX *lex)
case SQLCOM_CREATE_VIEW:
case SQLCOM_CREATE_TRIGGER:
case SQLCOM_CREATE_USER:
+ case SQLCOM_CREATE_ROLE:
case SQLCOM_ALTER_TABLE:
case SQLCOM_GRANT:
+ case SQLCOM_GRANT_ROLE:
case SQLCOM_REVOKE:
+ case SQLCOM_REVOKE_ROLE:
case SQLCOM_BEGIN:
case SQLCOM_RENAME_TABLE:
case SQLCOM_RENAME_USER:
@@ -291,6 +298,7 @@ sp_get_flags_for_command(LEX *lex)
case SQLCOM_DROP_DB:
case SQLCOM_REVOKE_ALL:
case SQLCOM_DROP_USER:
+ case SQLCOM_DROP_ROLE:
case SQLCOM_DROP_VIEW:
case SQLCOM_DROP_TRIGGER:
case SQLCOM_TRUNCATE:
@@ -311,6 +319,33 @@ sp_get_flags_for_command(LEX *lex)
case SQLCOM_UNINSTALL_PLUGIN:
flags= sp_head::HAS_COMMIT_OR_ROLLBACK;
break;
+ case SQLCOM_DELETE:
+ case SQLCOM_DELETE_MULTI:
+ {
+ /*
+ DELETE normally doesn't return resultset, but there are two exceptions:
+ - DELETE ... RETURNING
+ - EXPLAIN DELETE ...
+ */
+ if (lex->select_lex.item_list.is_empty() && !lex->describe)
+ flags= 0;
+ else
+ flags= sp_head::MULTI_RESULTS;
+ break;
+ }
+ case SQLCOM_UPDATE:
+ case SQLCOM_UPDATE_MULTI:
+ case SQLCOM_INSERT:
+ case SQLCOM_REPLACE:
+ case SQLCOM_REPLACE_SELECT:
+ case SQLCOM_INSERT_SELECT:
+ {
+ if (!lex->describe)
+ flags= 0;
+ else
+ flags= sp_head::MULTI_RESULTS;
+ break;
+ }
default:
flags= 0;
break;
@@ -385,9 +420,7 @@ sp_eval_expr(THD *thd, Field *result_field, Item **expr_item_ptr)
*/
thd->count_cuted_fields= CHECK_FIELD_ERROR_FOR_NULL;
- thd->abort_on_warning=
- thd->variables.sql_mode &
- (MODE_STRICT_TRANS_TABLES | MODE_STRICT_ALL_TABLES);
+ thd->abort_on_warning= thd->is_strict_mode();
thd->transaction.stmt.modified_non_trans_table= FALSE;
/* Save the value in the field. Convert the value if needed. */
@@ -461,6 +494,7 @@ sp_name::init_qname(THD *thd)
(int) m_db.length, (m_db.length ? m_db.str : ""),
dot, ".",
(int) m_name.length, m_name.str);
+ DBUG_ASSERT(ok_for_lower_case_names(m_db.str));
}
@@ -510,7 +544,7 @@ sp_head::operator new(size_t size) throw()
MEM_ROOT own_root;
sp_head *sp;
- init_sql_alloc(&own_root, MEM_ROOT_BLOCK_SIZE, MEM_ROOT_PREALLOC);
+ init_sql_alloc(&own_root, MEM_ROOT_BLOCK_SIZE, MEM_ROOT_PREALLOC, MYF(0));
sp= (sp_head *) alloc_root(&own_root, size);
if (sp == NULL)
DBUG_RETURN(NULL);
@@ -594,7 +628,7 @@ sp_head::init(LEX *lex)
types of stored procedures to simplify reset_lex()/restore_lex() code.
*/
lex->trg_table_fields.empty();
- my_init_dynamic_array(&m_instr, sizeof(sp_instr *), 16, 8);
+ my_init_dynamic_array(&m_instr, sizeof(sp_instr *), 16, 8, MYF(0));
m_param_begin= NULL;
m_param_end= NULL;
@@ -774,7 +808,7 @@ sp_head::~sp_head()
for (uint ip = 0 ; (i = get_instr(ip)) ; ip++)
delete i;
delete_dynamic(&m_instr);
- m_pcont->destroy();
+ delete m_pcont;
free_items();
/*
@@ -977,7 +1011,7 @@ subst_spvars(THD *thd, sp_instr *instr, LEX_STRING *query_str)
thd->query_name_consts= 0;
for (Item_splocal **splocal= sp_vars_uses.front();
- splocal < sp_vars_uses.back(); splocal++)
+ splocal <= sp_vars_uses.back(); splocal++)
{
Item *val;
@@ -1080,105 +1114,6 @@ void sp_head::recursion_level_error(THD *thd)
}
-/**
- Find an SQL handler for any condition (warning or error) after execution
- of a stored routine instruction. Basically, this function looks for an
- appropriate SQL handler in RT-contexts. If an SQL handler is found, it is
- remembered in the RT-context for future activation (the context can be
- inactive at the moment).
-
- If there is no pending condition, the function just returns.
-
- If there was an error during the execution, an SQL handler for it will be
- searched within the current and outer scopes.
-
- There might be several errors in the Warning Info (that's possible by using
- SIGNAL/RESIGNAL in nested scopes) -- the function is looking for an SQL
- handler for the latest (current) error only.
-
- If there was a warning during the execution, an SQL handler for it will be
- searched within the current scope only.
-
- If several warnings were thrown during the execution and there are different
- SQL handlers for them, it is not determined which SQL handler will be chosen.
- Only one SQL handler will be executed.
-
- If warnings and errors were thrown during the execution, the error takes
- precedence. I.e. error handler will be executed. If there is no handler
- for that error, condition will remain unhandled.
-
- Once a warning or an error has been handled it is not removed from
- Warning Info.
-
- According to The Standard (quoting PeterG):
-
- An SQL procedure statement works like this ...
- SQL/Foundation 13.5 <SQL procedure statement>
- (General Rules) (greatly summarized) says:
- (1) Empty diagnostics area, thus clearing the condition.
- (2) Execute statement.
- During execution, if Exception Condition occurs,
- set Condition Area = Exception Condition and stop
- statement.
- During execution, if No Data occurs,
- set Condition Area = No Data Condition and continue
- statement.
- During execution, if Warning occurs,
- and Condition Area is not already full due to
- an earlier No Data condition, set Condition Area
- = Warning and continue statement.
- (3) Finish statement.
- At end of execution, if Condition Area is not
- already full due to an earlier No Data or Warning,
- set Condition Area = Successful Completion.
- In effect, this system means there is a precedence:
- Exception trumps No Data, No Data trumps Warning,
- Warning trumps Successful Completion.
-
- NB: "Procedure statements" include any DDL or DML or
- control statements. So CREATE and DELETE and WHILE
- and CALL and RETURN are procedure statements. But
- DECLARE and END are not procedure statements.
-
- @param thd thread handle
- @param ctx runtime context of the stored routine
-*/
-
-static void
-find_handler_after_execution(THD *thd, sp_rcontext *ctx)
-{
- if (thd->is_error())
- {
- ctx->find_handler(thd,
- thd->stmt_da->sql_errno(),
- thd->stmt_da->get_sqlstate(),
- MYSQL_ERROR::WARN_LEVEL_ERROR,
- thd->stmt_da->message());
- }
- else if (thd->warning_info->statement_warn_count())
- {
- List_iterator<MYSQL_ERROR> it(thd->warning_info->warn_list());
- MYSQL_ERROR *err;
- while ((err= it++))
- {
- if ((err->get_level() != MYSQL_ERROR::WARN_LEVEL_WARN &&
- err->get_level() != MYSQL_ERROR::WARN_LEVEL_NOTE) ||
- err->handled())
- continue;
-
- if (ctx->find_handler(thd,
- err->get_sql_errno(),
- err->get_sqlstate(),
- err->get_level(),
- err->get_message_text()))
- {
- err->mark_handled();
- break;
- }
- }
- }
-}
-
/**
Execute the routine. The main instruction jump loop is there.
@@ -1228,8 +1163,8 @@ sp_head::execute(THD *thd, bool merge_da_on_success)
SERVER_STATUS_LAST_ROW_SENT;
Reprepare_observer *save_reprepare_observer= thd->m_reprepare_observer;
Object_creation_ctx *saved_creation_ctx;
- Warning_info *saved_warning_info;
- Warning_info warning_info(thd->warning_info->warn_id(), false);
+ Diagnostics_area *da= thd->get_stmt_da();
+ Warning_info sp_wi(da->warning_info_id(), false, true);
/*
Just reporting a stack overrun error
@@ -1259,7 +1194,7 @@ sp_head::execute(THD *thd, bool merge_da_on_success)
DBUG_RETURN(TRUE);
/* init per-instruction memroot */
- init_sql_alloc(&execute_mem_root, MEM_ROOT_BLOCK_SIZE, 0);
+ init_sql_alloc(&execute_mem_root, MEM_ROOT_BLOCK_SIZE, 0, MYF(0));
DBUG_ASSERT(!(m_flags & IS_INVOKED));
m_flags|= IS_INVOKED;
@@ -1300,9 +1235,8 @@ sp_head::execute(THD *thd, bool merge_da_on_success)
old_arena= thd->stmt_arena;
/* Push a new warning information area. */
- warning_info.append_warning_info(thd, thd->warning_info);
- saved_warning_info= thd->warning_info;
- thd->warning_info= &warning_info;
+ da->copy_sql_conditions_to_wi(thd, &sp_wi);
+ da->push_warning_info(&sp_wi);
/*
Switch query context. This has to be done early as this is sometimes
@@ -1378,6 +1312,7 @@ sp_head::execute(THD *thd, bool merge_da_on_success)
/* Discard the initial part of executing routines. */
thd->profiling.discard_current_query();
#endif
+ DEBUG_SYNC(thd, "sp_head_execute_before_loop");
do
{
sp_instr *i;
@@ -1403,7 +1338,7 @@ sp_head::execute(THD *thd, bool merge_da_on_success)
}
/* Reset number of warnings for this query. */
- thd->warning_info->reset_for_next_command();
+ thd->get_stmt_da()->reset_for_next_command();
DBUG_PRINT("execute", ("Instruction %u", ip));
@@ -1430,8 +1365,13 @@ sp_head::execute(THD *thd, bool merge_da_on_success)
if (thd->locked_tables_mode <= LTM_LOCK_TABLES)
thd->user_var_events_alloc= thd->mem_root;
+ sql_digest_state *parent_digest= thd->m_digest;
+ thd->m_digest= NULL;
+
err_status= i->execute(thd, &ip);
+ thd->m_digest= parent_digest;
+
if (i->free_list)
cleanup_items(i->free_list);
@@ -1454,19 +1394,10 @@ sp_head::execute(THD *thd, bool merge_da_on_success)
errors are not catchable by SQL handlers) or the connection has been
killed during execution.
*/
- if (!thd->is_fatal_error && !thd->killed_errno())
+ if (!thd->is_fatal_error && !thd->killed_errno() &&
+ ctx->handle_sql_condition(thd, &ip, i))
{
- /*
- Find SQL handler in the appropriate RT-contexts:
- - warnings can be handled by SQL handlers within
- the current scope only;
- - errors can be handled by any SQL handler from outer scope.
- */
- find_handler_after_execution(thd, ctx);
-
- /* If found, activate handler for the current scope. */
- if (ctx->activate_handler(thd, &ip, i, &execute_arena, &backup_arena))
- err_status= FALSE;
+ err_status= FALSE;
}
/* Reset sp_rcontext::end_partial_result_set flag. */
@@ -1512,9 +1443,40 @@ sp_head::execute(THD *thd, bool merge_da_on_success)
- if there was an exception during execution, warning info should be
propagated to the caller in any case.
*/
+ da->pop_warning_info();
+
if (err_status || merge_da_on_success)
- saved_warning_info->merge_with_routine_info(thd, thd->warning_info);
- thd->warning_info= saved_warning_info;
+ {
+ /*
+ If a routine body is empty or if a routine did not generate any warnings,
+ do not duplicate our own contents by appending the contents of the called
+ routine. We know that the called routine did not change its warning info.
+
+ On the other hand, if the routine body is not empty and some statement in
+ the routine generates a warning or uses tables, warning info is guaranteed
+ to have changed. In this case we know that the routine warning info
+ contains only new warnings, and thus we perform a copy.
+ */
+ if (da->warning_info_changed(&sp_wi))
+ {
+ /*
+ If the invocation of the routine was a standalone statement,
+ rather than a sub-statement, in other words, if it's a CALL
+ of a procedure, rather than invocation of a function or a
+ trigger, we need to clear the current contents of the caller's
+ warning info.
+
+ This is per MySQL rules: if a statement generates a warning,
+ warnings from the previous statement are flushed. Normally
+ it's done in push_warning(). However, here we don't use
+ push_warning() to avoid invocation of condition handlers or
+ escalation of warnings to errors.
+ */
+ da->opt_clear_warning_info(thd->query_id);
+ da->copy_sql_conditions_from_wi(thd, &sp_wi);
+ da->remove_marked_sql_conditions();
+ }
+ }
done:
DBUG_PRINT("info", ("err_status: %d killed: %d is_slave_error: %d report_error: %d",
@@ -1719,11 +1681,10 @@ sp_head::execute_trigger(THD *thd,
TODO: we should create sp_rcontext once per command and reuse it
on subsequent executions of a trigger.
*/
- init_sql_alloc(&call_mem_root, MEM_ROOT_BLOCK_SIZE, 0);
+ init_sql_alloc(&call_mem_root, MEM_ROOT_BLOCK_SIZE, 0, MYF(0));
thd->set_n_backup_active_arena(&call_arena, &backup_arena);
- if (!(nctx= new sp_rcontext(m_pcont, 0, octx)) ||
- nctx->init(thd))
+ if (!(nctx= sp_rcontext::create(thd, m_pcont, NULL)))
{
err_status= TRUE;
goto err_with_cleanup;
@@ -1836,11 +1797,10 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount,
TODO: we should create sp_rcontext once per command and reuse
it on subsequent executions of a function/trigger.
*/
- init_sql_alloc(&call_mem_root, MEM_ROOT_BLOCK_SIZE, 0);
+ init_sql_alloc(&call_mem_root, MEM_ROOT_BLOCK_SIZE, 0, MYF(0));
thd->set_n_backup_active_arena(&call_arena, &backup_arena);
- if (!(nctx= new sp_rcontext(m_pcont, return_value_fld, octx)) ||
- nctx->init(thd))
+ if (!(nctx= sp_rcontext::create(thd, m_pcont, return_value_fld)))
{
thd->restore_active_arena(&call_arena, &backup_arena);
err_status= TRUE;
@@ -1934,9 +1894,7 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount,
as one select and not resetting THD::user_var_events before
each invocation.
*/
- mysql_mutex_lock(&LOCK_thread_count);
- q= global_query_id;
- mysql_mutex_unlock(&LOCK_thread_count);
+ q= get_query_id();
mysql_bin_log.start_union_events(thd, q + 1);
binlog_save_options= thd->variables.option_bits;
thd->variables.option_bits&= ~OPTION_BIN_LOG;
@@ -1968,7 +1926,7 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount,
if (mysql_bin_log.write(&qinfo) &&
thd->binlog_evt_union.unioned_events_trans)
{
- push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_UNKNOWN_ERROR,
+ push_warning(thd, Sql_condition::WARN_LEVEL_WARN, ER_UNKNOWN_ERROR,
"Invoked ROUTINE modified a transactional table but MySQL "
"failed to reflect this change in the binary log");
err_status= TRUE;
@@ -2057,9 +2015,9 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
if (! octx)
{
/* Create a temporary old context. */
- if (!(octx= new sp_rcontext(m_pcont, NULL, octx)) || octx->init(thd))
+ if (!(octx= sp_rcontext::create(thd, m_pcont, NULL)))
{
- delete octx; /* Delete octx if it was init() that failed. */
+ DBUG_PRINT("error", ("Could not create octx"));
DBUG_RETURN(TRUE);
}
@@ -2072,8 +2030,7 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
thd->spcont->callers_arena= thd;
}
- if (!(nctx= new sp_rcontext(m_pcont, NULL, octx)) ||
- nctx->init(thd))
+ if (!(nctx= sp_rcontext::create(thd, m_pcont, NULL)))
{
delete nctx; /* Delete nctx if it was init() that failed. */
thd->spcont= save_spcont;
@@ -2096,12 +2053,12 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
if (!arg_item)
break;
- sp_variable_t *spvar= m_pcont->find_variable(i);
+ sp_variable *spvar= m_pcont->find_variable(i);
if (!spvar)
continue;
- if (spvar->mode != sp_param_in)
+ if (spvar->mode != sp_variable::MODE_IN)
{
Settable_routine_parameter *srp=
arg_item->get_settable_routine_parameter();
@@ -2113,10 +2070,10 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
break;
}
- srp->set_required_privilege(spvar->mode == sp_param_inout);
+ srp->set_required_privilege(spvar->mode == sp_variable::MODE_INOUT);
}
- if (spvar->mode == sp_param_out)
+ if (spvar->mode == sp_variable::MODE_OUT)
{
Item_null *null_item= new Item_null();
Item *tmp_item= null_item;
@@ -2124,6 +2081,7 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
if (!null_item ||
nctx->set_variable(thd, i, &tmp_item))
{
+ DBUG_PRINT("error", ("set variable failed"));
err_status= TRUE;
break;
}
@@ -2132,6 +2090,7 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
{
if (nctx->set_variable(thd, i, it_args.ref()))
{
+ DBUG_PRINT("error", ("set variable 2 failed"));
err_status= TRUE;
break;
}
@@ -2147,9 +2106,9 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
if (!thd->in_sub_stmt)
{
- thd->stmt_da->can_overwrite_status= TRUE;
+ thd->get_stmt_da()->set_overwrite_status(true);
thd->is_error() ? trans_rollback_stmt(thd) : trans_commit_stmt(thd);
- thd->stmt_da->can_overwrite_status= FALSE;
+ thd->get_stmt_da()->set_overwrite_status(false);
}
thd_proc_info(thd, "closing tables");
@@ -2196,7 +2155,10 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
#endif
if (!err_status)
+ {
err_status= execute(thd, TRUE);
+ DBUG_PRINT("info", ("execute returned %d", (int) err_status));
+ }
if (save_log_general)
thd->variables.option_bits &= ~OPTION_LOG_OFF;
@@ -2224,9 +2186,9 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
if (!arg_item)
break;
- sp_variable_t *spvar= m_pcont->find_variable(i);
+ sp_variable *spvar= m_pcont->find_variable(i);
- if (spvar->mode == sp_param_in)
+ if (spvar->mode == sp_variable::MODE_IN)
continue;
Settable_routine_parameter *srp=
@@ -2236,6 +2198,7 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
if (srp->set_value(thd, octx, nctx->get_item_addr(i)))
{
+ DBUG_PRINT("error", ("set value failed"));
err_status= TRUE;
break;
}
@@ -2367,6 +2330,11 @@ sp_head::restore_lex(THD *thd)
*/
if (sp_update_sp_used_routines(&m_sroutines, &sublex->sroutines))
DBUG_RETURN(TRUE);
+
+ /* If this substatement is a update query, then mark MODIFIES_DATA */
+ if (is_update_query(sublex->sql_command))
+ m_flags|= MODIFIES_DATA;
+
/*
Merge tables used by this statement (but not by its functions or
procedures) to multiset of tables used by this routine.
@@ -2386,7 +2354,7 @@ sp_head::restore_lex(THD *thd)
Put the instruction on the backpatch list, associated with the label.
*/
int
-sp_head::push_backpatch(sp_instr *i, sp_label_t *lab)
+sp_head::push_backpatch(sp_instr *i, sp_label *lab)
{
bp_t *bp= (bp_t *)sql_alloc(sizeof(bp_t));
@@ -2402,7 +2370,7 @@ sp_head::push_backpatch(sp_instr *i, sp_label_t *lab)
the current position.
*/
void
-sp_head::backpatch(sp_label_t *lab)
+sp_head::backpatch(sp_label *lab)
{
bp_t *bp;
uint dest= instructions();
@@ -2414,7 +2382,7 @@ sp_head::backpatch(sp_label_t *lab)
if (bp->lab == lab)
{
DBUG_PRINT("info", ("backpatch: (m_ip %d, label 0x%lx <%s>) to dest %d",
- bp->instr->m_ip, (ulong) lab, lab->name, dest));
+ bp->instr->m_ip, (ulong) lab, lab->name.str, dest));
bp->instr->backpatch(dest, lab->ctx);
}
}
@@ -2443,7 +2411,6 @@ sp_head::fill_field_definition(THD *thd, LEX *lex,
{
LEX_STRING cmt = { 0, 0 };
uint unused1= 0;
- int unused2= 0;
if (field_def->init(thd, (char*) "", field_type, lex->length, lex->dec,
lex->type, (Item*) 0, (Item*) 0, &cmt, 0,
@@ -2451,7 +2418,7 @@ sp_head::fill_field_definition(THD *thd, LEX *lex,
lex->charset ? lex->charset :
thd->variables.collation_database,
lex->uint_geom_type,
- lex->vcol_info, NULL))
+ lex->vcol_info, NULL, FALSE))
return TRUE;
if (field_def->interval_list.elements)
@@ -2460,8 +2427,7 @@ sp_head::fill_field_definition(THD *thd, LEX *lex,
sp_prepare_create_field(thd, field_def);
- if (prepare_create_field(field_def, &unused1, &unused2, &unused2,
- HA_CAN_GEOMETRY))
+ if (prepare_create_field(field_def, &unused1, HA_CAN_GEOMETRY))
{
return TRUE;
}
@@ -2532,8 +2498,13 @@ sp_head::set_definer(const char *definer, uint definerlen)
char host_name_holder[HOSTNAME_LENGTH + 1];
LEX_STRING host_name= { host_name_holder, HOSTNAME_LENGTH };
- parse_user(definer, definerlen, user_name.str, &user_name.length,
- host_name.str, &host_name.length);
+ if (parse_user(definer, definerlen, user_name.str, &user_name.length,
+ host_name.str, &host_name.length) &&
+ user_name.length && !host_name.length)
+ {
+ // 'user@' -> 'user@%'
+ host_name= host_not_specified;
+ }
set_definer(&user_name, &host_name);
}
@@ -2683,7 +2654,7 @@ sp_head::show_create_routine(THD *thd, int type)
Item_empty_string *stmt_fld=
new Item_empty_string(col3_caption,
- max(m_defstr.length, 1024));
+ MY_MAX(m_defstr.length, 1024));
stmt_fld->maybe_null= TRUE;
@@ -2883,7 +2854,7 @@ sp_head::show_routine_code(THD *thd)
field_list.push_back(new Item_uint("Pos", 9));
// 1024 is for not to confuse old clients
field_list.push_back(new Item_empty_string("Instruction",
- max(buffer.length(), 1024)));
+ MY_MAX(buffer.length(), 1024)));
if (protocol->send_result_set_metadata(&field_list, Protocol::SEND_NUM_ROWS |
Protocol::SEND_EOF))
DBUG_RETURN(1);
@@ -2904,7 +2875,7 @@ sp_head::show_routine_code(THD *thd)
Since this is for debugging purposes only, we don't bother to
introduce a special error code for it.
*/
- push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_UNKNOWN_ERROR, tmp);
+ push_warning(thd, Sql_condition::WARN_LEVEL_WARN, ER_UNKNOWN_ERROR, tmp);
}
protocol->prepare_for_resend();
protocol->store((longlong)ip);
@@ -3011,9 +2982,9 @@ sp_lex_keeper::reset_lex_and_exec_core(THD *thd, uint *nextp,
/* Here we also commit or rollback the current statement. */
if (! thd->in_sub_stmt)
{
- thd->stmt_da->can_overwrite_status= TRUE;
+ thd->get_stmt_da()->set_overwrite_status(true);
thd->is_error() ? trans_rollback_stmt(thd) : trans_commit_stmt(thd);
- thd->stmt_da->can_overwrite_status= FALSE;
+ thd->get_stmt_da()->set_overwrite_status(false);
}
thd_proc_info(thd, "closing tables");
close_thread_tables(thd);
@@ -3032,6 +3003,8 @@ sp_lex_keeper::reset_lex_and_exec_core(THD *thd, uint *nextp,
thd->mdl_context.release_statement_locks();
}
}
+ //TODO: why is this here if log_slow_query is in sp_instr_stmt_execute?
+ delete_explain_query(m_lex);
if (m_lex->query_tables_own_last)
{
@@ -3055,10 +3028,10 @@ sp_lex_keeper::reset_lex_and_exec_core(THD *thd, uint *nextp,
open_tables stage.
*/
if (!res || !thd->is_error() ||
- (thd->stmt_da->sql_errno() != ER_CANT_REOPEN_TABLE &&
- thd->stmt_da->sql_errno() != ER_NO_SUCH_TABLE &&
- thd->stmt_da->sql_errno() != ER_NO_SUCH_TABLE_IN_ENGINE &&
- thd->stmt_da->sql_errno() != ER_UPDATE_TABLE_USED))
+ (thd->get_stmt_da()->sql_errno() != ER_CANT_REOPEN_TABLE &&
+ thd->get_stmt_da()->sql_errno() != ER_NO_SUCH_TABLE &&
+ thd->get_stmt_da()->sql_errno() != ER_NO_SUCH_TABLE_IN_ENGINE &&
+ thd->get_stmt_da()->sql_errno() != ER_UPDATE_TABLE_USED))
thd->stmt_arena->state= Query_arena::STMT_EXECUTED;
/*
@@ -3091,7 +3064,8 @@ 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, FALSE, UINT_MAX, FALSE)
+ if (open_temporary_tables(thd, tables) ||
+ check_table_access(thd, SELECT_ACL, tables, FALSE, UINT_MAX, FALSE)
|| open_and_lock_tables(thd, tables, TRUE, 0))
result= -1;
else
@@ -3103,7 +3077,7 @@ int sp_instr::exec_open_and_lock_tables(THD *thd, TABLE_LIST *tables)
return result;
}
-uint sp_instr::get_cont_dest()
+uint sp_instr::get_cont_dest() const
{
return (m_ip+1);
}
@@ -3145,7 +3119,7 @@ sp_instr_stmt::execute(THD *thd, uint *nextp)
{
res= m_lex_keeper.reset_lex_and_exec_core(thd, nextp, FALSE, this);
- if (thd->stmt_da->is_eof())
+ if (thd->get_stmt_da()->is_eof())
{
/* Finalize server status flags after executing a statement. */
thd->update_server_status();
@@ -3155,6 +3129,11 @@ sp_instr_stmt::execute(THD *thd, uint *nextp)
query_cache_end_of_result(thd);
+ mysql_audit_general(thd, MYSQL_AUDIT_GENERAL_STATUS,
+ thd->get_stmt_da()->is_error() ?
+ thd->get_stmt_da()->sql_errno() : 0,
+ command_name[COM_QUERY].str);
+
if (!res && unlikely(thd->enable_slow_log))
log_slow_statement(thd);
}
@@ -3172,7 +3151,10 @@ sp_instr_stmt::execute(THD *thd, uint *nextp)
thd->query_name_consts= 0;
if (!thd->is_error())
- thd->stmt_da->reset_diagnostics_area();
+ {
+ res= 0;
+ thd->get_stmt_da()->reset_diagnostics_area();
+ }
}
DBUG_RETURN(res || thd->is_error());
}
@@ -3255,6 +3237,7 @@ sp_instr_set::exec_core(THD *thd, uint *nextp)
my_error(ER_OUT_OF_RESOURCES, MYF(ME_FATALERROR));
}
}
+ delete_explain_query(thd->lex);
*nextp = m_ip+1;
return res;
@@ -3265,7 +3248,7 @@ sp_instr_set::print(String *str)
{
/* set name@offset ... */
int rsrv = SP_INSTR_UINT_MAXLEN+6;
- sp_variable_t *var = m_ctx->find_variable(m_offset);
+ sp_variable *var = m_ctx->find_variable(m_offset);
/* 'var' should always be non-null, but just in case... */
if (var)
@@ -3318,7 +3301,7 @@ sp_instr_set_trigger_field::print(String *str)
sp_instr_opt_meta
*/
-uint sp_instr_opt_meta::get_cont_dest()
+uint sp_instr_opt_meta::get_cont_dest() const
{
return m_cont_dest;
}
@@ -3499,6 +3482,14 @@ int
sp_instr_freturn::exec_core(THD *thd, uint *nextp)
{
/*
+ RETURN is a "procedure statement" (in terms of the SQL standard).
+ That means, Diagnostics Area should be clean before its execution.
+ */
+
+ Diagnostics_area *da= thd->get_stmt_da();
+ da->clear_warning_info(da->warning_info_id());
+
+ /*
Change <next instruction pointer>, so that this will be the last
instruction in the stored function.
*/
@@ -3536,14 +3527,12 @@ int
sp_instr_hpush_jump::execute(THD *thd, uint *nextp)
{
DBUG_ENTER("sp_instr_hpush_jump::execute");
- List_iterator_fast<sp_cond_type_t> li(m_cond);
- sp_cond_type_t *p;
- while ((p= li++))
- thd->spcont->push_handler(p, m_ip+1, m_type);
+ int ret= thd->spcont->push_handler(m_handler, m_ip + 1);
*nextp= m_dest;
- DBUG_RETURN(0);
+
+ DBUG_RETURN(ret);
}
@@ -3553,27 +3542,22 @@ sp_instr_hpush_jump::print(String *str)
/* hpush_jump dest fsize type */
if (str->reserve(SP_INSTR_UINT_MAXLEN*2 + 21))
return;
+
str->qs_append(STRING_WITH_LEN("hpush_jump "));
str->qs_append(m_dest);
str->qs_append(' ');
str->qs_append(m_frame);
- switch (m_type) {
- case SP_HANDLER_NONE:
- str->qs_append(STRING_WITH_LEN(" NONE")); // This would be a bug
- break;
- case SP_HANDLER_EXIT:
+
+ switch (m_handler->type) {
+ case sp_handler::EXIT:
str->qs_append(STRING_WITH_LEN(" EXIT"));
break;
- case SP_HANDLER_CONTINUE:
+ case sp_handler::CONTINUE:
str->qs_append(STRING_WITH_LEN(" CONTINUE"));
break;
- case SP_HANDLER_UNDO:
- str->qs_append(STRING_WITH_LEN(" UNDO"));
- break;
default:
- // This would be a bug as well
- str->qs_append(STRING_WITH_LEN(" UNKNOWN:"));
- str->qs_append(m_type);
+ // The handler type must be either CONTINUE or EXIT.
+ DBUG_ASSERT(0);
}
}
@@ -3601,7 +3585,7 @@ sp_instr_hpush_jump::opt_mark(sp_head *sp, List<sp_instr> *leads)
above, so we start on m_dest+1 here.
m_opt_hpop is the hpop marking the end of the handler scope.
*/
- if (m_type == SP_HANDLER_CONTINUE)
+ if (m_handler->type == sp_handler::CONTINUE)
{
for (uint scope_ip= m_dest+1; scope_ip <= m_opt_hpop; scope_ip++)
sp->add_mark_lead(scope_ip, leads);
@@ -3643,13 +3627,11 @@ int
sp_instr_hreturn::execute(THD *thd, uint *nextp)
{
DBUG_ENTER("sp_instr_hreturn::execute");
- if (m_dest)
- *nextp= m_dest;
- else
- {
- *nextp= thd->spcont->pop_hstack();
- }
- thd->spcont->exit_handler();
+
+ uint continue_ip= thd->spcont->exit_handler(thd->get_stmt_da());
+
+ *nextp= m_dest ? m_dest : continue_ip;
+
DBUG_RETURN(0);
}
@@ -3661,12 +3643,17 @@ sp_instr_hreturn::print(String *str)
if (str->reserve(SP_INSTR_UINT_MAXLEN*2 + 9))
return;
str->qs_append(STRING_WITH_LEN("hreturn "));
- str->qs_append(m_frame);
if (m_dest)
{
- str->qs_append(' ');
+ // NOTE: this is legacy: hreturn instruction for EXIT handler
+ // should print out 0 as frame index.
+ str->qs_append(STRING_WITH_LEN("0 "));
str->qs_append(m_dest);
}
+ else
+ {
+ str->qs_append(m_frame);
+ }
}
@@ -3698,41 +3685,32 @@ sp_instr_hreturn::opt_mark(sp_head *sp, List<sp_instr> *leads)
int
sp_instr_cpush::execute(THD *thd, uint *nextp)
{
- Query_arena backup_arena;
DBUG_ENTER("sp_instr_cpush::execute");
- /*
- We should create cursors in the callers arena, as
- it could be (and usually is) used in several instructions.
- */
- thd->set_n_backup_active_arena(thd->spcont->callers_arena, &backup_arena);
-
- thd->spcont->push_cursor(&m_lex_keeper, this);
-
- thd->restore_active_arena(thd->spcont->callers_arena, &backup_arena);
+ int ret= thd->spcont->push_cursor(&m_lex_keeper, this);
*nextp= m_ip+1;
- DBUG_RETURN(0);
+ DBUG_RETURN(ret);
}
void
sp_instr_cpush::print(String *str)
{
- LEX_STRING n;
- my_bool found= m_ctx->find_cursor(m_cursor, &n);
+ const LEX_STRING *cursor_name= m_ctx->find_cursor(m_cursor);
+
/* cpush name@offset */
uint rsrv= SP_INSTR_UINT_MAXLEN+7;
- if (found)
- rsrv+= n.length;
+ if (cursor_name)
+ rsrv+= cursor_name->length;
if (str->reserve(rsrv))
return;
str->qs_append(STRING_WITH_LEN("cpush "));
- if (found)
+ if (cursor_name)
{
- str->qs_append(n.str, n.length);
+ str->qs_append(cursor_name->str, cursor_name->length);
str->qs_append('@');
}
str->qs_append(m_cursor);
@@ -3820,19 +3798,19 @@ sp_instr_copen::exec_core(THD *thd, uint *nextp)
void
sp_instr_copen::print(String *str)
{
- LEX_STRING n;
- my_bool found= m_ctx->find_cursor(m_cursor, &n);
+ const LEX_STRING *cursor_name= m_ctx->find_cursor(m_cursor);
+
/* copen name@offset */
uint rsrv= SP_INSTR_UINT_MAXLEN+7;
- if (found)
- rsrv+= n.length;
+ if (cursor_name)
+ rsrv+= cursor_name->length;
if (str->reserve(rsrv))
return;
str->qs_append(STRING_WITH_LEN("copen "));
- if (found)
+ if (cursor_name)
{
- str->qs_append(n.str, n.length);
+ str->qs_append(cursor_name->str, cursor_name->length);
str->qs_append('@');
}
str->qs_append(m_cursor);
@@ -3862,19 +3840,19 @@ sp_instr_cclose::execute(THD *thd, uint *nextp)
void
sp_instr_cclose::print(String *str)
{
- LEX_STRING n;
- my_bool found= m_ctx->find_cursor(m_cursor, &n);
+ const LEX_STRING *cursor_name= m_ctx->find_cursor(m_cursor);
+
/* cclose name@offset */
uint rsrv= SP_INSTR_UINT_MAXLEN+8;
- if (found)
- rsrv+= n.length;
+ if (cursor_name)
+ rsrv+= cursor_name->length;
if (str->reserve(rsrv))
return;
str->qs_append(STRING_WITH_LEN("cclose "));
- if (found)
+ if (cursor_name)
{
- str->qs_append(n.str, n.length);
+ str->qs_append(cursor_name->str, cursor_name->length);
str->qs_append('@');
}
str->qs_append(m_cursor);
@@ -3903,21 +3881,21 @@ sp_instr_cfetch::execute(THD *thd, uint *nextp)
void
sp_instr_cfetch::print(String *str)
{
- List_iterator_fast<struct sp_variable> li(m_varlist);
- sp_variable_t *pv;
- LEX_STRING n;
- my_bool found= m_ctx->find_cursor(m_cursor, &n);
+ List_iterator_fast<sp_variable> li(m_varlist);
+ sp_variable *pv;
+ const LEX_STRING *cursor_name= m_ctx->find_cursor(m_cursor);
+
/* cfetch name@offset vars... */
uint rsrv= SP_INSTR_UINT_MAXLEN+8;
- if (found)
- rsrv+= n.length;
+ if (cursor_name)
+ rsrv+= cursor_name->length;
if (str->reserve(rsrv))
return;
str->qs_append(STRING_WITH_LEN("cfetch "));
- if (found)
+ if (cursor_name)
{
- str->qs_append(n.str, n.length);
+ str->qs_append(cursor_name->str, cursor_name->length);
str->qs_append('@');
}
str->qs_append(m_cursor);
@@ -4162,7 +4140,7 @@ sp_head::merge_table_list(THD *thd, TABLE_LIST *table, LEX *lex_for_tmp_check)
return FALSE;
if (lex_for_tmp_check->sql_command == SQLCOM_CREATE_TABLE &&
lex_for_tmp_check->query_tables == table &&
- lex_for_tmp_check->create_info.options & HA_LEX_CREATE_TMP_TABLE)
+ lex_for_tmp_check->create_info.tmp_table())
{
tab->temp= TRUE;
tab->qname.length= temp_table_key_length;
diff --git a/sql/sp_head.h b/sql/sp_head.h
index 409db33ef02..dbdb957aa79 100644
--- a/sql/sp_head.h
+++ b/sql/sp_head.h
@@ -30,8 +30,9 @@
#include "my_global.h" /* NO_EMBEDDED_ACCESS_CHECKS */
#include "sql_class.h" // THD, set_var.h: THD
#include "set_var.h" // Item
-#include "sp.h"
+#include "sp_pcontext.h" // sp_pcontext
#include <stddef.h>
+#include "sp.h"
/**
@defgroup Stored_Routines Stored Routines
@@ -39,6 +40,11 @@
@{
*/
+// Values for the type enum. This reflects the order of the enum declaration
+// in the CREATE TABLE command.
+//#define TYPE_ENUM_FUNCTION 1 #define TYPE_ENUM_PROCEDURE 2 #define
+//TYPE_ENUM_TRIGGER 3 #define TYPE_ENUM_PROXY 4
+
Item_result
sp_map_result_type(enum enum_field_types type);
@@ -48,12 +54,9 @@ sp_map_item_type(enum enum_field_types type);
uint
sp_get_flags_for_command(LEX *lex);
-struct sp_label;
class sp_instr;
class sp_instr_opt_meta;
class sp_instr_jump_if_not;
-struct sp_cond_type;
-struct sp_variable;
/*************************************************************************/
@@ -119,6 +122,8 @@ public:
sp_name(LEX_STRING db, LEX_STRING name, bool use_explicit_name)
: m_db(db), m_name(name), m_explicit_name(use_explicit_name)
{
+ if (lower_case_table_names && m_db.str)
+ m_db.length= my_casedn_str(files_charset_info, m_db.str);
m_qname.str= 0;
m_qname.length= 0;
}
@@ -156,7 +161,21 @@ public:
LOG_SLOW_STATEMENTS= 256, // Used by events
LOG_GENERAL_LOG= 512, // Used by events
HAS_SQLCOM_RESET= 1024,
- HAS_SQLCOM_FLUSH= 2048
+ HAS_SQLCOM_FLUSH= 2048,
+
+ /**
+ Marks routines that directly (i.e. not by calling other routines)
+ change tables. Note that this flag is set automatically based on
+ type of statements used in the stored routine and is different
+ from routine characteristic provided by user in a form of CONTAINS
+ SQL, READS SQL DATA, MODIFIES SQL DATA clauses. The latter are
+ accepted by parser but pretty much ignored after that.
+ We don't rely on them:
+ a) for compatibility reasons.
+ b) because in CONTAINS SQL case they don't provide enough
+ information anyway.
+ */
+ MODIFIES_DATA= 4096
};
stored_procedure_type m_type;
@@ -274,6 +293,15 @@ public:
*/
Security_context m_security_ctx;
+ /**
+ List of all items (Item_trigger_field objects) representing fields in
+ old/new version of row in trigger. We use this list for checking whenever
+ all such fields are valid at trigger creation time and for binding these
+ fields to TABLE object at table open (although for latter pointer to table
+ being opened is probably enough).
+ */
+ SQL_I_List<Item_trigger_field> m_trg_table_fields;
+
static void *
operator new(size_t size) throw ();
@@ -318,11 +346,17 @@ public:
int
add_instr(sp_instr *instr);
- inline uint
- instructions()
- {
- return m_instr.elements;
- }
+ /**
+ Returns true if any substatement in the routine directly
+ (not through another routine) modifies data/changes table.
+
+ @sa Comment for MODIFIES_DATA flag.
+ */
+ bool modifies_data() const
+ { return m_flags & MODIFIES_DATA; }
+
+ inline uint instructions()
+ { return m_instr.elements; }
inline sp_instr *
last_instruction()
@@ -352,12 +386,12 @@ public:
/// Put the instruction on the backpatch list, associated with the label.
int
- push_backpatch(sp_instr *, struct sp_label *);
+ push_backpatch(sp_instr *, sp_label *);
/// Update all instruction with this label in the backpatch list to
/// the current position.
void
- backpatch(struct sp_label *);
+ backpatch(sp_label *);
/// Start a new cont. backpatch level. If 'i' is NULL, the level is just incr.
int
@@ -450,9 +484,10 @@ public:
else if (m_flags & HAS_SQLCOM_FLUSH)
my_error(ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG, MYF(0), "FLUSH");
- return test(m_flags &
- (CONTAINS_DYNAMIC_SQL|MULTI_RESULTS|HAS_SET_AUTOCOMMIT_STMT|
- HAS_COMMIT_OR_ROLLBACK|HAS_SQLCOM_RESET|HAS_SQLCOM_FLUSH));
+ return MY_TEST(m_flags &
+ (CONTAINS_DYNAMIC_SQL | MULTI_RESULTS |
+ HAS_SET_AUTOCOMMIT_STMT | HAS_COMMIT_OR_ROLLBACK |
+ HAS_SQLCOM_RESET | HAS_SQLCOM_FLUSH));
}
#ifndef DBUG_OFF
@@ -493,7 +528,7 @@ private:
DYNAMIC_ARRAY m_instr; ///< The "instructions"
typedef struct
{
- struct sp_label *lab;
+ sp_label *lab;
sp_instr *instr;
} bp_t;
List<bp_t> m_backpatch; ///< Instructions needing backpatching
@@ -593,7 +628,7 @@ public:
Get the continuation destination of this instruction.
@return the continuation destination
*/
- virtual uint get_cont_dest();
+ virtual uint get_cont_dest() const;
/*
Execute core function of instruction after all preparations (e.g.
@@ -865,7 +900,7 @@ public:
virtual void set_destination(uint old_dest, uint new_dest)
= 0;
- virtual uint get_cont_dest();
+ virtual uint get_cont_dest() const;
protected:
@@ -1016,15 +1051,21 @@ class sp_instr_hpush_jump : public sp_instr_jump
public:
- sp_instr_hpush_jump(uint ip, sp_pcontext *ctx, int htype, uint fp)
- : sp_instr_jump(ip, ctx), m_type(htype), m_frame(fp), m_opt_hpop(0)
+ sp_instr_hpush_jump(uint ip,
+ sp_pcontext *ctx,
+ sp_handler *handler)
+ :sp_instr_jump(ip, ctx),
+ m_handler(handler),
+ m_opt_hpop(0),
+ m_frame(ctx->current_var_count())
{
- m_cond.empty();
+ DBUG_ASSERT(m_handler->condition_values.elements == 0);
}
virtual ~sp_instr_hpush_jump()
{
- m_cond.empty();
+ m_handler->condition_values.empty();
+ m_handler= NULL;
}
virtual int execute(THD *thd, uint *nextp);
@@ -1048,17 +1089,24 @@ public:
m_opt_hpop= dest;
}
- inline void add_condition(struct sp_cond_type *cond)
- {
- m_cond.push_front(cond);
- }
+ void add_condition(sp_condition_value *condition_value)
+ { m_handler->condition_values.push_back(condition_value); }
+
+ sp_handler *get_handler()
+ { return m_handler; }
+
+private:
private:
+ /// Handler.
+ sp_handler *m_handler;
+
+ /// hpop marking end of handler scope.
+ uint m_opt_hpop;
- int m_type; ///< Handler type
+ // This attribute is needed for SHOW PROCEDURE CODE only (i.e. it's needed in
+ // debug version only). It's used in print().
uint m_frame;
- uint m_opt_hpop; // hpop marking end of handler scope.
- List<struct sp_cond_type> m_cond;
}; // class sp_instr_hpush_jump : public sp_instr_jump
@@ -1095,8 +1143,9 @@ class sp_instr_hreturn : public sp_instr_jump
public:
- sp_instr_hreturn(uint ip, sp_pcontext *ctx, uint fp)
- : sp_instr_jump(ip, ctx), m_frame(fp)
+ sp_instr_hreturn(uint ip, sp_pcontext *ctx)
+ :sp_instr_jump(ip, ctx),
+ m_frame(ctx->current_var_count())
{}
virtual ~sp_instr_hreturn()
@@ -1251,7 +1300,7 @@ public:
virtual void print(String *str);
- void add_to_varlist(struct sp_variable *var)
+ void add_to_varlist(sp_variable *var)
{
m_varlist.push_back(var);
}
@@ -1259,7 +1308,7 @@ public:
private:
uint m_cursor;
- List<struct sp_variable> m_varlist;
+ List<sp_variable> m_varlist;
}; // class sp_instr_cfetch : public sp_instr
diff --git a/sql/sp_pcontext.cc b/sql/sp_pcontext.cc
index 4c5087eaf27..11954921e06 100644
--- a/sql/sp_pcontext.cc
+++ b/sql/sp_pcontext.cc
@@ -13,6 +13,7 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+#include <my_global.h>
#include "sql_priv.h"
#include "unireg.h"
#ifdef USE_PRAGMA_IMPLEMENTATION
@@ -22,133 +23,86 @@
#include "sp_pcontext.h"
#include "sp_head.h"
-/* Initial size for the dynamic arrays in sp_pcontext */
-#define PCONTEXT_ARRAY_INIT_ALLOC 16
-/* Increment size for the dynamic arrays in sp_pcontext */
-#define PCONTEXT_ARRAY_INCREMENT_ALLOC 8
-
-/*
- Sanity check for SQLSTATEs. Will not check if it's really an existing
- state (there are just too many), but will check length and bad characters.
- Returns TRUE if it's ok, FALSE if it's bad.
-*/
-bool
-sp_cond_check(LEX_STRING *sqlstate)
+bool sp_condition_value::equals(const sp_condition_value *cv) const
{
- int i;
- const char *p;
+ DBUG_ASSERT(cv);
- if (sqlstate->length != 5)
- return FALSE;
- for (p= sqlstate->str, i= 0 ; i < 5 ; i++)
+ if (this == cv)
+ return true;
+
+ if (type != cv->type)
+ return false;
+
+ switch (type)
{
- char c = p[i];
+ case sp_condition_value::ERROR_CODE:
+ return (mysqlerr == cv->mysqlerr);
+
+ case sp_condition_value::SQLSTATE:
+ return (strcmp(sql_state, cv->sql_state) == 0);
- if ((c < '0' || '9' < c) &&
- (c < 'A' || 'Z' < c))
- return FALSE;
+ default:
+ return true;
}
- /* SQLSTATE class '00' : completion condition */
- if (strncmp(sqlstate->str, "00", 2) == 0)
- return FALSE;
- return TRUE;
}
+
+void sp_pcontext::init(uint var_offset,
+ uint cursor_offset,
+ int num_case_expressions)
+{
+ m_var_offset= var_offset;
+ m_cursor_offset= cursor_offset;
+ m_num_case_exprs= num_case_expressions;
+
+ m_labels.empty();
+}
+
+
sp_pcontext::sp_pcontext()
: Sql_alloc(),
- m_max_var_index(0), m_max_cursor_index(0), m_max_handler_index(0),
- m_context_handlers(0), m_parent(NULL), m_pboundary(0),
- m_label_scope(LABEL_DEFAULT_SCOPE)
+ m_max_var_index(0), m_max_cursor_index(0),
+ m_parent(NULL), m_pboundary(0),
+ m_scope(REGULAR_SCOPE)
{
- (void) my_init_dynamic_array(&m_vars, sizeof(sp_variable_t *),
- PCONTEXT_ARRAY_INIT_ALLOC,
- PCONTEXT_ARRAY_INCREMENT_ALLOC);
- (void) my_init_dynamic_array(&m_case_expr_id_lst, sizeof(int),
- PCONTEXT_ARRAY_INIT_ALLOC,
- PCONTEXT_ARRAY_INCREMENT_ALLOC);
- (void) my_init_dynamic_array(&m_conds, sizeof(sp_cond_type_t *),
- PCONTEXT_ARRAY_INIT_ALLOC,
- PCONTEXT_ARRAY_INCREMENT_ALLOC);
- (void) my_init_dynamic_array(&m_cursors, sizeof(LEX_STRING),
- PCONTEXT_ARRAY_INIT_ALLOC,
- PCONTEXT_ARRAY_INCREMENT_ALLOC);
- (void) my_init_dynamic_array(&m_handlers, sizeof(sp_cond_type_t *),
- PCONTEXT_ARRAY_INIT_ALLOC,
- PCONTEXT_ARRAY_INCREMENT_ALLOC);
- m_label.empty();
- m_children.empty();
-
- m_var_offset= m_cursor_offset= 0;
- m_num_case_exprs= 0;
+ init(0, 0, 0);
}
-sp_pcontext::sp_pcontext(sp_pcontext *prev, label_scope_type label_scope)
+
+sp_pcontext::sp_pcontext(sp_pcontext *prev, sp_pcontext::enum_scope scope)
: Sql_alloc(),
- m_max_var_index(0), m_max_cursor_index(0), m_max_handler_index(0),
- m_context_handlers(0), m_parent(prev), m_pboundary(0),
- m_label_scope(label_scope)
+ m_max_var_index(0), m_max_cursor_index(0),
+ m_parent(prev), m_pboundary(0),
+ m_scope(scope)
{
- (void) my_init_dynamic_array(&m_vars, sizeof(sp_variable_t *),
- PCONTEXT_ARRAY_INIT_ALLOC,
- PCONTEXT_ARRAY_INCREMENT_ALLOC);
- (void) my_init_dynamic_array(&m_case_expr_id_lst, sizeof(int),
- PCONTEXT_ARRAY_INIT_ALLOC,
- PCONTEXT_ARRAY_INCREMENT_ALLOC);
- (void) my_init_dynamic_array(&m_conds, sizeof(sp_cond_type_t *),
- PCONTEXT_ARRAY_INIT_ALLOC,
- PCONTEXT_ARRAY_INCREMENT_ALLOC);
- (void) my_init_dynamic_array(&m_cursors, sizeof(LEX_STRING),
- PCONTEXT_ARRAY_INIT_ALLOC,
- PCONTEXT_ARRAY_INCREMENT_ALLOC);
- (void) my_init_dynamic_array(&m_handlers, sizeof(sp_cond_type_t *),
- PCONTEXT_ARRAY_INIT_ALLOC,
- PCONTEXT_ARRAY_INCREMENT_ALLOC);
- m_label.empty();
- m_children.empty();
-
- m_var_offset= prev->m_var_offset + prev->m_max_var_index;
- m_cursor_offset= prev->current_cursor_count();
- m_num_case_exprs= prev->get_num_case_exprs();
+ init(prev->m_var_offset + prev->m_max_var_index,
+ prev->current_cursor_count(),
+ prev->get_num_case_exprs());
}
-void
-sp_pcontext::destroy()
+
+sp_pcontext::~sp_pcontext()
{
- List_iterator_fast<sp_pcontext> li(m_children);
- sp_pcontext *child;
-
- while ((child= li++))
- child->destroy();
-
- m_children.empty();
- m_label.empty();
- delete_dynamic(&m_vars);
- delete_dynamic(&m_case_expr_id_lst);
- delete_dynamic(&m_conds);
- delete_dynamic(&m_cursors);
- delete_dynamic(&m_handlers);
+ for (size_t i= 0; i < m_children.elements(); ++i)
+ delete m_children.at(i);
}
-sp_pcontext *
-sp_pcontext::push_context(label_scope_type label_scope)
+
+sp_pcontext *sp_pcontext::push_context(THD *thd, sp_pcontext::enum_scope scope)
{
- sp_pcontext *child= new sp_pcontext(this, label_scope);
+ sp_pcontext *child= new (thd->mem_root) sp_pcontext(this, scope);
if (child)
- m_children.push_back(child);
+ m_children.append(child);
return child;
}
-sp_pcontext *
-sp_pcontext::pop_context()
+
+sp_pcontext *sp_pcontext::pop_context()
{
m_parent->m_max_var_index+= m_max_var_index;
- uint submax= max_handler_index();
- if (submax > m_parent->m_max_handler_index)
- m_parent->m_max_handler_index= submax;
-
- submax= max_cursor_index();
+ uint submax= max_cursor_index();
if (submax > m_parent->m_max_cursor_index)
m_parent->m_max_cursor_index= submax;
@@ -158,142 +112,118 @@ sp_pcontext::pop_context()
return m_parent;
}
-uint
-sp_pcontext::diff_handlers(sp_pcontext *ctx, bool exclusive)
+
+uint sp_pcontext::diff_handlers(const sp_pcontext *ctx, bool exclusive) const
{
uint n= 0;
- sp_pcontext *pctx= this;
- sp_pcontext *last_ctx= NULL;
+ const sp_pcontext *pctx= this;
+ const sp_pcontext *last_ctx= NULL;
while (pctx && pctx != ctx)
{
- n+= pctx->m_context_handlers;
+ n+= pctx->m_handlers.elements();
last_ctx= pctx;
pctx= pctx->parent_context();
}
if (pctx)
- return (exclusive && last_ctx ? n - last_ctx->m_context_handlers : n);
+ return (exclusive && last_ctx ? n - last_ctx->m_handlers.elements() : n);
return 0; // Didn't find ctx
}
-uint
-sp_pcontext::diff_cursors(sp_pcontext *ctx, bool exclusive)
+
+uint sp_pcontext::diff_cursors(const sp_pcontext *ctx, bool exclusive) const
{
uint n= 0;
- sp_pcontext *pctx= this;
- sp_pcontext *last_ctx= NULL;
+ const sp_pcontext *pctx= this;
+ const sp_pcontext *last_ctx= NULL;
while (pctx && pctx != ctx)
{
- n+= pctx->m_cursors.elements;
+ n+= pctx->m_cursors.elements();
last_ctx= pctx;
pctx= pctx->parent_context();
}
if (pctx)
- return (exclusive && last_ctx ? n - last_ctx->m_cursors.elements : n);
+ return (exclusive && last_ctx ? n - last_ctx->m_cursors.elements() : n);
return 0; // Didn't find ctx
}
-/*
- This does a linear search (from newer to older variables, in case
- we have shadowed names).
- It's possible to have a more efficient allocation and search method,
- but it might not be worth it. The typical number of parameters and
- variables will in most cases be low (a handfull).
- ...and, this is only called during parsing.
-*/
-sp_variable_t *
-sp_pcontext::find_variable(LEX_STRING *name, my_bool scoped)
+
+sp_variable *sp_pcontext::find_variable(LEX_STRING name,
+ bool current_scope_only) const
{
- uint i= m_vars.elements - m_pboundary;
+ uint i= m_vars.elements() - m_pboundary;
while (i--)
{
- sp_variable_t *p;
+ sp_variable *p= m_vars.at(i);
- get_dynamic(&m_vars, (uchar*)&p, i);
if (my_strnncoll(system_charset_info,
- (const uchar *)name->str, name->length,
+ (const uchar *)name.str, name.length,
(const uchar *)p->name.str, p->name.length) == 0)
{
return p;
}
}
- if (!scoped && m_parent)
- return m_parent->find_variable(name, scoped);
- return NULL;
+
+ return (!current_scope_only && m_parent) ?
+ m_parent->find_variable(name, false) :
+ NULL;
}
-/*
- Find a variable by offset from the top.
- This used for two things:
- - When evaluating parameters at the beginning, and setting out parameters
- at the end, of invokation. (Top frame only, so no recursion then.)
- - For printing of sp_instr_set. (Debug mode only.)
-*/
-sp_variable_t *
-sp_pcontext::find_variable(uint offset)
+
+sp_variable *sp_pcontext::find_variable(uint offset) const
{
- if (m_var_offset <= offset && offset < m_var_offset + m_vars.elements)
- { // This frame
- sp_variable_t *p;
+ if (m_var_offset <= offset && offset < m_var_offset + m_vars.elements())
+ return m_vars.at(offset - m_var_offset); // This frame
- get_dynamic(&m_vars, (uchar*)&p, offset - m_var_offset);
- return p;
- }
- if (m_parent)
- return m_parent->find_variable(offset); // Some previous frame
- return NULL; // index out of bounds
+ return m_parent ?
+ m_parent->find_variable(offset) : // Some previous frame
+ NULL; // Index out of bounds
}
-sp_variable_t *
-sp_pcontext::push_variable(LEX_STRING *name, enum enum_field_types type,
- sp_param_mode_t mode)
+
+sp_variable *sp_pcontext::add_variable(THD *thd,
+ LEX_STRING name,
+ enum enum_field_types type,
+ sp_variable::enum_mode mode)
{
- sp_variable_t *p= (sp_variable_t *)sql_alloc(sizeof(sp_variable_t));
+ sp_variable *p=
+ new (thd->mem_root) sp_variable(name, type,mode, current_var_count());
if (!p)
return NULL;
++m_max_var_index;
- p->name.str= name->str;
- p->name.length= name->length;
- p->type= type;
- p->mode= mode;
- p->offset= current_var_count();
- p->dflt= NULL;
- if (insert_dynamic(&m_vars, (uchar*)&p))
- return NULL;
- return p;
+ return m_vars.append(p) ? NULL : p;
}
-sp_label_t *
-sp_pcontext::push_label(char *name, uint ip)
+sp_label *sp_pcontext::push_label(THD *thd, LEX_STRING name, uint ip)
{
- sp_label_t *lab = (sp_label_t *)sql_alloc(sizeof(sp_label_t));
+ sp_label *label=
+ new (thd->mem_root) sp_label(name, ip, sp_label::IMPLICIT, this);
- if (lab)
- {
- lab->name= name;
- lab->ip= ip;
- lab->type= SP_LAB_IMPL;
- lab->ctx= this;
- m_label.push_front(lab);
- }
- return lab;
+ if (!label)
+ return NULL;
+
+ m_labels.push_front(label);
+
+ return label;
}
-sp_label_t *
-sp_pcontext::find_label(char *name)
+
+sp_label *sp_pcontext::find_label(LEX_STRING name)
{
- List_iterator_fast<sp_label_t> li(m_label);
- sp_label_t *lab;
+ List_iterator_fast<sp_label> li(m_labels);
+ sp_label *lab;
while ((lab= li++))
- if (my_strcasecmp(system_charset_info, name, lab->name) == 0)
+ {
+ if (my_strcasecmp(system_charset_info, name.str, lab->name.str) == 0)
return lab;
+ }
/*
Note about exception handlers.
@@ -303,159 +233,253 @@ sp_pcontext::find_label(char *name)
In short, a DECLARE HANDLER block can not refer
to labels from the parent context, as they are out of scope.
*/
- if (m_parent && (m_label_scope == LABEL_DEFAULT_SCOPE))
- return m_parent->find_label(name);
- return NULL;
+ return (m_parent && (m_scope == REGULAR_SCOPE)) ?
+ m_parent->find_label(name) :
+ NULL;
}
-int
-sp_pcontext::push_cond(LEX_STRING *name, sp_cond_type_t *val)
+
+bool sp_pcontext::add_condition(THD *thd,
+ LEX_STRING name,
+ sp_condition_value *value)
{
- sp_cond_t *p= (sp_cond_t *)sql_alloc(sizeof(sp_cond_t));
+ sp_condition *p= new (thd->mem_root) sp_condition(name, value);
if (p == NULL)
- return 1;
- p->name.str= name->str;
- p->name.length= name->length;
- p->val= val;
- return insert_dynamic(&m_conds, (uchar *)&p);
+ return true;
+
+ return m_conditions.append(p);
}
-/*
- See comment for find_variable() above
-*/
-sp_cond_type_t *
-sp_pcontext::find_cond(LEX_STRING *name, my_bool scoped)
+
+sp_condition_value *sp_pcontext::find_condition(LEX_STRING name,
+ bool current_scope_only) const
{
- uint i= m_conds.elements;
+ uint i= m_conditions.elements();
while (i--)
{
- sp_cond_t *p;
+ sp_condition *p= m_conditions.at(i);
- get_dynamic(&m_conds, (uchar*)&p, i);
if (my_strnncoll(system_charset_info,
- (const uchar *)name->str, name->length,
- (const uchar *)p->name.str, p->name.length) == 0)
+ (const uchar *) name.str, name.length,
+ (const uchar *) p->name.str, p->name.length) == 0)
{
- return p->val;
+ return p->value;
}
}
- if (!scoped && m_parent)
- return m_parent->find_cond(name, scoped);
- return NULL;
+
+ return (!current_scope_only && m_parent) ?
+ m_parent->find_condition(name, false) :
+ NULL;
}
-/*
- This only searches the current context, for error checking of
- duplicates.
- Returns TRUE if found.
-*/
-bool
-sp_pcontext::find_handler(sp_cond_type_t *cond)
+
+sp_handler *sp_pcontext::add_handler(THD *thd,
+ sp_handler::enum_type type)
{
- uint i= m_handlers.elements;
+ sp_handler *h= new (thd->mem_root) sp_handler(type);
- while (i--)
+ if (!h)
+ return NULL;
+
+ return m_handlers.append(h) ? NULL : h;
+}
+
+
+bool sp_pcontext::check_duplicate_handler(
+ const sp_condition_value *cond_value) const
+{
+ for (size_t i= 0; i < m_handlers.elements(); ++i)
{
- sp_cond_type_t *p;
+ sp_handler *h= m_handlers.at(i);
+
+ List_iterator_fast<sp_condition_value> li(h->condition_values);
+ sp_condition_value *cv;
- get_dynamic(&m_handlers, (uchar*)&p, i);
- if (cond->type == p->type)
+ while ((cv= li++))
{
- switch (p->type)
+ if (cond_value->equals(cv))
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+sp_handler*
+sp_pcontext::find_handler(const char *sql_state,
+ uint sql_errno,
+ Sql_condition::enum_warning_level level) const
+{
+ sp_handler *found_handler= NULL;
+ sp_condition_value *found_cv= NULL;
+
+ for (size_t i= 0; i < m_handlers.elements(); ++i)
+ {
+ sp_handler *h= m_handlers.at(i);
+
+ List_iterator_fast<sp_condition_value> li(h->condition_values);
+ sp_condition_value *cv;
+
+ while ((cv= li++))
+ {
+ switch (cv->type)
{
- case sp_cond_type_t::number:
- if (cond->mysqlerr == p->mysqlerr)
- return TRUE;
- break;
- case sp_cond_type_t::state:
- if (strcmp(cond->sqlstate, p->sqlstate) == 0)
- return TRUE;
- break;
- default:
- return TRUE;
+ case sp_condition_value::ERROR_CODE:
+ if (sql_errno == cv->mysqlerr &&
+ (!found_cv ||
+ found_cv->type > sp_condition_value::ERROR_CODE))
+ {
+ found_cv= cv;
+ found_handler= h;
+ }
+ break;
+
+ case sp_condition_value::SQLSTATE:
+ if (strcmp(sql_state, cv->sql_state) == 0 &&
+ (!found_cv ||
+ found_cv->type > sp_condition_value::SQLSTATE))
+ {
+ found_cv= cv;
+ found_handler= h;
+ }
+ break;
+
+ case sp_condition_value::WARNING:
+ if ((is_sqlstate_warning(sql_state) ||
+ level == Sql_condition::WARN_LEVEL_WARN) && !found_cv)
+ {
+ found_cv= cv;
+ found_handler= h;
+ }
+ break;
+
+ case sp_condition_value::NOT_FOUND:
+ if (is_sqlstate_not_found(sql_state) && !found_cv)
+ {
+ found_cv= cv;
+ found_handler= h;
+ }
+ break;
+
+ case sp_condition_value::EXCEPTION:
+ if (is_sqlstate_exception(sql_state) &&
+ level == Sql_condition::WARN_LEVEL_ERROR && !found_cv)
+ {
+ found_cv= cv;
+ found_handler= h;
+ }
+ break;
}
}
}
- return FALSE;
+
+ if (found_handler)
+ return found_handler;
+
+
+ // There is no appropriate handler in this parsing context. We need to look up
+ // in parent contexts. There might be two cases here:
+ //
+ // 1. The current context has REGULAR_SCOPE. That means, it's a simple
+ // BEGIN..END block:
+ // ...
+ // BEGIN
+ // ... # We're here.
+ // END
+ // ...
+ // In this case we simply call find_handler() on parent's context recursively.
+ //
+ // 2. The current context has HANDLER_SCOPE. That means, we're inside an
+ // SQL-handler block:
+ // ...
+ // DECLARE ... HANDLER FOR ...
+ // BEGIN
+ // ... # We're here.
+ // END
+ // ...
+ // In this case we can not just call parent's find_handler(), because
+ // parent's handler don't catch conditions from this scope. Instead, we should
+ // try to find first parent context (we might have nested handler
+ // declarations), which has REGULAR_SCOPE (i.e. which is regular BEGIN..END
+ // block).
+
+ const sp_pcontext *p= this;
+
+ while (p && p->m_scope == HANDLER_SCOPE)
+ p= p->m_parent;
+
+ if (!p || !p->m_parent)
+ return NULL;
+
+ return p->m_parent->find_handler(sql_state, sql_errno, level);
}
-int
-sp_pcontext::push_cursor(LEX_STRING *name)
+
+bool sp_pcontext::add_cursor(LEX_STRING name)
{
- LEX_STRING n;
+ if (m_cursors.elements() == m_max_cursor_index)
+ ++m_max_cursor_index;
- if (m_cursors.elements == m_max_cursor_index)
- m_max_cursor_index+= 1;
- n.str= name->str;
- n.length= name->length;
- return insert_dynamic(&m_cursors, (uchar *)&n);
+ return m_cursors.append(name);
}
-/*
- See comment for find_variable() above
-*/
-my_bool
-sp_pcontext::find_cursor(LEX_STRING *name, uint *poff, my_bool scoped)
+
+bool sp_pcontext::find_cursor(LEX_STRING name,
+ uint *poff,
+ bool current_scope_only) const
{
- uint i= m_cursors.elements;
+ uint i= m_cursors.elements();
while (i--)
{
- LEX_STRING n;
+ LEX_STRING n= m_cursors.at(i);
- get_dynamic(&m_cursors, (uchar*)&n, i);
if (my_strnncoll(system_charset_info,
- (const uchar *)name->str, name->length,
- (const uchar *)n.str, n.length) == 0)
+ (const uchar *) name.str, name.length,
+ (const uchar *) n.str, n.length) == 0)
{
*poff= m_cursor_offset + i;
- return TRUE;
+ return true;
}
}
- if (!scoped && m_parent)
- return m_parent->find_cursor(name, poff, scoped);
- return FALSE;
+
+ return (!current_scope_only && m_parent) ?
+ m_parent->find_cursor(name, poff, false) :
+ false;
}
-void
-sp_pcontext::retrieve_field_definitions(List<Create_field> *field_def_lst)
+void sp_pcontext::retrieve_field_definitions(
+ List<Create_field> *field_def_lst) const
{
/* Put local/context fields in the result list. */
- for (uint i = 0; i < m_vars.elements; ++i)
+ for (size_t i= 0; i < m_vars.elements(); ++i)
{
- sp_variable_t *var_def;
- get_dynamic(&m_vars, (uchar*) &var_def, i);
+ sp_variable *var_def= m_vars.at(i);
field_def_lst->push_back(&var_def->field_def);
}
/* Put the fields of the enclosed contexts in the result list. */
- List_iterator_fast<sp_pcontext> li(m_children);
- sp_pcontext *ctx;
-
- while ((ctx = li++))
- ctx->retrieve_field_definitions(field_def_lst);
+ for (size_t i= 0; i < m_children.elements(); ++i)
+ m_children.at(i)->retrieve_field_definitions(field_def_lst);
}
-/*
- Find a cursor by offset from the top.
- This is only used for debugging.
-*/
-my_bool
-sp_pcontext::find_cursor(uint offset, LEX_STRING *n)
+
+const LEX_STRING *sp_pcontext::find_cursor(uint offset) const
{
if (m_cursor_offset <= offset &&
- offset < m_cursor_offset + m_cursors.elements)
- { // This frame
- get_dynamic(&m_cursors, (uchar*)n, offset - m_cursor_offset);
- return TRUE;
+ offset < m_cursor_offset + m_cursors.elements())
+ {
+ return &m_cursors.at(offset - m_cursor_offset); // This frame
}
- if (m_parent)
- return m_parent->find_cursor(offset, n); // Some previous frame
- return FALSE; // index out of bounds
+
+ return m_parent ?
+ m_parent->find_cursor(offset) : // Some previous frame
+ NULL; // Index out of bounds
}
diff --git a/sql/sp_pcontext.h b/sql/sp_pcontext.h
index f1d0d250c47..4d8623108aa 100644
--- a/sql/sp_pcontext.h
+++ b/sql/sp_pcontext.h
@@ -24,438 +24,541 @@
#include "sql_string.h" // LEX_STRING
#include "mysql_com.h" // enum_field_types
#include "field.h" // Create_field
+#include "sql_array.h" // Dynamic_array
-class sp_pcontext;
-typedef enum
-{
- sp_param_in,
- sp_param_out,
- sp_param_inout
-} sp_param_mode_t;
+/// This class represents a stored program variable or a parameter
+/// (also referenced as 'SP-variable').
-typedef struct sp_variable
+class sp_variable : public Sql_alloc
{
- LEX_STRING name;
- enum enum_field_types type;
- sp_param_mode_t mode;
-
- /*
- offset -- this the index to the variable's value in the runtime frame.
- This is calculated during parsing and used when creating sp_instr_set
- instructions and Item_splocal items.
- I.e. values are set/referred by array indexing in runtime.
- */
- uint offset;
-
- Item *dflt;
- Create_field field_def;
-} sp_variable_t;
+public:
+ enum enum_mode
+ {
+ MODE_IN,
+ MODE_OUT,
+ MODE_INOUT
+ };
+ /// Name of the SP-variable.
+ LEX_STRING name;
-#define SP_LAB_IMPL 0 // Implicit label generated by parser
-#define SP_LAB_BEGIN 1 // Label at BEGIN
-#define SP_LAB_ITER 2 // Label at iteration control
+ /// Field-type of the SP-variable.
+ enum enum_field_types type;
-/*
- An SQL/PSM label. Can refer to the identifier used with the
- "label_name:" construct which may precede some SQL/PSM statements, or
- to an implicit implementation-dependent identifier which the parser
- inserts before a high-level flow control statement such as
- IF/WHILE/REPEAT/LOOP, when such statement is rewritten into
- a combination of low-level jump/jump_if instructions and labels.
-*/
+ /// Mode of the SP-variable.
+ enum_mode mode;
-typedef struct sp_label
-{
- char *name;
- uint ip; // Instruction index
- int type; // begin/iter or ref/free
- sp_pcontext *ctx; // The label's context
-} sp_label_t;
+ /// The index to the variable's value in the runtime frame.
+ ///
+ /// It is calculated during parsing and used when creating sp_instr_set
+ /// instructions and Item_splocal items. I.e. values are set/referred by
+ /// array indexing in runtime.
+ uint offset;
-typedef struct sp_cond_type
-{
- enum { number, state, warning, notfound, exception } type;
- char sqlstate[SQLSTATE_LENGTH+1];
- uint mysqlerr;
-} sp_cond_type_t;
+ /// Default value of the SP-variable (if any).
+ Item *default_value;
-/*
- Sanity check for SQLSTATEs. Will not check if it's really an existing
- state (there are just too many), but will check length bad characters.
-*/
-extern bool
-sp_cond_check(LEX_STRING *sqlstate);
+ /// Full type information (field meta-data) of the SP-variable.
+ Create_field field_def;
-typedef struct sp_cond
-{
- LEX_STRING name;
- sp_cond_type_t *val;
-} sp_cond_t;
-
-/**
- The scope of a label in Stored Procedures,
- for name resolution of labels in a parsing context.
-*/
-enum label_scope_type
-{
- /**
- The labels declared in a parent context are in scope.
- */
- LABEL_DEFAULT_SCOPE,
- /**
- The labels declared in a parent context are not in scope.
- */
- LABEL_HANDLER_SCOPE
+public:
+ sp_variable(LEX_STRING _name, enum_field_types _type, enum_mode _mode,
+ uint _offset)
+ :Sql_alloc(),
+ name(_name),
+ type(_type),
+ mode(_mode),
+ offset(_offset),
+ default_value(NULL)
+ { }
};
-/**
- The parse-time context, used to keep track of declared variables/parameters,
- conditions, handlers, cursors and labels, during parsing.
- sp_contexts are organized as a tree, with one object for each begin-end
- block, one object for each exception handler,
- plus a root-context for the parameters.
- This is used during parsing for looking up defined names (e.g. declared
- variables and visible labels), for error checking, and to calculate offsets
- to be used at runtime. (During execution variable values, active handlers
- and cursors, etc, are referred to by an index in a stack.)
- Parsing contexts for exception handlers limit the visibility of labels.
- The pcontext tree is also kept during execution and is used for error
- checking (e.g. correct number of parameters), and in the future, used by
- the debugger.
-*/
+///////////////////////////////////////////////////////////////////////////
-class sp_pcontext : public Sql_alloc
+/// This class represents an SQL/PSM label. Can refer to the identifier
+/// used with the "label_name:" construct which may precede some SQL/PSM
+/// statements, or to an implicit implementation-dependent identifier which
+/// the parser inserts before a high-level flow control statement such as
+/// IF/WHILE/REPEAT/LOOP, when such statement is rewritten into a
+/// combination of low-level jump/jump_if instructions and labels.
+
+class sp_label : public Sql_alloc
{
public:
-
- /**
- Constructor.
- Builds a parsing context root node.
- */
- sp_pcontext();
-
- // Free memory
- void
- destroy();
-
- /**
- Create and push a new context in the tree.
- @param label_scope label scope for the new parsing context
- @return the node created
- */
- sp_pcontext *
- push_context(label_scope_type label_scope);
-
- /**
- Pop a node from the parsing context tree.
- @return the parent node
- */
- sp_pcontext *
- pop_context();
-
- sp_pcontext *
- parent_context()
+ enum enum_type
{
- return m_parent;
- }
+ /// Implicit label generated by parser.
+ IMPLICIT,
- /*
- Number of handlers/cursors to pop between this context and 'ctx'.
- If 'exclusive' is true, don't count the last block we are leaving;
- this is used for LEAVE where we will jump to the cpop/hpop instructions.
- */
- uint
- diff_handlers(sp_pcontext *ctx, bool exclusive);
- uint
- diff_cursors(sp_pcontext *ctx, bool exclusive);
-
-
- //
- // Parameters and variables
- //
-
- /*
- The maximum number of variables used in this and all child contexts
- In the root, this gives us the number of slots needed for variables
- during execution.
- */
- inline uint
- max_var_index()
- {
- return m_max_var_index;
- }
+ /// Label at BEGIN.
+ BEGIN,
- /*
- The current number of variables used in the parents (from the root),
- including this context.
- */
- inline uint
- current_var_count()
- {
- return m_var_offset + m_vars.elements;
- }
+ /// Label at iteration control
+ ITERATION
+ };
- /* The number of variables in this context alone */
- inline uint
- context_var_count()
- {
- return m_vars.elements;
- }
+ /// Name of the label.
+ LEX_STRING name;
- /* Map index in this pcontext to runtime offset */
- inline uint
- var_context2runtime(uint i)
- {
- return m_var_offset + i;
- }
+ /// Instruction pointer of the label.
+ uint ip;
- /* Set type of variable. 'i' is the offset from the top */
- inline void
- set_type(uint i, enum enum_field_types type)
- {
- sp_variable_t *p= find_variable(i);
+ /// Type of the label.
+ enum_type type;
- if (p)
- p->type= type;
- }
+ /// Scope of the label.
+ class sp_pcontext *ctx;
- /* Set default value of variable. 'i' is the offset from the top */
- inline void
- set_default(uint i, Item *it)
- {
- sp_variable_t *p= find_variable(i);
+public:
+ sp_label(LEX_STRING _name, uint _ip, enum_type _type, sp_pcontext *_ctx)
+ :Sql_alloc(),
+ name(_name),
+ ip(_ip),
+ type(_type),
+ ctx(_ctx)
+ { }
+};
- if (p)
- p->dflt= it;
- }
+///////////////////////////////////////////////////////////////////////////
+
+/// This class represents condition-value term in DECLARE CONDITION or
+/// DECLARE HANDLER statements. sp_condition_value has little to do with
+/// SQL-conditions.
+///
+/// In some sense, this class is a union -- a set of filled attributes
+/// depends on the sp_condition_value::type value.
- sp_variable_t *
- push_variable(LEX_STRING *name, enum enum_field_types type,
- sp_param_mode_t mode);
-
- /*
- Retrieve definitions of fields from the current context and its
- children.
- */
- void
- retrieve_field_definitions(List<Create_field> *field_def_lst);
-
- // Find by name
- sp_variable_t *
- find_variable(LEX_STRING *name, my_bool scoped=0);
-
- // Find by offset (from the top)
- sp_variable_t *
- find_variable(uint offset);
-
- /*
- Set the current scope boundary (for default values).
- The argument is the number of variables to skip.
- */
- inline void
- declare_var_boundary(uint n)
+class sp_condition_value : public Sql_alloc
+{
+public:
+ enum enum_type
{
- m_pboundary= n;
- }
+ ERROR_CODE,
+ SQLSTATE,
+ WARNING,
+ NOT_FOUND,
+ EXCEPTION
+ };
- /*
- CASE expressions support.
- */
+ /// Type of the condition value.
+ enum_type type;
- inline int
- register_case_expr()
- {
- return m_num_case_exprs++;
- }
+ /// SQLSTATE of the condition value.
+ char sql_state[SQLSTATE_LENGTH+1];
- inline int
- get_num_case_exprs() const
- {
- return m_num_case_exprs;
- }
+ /// MySQL error code of the condition value.
+ uint mysqlerr;
- inline bool
- push_case_expr_id(int case_expr_id)
+public:
+ sp_condition_value(uint _mysqlerr)
+ :Sql_alloc(),
+ type(ERROR_CODE),
+ mysqlerr(_mysqlerr)
+ { }
+
+ sp_condition_value(const char *_sql_state)
+ :Sql_alloc(),
+ type(SQLSTATE)
{
- return insert_dynamic(&m_case_expr_id_lst, (uchar*) &case_expr_id);
+ memcpy(sql_state, _sql_state, SQLSTATE_LENGTH);
+ sql_state[SQLSTATE_LENGTH]= 0;
}
- inline void
- pop_case_expr_id()
+ sp_condition_value(enum_type _type)
+ :Sql_alloc(),
+ type(_type)
{
- pop_dynamic(&m_case_expr_id_lst);
+ DBUG_ASSERT(type != ERROR_CODE && type != SQLSTATE);
}
- inline int
- get_current_case_expr_id() const
- {
- int case_expr_id;
+ /// Check if two instances of sp_condition_value are equal or not.
+ ///
+ /// @param cv another instance of sp_condition_value to check.
+ ///
+ /// @return true if the instances are equal, false otherwise.
+ bool equals(const sp_condition_value *cv) const;
+};
- get_dynamic((DYNAMIC_ARRAY*)&m_case_expr_id_lst, (uchar*) &case_expr_id,
- m_case_expr_id_lst.elements - 1);
+///////////////////////////////////////////////////////////////////////////
- return case_expr_id;
- }
+/// This class represents 'DECLARE CONDITION' statement.
+/// sp_condition has little to do with SQL-conditions.
- //
- // Labels
- //
+class sp_condition : public Sql_alloc
+{
+public:
+ /// Name of the condition.
+ LEX_STRING name;
- sp_label_t *
- push_label(char *name, uint ip);
+ /// Value of the condition.
+ sp_condition_value *value;
- sp_label_t *
- find_label(char *name);
+public:
+ sp_condition(LEX_STRING _name, sp_condition_value *_value)
+ :Sql_alloc(),
+ name(_name),
+ value(_value)
+ { }
+};
- inline sp_label_t *
- last_label()
- {
- sp_label_t *lab= m_label.head();
+///////////////////////////////////////////////////////////////////////////
- if (!lab && m_parent)
- lab= m_parent->last_label();
- return lab;
- }
+/// This class represents 'DECLARE HANDLER' statement.
- inline sp_label_t *
- pop_label()
+class sp_handler : public Sql_alloc
+{
+public:
+ /// Enumeration of possible handler types.
+ /// Note: UNDO handlers are not (and have never been) supported.
+ enum enum_type
{
- return m_label.pop();
- }
+ EXIT,
+ CONTINUE
+ };
- //
- // Conditions
- //
+ /// Handler type.
+ enum_type type;
- int
- push_cond(LEX_STRING *name, sp_cond_type_t *val);
+ /// Conditions caught by this handler.
+ List<sp_condition_value> condition_values;
- sp_cond_type_t *
- find_cond(LEX_STRING *name, my_bool scoped=0);
+public:
+ /// The constructor.
+ ///
+ /// @param _type SQL-handler type.
+ sp_handler(enum_type _type)
+ :Sql_alloc(),
+ type(_type)
+ { }
+};
- //
- // Handlers
- //
+///////////////////////////////////////////////////////////////////////////
+
+/// The class represents parse-time context, which keeps track of declared
+/// variables/parameters, conditions, handlers, cursors and labels.
+///
+/// sp_pcontext objects are organized in a tree according to the following
+/// rules:
+/// - one sp_pcontext object corresponds for for each BEGIN..END block;
+/// - one sp_pcontext object corresponds for each exception handler;
+/// - one additional sp_pcontext object is created to contain
+/// Stored Program parameters.
+///
+/// sp_pcontext objects are used both at parse-time and at runtime.
+///
+/// During the parsing stage sp_pcontext objects are used:
+/// - to look up defined names (e.g. declared variables and visible
+/// labels);
+/// - to check for duplicates;
+/// - for error checking;
+/// - to calculate offsets to be used at runtime.
+///
+/// During the runtime phase, a tree of sp_pcontext objects is used:
+/// - for error checking (e.g. to check correct number of parameters);
+/// - to resolve SQL-handlers.
- inline void
- push_handler(sp_cond_type_t *cond)
+class sp_pcontext : public Sql_alloc
+{
+public:
+ enum enum_scope
{
- insert_dynamic(&m_handlers, (uchar*)&cond);
- }
-
- bool
- find_handler(sp_cond_type *cond);
+ /// REGULAR_SCOPE designates regular BEGIN ... END blocks.
+ REGULAR_SCOPE,
- inline uint
- max_handler_index()
- {
- return m_max_handler_index + m_context_handlers;
- }
+ /// HANDLER_SCOPE designates SQL-handler blocks.
+ HANDLER_SCOPE
+ };
- inline void
- add_handlers(uint n)
+public:
+ sp_pcontext();
+ ~sp_pcontext();
+
+
+ /// Create and push a new context in the tree.
+
+ /// @param thd thread context.
+ /// @param scope scope of the new parsing context.
+ /// @return the node created.
+ sp_pcontext *push_context(THD *thd, enum_scope scope);
+
+ /// Pop a node from the parsing context tree.
+ /// @return the parent node.
+ sp_pcontext *pop_context();
+
+ sp_pcontext *parent_context() const
+ { return m_parent; }
+
+ /// Calculate and return the number of handlers to pop between the given
+ /// context and this one.
+ ///
+ /// @param ctx the other parsing context.
+ /// @param exclusive specifies if the last scope should be excluded.
+ ///
+ /// @return the number of handlers to pop between the given context and
+ /// this one. If 'exclusive' is true, don't count the last scope we are
+ /// leaving; this is used for LEAVE where we will jump to the hpop
+ /// instructions.
+ uint diff_handlers(const sp_pcontext *ctx, bool exclusive) const;
+
+ /// Calculate and return the number of cursors to pop between the given
+ /// context and this one.
+ ///
+ /// @param ctx the other parsing context.
+ /// @param exclusive specifies if the last scope should be excluded.
+ ///
+ /// @return the number of cursors to pop between the given context and
+ /// this one. If 'exclusive' is true, don't count the last scope we are
+ /// leaving; this is used for LEAVE where we will jump to the cpop
+ /// instructions.
+ uint diff_cursors(const sp_pcontext *ctx, bool exclusive) const;
+
+ /////////////////////////////////////////////////////////////////////////
+ // SP-variables (parameters and variables).
+ /////////////////////////////////////////////////////////////////////////
+
+ /// @return the maximum number of variables used in this and all child
+ /// contexts. For the root parsing context, this gives us the number of
+ /// slots needed for variables during the runtime phase.
+ uint max_var_index() const
+ { return m_max_var_index; }
+
+ /// @return the current number of variables used in the parent contexts
+ /// (from the root), including this context.
+ uint current_var_count() const
+ { return m_var_offset + m_vars.elements(); }
+
+ /// @return the number of variables in this context alone.
+ uint context_var_count() const
+ { return m_vars.elements(); }
+
+ /// @return map index in this parsing context to runtime offset.
+ uint var_context2runtime(uint i) const
+ { return m_var_offset + i; }
+
+ /// Add SP-variable to the parsing context.
+ ///
+ /// @param thd Thread context.
+ /// @param name Name of the SP-variable.
+ /// @param type Type of the SP-variable.
+ /// @param mode Mode of the SP-variable.
+ ///
+ /// @return instance of newly added SP-variable.
+ sp_variable *add_variable(THD *thd,
+ LEX_STRING name,
+ enum enum_field_types type,
+ sp_variable::enum_mode mode);
+
+ /// Retrieve full type information about SP-variables in this parsing
+ /// context and its children.
+ ///
+ /// @param field_def_lst[out] Container to store type information.
+ void retrieve_field_definitions(List<Create_field> *field_def_lst) const;
+
+ /// Find SP-variable by name.
+ ///
+ /// The function does a linear search (from newer to older variables,
+ /// in case we have shadowed names).
+ ///
+ /// The function is called only at parsing time.
+ ///
+ /// @param name Variable name.
+ /// @param current_scope_only A flag if we search only in current scope.
+ ///
+ /// @return instance of found SP-variable, or NULL if not found.
+ sp_variable *find_variable(LEX_STRING name, bool current_scope_only) const;
+
+ /// Find SP-variable by the offset in the root parsing context.
+ ///
+ /// The function is used for two things:
+ /// - When evaluating parameters at the beginning, and setting out parameters
+ /// at the end, of invocation. (Top frame only, so no recursion then.)
+ /// - For printing of sp_instr_set. (Debug mode only.)
+ ///
+ /// @param offset Variable offset in the root parsing context.
+ ///
+ /// @return instance of found SP-variable, or NULL if not found.
+ sp_variable *find_variable(uint offset) const;
+
+ /// Set the current scope boundary (for default values).
+ ///
+ /// @param n The number of variables to skip.
+ void declare_var_boundary(uint n)
+ { m_pboundary= n; }
+
+ /////////////////////////////////////////////////////////////////////////
+ // CASE expressions.
+ /////////////////////////////////////////////////////////////////////////
+
+ int register_case_expr()
+ { return m_num_case_exprs++; }
+
+ int get_num_case_exprs() const
+ { return m_num_case_exprs; }
+
+ bool push_case_expr_id(int case_expr_id)
+ { return m_case_expr_ids.append(case_expr_id); }
+
+ void pop_case_expr_id()
+ { m_case_expr_ids.pop(); }
+
+ int get_current_case_expr_id() const
+ { return *m_case_expr_ids.back(); }
+
+ /////////////////////////////////////////////////////////////////////////
+ // Labels.
+ /////////////////////////////////////////////////////////////////////////
+
+ sp_label *push_label(THD *thd, LEX_STRING name, uint ip);
+
+ sp_label *find_label(LEX_STRING name);
+
+ sp_label *last_label()
{
- m_context_handlers+= n;
- }
-
- //
- // Cursors
- //
+ sp_label *label= m_labels.head();
- int
- push_cursor(LEX_STRING *name);
+ if (!label && m_parent)
+ label= m_parent->last_label();
- my_bool
- find_cursor(LEX_STRING *name, uint *poff, my_bool scoped=0);
-
- /* Find by offset (for debugging only) */
- my_bool
- find_cursor(uint offset, LEX_STRING *n);
-
- inline uint
- max_cursor_index()
- {
- return m_max_cursor_index + m_cursors.elements;
- }
-
- inline uint
- current_cursor_count()
- {
- return m_cursor_offset + m_cursors.elements;
+ return label;
}
-protected:
+ sp_label *pop_label()
+ { return m_labels.pop(); }
+
+ /////////////////////////////////////////////////////////////////////////
+ // Conditions.
+ /////////////////////////////////////////////////////////////////////////
+
+ bool add_condition(THD *thd, LEX_STRING name, sp_condition_value *value);
+
+ /// See comment for find_variable() above.
+ sp_condition_value *find_condition(LEX_STRING name,
+ bool current_scope_only) const;
+
+ /////////////////////////////////////////////////////////////////////////
+ // Handlers.
+ /////////////////////////////////////////////////////////////////////////
+
+ sp_handler *add_handler(THD* thd, sp_handler::enum_type type);
+
+ /// This is an auxilary parsing-time function to check if an SQL-handler
+ /// exists in the current parsing context (current scope) for the given
+ /// SQL-condition. This function is used to check for duplicates during
+ /// the parsing phase.
+ ///
+ /// This function can not be used during the runtime phase to check
+ /// SQL-handler existence because it searches for the SQL-handler in the
+ /// current scope only (during runtime, current and parent scopes
+ /// should be checked according to the SQL-handler resolution rules).
+ ///
+ /// @param condition_value the handler condition value
+ /// (not SQL-condition!).
+ ///
+ /// @retval true if such SQL-handler exists.
+ /// @retval false otherwise.
+ bool check_duplicate_handler(const sp_condition_value *cond_value) const;
+
+ /// Find an SQL handler for the given SQL condition according to the
+ /// SQL-handler resolution rules. This function is used at runtime.
+ ///
+ /// @param sql_state The SQL condition state
+ /// @param sql_errno The error code
+ /// @param level The SQL condition level
+ ///
+ /// @return a pointer to the found SQL-handler or NULL.
+ sp_handler *find_handler(const char *sql_state,
+ uint sql_errno,
+ Sql_condition::enum_warning_level level) const;
+
+ /////////////////////////////////////////////////////////////////////////
+ // Cursors.
+ /////////////////////////////////////////////////////////////////////////
+
+ bool add_cursor(LEX_STRING name);
+
+ /// See comment for find_variable() above.
+ bool find_cursor(LEX_STRING name, uint *poff, bool current_scope_only) const;
+
+ /// Find cursor by offset (for debugging only).
+ const LEX_STRING *find_cursor(uint offset) const;
+
+ uint max_cursor_index() const
+ { return m_max_cursor_index + m_cursors.elements(); }
+
+ uint current_cursor_count() const
+ { return m_cursor_offset + m_cursors.elements(); }
- /**
- Constructor for a tree node.
- @param prev the parent parsing context
- @param label_scope label_scope for this parsing context
- */
- sp_pcontext(sp_pcontext *prev, label_scope_type label_scope);
-
- /*
- m_max_var_index -- number of variables (including all types of arguments)
- in this context including all children contexts.
-
- m_max_var_index >= m_vars.elements.
+private:
+ /// Constructor for a tree node.
+ /// @param prev the parent parsing context
+ /// @param scope scope of this parsing context
+ sp_pcontext(sp_pcontext *prev, enum_scope scope);
- m_max_var_index of the root parsing context contains number of all
- variables (including arguments) in all enclosed contexts.
- */
- uint m_max_var_index;
+ void init(uint var_offset, uint cursor_offset, int num_case_expressions);
- // The maximum sub context's framesizes
- uint m_max_cursor_index;
- uint m_max_handler_index;
- uint m_context_handlers; // No. of handlers in this context
+ /* Prevent use of these */
+ sp_pcontext(const sp_pcontext &);
+ void operator=(sp_pcontext &);
private:
+ /// m_max_var_index -- number of variables (including all types of arguments)
+ /// in this context including all children contexts.
+ ///
+ /// m_max_var_index >= m_vars.elements().
+ ///
+ /// m_max_var_index of the root parsing context contains number of all
+ /// variables (including arguments) in all enclosed contexts.
+ uint m_max_var_index;
+
+ /// The maximum sub context's framesizes.
+ uint m_max_cursor_index;
- sp_pcontext *m_parent; // Parent context
-
- /*
- m_var_offset -- this is an index of the first variable in this
- parsing context.
-
- m_var_offset is 0 for root context.
+ /// Parent context.
+ sp_pcontext *m_parent;
- Since now each variable is stored in separate place, no reuse is done,
- so m_var_offset is different for all enclosed contexts.
- */
+ /// An index of the first SP-variable in this parsing context. The index
+ /// belongs to a runtime table of SP-variables.
+ ///
+ /// Note:
+ /// - m_var_offset is 0 for root parsing context;
+ /// - m_var_offset is different for all nested parsing contexts.
uint m_var_offset;
- uint m_cursor_offset; // Cursor offset for this context
+ /// Cursor offset for this context.
+ uint m_cursor_offset;
- /*
- Boundary for finding variables in this context. This is the number
- of variables currently "invisible" to default clauses.
- This is normally 0, but will be larger during parsing of
- DECLARE ... DEFAULT, to get the scope right for DEFAULT values.
- */
+ /// Boundary for finding variables in this context. This is the number of
+ /// variables currently "invisible" to default clauses. This is normally 0,
+ /// but will be larger during parsing of DECLARE ... DEFAULT, to get the
+ /// scope right for DEFAULT values.
uint m_pboundary;
int m_num_case_exprs;
- DYNAMIC_ARRAY m_vars; // Parameters/variables
- DYNAMIC_ARRAY m_case_expr_id_lst; /* Stack of CASE expression ids. */
- DYNAMIC_ARRAY m_conds; // Conditions
- DYNAMIC_ARRAY m_cursors; // Cursors
- DYNAMIC_ARRAY m_handlers; // Handlers, for checking for duplicates
+ /// SP parameters/variables.
+ Dynamic_array<sp_variable *> m_vars;
- List<sp_label_t> m_label; // The label list
+ /// Stack of CASE expression ids.
+ Dynamic_array<int> m_case_expr_ids;
- List<sp_pcontext> m_children; // Children contexts, used for destruction
+ /// Stack of SQL-conditions.
+ Dynamic_array<sp_condition *> m_conditions;
- /**
- Scope of labels for this parsing context.
- */
- label_scope_type m_label_scope;
+ /// Stack of cursors.
+ Dynamic_array<LEX_STRING> m_cursors;
-private:
- sp_pcontext(const sp_pcontext &); /* Prevent use of these */
- void operator=(sp_pcontext &);
+ /// Stack of SQL-handlers.
+ Dynamic_array<sp_handler *> m_handlers;
+
+ /// List of labels.
+ List<sp_label> m_labels;
+
+ /// Children contexts, used for destruction.
+ Dynamic_array<sp_pcontext *> m_children;
+
+ /// Scope of this parsing context.
+ enum_scope m_scope;
}; // class sp_pcontext : public Sql_alloc
diff --git a/sql/sp_rcontext.cc b/sql/sp_rcontext.cc
index 30acfebabb2..a5a6a61f73c 100644
--- a/sql/sp_rcontext.cc
+++ b/sql/sp_rcontext.cc
@@ -13,6 +13,7 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+#include <my_global.h>
#include "sql_priv.h"
#include "unireg.h"
#ifdef USE_PRAGMA_IMPLEMENTATION
@@ -26,23 +27,21 @@
#include "sp_pcontext.h"
#include "sql_select.h" // create_virtual_tmp_table
-sp_rcontext::sp_rcontext(sp_pcontext *root_parsing_ctx,
+///////////////////////////////////////////////////////////////////////////
+// sp_rcontext implementation.
+///////////////////////////////////////////////////////////////////////////
+
+
+sp_rcontext::sp_rcontext(const sp_pcontext *root_parsing_ctx,
Field *return_value_fld,
- sp_rcontext *prev_runtime_ctx)
- :end_partial_result_set(FALSE),
+ bool in_sub_stmt)
+ :end_partial_result_set(false),
m_root_parsing_ctx(root_parsing_ctx),
- m_var_table(0),
- m_var_items(0),
+ m_var_table(NULL),
m_return_value_fld(return_value_fld),
- m_return_value_set(FALSE),
- in_sub_stmt(FALSE),
- m_hcount(0),
- m_hsp(0),
- m_ihsp(0),
- m_hfound(-1),
- m_ccount(0),
- m_case_expr_holders(0),
- m_prev_runtime_ctx(prev_runtime_ctx)
+ m_return_value_set(false),
+ m_in_sub_stmt(in_sub_stmt),
+ m_ccount(0)
{
}
@@ -51,422 +50,324 @@ sp_rcontext::~sp_rcontext()
{
if (m_var_table)
free_blobs(m_var_table);
+
+ // Leave m_handlers, m_handler_call_stack, m_var_items, m_cstack
+ // and m_case_expr_holders untouched.
+ // They are allocated in mem roots and will be freed accordingly.
}
-/*
- Initialize sp_rcontext instance.
+sp_rcontext *sp_rcontext::create(THD *thd,
+ const sp_pcontext *root_parsing_ctx,
+ Field *return_value_fld)
+{
+ sp_rcontext *ctx= new (thd->mem_root) sp_rcontext(root_parsing_ctx,
+ return_value_fld,
+ thd->in_sub_stmt);
- SYNOPSIS
- thd Thread handle
- RETURN
- FALSE on success
- TRUE on error
-*/
+ if (!ctx)
+ return NULL;
-bool sp_rcontext::init(THD *thd)
-{
- uint handler_count= m_root_parsing_ctx->max_handler_index();
-
- in_sub_stmt= thd->in_sub_stmt;
-
- if (init_var_table(thd) || init_var_items())
- return TRUE;
-
- if (!(m_raised_conditions= new (thd->mem_root) Sql_condition_info[handler_count]))
- return TRUE;
-
- return
- !(m_handler=
- (sp_handler_t*)thd->alloc(handler_count * sizeof(sp_handler_t))) ||
- !(m_hstack=
- (uint*)thd->alloc(handler_count * sizeof(uint))) ||
- !(m_in_handler=
- (sp_active_handler_t*)thd->alloc(handler_count *
- sizeof(sp_active_handler_t))) ||
- !(m_cstack=
- (sp_cursor**)thd->alloc(m_root_parsing_ctx->max_cursor_index() *
- sizeof(sp_cursor*))) ||
- !(m_case_expr_holders=
- (Item_cache**)thd->calloc(m_root_parsing_ctx->get_num_case_exprs() *
- sizeof (Item_cache*)));
+ if (ctx->alloc_arrays(thd) ||
+ ctx->init_var_table(thd) ||
+ ctx->init_var_items(thd))
+ {
+ delete ctx;
+ return NULL;
+ }
+
+ return ctx;
}
-/*
- Create and initialize a table to store SP-vars.
+bool sp_rcontext::alloc_arrays(THD *thd)
+{
+ {
+ size_t n= m_root_parsing_ctx->max_cursor_index();
+ m_cstack.reset(
+ static_cast<sp_cursor **> (
+ thd->alloc(n * sizeof (sp_cursor*))),
+ n);
+ }
+
+ {
+ size_t n= m_root_parsing_ctx->get_num_case_exprs();
+ m_case_expr_holders.reset(
+ static_cast<Item_cache **> (
+ thd->calloc(n * sizeof (Item_cache*))),
+ n);
+ }
+
+ return !m_cstack.array() || !m_case_expr_holders.array();
+}
- SYNOPSIS
- thd Thread handler.
- RETURN
- FALSE on success
- TRUE on error
-*/
-bool
-sp_rcontext::init_var_table(THD *thd)
+bool sp_rcontext::init_var_table(THD *thd)
{
List<Create_field> field_def_lst;
if (!m_root_parsing_ctx->max_var_index())
- return FALSE;
+ return false;
m_root_parsing_ctx->retrieve_field_definitions(&field_def_lst);
DBUG_ASSERT(field_def_lst.elements == m_root_parsing_ctx->max_var_index());
-
+
if (!(m_var_table= create_virtual_tmp_table(thd, field_def_lst)))
- return TRUE;
+ return true;
- m_var_table->copy_blobs= TRUE;
- m_var_table->alias.set("", 0, table_alias_charset);
+ m_var_table->copy_blobs= true;
+ m_var_table->alias.set("", 0, m_var_table->alias.charset());
- return FALSE;
+ return false;
}
-/*
- Create and initialize an Item-adapter (Item_field) for each SP-var field.
-
- RETURN
- FALSE on success
- TRUE on error
-*/
-
-bool
-sp_rcontext::init_var_items()
+bool sp_rcontext::init_var_items(THD *thd)
{
- uint idx;
uint num_vars= m_root_parsing_ctx->max_var_index();
- if (!(m_var_items= (Item**) sql_alloc(num_vars * sizeof (Item *))))
- return TRUE;
+ m_var_items.reset(
+ static_cast<Item **> (
+ thd->alloc(num_vars * sizeof (Item *))),
+ num_vars);
+
+ if (!m_var_items.array())
+ return true;
- for (idx = 0; idx < num_vars; ++idx)
+ for (uint idx = 0; idx < num_vars; ++idx)
{
if (!(m_var_items[idx]= new Item_field(m_var_table->field[idx])))
- return TRUE;
+ return true;
}
- return FALSE;
+ return false;
}
-bool
-sp_rcontext::set_return_value(THD *thd, Item **return_value_item)
+bool sp_rcontext::set_return_value(THD *thd, Item **return_value_item)
{
DBUG_ASSERT(m_return_value_fld);
- m_return_value_set = TRUE;
+ m_return_value_set = true;
return sp_eval_expr(thd, m_return_value_fld, return_value_item);
}
-#define IS_WARNING_CONDITION(S) ((S)[0] == '0' && (S)[1] == '1')
-#define IS_NOT_FOUND_CONDITION(S) ((S)[0] == '0' && (S)[1] == '2')
-#define IS_EXCEPTION_CONDITION(S) ((S)[0] != '0' || (S)[1] > '2')
-
-/**
- Find an SQL handler for the given error.
-
- SQL handlers are pushed on the stack m_handler, with the latest/innermost
- one on the top; we then search for matching handlers from the top and
- down.
-
- We search through all the handlers, looking for the most specific one
- (sql_errno more specific than sqlstate more specific than the rest).
- Note that mysql error code handlers is a MySQL extension, not part of
- the standard.
-
- SQL handlers for warnings are searched in the current scope only.
-
- SQL handlers for errors are searched in the current and in outer scopes.
- That's why finding and activation of handler must be separated: an errror
- handler might be located in the outer scope, which is not active at the
- moment. Before such handler can be activated, execution flow should
- unwind to that scope.
-
- Found SQL handler is remembered in m_hfound for future activation.
- If no handler is found, m_hfound is -1.
-
- @param thd Thread handle
- @param sql_errno The error code
- @param sqlstate The error SQL state
- @param level The error level
- @param msg The error message
-
- @retval TRUE if an SQL handler was found
- @retval FALSE otherwise
-*/
-
-bool
-sp_rcontext::find_handler(THD *thd,
- uint sql_errno,
- const char *sqlstate,
- MYSQL_ERROR::enum_warning_level level,
- const char *msg)
+bool sp_rcontext::push_cursor(sp_lex_keeper *lex_keeper,
+ sp_instr_cpush *i)
{
- int i= m_hcount;
-
- /* Reset previously found handler. */
- m_hfound= -1;
-
/*
- If this is a fatal sub-statement error, and this runtime
- context corresponds to a sub-statement, no CONTINUE/EXIT
- handlers from this context are applicable: try to locate one
- in the outer scope.
+ We should create cursors in the callers arena, as
+ it could be (and usually is) used in several instructions.
*/
- if (thd->is_fatal_sub_stmt_error && in_sub_stmt)
- i= 0;
-
- /* Search handlers from the latest (innermost) to the oldest (outermost) */
- while (i--)
- {
- sp_cond_type_t *cond= m_handler[i].cond;
- int j= m_ihsp;
-
- /* Check active handlers, to avoid invoking one recursively */
- while (j--)
- if (m_in_handler[j].ip == m_handler[i].handler)
- break;
- if (j >= 0)
- continue; // Already executing this handler
+ sp_cursor *c= new (callers_arena->mem_root) sp_cursor(lex_keeper, i);
- switch (cond->type)
- {
- case sp_cond_type_t::number:
- if (sql_errno == cond->mysqlerr &&
- (m_hfound < 0 || m_handler[m_hfound].cond->type > sp_cond_type_t::number))
- m_hfound= i; // Always the most specific
- break;
- case sp_cond_type_t::state:
- if (strcmp(sqlstate, cond->sqlstate) == 0 &&
- (m_hfound < 0 || m_handler[m_hfound].cond->type > sp_cond_type_t::state))
- m_hfound= i;
- break;
- case sp_cond_type_t::warning:
- if ((IS_WARNING_CONDITION(sqlstate) ||
- level == MYSQL_ERROR::WARN_LEVEL_WARN) &&
- m_hfound < 0)
- m_hfound= i;
- break;
- case sp_cond_type_t::notfound:
- if (IS_NOT_FOUND_CONDITION(sqlstate) && m_hfound < 0)
- m_hfound= i;
- break;
- case sp_cond_type_t::exception:
- if (IS_EXCEPTION_CONDITION(sqlstate) &&
- level == MYSQL_ERROR::WARN_LEVEL_ERROR &&
- m_hfound < 0)
- m_hfound= i;
- break;
- }
- }
-
- if (m_hfound >= 0)
- {
- DBUG_ASSERT((uint) m_hfound < m_root_parsing_ctx->max_handler_index());
-
- m_raised_conditions[m_hfound].clear();
- m_raised_conditions[m_hfound].set(sql_errno, sqlstate, level, msg);
-
- return TRUE;
- }
+ if (c == NULL)
+ return true;
- /*
- Only "exception conditions" are propagated to handlers in calling
- contexts. If no handler is found locally for a "completion condition"
- (warning or "not found") we will simply resume execution.
- */
- if (m_prev_runtime_ctx && IS_EXCEPTION_CONDITION(sqlstate) &&
- level == MYSQL_ERROR::WARN_LEVEL_ERROR)
- {
- return m_prev_runtime_ctx->find_handler(thd, sql_errno, sqlstate,
- level, msg);
- }
-
- return FALSE;
+ m_cstack[m_ccount++]= c;
+ return false;
}
-void
-sp_rcontext::push_cursor(sp_lex_keeper *lex_keeper, sp_instr_cpush *i)
-{
- DBUG_ENTER("sp_rcontext::push_cursor");
- DBUG_ASSERT(m_ccount < m_root_parsing_ctx->max_cursor_index());
- m_cstack[m_ccount++]= new sp_cursor(lex_keeper, i);
- DBUG_PRINT("info", ("m_ccount: %d", m_ccount));
- DBUG_VOID_RETURN;
-}
-void
-sp_rcontext::pop_cursors(uint count)
+void sp_rcontext::pop_cursors(uint count)
{
- DBUG_ENTER("sp_rcontext::pop_cursors");
DBUG_ASSERT(m_ccount >= count);
+
while (count--)
- {
delete m_cstack[--m_ccount];
- }
- DBUG_PRINT("info", ("m_ccount: %d", m_ccount));
- DBUG_VOID_RETURN;
}
-void
-sp_rcontext::push_handler(struct sp_cond_type *cond, uint h, int type)
-{
- DBUG_ENTER("sp_rcontext::push_handler");
- DBUG_ASSERT(m_hcount < m_root_parsing_ctx->max_handler_index());
-
- m_handler[m_hcount].cond= cond;
- m_handler[m_hcount].handler= h;
- m_handler[m_hcount].type= type;
- m_hcount+= 1;
- DBUG_PRINT("info", ("m_hcount: %d", m_hcount));
- DBUG_VOID_RETURN;
-}
-
-void
-sp_rcontext::pop_handlers(uint count)
+bool sp_rcontext::push_handler(sp_handler *handler, uint first_ip)
{
- DBUG_ENTER("sp_rcontext::pop_handlers");
- DBUG_ASSERT(m_hcount >= count);
+ /*
+ We should create handler entries in the callers arena, as
+ they could be (and usually are) used in several instructions.
+ */
+ sp_handler_entry *he=
+ new (callers_arena->mem_root) sp_handler_entry(handler, first_ip);
- m_hcount-= count;
+ if (he == NULL)
+ return true;
- DBUG_PRINT("info", ("m_hcount: %d", m_hcount));
- DBUG_VOID_RETURN;
+ return m_handlers.append(he);
}
-void
-sp_rcontext::push_hstack(uint h)
-{
- DBUG_ENTER("sp_rcontext::push_hstack");
- DBUG_ASSERT(m_hsp < m_root_parsing_ctx->max_handler_index());
-
- m_hstack[m_hsp++]= h;
- DBUG_PRINT("info", ("m_hsp: %d", m_hsp));
- DBUG_VOID_RETURN;
-}
-
-uint
-sp_rcontext::pop_hstack()
+void sp_rcontext::pop_handlers(size_t count)
{
- uint handler;
- DBUG_ENTER("sp_rcontext::pop_hstack");
- DBUG_ASSERT(m_hsp);
-
- handler= m_hstack[--m_hsp];
+ DBUG_ASSERT(m_handlers.elements() >= count);
- DBUG_PRINT("info", ("m_hsp: %d", m_hsp));
- DBUG_RETURN(handler);
+ for (size_t i= 0; i < count; ++i)
+ m_handlers.pop();
}
-/**
- Prepare found handler to be executed.
-
- @retval TRUE if an SQL handler is activated (was found) and IP of the
- first handler instruction.
- @retval FALSE if there is no active handler
-*/
-bool
-sp_rcontext::activate_handler(THD *thd,
- uint *ip,
- sp_instr *instr,
- Query_arena *execute_arena,
- Query_arena *backup_arena)
+bool sp_rcontext::handle_sql_condition(THD *thd,
+ uint *ip,
+ const sp_instr *cur_spi)
{
- if (m_hfound < 0)
- return FALSE;
+ DBUG_ENTER("sp_rcontext::handle_sql_condition");
- switch (m_handler[m_hfound].type) {
- case SP_HANDLER_NONE:
- break;
+ /*
+ If this is a fatal sub-statement error, and this runtime
+ context corresponds to a sub-statement, no CONTINUE/EXIT
+ handlers from this context are applicable: try to locate one
+ in the outer scope.
+ */
+ if (thd->is_fatal_sub_stmt_error && m_in_sub_stmt)
+ DBUG_RETURN(false);
- case SP_HANDLER_CONTINUE:
- thd->restore_active_arena(execute_arena, backup_arena);
- thd->set_n_backup_active_arena(execute_arena, backup_arena);
- push_hstack(instr->get_cont_dest());
+ Diagnostics_area *da= thd->get_stmt_da();
+ const sp_handler *found_handler= NULL;
+ const Sql_condition *found_condition= NULL;
- /* Fall through */
+ if (thd->is_error())
+ {
+ found_handler=
+ cur_spi->m_ctx->find_handler(da->get_sqlstate(),
+ da->sql_errno(),
+ Sql_condition::WARN_LEVEL_ERROR);
+
+ if (found_handler)
+ found_condition= da->get_error_condition();
+
+ /*
+ Found condition can be NULL if the diagnostics area was full
+ when the error was raised. It can also be NULL if
+ Diagnostics_area::set_error_status(uint sql_error) was used.
+ In these cases, make a temporary Sql_condition here so the
+ error can be handled.
+ */
+ if (!found_condition)
+ {
+ Sql_condition *condition=
+ new (callers_arena->mem_root) Sql_condition(callers_arena->mem_root);
+ condition->set(da->sql_errno(), da->get_sqlstate(),
+ Sql_condition::WARN_LEVEL_ERROR,
+ da->message());
+ found_condition= condition;
+ }
+ }
+ else if (da->current_statement_warn_count())
+ {
+ Diagnostics_area::Sql_condition_iterator it= da->sql_conditions();
+ const Sql_condition *c;
- default:
- /* End aborted result set. */
+ // Here we need to find the last warning/note from the stack.
+ // In MySQL most substantial warning is the last one.
+ // (We could have used a reverse iterator here if one existed)
- if (end_partial_result_set)
- thd->protocol->end_partial_result_set(thd);
+ while ((c= it++))
+ {
+ if (c->get_level() == Sql_condition::WARN_LEVEL_WARN ||
+ c->get_level() == Sql_condition::WARN_LEVEL_NOTE)
+ {
+ const sp_handler *handler=
+ cur_spi->m_ctx->find_handler(c->get_sqlstate(),
+ c->get_sql_errno(),
+ c->get_level());
+ if (handler)
+ {
+ found_handler= handler;
+ found_condition= c;
+ }
+ }
+ }
+ }
- /* Enter handler. */
+ if (!found_handler)
+ DBUG_RETURN(false);
- DBUG_ASSERT(m_ihsp < m_root_parsing_ctx->max_handler_index());
- DBUG_ASSERT(m_hfound >= 0);
+ // At this point, we know that:
+ // - there is a pending SQL-condition (error or warning);
+ // - there is an SQL-handler for it.
- m_in_handler[m_ihsp].ip= m_handler[m_hfound].handler;
- m_in_handler[m_ihsp].index= m_hfound;
- m_ihsp++;
+ DBUG_ASSERT(found_condition);
- DBUG_PRINT("info", ("Entering handler..."));
- DBUG_PRINT("info", ("m_ihsp: %d", m_ihsp));
+ sp_handler_entry *handler_entry= NULL;
+ for (size_t i= 0; i < m_handlers.elements(); ++i)
+ {
+ sp_handler_entry *h= m_handlers.at(i);
- /* Reset error state. */
+ if (h->handler == found_handler)
+ {
+ handler_entry= h;
+ break;
+ }
+ }
- thd->clear_error();
- thd->reset_killed(); // Some errors set thd->killed
- // (e.g. "bad data").
+ /*
+ handler_entry usually should not be NULL here, as that indicates
+ that the parser context thinks a HANDLER should be activated,
+ but the runtime context cannot find it.
+
+ However, this can happen (and this is in line with the Standard)
+ if SQL-condition has been raised before DECLARE HANDLER instruction
+ is processed.
+
+ For example:
+ CREATE PROCEDURE p()
+ BEGIN
+ DECLARE v INT DEFAULT 'get'; -- raises SQL-warning here
+ DECLARE EXIT HANDLER ... -- this handler does not catch the warning
+ END
+ */
+ if (!handler_entry)
+ DBUG_RETURN(false);
- /* Return IP of the activated SQL handler. */
- *ip= m_handler[m_hfound].handler;
+ // Mark active conditions so that they can be deleted when the handler exits.
+ da->mark_sql_conditions_for_removal();
- /* Reset found handler. */
- m_hfound= -1;
- }
+ uint continue_ip= handler_entry->handler->type == sp_handler::CONTINUE ?
+ cur_spi->get_cont_dest() : 0;
- return TRUE;
-}
+ /* End aborted result set. */
+ if (end_partial_result_set)
+ thd->protocol->end_partial_result_set(thd);
-void
-sp_rcontext::exit_handler()
-{
- DBUG_ENTER("sp_rcontext::exit_handler");
- DBUG_ASSERT(m_ihsp);
+ /* Reset error state. */
+ thd->clear_error();
+ thd->killed= NOT_KILLED; // Some errors set thd->killed
+ // (e.g. "bad data").
+
+ /* Add a frame to handler-call-stack. */
+ Sql_condition_info *cond_info=
+ new (callers_arena->mem_root) Sql_condition_info(found_condition,
+ callers_arena);
+ Handler_call_frame *frame=
+ new (callers_arena->mem_root) Handler_call_frame(cond_info, continue_ip);
+ m_handler_call_stack.append(frame);
- uint hindex= m_in_handler[m_ihsp-1].index;
- m_raised_conditions[hindex].clear();
- m_ihsp-= 1;
+ *ip= handler_entry->first_ip;
- DBUG_PRINT("info", ("m_ihsp: %d", m_ihsp));
- DBUG_VOID_RETURN;
+ DBUG_RETURN(true);
}
-Sql_condition_info* sp_rcontext::raised_condition() const
+
+uint sp_rcontext::exit_handler(Diagnostics_area *da)
{
- if (m_ihsp > 0)
- {
- uint hindex= m_in_handler[m_ihsp - 1].index;
- Sql_condition_info *raised= & m_raised_conditions[hindex];
- return raised;
- }
+ DBUG_ENTER("sp_rcontext::exit_handler");
+ DBUG_ASSERT(m_handler_call_stack.elements() > 0);
- if (m_prev_runtime_ctx)
- return m_prev_runtime_ctx->raised_condition();
+ Handler_call_frame *f= m_handler_call_stack.pop();
- return NULL;
-}
+ /*
+ Remove the SQL conditions that were present in DA when the
+ handler was activated.
+ */
+ da->remove_marked_sql_conditions();
+ uint continue_ip= f->continue_ip;
-int
-sp_rcontext::set_variable(THD *thd, uint var_idx, Item **value)
-{
- return set_variable(thd, m_var_table->field[var_idx], value);
+ DBUG_RETURN(continue_ip);
}
-int
-sp_rcontext::set_variable(THD *thd, Field *field, Item **value)
+int sp_rcontext::set_variable(THD *thd, Field *field, Item **value)
{
if (!value)
{
@@ -478,25 +379,47 @@ sp_rcontext::set_variable(THD *thd, Field *field, Item **value)
}
-Item *
-sp_rcontext::get_item(uint var_idx)
+Item_cache *sp_rcontext::create_case_expr_holder(THD *thd,
+ const Item *item) const
{
- return m_var_items[var_idx];
+ Item_cache *holder;
+ Query_arena current_arena;
+
+ thd->set_n_backup_active_arena(thd->spcont->callers_arena, &current_arena);
+
+ holder= Item_cache::get_cache(item);
+
+ thd->restore_active_arena(thd->spcont->callers_arena, &current_arena);
+
+ return holder;
}
-Item **
-sp_rcontext::get_item_addr(uint var_idx)
+bool sp_rcontext::set_case_expr(THD *thd, int case_expr_id,
+ Item **case_expr_item_ptr)
{
- return m_var_items + var_idx;
+ Item *case_expr_item= sp_prepare_func_item(thd, case_expr_item_ptr);
+ if (!case_expr_item)
+ return true;
+
+ if (!m_case_expr_holders[case_expr_id] ||
+ m_case_expr_holders[case_expr_id]->result_type() !=
+ case_expr_item->result_type())
+ {
+ m_case_expr_holders[case_expr_id]=
+ create_case_expr_holder(thd, case_expr_item);
+ }
+
+ m_case_expr_holders[case_expr_id]->store(case_expr_item);
+ m_case_expr_holders[case_expr_id]->cache_value();
+ return false;
}
-/*
- *
- * sp_cursor
- *
- */
+///////////////////////////////////////////////////////////////////////////
+// sp_cursor implementation.
+///////////////////////////////////////////////////////////////////////////
+
sp_cursor::sp_cursor(sp_lex_keeper *lex_keeper, sp_instr_cpush *i)
:m_lex_keeper(lex_keeper),
@@ -523,8 +446,7 @@ sp_cursor::sp_cursor(sp_lex_keeper *lex_keeper, sp_instr_cpush *i)
0 in case of success, -1 otherwise
*/
-int
-sp_cursor::open(THD *thd)
+int sp_cursor::open(THD *thd)
{
if (server_side_cursor)
{
@@ -538,8 +460,7 @@ sp_cursor::open(THD *thd)
}
-int
-sp_cursor::close(THD *thd)
+int sp_cursor::close(THD *thd)
{
if (! server_side_cursor)
{
@@ -551,16 +472,14 @@ sp_cursor::close(THD *thd)
}
-void
-sp_cursor::destroy()
+void sp_cursor::destroy()
{
delete server_side_cursor;
- server_side_cursor= 0;
+ server_side_cursor= NULL;
}
-int
-sp_cursor::fetch(THD *thd, List<struct sp_variable> *vars)
+int sp_cursor::fetch(THD *thd, List<sp_variable> *vars)
{
if (! server_side_cursor)
{
@@ -575,7 +494,7 @@ sp_cursor::fetch(THD *thd, List<struct sp_variable> *vars)
}
DBUG_EXECUTE_IF("bug23032_emit_warning",
- push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning(thd, Sql_condition::WARN_LEVEL_WARN,
ER_UNKNOWN_ERROR,
ER(ER_UNKNOWN_ERROR)););
@@ -599,108 +518,13 @@ sp_cursor::fetch(THD *thd, List<struct sp_variable> *vars)
}
-/*
- Create an instance of appropriate Item_cache class depending on the
- specified type in the callers arena.
-
- SYNOPSIS
- thd thread handler
- result_type type of the expression
+///////////////////////////////////////////////////////////////////////////
+// sp_cursor::Select_fetch_into_spvars implementation.
+///////////////////////////////////////////////////////////////////////////
- RETURN
- Pointer to valid object on success
- NULL on error
- NOTE
- We should create cache items in the callers arena, as they are used
- between in several instructions.
-*/
-
-Item_cache *
-sp_rcontext::create_case_expr_holder(THD *thd, const Item *item)
-{
- Item_cache *holder;
- Query_arena current_arena;
-
- thd->set_n_backup_active_arena(thd->spcont->callers_arena, &current_arena);
-
- holder= Item_cache::get_cache(item);
-
- thd->restore_active_arena(thd->spcont->callers_arena, &current_arena);
-
- return holder;
-}
-
-
-/*
- Set CASE expression to the specified value.
-
- SYNOPSIS
- thd thread handler
- case_expr_id identifier of the CASE expression
- case_expr_item a value of the CASE expression
-
- RETURN
- FALSE on success
- TRUE on error
-
- NOTE
- The idea is to reuse Item_cache for the expression of the one CASE
- statement. This optimization takes place when there is CASE statement
- inside of a loop. So, in other words, we will use the same object on each
- iteration instead of creating a new one for each iteration.
-
- TODO
- Hypothetically, a type of CASE expression can be different for each
- iteration. For instance, this can happen if the expression contains a
- session variable (something like @@VAR) and its type is changed from one
- iteration to another.
-
- In order to cope with this problem, we check type each time, when we use
- already created object. If the type does not match, we re-create Item.
- This also can (should?) be optimized.
-*/
-
-int
-sp_rcontext::set_case_expr(THD *thd, int case_expr_id, Item **case_expr_item_ptr)
-{
- Item *case_expr_item= sp_prepare_func_item(thd, case_expr_item_ptr);
- if (!case_expr_item)
- return TRUE;
-
- if (!m_case_expr_holders[case_expr_id] ||
- m_case_expr_holders[case_expr_id]->result_type() !=
- case_expr_item->result_type())
- {
- m_case_expr_holders[case_expr_id]=
- create_case_expr_holder(thd, case_expr_item);
- }
-
- m_case_expr_holders[case_expr_id]->store(case_expr_item);
- m_case_expr_holders[case_expr_id]->cache_value();
- return FALSE;
-}
-
-
-Item *
-sp_rcontext::get_case_expr(int case_expr_id)
-{
- return m_case_expr_holders[case_expr_id];
-}
-
-
-Item **
-sp_rcontext::get_case_expr_addr(int case_expr_id)
-{
- return (Item**) m_case_expr_holders + case_expr_id;
-}
-
-
-/***************************************************************************
- Select_fetch_into_spvars
-****************************************************************************/
-
-int Select_fetch_into_spvars::prepare(List<Item> &fields, SELECT_LEX_UNIT *u)
+int sp_cursor::Select_fetch_into_spvars::prepare(List<Item> &fields,
+ SELECT_LEX_UNIT *u)
{
/*
Cache the number of columns in the result set in order to easily
@@ -711,11 +535,11 @@ int Select_fetch_into_spvars::prepare(List<Item> &fields, SELECT_LEX_UNIT *u)
}
-int Select_fetch_into_spvars::send_data(List<Item> &items)
+int sp_cursor::Select_fetch_into_spvars::send_data(List<Item> &items)
{
- List_iterator_fast<struct sp_variable> spvar_iter(*spvar_list);
+ List_iterator_fast<sp_variable> spvar_iter(*spvar_list);
List_iterator_fast<Item> item_iter(items);
- sp_variable_t *spvar;
+ sp_variable *spvar;
Item *item;
/* Must be ensured by the caller */
@@ -728,7 +552,7 @@ int Select_fetch_into_spvars::send_data(List<Item> &items)
for (; spvar= spvar_iter++, item= item_iter++; )
{
if (thd->spcont->set_variable(thd, spvar->offset, &item))
- return 1;
+ return true;
}
- return 0;
+ return false;
}
diff --git a/sql/sp_rcontext.h b/sql/sp_rcontext.h
index 5008a73d96c..c48025da93d 100644
--- a/sql/sp_rcontext.h
+++ b/sql/sp_rcontext.h
@@ -22,80 +22,18 @@
#endif
#include "sql_class.h" // select_result_interceptor
+#include "sp_pcontext.h" // sp_condition_value
+
+///////////////////////////////////////////////////////////////////////////
+// sp_rcontext declaration.
+///////////////////////////////////////////////////////////////////////////
-struct sp_cond_type;
class sp_cursor;
-struct sp_variable;
class sp_lex_keeper;
class sp_instr_cpush;
class Query_arena;
class sp_head;
-class sp_pcontext;
class Item_cache;
-typedef class st_select_lex_unit SELECT_LEX_UNIT;
-class Server_side_cursor;
-
-#define SP_HANDLER_NONE 0
-#define SP_HANDLER_EXIT 1
-#define SP_HANDLER_CONTINUE 2
-#define SP_HANDLER_UNDO 3
-
-typedef struct
-{
- /** Condition caught by this HANDLER. */
- struct sp_cond_type *cond;
- /** Location (instruction pointer) of the handler code. */
- uint handler;
- /** Handler type (EXIT, CONTINUE). */
- int type;
-} sp_handler_t;
-
-typedef struct
-{
- /** Instruction pointer of the active handler. */
- uint ip;
- /** Handler index of the active handler. */
- uint index;
-} sp_active_handler_t;
-
-
-class Sql_condition_info : public Sql_alloc
-{
-public:
- /** SQL error code. */
- uint m_sql_errno;
-
- /** Error level. */
- MYSQL_ERROR::enum_warning_level m_level;
-
- /** SQLSTATE. */
- char m_sql_state[SQLSTATE_LENGTH + 1];
-
- /** Text message. */
- char m_message[MYSQL_ERRMSG_SIZE];
-
- void set(uint sql_errno, const char* sqlstate,
- MYSQL_ERROR::enum_warning_level level,
- const char* msg)
- {
- m_sql_errno= sql_errno;
- m_level= level;
-
- memcpy(m_sql_state, sqlstate, SQLSTATE_LENGTH);
- m_sql_state[SQLSTATE_LENGTH]= '\0';
-
- strncpy(m_message, msg, MYSQL_ERRMSG_SIZE);
- }
-
- void clear()
- {
- m_sql_errno= 0;
- m_level= MYSQL_ERROR::WARN_LEVEL_ERROR;
-
- m_sql_state[0]= '\0';
- m_message[0]= '\0';
- }
-};
/*
@@ -119,251 +57,412 @@ public:
class sp_rcontext : public Sql_alloc
{
- sp_rcontext(const sp_rcontext &); /* Prevent use of these */
- void operator=(sp_rcontext &);
-
- public:
-
- /*
- Arena used to (re) allocate items on . E.g. reallocate INOUT/OUT
- SP parameters when they don't fit into prealloced items. This
- is common situation with String items. It is used mainly in
- sp_eval_func_item().
- */
- Query_arena *callers_arena;
-
- /*
- End a open result set before start executing a continue/exit
- handler if one is found as otherwise the client will hang
- due to a violation of the client/server protocol.
- */
- bool end_partial_result_set;
-
-#ifndef DBUG_OFF
- /*
- The routine for which this runtime context is created. Used for checking
- if correct runtime context is used for variable handling.
- */
- sp_head *sp;
-#endif
-
- sp_rcontext(sp_pcontext *root_parsing_ctx, Field *return_value_fld,
- sp_rcontext *prev_runtime_ctx);
- bool init(THD *thd);
+public:
+ /// Construct and properly initialize a new sp_rcontext instance. The static
+ /// create-function is needed because we need a way to return an error from
+ /// the constructor.
+ ///
+ /// @param thd Thread handle.
+ /// @param root_parsing_ctx Top-level parsing context for this stored program.
+ /// @param return_value_fld Field object to store the return value
+ /// (for stored functions only).
+ ///
+ /// @return valid sp_rcontext object or NULL in case of OOM-error.
+ static sp_rcontext *create(THD *thd,
+ const sp_pcontext *root_parsing_ctx,
+ Field *return_value_fld);
~sp_rcontext();
- int
- set_variable(THD *thd, uint var_idx, Item **value);
-
- Item *
- get_item(uint var_idx);
+private:
+ sp_rcontext(const sp_pcontext *root_parsing_ctx,
+ Field *return_value_fld,
+ bool in_sub_stmt);
- Item **
- get_item_addr(uint var_idx);
+ // Prevent use of copying constructor and operator.
+ sp_rcontext(const sp_rcontext &);
+ void operator=(sp_rcontext &);
- bool
- set_return_value(THD *thd, Item **return_value_item);
+private:
+ /// This is an auxillary class to store entering instruction pointer for an
+ /// SQL-handler.
+ class sp_handler_entry : public Sql_alloc
+ {
+ public:
+ /// Handler definition (from parsing context).
+ const sp_handler *handler;
+
+ /// Instruction pointer to the first instruction.
+ uint first_ip;
+
+ /// The constructor.
+ ///
+ /// @param _handler sp_handler object.
+ /// @param _first_ip first instruction pointer.
+ sp_handler_entry(const sp_handler *_handler, uint _first_ip)
+ :handler(_handler), first_ip(_first_ip)
+ { }
+ };
- inline bool
- is_return_value_set() const
+public:
+ /// This class stores basic information about SQL-condition, such as:
+ /// - SQL error code;
+ /// - error level;
+ /// - SQLSTATE;
+ /// - text message.
+ ///
+ /// It's used to organize runtime SQL-handler call stack.
+ ///
+ /// Standard Sql_condition class can not be used, because we don't always have
+ /// an Sql_condition object for an SQL-condition in Diagnostics_area.
+ ///
+ /// Eventually, this class should be moved to sql_error.h, and be a part of
+ /// standard SQL-condition processing (Diagnostics_area should contain an
+ /// object for active SQL-condition, not just information stored in DA's
+ /// fields).
+ class Sql_condition_info : public Sql_alloc
{
- return m_return_value_set;
- }
+ public:
+ /// SQL error code.
+ uint sql_errno;
+
+ /// Error level.
+ Sql_condition::enum_warning_level level;
+
+ /// SQLSTATE.
+ char sql_state[SQLSTATE_LENGTH + 1];
+
+ /// Text message.
+ char *message;
+
+ /// The constructor.
+ ///
+ /// @param _sql_condition The SQL condition.
+ /// @param arena Query arena for SP
+ Sql_condition_info(const Sql_condition *_sql_condition,
+ Query_arena *arena)
+ :sql_errno(_sql_condition->get_sql_errno()),
+ level(_sql_condition->get_level())
+ {
+ memcpy(sql_state, _sql_condition->get_sqlstate(), SQLSTATE_LENGTH);
+ sql_state[SQLSTATE_LENGTH]= '\0';
+
+ message= strdup_root(arena->mem_root, _sql_condition->get_message_text());
+ }
+ };
- /*
- SQL handlers support.
- */
+private:
+ /// This class represents a call frame of SQL-handler (one invocation of a
+ /// handler). Basically, it's needed to store continue instruction pointer for
+ /// CONTINUE SQL-handlers.
+ class Handler_call_frame : public Sql_alloc
+ {
+ public:
+ /// SQL-condition, triggered handler activation.
+ const Sql_condition_info *sql_condition;
+
+ /// Continue-instruction-pointer for CONTINUE-handlers.
+ /// The attribute contains 0 for EXIT-handlers.
+ uint continue_ip;
+
+ /// The constructor.
+ ///
+ /// @param _sql_condition SQL-condition, triggered handler activation.
+ /// @param _continue_ip Continue instruction pointer.
+ Handler_call_frame(const Sql_condition_info *_sql_condition,
+ uint _continue_ip)
+ :sql_condition(_sql_condition),
+ continue_ip(_continue_ip)
+ { }
+ };
- void push_handler(struct sp_cond_type *cond, uint h, int type);
+public:
+ /// Arena used to (re) allocate items on. E.g. reallocate INOUT/OUT
+ /// SP-variables when they don't fit into prealloced items. This is common
+ /// situation with String items. It is used mainly in sp_eval_func_item().
+ Query_arena *callers_arena;
- void pop_handlers(uint count);
+ /// Flag to end an open result set before start executing an SQL-handler
+ /// (if one is found). Otherwise the client will hang due to a violation
+ /// of the client/server protocol.
+ bool end_partial_result_set;
- bool
- find_handler(THD *thd,
- uint sql_errno,
- const char *sqlstate,
- MYSQL_ERROR::enum_warning_level level,
- const char *msg);
+#ifndef DBUG_OFF
+ /// The stored program for which this runtime context is created. Used for
+ /// checking if correct runtime context is used for variable handling.
+ sp_head *sp;
+#endif
- Sql_condition_info *raised_condition() const;
+ /////////////////////////////////////////////////////////////////////////
+ // SP-variables.
+ /////////////////////////////////////////////////////////////////////////
- void
- push_hstack(uint h);
+ int set_variable(THD *thd, uint var_idx, Item **value)
+ { return set_variable(thd, m_var_table->field[var_idx], value); }
- uint
- pop_hstack();
+ Item *get_item(uint var_idx) const
+ { return m_var_items[var_idx]; }
- bool
- activate_handler(THD *thd,
- uint *ip,
- sp_instr *instr,
- Query_arena *execute_arena,
- Query_arena *backup_arena);
+ Item **get_item_addr(uint var_idx) const
+ { return m_var_items.array() + var_idx; }
+ bool set_return_value(THD *thd, Item **return_value_item);
- void
- exit_handler();
+ bool is_return_value_set() const
+ { return m_return_value_set; }
- void
- push_cursor(sp_lex_keeper *lex_keeper, sp_instr_cpush *i);
+ /////////////////////////////////////////////////////////////////////////
+ // SQL-handlers.
+ /////////////////////////////////////////////////////////////////////////
- void
- pop_cursors(uint count);
+ /// Create a new sp_handler_entry instance and push it to the handler call
+ /// stack.
+ ///
+ /// @param handler SQL-handler object.
+ /// @param first_ip First instruction pointer of the handler.
+ ///
+ /// @return error flag.
+ /// @retval false on success.
+ /// @retval true on error.
+ bool push_handler(sp_handler *handler, uint first_ip);
- inline void
- pop_all_cursors()
- {
- pop_cursors(m_ccount);
- }
+ /// Pop and delete given number of sp_handler_entry instances from the handler
+ /// call stack.
+ ///
+ /// @param count Number of handler entries to pop & delete.
+ void pop_handlers(size_t count);
- inline sp_cursor *
- get_cursor(uint i)
+ const Sql_condition_info *raised_condition() const
{
- return m_cstack[i];
+ return m_handler_call_stack.elements() ?
+ (*m_handler_call_stack.back())->sql_condition : NULL;
}
- /*
- CASE expressions support.
- */
+ /// Handle current SQL condition (if any).
+ ///
+ /// This is the public-interface function to handle SQL conditions in
+ /// stored routines.
+ ///
+ /// @param thd Thread handle.
+ /// @param ip[out] Instruction pointer to the first handler
+ /// instruction.
+ /// @param cur_spi Current SP instruction.
+ ///
+ /// @retval true if an SQL-handler has been activated. That means, all of
+ /// the following conditions are satisfied:
+ /// - the SP-instruction raised SQL-condition(s),
+ /// - and there is an SQL-handler to process at least one of those
+ /// SQL-conditions,
+ /// - and that SQL-handler has been activated.
+ /// Note, that the return value has nothing to do with "error flag"
+ /// semantics.
+ ///
+ /// @retval false otherwise.
+ bool handle_sql_condition(THD *thd,
+ uint *ip,
+ const sp_instr *cur_spi);
+
+ /// Remove latest call frame from the handler call stack.
+ ///
+ /// @param da Diagnostics area containing handled conditions.
+ ///
+ /// @return continue instruction pointer of the removed handler.
+ uint exit_handler(Diagnostics_area *da);
+
+ /////////////////////////////////////////////////////////////////////////
+ // Cursors.
+ /////////////////////////////////////////////////////////////////////////
+
+ /// Create a new sp_cursor instance and push it to the cursor stack.
+ ///
+ /// @param lex_keeper SP-instruction execution helper.
+ /// @param i Cursor-push instruction.
+ ///
+ /// @return error flag.
+ /// @retval false on success.
+ /// @retval true on error.
+ bool push_cursor(sp_lex_keeper *lex_keeper, sp_instr_cpush *i);
+
+ /// Pop and delete given number of sp_cursor instance from the cursor stack.
+ ///
+ /// @param count Number of cursors to pop & delete.
+ void pop_cursors(uint count);
+
+ void pop_all_cursors()
+ { pop_cursors(m_ccount); }
+
+ sp_cursor *get_cursor(uint i) const
+ { return m_cstack[i]; }
+
+ /////////////////////////////////////////////////////////////////////////
+ // CASE expressions.
+ /////////////////////////////////////////////////////////////////////////
+
+ /// Set CASE expression to the specified value.
+ ///
+ /// @param thd Thread handler.
+ /// @param case_expr_id The CASE expression identifier.
+ /// @param case_expr_item The CASE expression value
+ ///
+ /// @return error flag.
+ /// @retval false on success.
+ /// @retval true on error.
+ ///
+ /// @note The idea is to reuse Item_cache for the expression of the one
+ /// CASE statement. This optimization takes place when there is CASE
+ /// statement inside of a loop. So, in other words, we will use the same
+ /// object on each iteration instead of creating a new one for each
+ /// iteration.
+ ///
+ /// TODO
+ /// Hypothetically, a type of CASE expression can be different for each
+ /// iteration. For instance, this can happen if the expression contains
+ /// a session variable (something like @@VAR) and its type is changed
+ /// from one iteration to another.
+ ///
+ /// In order to cope with this problem, we check type each time, when we
+ /// use already created object. If the type does not match, we re-create
+ /// Item. This also can (should?) be optimized.
+ bool set_case_expr(THD *thd, int case_expr_id, Item **case_expr_item_ptr);
+
+ Item *get_case_expr(int case_expr_id) const
+ { return m_case_expr_holders[case_expr_id]; }
+
+ Item ** get_case_expr_addr(int case_expr_id) const
+ { return (Item**) m_case_expr_holders.array() + case_expr_id; }
- int
- set_case_expr(THD *thd, int case_expr_id, Item **case_expr_item_ptr);
+private:
+ /// Internal function to allocate memory for arrays.
+ ///
+ /// @param thd Thread handle.
+ ///
+ /// @return error flag: false on success, true in case of failure.
+ bool alloc_arrays(THD *thd);
+
+ /// Create and initialize a table to store SP-variables.
+ ///
+ /// param thd Thread handle.
+ ///
+ /// @return error flag.
+ /// @retval false on success.
+ /// @retval true on error.
+ bool init_var_table(THD *thd);
- Item *
- get_case_expr(int case_expr_id);
+ /// Create and initialize an Item-adapter (Item_field) for each SP-var field.
+ ///
+ /// param thd Thread handle.
+ ///
+ /// @return error flag.
+ /// @retval false on success.
+ /// @retval true on error.
+ bool init_var_items(THD *thd);
+
+ /// Create an instance of appropriate Item_cache class depending on the
+ /// specified type in the callers arena.
+ ///
+ /// @note We should create cache items in the callers arena, as they are
+ /// used between in several instructions.
+ ///
+ /// @param thd Thread handler.
+ /// @param item Item to get the expression type.
+ ///
+ /// @return Pointer to valid object on success, or NULL in case of error.
+ Item_cache *create_case_expr_holder(THD *thd, const Item *item) const;
- Item **
- get_case_expr_addr(int case_expr_id);
+ int set_variable(THD *thd, Field *field, Item **value);
private:
- sp_pcontext *m_root_parsing_ctx;
+ /// Top-level (root) parsing context for this runtime context.
+ const sp_pcontext *m_root_parsing_ctx;
- /* Virtual table for storing variables. */
+ /// Virtual table for storing SP-variables.
TABLE *m_var_table;
- /*
- Collection of Item_field proxies, each of them points to the corresponding
- field in m_var_table.
- */
- Item **m_var_items;
+ /// Collection of Item_field proxies, each of them points to the
+ /// corresponding field in m_var_table.
+ Bounds_checked_array<Item *> m_var_items;
- /*
- This is a pointer to a field, which should contain return value for stored
- functions (only). For stored procedures, this pointer is NULL.
- */
+ /// This is a pointer to a field, which should contain return value for
+ /// stored functions (only). For stored procedures, this pointer is NULL.
Field *m_return_value_fld;
- /*
- Indicates whether the return value (in m_return_value_fld) has been set
- during execution.
- */
+ /// Indicates whether the return value (in m_return_value_fld) has been
+ /// set during execution.
bool m_return_value_set;
- /**
- TRUE if the context is created for a sub-statement.
- */
- bool in_sub_stmt;
+ /// Flag to tell if the runtime context is created for a sub-statement.
+ bool m_in_sub_stmt;
- sp_handler_t *m_handler; // Visible handlers
+ /// Stack of visible handlers.
+ Dynamic_array<sp_handler_entry *> m_handlers;
- /**
- SQL conditions caught by each handler.
- This is an array indexed by handler index.
- */
- Sql_condition_info *m_raised_conditions;
+ /// Stack of caught SQL conditions.
+ Dynamic_array<Handler_call_frame *> m_handler_call_stack;
- uint m_hcount; // Stack pointer for m_handler
- uint *m_hstack; // Return stack for continue handlers
- uint m_hsp; // Stack pointer for m_hstack
- /** Active handler stack. */
- sp_active_handler_t *m_in_handler;
- uint m_ihsp; // Stack pointer for m_in_handler
- int m_hfound; // Set by find_handler; -1 if not found
+ /// Stack of cursors.
+ Bounds_checked_array<sp_cursor *> m_cstack;
- sp_cursor **m_cstack;
+ /// Current number of cursors in m_cstack.
uint m_ccount;
- Item_cache **m_case_expr_holders;
-
- /* Previous runtime context (NULL if none) */
- sp_rcontext *m_prev_runtime_ctx;
-
-private:
- bool init_var_table(THD *thd);
- bool init_var_items();
-
- Item_cache *create_case_expr_holder(THD *thd, const Item *item);
-
- int set_variable(THD *thd, Field *field, Item **value);
+ /// Array of CASE expression holders.
+ Bounds_checked_array<Item_cache *> m_case_expr_holders;
}; // class sp_rcontext : public Sql_alloc
+///////////////////////////////////////////////////////////////////////////
+// sp_cursor declaration.
+///////////////////////////////////////////////////////////////////////////
-/*
- An interceptor of cursor result set used to implement
- FETCH <cname> INTO <varlist>.
-*/
-
-class Select_fetch_into_spvars: public select_result_interceptor
-{
- List<struct sp_variable> *spvar_list;
- uint field_count;
-public:
- Select_fetch_into_spvars() {} /* Remove gcc warning */
- uint get_field_count() { return field_count; }
- void set_spvar_list(List<struct sp_variable> *vars) { spvar_list= vars; }
-
- virtual bool send_eof() { return FALSE; }
- virtual int send_data(List<Item> &items);
- virtual int prepare(List<Item> &list, SELECT_LEX_UNIT *u);
-};
-
+class Server_side_cursor;
+typedef class st_select_lex_unit SELECT_LEX_UNIT;
/* A mediator between stored procedures and server side cursors */
class sp_cursor : public Sql_alloc
{
-public:
+private:
+ /// An interceptor of cursor result set used to implement
+ /// FETCH <cname> INTO <varlist>.
+ class Select_fetch_into_spvars: public select_result_interceptor
+ {
+ List<sp_variable> *spvar_list;
+ uint field_count;
+ public:
+ Select_fetch_into_spvars() {} /* Remove gcc warning */
+ uint get_field_count() { return field_count; }
+ void set_spvar_list(List<sp_variable> *vars) { spvar_list= vars; }
+
+ virtual bool send_eof() { return FALSE; }
+ virtual int send_data(List<Item> &items);
+ virtual int prepare(List<Item> &list, SELECT_LEX_UNIT *u);
+};
+public:
sp_cursor(sp_lex_keeper *lex_keeper, sp_instr_cpush *i);
virtual ~sp_cursor()
- {
- destroy();
- }
+ { destroy(); }
- sp_lex_keeper *
- get_lex_keeper() { return m_lex_keeper; }
+ sp_lex_keeper *get_lex_keeper() { return m_lex_keeper; }
- int
- open(THD *thd);
+ int open(THD *thd);
- int
- close(THD *thd);
+ int close(THD *thd);
- inline bool
- is_open()
- {
- return test(server_side_cursor);
- }
+ my_bool is_open()
+ { return MY_TEST(server_side_cursor); }
- int
- fetch(THD *, List<struct sp_variable> *vars);
+ int fetch(THD *, List<sp_variable> *vars);
- inline sp_instr_cpush *
- get_instr()
- {
- return m_i;
- }
+ sp_instr_cpush *get_instr()
+ { return m_i; }
private:
-
Select_fetch_into_spvars result;
sp_lex_keeper *m_lex_keeper;
Server_side_cursor *server_side_cursor;
sp_instr_cpush *m_i; // My push instruction
- void
- destroy();
+ void destroy();
}; // class sp_cursor : public Sql_alloc
diff --git a/sql/spatial.cc b/sql/spatial.cc
index c5dc0c2140c..bfe302f332e 100644
--- a/sql/spatial.cc
+++ b/sql/spatial.cc
@@ -15,8 +15,8 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+#include <my_global.h>
#include "sql_priv.h"
-#include "my_global.h" // REQUIRED for HAVE_* below
#include "spatial.h"
#include "gstream.h" // Gis_read_stream
#include "sql_string.h" // String
diff --git a/sql/spatial.h b/sql/spatial.h
index ee2d6d5ec8d..3a6055add06 100644
--- a/sql/spatial.h
+++ b/sql/spatial.h
@@ -196,8 +196,8 @@ struct MBR
if (d != mbr->dimension() || d <= 0 || contains(mbr) || within(mbr))
return 0;
- MBR intersection(max(xmin, mbr->xmin), max(ymin, mbr->ymin),
- min(xmax, mbr->xmax), min(ymax, mbr->ymax));
+ MBR intersection(MY_MAX(xmin, mbr->xmin), MY_MAX(ymin, mbr->ymin),
+ MY_MIN(xmax, mbr->xmax), MY_MIN(ymax, mbr->ymax));
return (d == intersection.dimension());
}
diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc
index 55eff83cb89..e6fb4911f01 100644
--- a/sql/sql_acl.cc
+++ b/sql/sql_acl.cc
@@ -25,7 +25,7 @@
in the relevant fields. Empty strings comes last.
*/
-#include "my_global.h" /* NO_EMBEDDED_ACCESS_CHECKS */
+#include <my_global.h> /* NO_EMBEDDED_ACCESS_CHECKS */
#include "sql_priv.h"
#include "sql_acl.h" // MYSQL_DB_FIELD_COUNT, ACL_ACCESS
#include "sql_base.h" // close_mysql_tables
@@ -38,19 +38,20 @@
#include "records.h" // READ_RECORD, read_record_info,
// init_read_record, end_read_record
#include "rpl_filter.h" // rpl_filter
+#include "rpl_rli.h"
#include <m_ctype.h>
#include <stdarg.h>
#include "sp_head.h"
#include "sp.h"
#include "transaction.h"
#include "lock.h" // MYSQL_LOCK_IGNORE_TIMEOUT
-#include "records.h" // init_read_record, end_read_record
#include <sql_common.h>
#include <mysql/plugin_auth.h>
#include "sql_connect.h"
#include "hostname.h"
#include "sql_db.h"
#include "sql_array.h"
+#include "sql_hset.h"
#include "sql_plugin_compat.h"
@@ -59,15 +60,15 @@ bool mysql_user_table_is_in_short_password_format= false;
static const
TABLE_FIELD_TYPE mysql_db_table_fields[MYSQL_DB_FIELD_COUNT] = {
{
- { C_STRING_WITH_LEN("Host") },
+ { C_STRING_WITH_LEN("Host") },
{ C_STRING_WITH_LEN("char(60)") },
{NULL, 0}
- },
+ },
{
- { C_STRING_WITH_LEN("Db") },
+ { C_STRING_WITH_LEN("Db") },
{ C_STRING_WITH_LEN("char(64)") },
{NULL, 0}
- },
+ },
{
{ C_STRING_WITH_LEN("User") },
{ C_STRING_WITH_LEN("char(") },
@@ -171,24 +172,45 @@ TABLE_FIELD_TYPE mysql_db_table_fields[MYSQL_DB_FIELD_COUNT] = {
};
const TABLE_FIELD_DEF
- mysql_db_table_def= {MYSQL_DB_FIELD_COUNT, mysql_db_table_fields};
+mysql_db_table_def= {MYSQL_DB_FIELD_COUNT, mysql_db_table_fields, 0, (uint*) 0 };
static LEX_STRING native_password_plugin_name= {
C_STRING_WITH_LEN("mysql_native_password")
};
-
+
static LEX_STRING old_password_plugin_name= {
C_STRING_WITH_LEN("mysql_old_password")
};
-
+
/// @todo make it configurable
LEX_STRING *default_auth_plugin_name= &native_password_plugin_name;
+/*
+ Wildcard host, matches any hostname
+*/
+LEX_STRING host_not_specified= { C_STRING_WITH_LEN("%") };
+
+/*
+ Constants, used in the SHOW GRANTS command.
+ Their actual string values are irrelevant, they're always compared
+ as pointers to these string constants.
+*/
+LEX_STRING current_user= { C_STRING_WITH_LEN("*current_user") };
+LEX_STRING current_role= { C_STRING_WITH_LEN("*current_role") };
+LEX_STRING current_user_and_current_role= { C_STRING_WITH_LEN("*current_user_and_current_role") };
+
+
#ifndef NO_EMBEDDED_ACCESS_CHECKS
static plugin_ref old_password_plugin;
#endif
static plugin_ref native_password_plugin;
+static char *safe_str(char *str)
+{ return str ? str : const_cast<char*>(""); }
+
+static const char *safe_str(const char *str)
+{ return str ? str : ""; }
+
/* Classes */
struct acl_host_and_ip
@@ -197,6 +219,12 @@ struct acl_host_and_ip
long ip, ip_mask; // Used with masked ip:s
};
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+static bool compare_hostname(const acl_host_and_ip *, const char *, const char *);
+#else
+#define compare_hostname(X,Y,Z) 0
+#endif
+
class ACL_ACCESS {
public:
ulong sort;
@@ -212,15 +240,27 @@ public:
char *db;
};
-class ACL_USER :public ACL_ACCESS
+class ACL_USER_BASE :public ACL_ACCESS
+{
+
+public:
+ static void *operator new(size_t size, MEM_ROOT *mem_root)
+ { return (void*) alloc_root(mem_root, size); }
+
+ uchar flags; // field used to store various state information
+ LEX_STRING user;
+ /* list to hold references to granted roles (ACL_ROLE instances) */
+ DYNAMIC_ARRAY role_grants;
+};
+
+class ACL_USER :public ACL_USER_BASE
{
public:
acl_host_and_ip host;
uint hostname_length;
USER_RESOURCES user_resource;
- char *user;
uint8 salt[SCRAMBLE_LENGTH + 1]; // scrambled password in binary form
- uint8 salt_len; // 0 - no password, 4 - 3.20, 8 - 4.0, 20 - 4.1.1
+ uint8 salt_len; // 0 - no password, 4 - 3.20, 8 - 4.0, 20 - 4.1.1
enum SSL_type ssl_type;
const char *ssl_cipher, *x509_issuer, *x509_subject;
LEX_STRING plugin;
@@ -232,7 +272,8 @@ public:
if (!dst)
return 0;
*dst= *this;
- dst->user= safe_strdup_root(root, user);
+ dst->user.str= safe_strdup_root(root, user.str);
+ dst->user.length= user.length;
dst->ssl_cipher= safe_strdup_root(root, ssl_cipher);
dst->x509_issuer= safe_strdup_root(root, x509_issuer);
dst->x509_subject= safe_strdup_root(root, x509_subject);
@@ -243,8 +284,55 @@ public:
dst->plugin.str= strmake_root(root, plugin.str, plugin.length);
dst->auth_string.str= safe_strdup_root(root, auth_string.str);
dst->host.hostname= safe_strdup_root(root, host.hostname);
+ bzero(&dst->role_grants, sizeof(role_grants));
return dst;
}
+
+ int cmp(const char *user2, const char *host2)
+ {
+ CHARSET_INFO *cs= system_charset_info;
+ int res;
+ res= strcmp(safe_str(user.str), safe_str(user2));
+ if (!res)
+ res= my_strcasecmp(cs, host.hostname, host2);
+ return res;
+ }
+
+ bool eq(const char *user2, const char *host2) { return !cmp(user2, host2); }
+
+ bool wild_eq(const char *user2, const char *host2, const char *ip2)
+ {
+ if (strcmp(safe_str(user.str), safe_str(user2)))
+ return false;
+
+ return compare_hostname(&host, host2, ip2 ? ip2 : host2);
+ }
+};
+
+class ACL_ROLE :public ACL_USER_BASE
+{
+public:
+ /*
+ In case of granting a role to a role, the access bits are merged together
+ via a bit OR operation and placed in the ACL_USER::access field.
+
+ When rebuilding role_grants via the rebuild_role_grant function,
+ the ACL_USER::access field needs to be reset first. The field
+ initial_role_access holds initial grants, as granted directly to the role
+ */
+ ulong initial_role_access;
+ /*
+ In subgraph traversal, when we need to traverse only a part of the graph
+ (e.g. all direct and indirect grantees of a role X), the counter holds the
+ number of affected neighbour nodes.
+ See also propagate_role_grants()
+ */
+ uint counter;
+ DYNAMIC_ARRAY parent_grantee; // array of backlinks to elements granted
+
+ ACL_ROLE(ACL_USER * user, MEM_ROOT *mem);
+ ACL_ROLE(const char * rolename, ulong privileges, MEM_ROOT *mem);
+
};
class ACL_DB :public ACL_ACCESS
@@ -252,16 +340,30 @@ class ACL_DB :public ACL_ACCESS
public:
acl_host_and_ip host;
char *user,*db;
+ ulong initial_access; /* access bits present in the table */
};
+#ifndef DBUG_OFF
+/* status variables, only visible in SHOW STATUS after -#d,role_merge_stats */
+ulong role_global_merges= 0, role_db_merges= 0, role_table_merges= 0,
+ role_column_merges= 0, role_routine_merges= 0;
+#endif
#ifndef NO_EMBEDDED_ACCESS_CHECKS
static void update_hostname(acl_host_and_ip *host, const char *hostname);
static ulong get_sort(uint count,...);
-static bool compare_hostname(const acl_host_and_ip *host, const char *hostname,
- const char *ip);
-static bool show_proxy_grants (THD *thd, LEX_USER *user,
- char *buff, size_t buffsize);
+static bool show_proxy_grants (THD *, const char *, const char *,
+ char *, size_t);
+static bool show_role_grants(THD *, const char *, const char *,
+ ACL_USER_BASE *, char *, size_t);
+static bool show_global_privileges(THD *, ACL_USER_BASE *,
+ bool, char *, size_t);
+static bool show_database_privileges(THD *, const char *, const char *,
+ char *, size_t);
+static bool show_table_and_column_privileges(THD *, const char *, const char *,
+ char *, size_t);
+static int show_routine_grants(THD *, const char *, const char *, HASH *,
+ const char *, int, char *, int);
class ACL_PROXY_USER :public ACL_ACCESS
{
@@ -271,11 +373,11 @@ class ACL_PROXY_USER :public ACL_ACCESS
const char *proxied_user;
bool with_grant;
- typedef enum {
- MYSQL_PROXIES_PRIV_HOST,
- MYSQL_PROXIES_PRIV_USER,
+ typedef enum {
+ MYSQL_PROXIES_PRIV_HOST,
+ MYSQL_PROXIES_PRIV_USER,
MYSQL_PROXIES_PRIV_PROXIED_HOST,
- MYSQL_PROXIES_PRIV_PROXIED_USER,
+ MYSQL_PROXIES_PRIV_PROXIED_USER,
MYSQL_PROXIES_PRIV_WITH_GRANT,
MYSQL_PROXIES_PRIV_GRANTOR,
MYSQL_PROXIES_PRIV_TIMESTAMP } old_acl_proxy_users;
@@ -287,16 +389,14 @@ public:
bool with_grant_arg)
{
user= (user_arg && *user_arg) ? user_arg : NULL;
- update_hostname (&host,
- (host_arg && *host_arg) ? host_arg : NULL);
- proxied_user= (proxied_user_arg && *proxied_user_arg) ?
+ update_hostname (&host, (host_arg && *host_arg) ? host_arg : NULL);
+ proxied_user= (proxied_user_arg && *proxied_user_arg) ?
proxied_user_arg : NULL;
- update_hostname (&proxied_host,
+ update_hostname (&proxied_host,
(proxied_host_arg && *proxied_host_arg) ?
proxied_host_arg : NULL);
with_grant= with_grant_arg;
- sort= get_sort(4, host.hostname, user,
- proxied_host.hostname, proxied_user);
+ sort= get_sort(4, host.hostname, user, proxied_host.hostname, proxied_user);
}
void init(MEM_ROOT *mem, const char *host_arg, const char *user_arg,
@@ -305,9 +405,9 @@ public:
{
init ((host_arg && *host_arg) ? strdup_root (mem, host_arg) : NULL,
(user_arg && *user_arg) ? strdup_root (mem, user_arg) : NULL,
- (proxied_host_arg && *proxied_host_arg) ?
+ (proxied_host_arg && *proxied_host_arg) ?
strdup_root (mem, proxied_host_arg) : NULL,
- (proxied_user_arg && *proxied_user_arg) ?
+ (proxied_user_arg && *proxied_user_arg) ?
strdup_root (mem, proxied_user_arg) : NULL,
with_grant_arg);
}
@@ -326,29 +426,27 @@ public:
const char *get_host() { return host.hostname; }
const char *get_proxied_user() { return proxied_user; }
const char *get_proxied_host() { return proxied_host.hostname; }
- void set_user(MEM_ROOT *mem, const char *user_arg)
- {
+ void set_user(MEM_ROOT *mem, const char *user_arg)
+ {
user= user_arg && *user_arg ? strdup_root(mem, user_arg) : NULL;
}
- void set_host(MEM_ROOT *mem, const char *host_arg)
- {
- update_hostname(&host,
- (host_arg && *host_arg) ?
- strdup_root(mem, host_arg) : NULL);
+ void set_host(MEM_ROOT *mem, const char *host_arg)
+ {
+ update_hostname(&host, safe_strdup_root(mem, host_arg));
}
bool check_validity(bool check_no_resolve)
{
- if (check_no_resolve &&
+ if (check_no_resolve &&
(hostname_requires_resolving(host.hostname) ||
hostname_requires_resolving(proxied_host.hostname)))
{
sql_print_warning("'proxies_priv' entry '%s@%s %s@%s' "
"ignored in --skip-name-resolve mode.",
- proxied_user ? proxied_user : "",
- proxied_host.hostname ? proxied_host.hostname : "",
- user ? user : "",
- host.hostname ? host.hostname : "");
+ safe_str(proxied_user),
+ safe_str(proxied_host.hostname),
+ safe_str(user),
+ safe_str(host.hostname));
return TRUE;
}
return FALSE;
@@ -362,22 +460,15 @@ public:
"compare_hostname(%s,%s,%s) &&"
"wild_compare (%s,%s) &&"
"wild_compare (%s,%s)",
- host.hostname ? host.hostname : "<NULL>",
- host_arg ? host_arg : "<NULL>",
- ip_arg ? ip_arg : "<NULL>",
- proxied_host.hostname ? proxied_host.hostname : "<NULL>",
- host_arg ? host_arg : "<NULL>",
- ip_arg ? ip_arg : "<NULL>",
- user_arg ? user_arg : "<NULL>",
- user ? user : "<NULL>",
- proxied_user_arg ? proxied_user_arg : "<NULL>",
- proxied_user ? proxied_user : "<NULL>"));
+ host.hostname, host_arg, ip_arg, proxied_host.hostname,
+ host_arg, ip_arg, user_arg, user,
+ proxied_user_arg, proxied_user));
DBUG_RETURN(compare_hostname(&host, host_arg, ip_arg) &&
compare_hostname(&proxied_host, host_arg, ip_arg) &&
(!user ||
(user_arg && !wild_compare(user_arg, user, TRUE))) &&
- (!proxied_user ||
- (proxied_user && !wild_compare(proxied_user_arg,
+ (!proxied_user ||
+ (proxied_user && !wild_compare(proxied_user_arg,
proxied_user, TRUE))));
}
@@ -395,21 +486,16 @@ public:
"strcmp(%s,%s) &&"
"wild_compare (%s,%s) &&"
"wild_compare (%s,%s)",
- user ? user : "<NULL>",
- grant->user ? grant->user : "<NULL>",
- proxied_user ? proxied_user : "<NULL>",
- grant->proxied_user ? grant->proxied_user : "<NULL>",
- host.hostname ? host.hostname : "<NULL>",
- grant->host.hostname ? grant->host.hostname : "<NULL>",
- proxied_host.hostname ? proxied_host.hostname : "<NULL>",
- grant->proxied_host.hostname ?
- grant->proxied_host.hostname : "<NULL>"));
+ user, grant->user, proxied_user, grant->proxied_user,
+ host.hostname, grant->host.hostname,
+ proxied_host.hostname, grant->proxied_host.hostname));
- DBUG_RETURN(auth_element_equals(user, grant->user) &&
- auth_element_equals(proxied_user, grant->proxied_user) &&
- auth_element_equals(host.hostname, grant->host.hostname) &&
- auth_element_equals(proxied_host.hostname,
- grant->proxied_host.hostname));
+ bool res= auth_element_equals(user, grant->user) &&
+ auth_element_equals(proxied_user, grant->proxied_user) &&
+ auth_element_equals(host.hostname, grant->host.hostname) &&
+ auth_element_equals(proxied_host.hostname,
+ grant->proxied_host.hostname);
+ DBUG_RETURN(res);
}
@@ -446,23 +532,21 @@ public:
with_grant= grant->with_grant;
}
- static int store_pk(TABLE *table,
- const LEX_STRING *host,
+ static int store_pk(TABLE *table,
+ const LEX_STRING *host,
const LEX_STRING *user,
- const LEX_STRING *proxied_host,
+ const LEX_STRING *proxied_host,
const LEX_STRING *proxied_user)
{
DBUG_ENTER("ACL_PROXY_USER::store_pk");
DBUG_PRINT("info", ("host=%s, user=%s, proxied_host=%s, proxied_user=%s",
- host->str ? host->str : "<NULL>",
- user->str ? user->str : "<NULL>",
- proxied_host->str ? proxied_host->str : "<NULL>",
- proxied_user->str ? proxied_user->str : "<NULL>"));
- if (table->field[MYSQL_PROXIES_PRIV_HOST]->store(host->str,
+ host->str, user->str,
+ proxied_host->str, proxied_user->str));
+ if (table->field[MYSQL_PROXIES_PRIV_HOST]->store(host->str,
host->length,
system_charset_info))
DBUG_RETURN(TRUE);
- if (table->field[MYSQL_PROXIES_PRIV_USER]->store(user->str,
+ if (table->field[MYSQL_PROXIES_PRIV_USER]->store(user->str,
user->length,
system_charset_info))
DBUG_RETURN(TRUE);
@@ -490,10 +574,10 @@ public:
if (store_pk(table, host, user, proxied_host, proxied_user))
DBUG_RETURN(TRUE);
DBUG_PRINT("info", ("with_grant=%s", with_grant ? "TRUE" : "FALSE"));
- if (table->field[MYSQL_PROXIES_PRIV_WITH_GRANT]->store(with_grant ? 1 : 0,
- TRUE))
+ if (table->field[MYSQL_PROXIES_PRIV_WITH_GRANT]->store(with_grant ? 1 : 0,
+ TRUE))
DBUG_RETURN(TRUE);
- if (table->field[MYSQL_PROXIES_PRIV_GRANTOR]->store(grantor,
+ if (table->field[MYSQL_PROXIES_PRIV_GRANTOR]->store(grantor,
strlen(grantor),
system_charset_info))
DBUG_RETURN(TRUE);
@@ -520,6 +604,81 @@ static uchar* acl_entry_get_key(acl_entry *entry, size_t *length,
return (uchar*) entry->key;
}
+static uchar* acl_role_get_key(ACL_ROLE *entry, size_t *length,
+ my_bool not_used __attribute__((unused)))
+{
+ *length=(uint) entry->user.length;
+ return (uchar*) entry->user.str;
+}
+
+struct ROLE_GRANT_PAIR : public Sql_alloc
+{
+ char *u_uname;
+ char *u_hname;
+ char *r_uname;
+ LEX_STRING hashkey;
+ bool with_admin;
+
+ bool init(MEM_ROOT *mem, char *username, char *hostname, char *rolename,
+ bool with_admin_option);
+};
+
+static uchar* acl_role_map_get_key(ROLE_GRANT_PAIR *entry, size_t *length,
+ my_bool not_used __attribute__((unused)))
+{
+ *length=(uint) entry->hashkey.length;
+ return (uchar*) entry->hashkey.str;
+}
+
+bool ROLE_GRANT_PAIR::init(MEM_ROOT *mem, char *username,
+ char *hostname, char *rolename,
+ bool with_admin_option)
+{
+ if (!this)
+ return true;
+
+ size_t uname_l = username ? strlen(username) : 0;
+ size_t hname_l = hostname ? strlen(hostname) : 0;
+ size_t rname_l = rolename ? strlen(rolename) : 0;
+ /*
+ Create a buffer that holds all 3 NULL terminated strings in succession
+ To save memory space, the same buffer is used as the hashkey
+ */
+ size_t bufflen = uname_l + hname_l + rname_l + 3; //add the '\0' aswell
+ char *buff= (char *)alloc_root(mem, bufflen);
+ if (!buff)
+ return true;
+
+ /*
+ Offsets in the buffer for all 3 strings
+ */
+ char *username_pos= buff;
+ char *hostname_pos= buff + uname_l + 1;
+ char *rolename_pos= buff + uname_l + hname_l + 2;
+
+ if (username) //prevent undefined behaviour
+ memcpy(username_pos, username, uname_l);
+ username_pos[uname_l]= '\0'; //#1 string terminator
+ u_uname= username_pos;
+
+ if (hostname) //prevent undefined behaviour
+ memcpy(hostname_pos, hostname, hname_l);
+ hostname_pos[hname_l]= '\0'; //#2 string terminator
+ u_hname= hostname_pos;
+
+ if (rolename) //prevent undefined behaviour
+ memcpy(rolename_pos, rolename, rname_l);
+ rolename_pos[rname_l]= '\0'; //#3 string terminator
+ r_uname= rolename_pos;
+
+ hashkey.str = buff;
+ hashkey.length = bufflen;
+
+ with_admin= with_admin_option;
+
+ return false;
+}
+
#define IP_ADDR_STRLEN (3 + 1 + 3 + 1 + 3 + 1 + 3)
#define ACL_KEY_LENGTH (IP_ADDR_STRLEN + 1 + NAME_LEN + \
1 + USERNAME_LENGTH + 1)
@@ -542,47 +701,138 @@ static uchar* acl_entry_get_key(acl_entry *entry, size_t *length,
#endif /* HAVE_OPENSSL && !EMBEDDED_LIBRARY */
#define NORMAL_HANDSHAKE_SIZE 6
+#define ROLE_ASSIGN_COLUMN_IDX 43
+/* various flags valid for ACL_USER */
+#define IS_ROLE (1L << 0)
+/* Flag to mark that a ROLE is on the recursive DEPTH_FIRST_SEARCH stack */
+#define ROLE_ON_STACK (1L << 1)
+/*
+ Flag to mark that a ROLE and all it's neighbours have
+ been visited
+*/
+#define ROLE_EXPLORED (1L << 2)
+/* Flag to mark that on_node was already called for this role */
+#define ROLE_OPENED (1L << 3)
+
static DYNAMIC_ARRAY acl_hosts, acl_users, acl_dbs, acl_proxy_users;
-static MEM_ROOT mem, memex;
+static HASH acl_roles;
+/*
+ An hash containing mappings user <--> role
+
+ A hash is used so as to make updates quickly
+ The hashkey used represents all the entries combined
+*/
+static HASH acl_roles_mappings;
+static MEM_ROOT acl_memroot, grant_memroot;
static bool initialized=0;
static bool allow_all_hosts=1;
static HASH acl_check_hosts, column_priv_hash, proc_priv_hash, func_priv_hash;
static DYNAMIC_ARRAY acl_wild_hosts;
-static hash_filo *acl_cache;
+static Hash_filo<acl_entry> *acl_cache;
static uint grant_version=0; /* Version of priv tables. incremented by acl_load */
static ulong get_access(TABLE *form,uint fieldnr, uint *next_field=0);
+static bool check_is_role(TABLE *form);
static int acl_compare(ACL_ACCESS *a,ACL_ACCESS *b);
static ulong get_sort(uint count,...);
static void init_check_host(void);
static void rebuild_check_host(void);
-static ACL_USER *find_acl_user(const char *host, const char *user,
- my_bool exact);
+static void rebuild_role_grants(void);
+static ACL_USER *find_user_exact(const char *host, const char *user);
+static ACL_USER *find_user_wild(const char *host, const char *user, const char *ip= 0);
+static ACL_ROLE *find_acl_role(const char *user);
+static ROLE_GRANT_PAIR *find_role_grant_pair(const LEX_STRING *u, const LEX_STRING *h, const LEX_STRING *r);
+static ACL_USER_BASE *find_acl_user_base(const char *user, const char *host);
static bool update_user_table(THD *thd, TABLE *table, const char *host,
const char *user, const char *new_password,
uint new_password_len);
static my_bool acl_load(THD *thd, TABLE_LIST *tables);
static my_bool grant_load(THD *thd, TABLE_LIST *tables);
static inline void get_grantor(THD *thd, char* grantor);
+static bool add_role_user_mapping(const char *uname, const char *hname, const char *rname);
+
+#define ROLE_CYCLE_FOUND 2
+static int traverse_role_graph_up(ACL_ROLE *, void *,
+ int (*) (ACL_ROLE *, void *),
+ int (*) (ACL_ROLE *, ACL_ROLE *, void *));
+
+static int traverse_role_graph_down(ACL_USER_BASE *, void *,
+ int (*) (ACL_USER_BASE *, void *),
+ int (*) (ACL_USER_BASE *, ACL_ROLE *, void *));
+
/*
Enumeration of various ACL's and Hashes used in handle_grant_struct()
*/
enum enum_acl_lists
{
USER_ACL= 0,
+ ROLE_ACL,
DB_ACL,
COLUMN_PRIVILEGES_HASH,
PROC_PRIVILEGES_HASH,
FUNC_PRIVILEGES_HASH,
- PROXY_USERS_ACL
+ PROXY_USERS_ACL,
+ ROLES_MAPPINGS_HASH
};
-/*
- Convert scrambled password to binary form, according to scramble type,
+ACL_ROLE::ACL_ROLE(ACL_USER *user, MEM_ROOT *root) : counter(0)
+{
+
+ access= user->access;
+ /* set initial role access the same as the table row privileges */
+ initial_role_access= user->access;
+ this->user.str= safe_strdup_root(root, user->user.str);
+ this->user.length= user->user.length;
+ bzero(&role_grants, sizeof(role_grants));
+ bzero(&parent_grantee, sizeof(parent_grantee));
+ flags= IS_ROLE;
+}
+
+ACL_ROLE::ACL_ROLE(const char * rolename, ulong privileges, MEM_ROOT *root) :
+ initial_role_access(privileges), counter(0)
+{
+ this->access= initial_role_access;
+ this->user.str= safe_strdup_root(root, rolename);
+ this->user.length= strlen(rolename);
+ bzero(&role_grants, sizeof(role_grants));
+ bzero(&parent_grantee, sizeof(parent_grantee));
+ flags= IS_ROLE;
+}
+
+
+static bool is_invalid_role_name(const char *str)
+{
+ if (*str && strcasecmp(str, "PUBLIC") && strcasecmp(str, "NONE"))
+ return false;
+
+ my_error(ER_INVALID_ROLE, MYF(0), str);
+ return true;
+}
+
+
+static void free_acl_user(ACL_USER *user)
+{
+ delete_dynamic(&(user->role_grants));
+}
+
+static void free_acl_role(ACL_ROLE *role)
+{
+ delete_dynamic(&(role->role_grants));
+ delete_dynamic(&(role->parent_grantee));
+}
+
+/**
+ Convert scrambled password to binary form, according to scramble type,
Binary form is stored in user.salt.
+
+ @param acl_user The object where to store the salt
+ @param password The password hash containing the salt
+ @param password_len The length of the password hash
+
+ Despite the name of the function it is used when loading ACLs from disk
+ to store the password hash in the ACL_USER object.
*/
-static
-void
+static void
set_user_salt(ACL_USER *acl_user, const char *password, uint password_len)
{
if (password_len == SCRAMBLED_PASSWORD_CHAR_LENGTH)
@@ -613,35 +863,42 @@ static char *fix_plugin_ptr(char *name)
}
/**
- Fix ACL::plugin pointer to point to a hard-coded string, if appropriate
+ Fix a LEX_STRING *plugin pointer to point to a hard-coded string,
+ if appropriate
Make sure that if ACL_USER's plugin is a built-in, then it points
to a hard coded string, not to an allocated copy. Run-time, for
authentication, we want to be able to detect built-ins by comparing
pointers, not strings.
- Additionally - update the salt if the plugin is built-in.
-
@retval 0 the pointers were fixed
@retval 1 this ACL_USER uses a not built-in plugin
*/
-static bool fix_user_plugin_ptr(ACL_USER *user)
+static bool fix_user_plugin_ptr(LEX_STRING *plugin_ptr)
{
- user->salt_len= 0;
- if (my_strcasecmp(system_charset_info, user->plugin.str,
+ DBUG_ASSERT(plugin_ptr);
+ if (my_strcasecmp(system_charset_info, plugin_ptr->str,
native_password_plugin_name.str) == 0)
- user->plugin= native_password_plugin_name;
+ *plugin_ptr= native_password_plugin_name;
else
- if (my_strcasecmp(system_charset_info, user->plugin.str,
+ if (my_strcasecmp(system_charset_info, plugin_ptr->str,
old_password_plugin_name.str) == 0)
- user->plugin= old_password_plugin_name;
+ *plugin_ptr= old_password_plugin_name;
else
return true;
-
- set_user_salt(user, user->auth_string.str, user->auth_string.length);
+
return false;
}
+static bool get_YN_as_bool(Field *field)
+{
+ char buff[2];
+ String res(buff,sizeof(buff),&my_charset_latin1);
+ field->val_str(&res);
+ return res[0] == 'Y' || res[0] == 'y';
+}
+
+
/*
Initialize structures responsible for user/db-level privilege checking and
load privilege information for them from tables in the 'mysql' database.
@@ -666,7 +923,7 @@ my_bool acl_init(bool dont_read_acl_tables)
my_bool return_val;
DBUG_ENTER("acl_init");
- acl_cache= new hash_filo(ACL_CACHE_SIZE, 0, 0,
+ acl_cache= new Hash_filo<acl_entry>(ACL_CACHE_SIZE, 0, 0,
(my_hash_get_key) acl_entry_get_key,
(my_hash_free_key) free,
&my_charset_utf8_bin);
@@ -702,19 +959,33 @@ my_bool acl_init(bool dont_read_acl_tables)
*/
return_val= acl_reload(thd);
delete thd;
- /* Remember that we don't have a THD */
- my_pthread_setspecific_ptr(THR_THD, 0);
DBUG_RETURN(return_val);
}
/**
+ Check if the password length provided matches supported native formats.
+*/
+static bool password_length_valid(int password_length)
+{
+ switch (password_length)
+ {
+ case 0: /* no password */
+ case SCRAMBLED_PASSWORD_CHAR_LENGTH:
+ return TRUE;
+ case SCRAMBLED_PASSWORD_CHAR_LENGTH_323:
+ return TRUE;
+ default:
+ return FALSE;
+ }
+}
+
+/**
Choose from either native or old password plugins when assigning a password
*/
-static bool
-set_user_plugin (ACL_USER *user, int password_len)
+static bool set_user_plugin (ACL_USER *user, int password_len)
{
- switch (password_len)
+ switch (password_len)
{
case 0: /* no password */
case SCRAMBLED_PASSWORD_CHAR_LENGTH:
@@ -725,8 +996,8 @@ set_user_plugin (ACL_USER *user, int password_len)
return FALSE;
default:
sql_print_warning("Found invalid password for user: '%s@%s'; "
- "Ignoring user", user->user ? user->user : "",
- user->host.hostname ? user->host.hostname : "");
+ "Ignoring user", safe_str(user->user.str),
+ safe_str(user->host.hostname));
return TRUE;
}
}
@@ -739,8 +1010,9 @@ set_user_plugin (ACL_USER *user, int password_len)
SYNOPSIS
acl_load()
thd Current thread
- tables List containing open "mysql.host", "mysql.user" and
- "mysql.db" tables.
+ tables List containing open "mysql.host", "mysql.user",
+ "mysql.db", "mysql.proxies_priv" and "mysql.roles_mapping"
+ tables.
RETURN VALUES
FALSE Success
@@ -762,73 +1034,76 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables)
grant_version++; /* Privileges updated */
- acl_cache->clear(1); // Clear locked hostname cache
-
- init_sql_alloc(&mem, ACL_ALLOC_BLOCK_SIZE, 0);
- if (init_read_record(&read_record_info,thd,table= tables[0].table,NULL,1,0,
- FALSE))
- goto end;
-
- table->use_all_columns();
- (void) my_init_dynamic_array(&acl_hosts,sizeof(ACL_HOST),20,50);
- while (!(read_record_info.read_record(&read_record_info)))
+ init_sql_alloc(&acl_memroot, ACL_ALLOC_BLOCK_SIZE, 0, MYF(0));
+ (void) my_init_dynamic_array(&acl_hosts,sizeof(ACL_HOST), 20, 50, MYF(0));
+ if (tables[0].table) // "host" table may not exist (e.g. in MySQL 5.6.7+)
{
- ACL_HOST host;
- update_hostname(&host.host,get_field(&mem, table->field[0]));
- host.db= get_field(&mem, table->field[1]);
- if (lower_case_table_names && host.db)
+ if (init_read_record(&read_record_info, thd, table= tables[0].table,
+ NULL, 1, 1, FALSE))
+ goto end;
+ table->use_all_columns();
+ while (!(read_record_info.read_record(&read_record_info)))
{
- /*
- convert db to lower case and give a warning if the db wasn't
- already in lower case
- */
- char *end = strnmov(tmp_name, host.db, sizeof(tmp_name));
- if (end >= tmp_name + sizeof(tmp_name))
+ ACL_HOST host;
+ update_hostname(&host.host,get_field(&acl_memroot, table->field[0]));
+ host.db= get_field(&acl_memroot, table->field[1]);
+ if (lower_case_table_names && host.db)
+ {
+ /*
+ convert db to lower case and give a warning if the db wasn't
+ already in lower case
+ */
+ char *end = strnmov(tmp_name, host.db, sizeof(tmp_name));
+ if (end >= tmp_name + sizeof(tmp_name))
+ {
+ sql_print_warning(ER(ER_WRONG_DB_NAME), host.db);
+ continue;
+ }
+ my_casedn_str(files_charset_info, host.db);
+ if (strcmp(host.db, tmp_name) != 0)
+ sql_print_warning("'host' entry '%s|%s' had database in mixed "
+ "case that has been forced to lowercase because "
+ "lower_case_table_names is set. It will not be "
+ "possible to remove this privilege using REVOKE.",
+ host.host.hostname, host.db);
+ }
+ host.access= get_access(table,2);
+ host.access= fix_rights_for_db(host.access);
+ host.sort= get_sort(2,host.host.hostname,host.db);
+ if (check_no_resolve && hostname_requires_resolving(host.host.hostname))
{
- sql_print_warning(ER(ER_WRONG_DB_NAME), host.db);
+ sql_print_warning("'host' entry '%s|%s' "
+ "ignored in --skip-name-resolve mode.",
+ safe_str(host.host.hostname),
+ safe_str(host.db));
continue;
}
- my_casedn_str(files_charset_info, host.db);
- if (strcmp(host.db, tmp_name) != 0)
- sql_print_warning("'host' entry '%s|%s' had database in mixed "
- "case that has been forced to lowercase because "
- "lower_case_table_names is set. It will not be "
- "possible to remove this privilege using REVOKE.",
- host.host.hostname ? host.host.hostname : "",
- host.db ? host.db : "");
- }
- host.access= get_access(table,2);
- host.access= fix_rights_for_db(host.access);
- host.sort= get_sort(2,host.host.hostname,host.db);
- if (check_no_resolve && hostname_requires_resolving(host.host.hostname))
- {
- sql_print_warning("'host' entry '%s|%s' "
- "ignored in --skip-name-resolve mode.",
- host.host.hostname ? host.host.hostname : "",
- host.db ? host.db : "");
- continue;
- }
#ifndef TO_BE_REMOVED
- if (table->s->fields == 8)
- { // Without grant
- if (host.access & CREATE_ACL)
- host.access|=REFERENCES_ACL | INDEX_ACL | ALTER_ACL | CREATE_TMP_ACL;
- }
+ if (table->s->fields == 8)
+ { // Without grant
+ if (host.access & CREATE_ACL)
+ host.access|=REFERENCES_ACL | INDEX_ACL | ALTER_ACL | CREATE_TMP_ACL;
+ }
#endif
- (void) push_dynamic(&acl_hosts,(uchar*) &host);
+ (void) push_dynamic(&acl_hosts,(uchar*) &host);
+ }
+ my_qsort((uchar*) dynamic_element(&acl_hosts,0,ACL_HOST*),acl_hosts.elements,
+ sizeof(ACL_HOST),(qsort_cmp) acl_compare);
+ end_read_record(&read_record_info);
}
- my_qsort((uchar*) dynamic_element(&acl_hosts,0,ACL_HOST*),acl_hosts.elements,
- sizeof(ACL_HOST),(qsort_cmp) acl_compare);
- end_read_record(&read_record_info);
freeze_size(&acl_hosts);
- if (init_read_record(&read_record_info,thd,table=tables[1].table,NULL,1,0,
- FALSE))
+ if (init_read_record(&read_record_info, thd, table=tables[1].table,
+ NULL, 1, 1, FALSE))
goto end;
-
table->use_all_columns();
- (void) my_init_dynamic_array(&acl_users,sizeof(ACL_USER),50,100);
- username_char_length= min(table->field[1]->char_length(), USERNAME_CHAR_LENGTH);
+ (void) my_init_dynamic_array(&acl_users,sizeof(ACL_USER), 50, 100, MYF(0));
+ (void) my_hash_init2(&acl_roles,50, &my_charset_utf8_bin,
+ 0, 0, 0, (my_hash_get_key) acl_role_get_key, 0,
+ (void (*)(void *))free_acl_role, 0);
+
+ username_char_length= MY_MIN(table->field[1]->char_length(),
+ USERNAME_CHAR_LENGTH);
password_length= table->field[2]->field_length /
table->field[2]->charset()->mbmaxlen;
if (password_length < SCRAMBLED_PASSWORD_CHAR_LENGTH_323)
@@ -874,25 +1149,42 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables)
while (!(read_record_info.read_record(&read_record_info)))
{
ACL_USER user;
+ bool is_role= FALSE;
bzero(&user, sizeof(user));
- update_hostname(&user.host, get_field(&mem, table->field[0]));
- user.user= get_field(&mem, table->field[1]);
- if (check_no_resolve && hostname_requires_resolving(user.host.hostname))
+ update_hostname(&user.host, get_field(&acl_memroot, table->field[0]));
+ char *username= get_field(&acl_memroot, table->field[1]);
+ user.user.str= username;
+ user.user.length= username? strlen(username) : 0;
+
+ /*
+ If the user entry is a role, skip password and hostname checks
+ A user can not log in with a role so some checks are not necessary
+ */
+ is_role= check_is_role(table);
+
+ if (is_role && is_invalid_role_name(username))
+ {
+ thd->clear_error(); // the warning is still issued
+ continue;
+ }
+
+ if (!is_role && check_no_resolve &&
+ hostname_requires_resolving(user.host.hostname))
{
sql_print_warning("'user' entry '%s@%s' "
"ignored in --skip-name-resolve mode.",
- user.user ? user.user : "",
- user.host.hostname ? user.host.hostname : "");
+ safe_str(user.user.str),
+ safe_str(user.host.hostname));
continue;
}
- char *password= get_field(&mem, table->field[2]);
+ char *password= get_field(&acl_memroot, table->field[2]);
uint password_len= password ? strlen(password) : 0;
- user.auth_string.str= password ? password : const_cast<char*>("");
+ user.auth_string.str= safe_str(password);
user.auth_string.length= password_len;
set_user_salt(&user, password, password_len);
- if (set_user_plugin(&user, password_len))
+ if (!is_role && set_user_plugin(&user, password_len))
continue;
{
@@ -934,7 +1226,7 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables)
if (table->s->fields <= 38 && (user.access & SUPER_ACL))
user.access|= TRIGGER_ACL;
- user.sort= get_sort(2,user.host.hostname,user.user);
+ user.sort= get_sort(2, user.host.hostname, user.user.str);
user.hostname_length= (user.host.hostname ?
(uint) strlen(user.host.hostname) : 0);
@@ -951,9 +1243,9 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables)
else /* !strcmp(ssl_type, "SPECIFIED") */
user.ssl_type=SSL_TYPE_SPECIFIED;
- user.ssl_cipher= get_field(&mem, table->field[next_field++]);
- user.x509_issuer= get_field(&mem, table->field[next_field++]);
- user.x509_subject= get_field(&mem, table->field[next_field++]);
+ user.ssl_cipher= get_field(&acl_memroot, table->field[next_field++]);
+ user.x509_issuer= get_field(&acl_memroot, table->field[next_field++]);
+ user.x509_subject= get_field(&acl_memroot, table->field[next_field++]);
char *ptr = get_field(thd->mem_root, table->field[next_field++]);
user.user_resource.questions=ptr ? atoi(ptr) : 0;
@@ -972,28 +1264,49 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables)
user.user_resource.user_conn= ptr ? atoi(ptr) : 0;
}
- if (table->s->fields >= 41)
+ if (!is_role && table->s->fields >= 41)
{
/* We may have plugin & auth_String fields */
- char *tmpstr= get_field(&mem, table->field[next_field++]);
+ char *tmpstr= get_field(&acl_memroot, table->field[next_field++]);
if (tmpstr)
{
+ LEX_STRING auth;
+ auth.str = safe_str(get_field(&acl_memroot, table->field[next_field++]));
+ auth.length = strlen(auth.str);
user.plugin.str= tmpstr;
user.plugin.length= strlen(user.plugin.str);
- if (user.auth_string.length)
+ if (fix_user_plugin_ptr(&user.plugin)) // Non native plugin.
{
- sql_print_warning("'user' entry '%s@%s' has both a password "
- "and an authentication plugin specified. The "
- "password will be ignored.",
- user.user ? user.user : "",
- user.host.hostname ? user.host.hostname : "");
+ user.auth_string= auth;
+ if (password_len)
+ {
+ sql_print_warning("'user' entry '%s@%s' has both a password "
+ "and an authentication plugin specified. The "
+ "password will be ignored.",
+ safe_str(user.user.str),
+ safe_str(user.host.hostname));
+ }
+ }
+ else // Native plugin.
+ {
+ /*
+ Password field, if not empty, has precedence over
+ authentication_string field, only for native plugins.
+ See MDEV-6253 and MDEV-7985 for reasoning.
+ */
+ if (!password_len)
+ {
+ user.auth_string = auth;
+ if (!password_length_valid(auth.length))
+ {
+ sql_print_warning("Found invalid password for user: '%s@%s';"
+ " Ignoring user", safe_str(user.user.str),
+ safe_str(user.host.hostname));
+ continue;
+ }
+ set_user_salt(&user, auth.str, auth.length);
+ }
}
- user.auth_string.str= get_field(&mem, table->field[next_field++]);
- if (!user.auth_string.str)
- user.auth_string.str= const_cast<char*>("");
- user.auth_string.length= strlen(user.auth_string.str);
-
- fix_user_plugin_ptr(&user);
}
}
}
@@ -1014,7 +1327,26 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables)
user.access|= SUPER_ACL | EXECUTE_ACL;
#endif
}
- (void) push_dynamic(&acl_users,(uchar*) &user);
+
+ (void) my_init_dynamic_array(&user.role_grants,sizeof(ACL_ROLE *),
+ 8, 8, MYF(0));
+
+ if (is_role)
+ {
+ DBUG_PRINT("info", ("Found role %s", user.user.str));
+ ACL_ROLE *entry= new (&acl_memroot) ACL_ROLE(&user, &acl_memroot);
+ entry->role_grants = user.role_grants;
+ (void) my_init_dynamic_array(&entry->parent_grantee,
+ sizeof(ACL_USER_BASE *), 8, 8, MYF(0));
+ my_hash_insert(&acl_roles, (uchar *)entry);
+
+ continue;
+ }
+ else
+ {
+ DBUG_PRINT("info", ("Found user %s", user.user.str));
+ (void) push_dynamic(&acl_users,(uchar*) &user);
+ }
if (!user.host.hostname ||
(user.host.hostname[0] == wild_many && !user.host.hostname[1]))
allow_all_hosts=1; // Anyone can connect
@@ -1025,34 +1357,35 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables)
end_read_record(&read_record_info);
freeze_size(&acl_users);
- if (init_read_record(&read_record_info,thd,table=tables[2].table,NULL,1,0,
- FALSE))
+ if (init_read_record(&read_record_info, thd, table=tables[2].table,
+ NULL, 1, 1, FALSE))
goto end;
-
table->use_all_columns();
- (void) my_init_dynamic_array(&acl_dbs,sizeof(ACL_DB),50,100);
+ (void) my_init_dynamic_array(&acl_dbs,sizeof(ACL_DB), 50, 100, MYF(0));
while (!(read_record_info.read_record(&read_record_info)))
{
ACL_DB db;
- update_hostname(&db.host,get_field(&mem, table->field[MYSQL_DB_FIELD_HOST]));
- db.db=get_field(&mem, table->field[MYSQL_DB_FIELD_DB]);
+ db.user=get_field(&acl_memroot, table->field[MYSQL_DB_FIELD_USER]);
+ const char *hostname= get_field(&acl_memroot, table->field[MYSQL_DB_FIELD_HOST]);
+ if (!hostname && find_acl_role(db.user))
+ hostname= "";
+ update_hostname(&db.host, hostname);
+ db.db=get_field(&acl_memroot, table->field[MYSQL_DB_FIELD_DB]);
if (!db.db)
{
sql_print_warning("Found an entry in the 'db' table with empty database name; Skipped");
continue;
}
- db.user=get_field(&mem, table->field[MYSQL_DB_FIELD_USER]);
if (check_no_resolve && hostname_requires_resolving(db.host.hostname))
{
sql_print_warning("'db' entry '%s %s@%s' "
"ignored in --skip-name-resolve mode.",
- db.db,
- db.user ? db.user : "",
- db.host.hostname ? db.host.hostname : "");
+ db.db, safe_str(db.user), safe_str(db.host.hostname));
continue;
}
db.access=get_access(table,3);
db.access=fix_rights_for_db(db.access);
+ db.initial_access= db.access;
if (lower_case_table_names)
{
/*
@@ -1072,9 +1405,7 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables)
"case that has been forced to lowercase because "
"lower_case_table_names is set. It will not be "
"possible to remove this privilege using REVOKE.",
- db.db,
- db.user ? db.user : "",
- db.host.hostname ? db.host.hostname : "");
+ db.db, safe_str(db.user), safe_str(db.host.hostname));
}
}
db.sort=get_sort(3,db.host.hostname,db.db,db.user);
@@ -1092,17 +1423,18 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables)
end_read_record(&read_record_info);
freeze_size(&acl_dbs);
- (void) my_init_dynamic_array(&acl_proxy_users, sizeof(ACL_PROXY_USER),
- 50, 100);
+ (void) my_init_dynamic_array(&acl_proxy_users, sizeof(ACL_PROXY_USER),
+ 50, 100, MYF(0));
if (tables[3].table)
{
- init_read_record(&read_record_info, thd, table= tables[3].table, NULL, 1,
- 0, FALSE);
+ if (init_read_record(&read_record_info, thd, table= tables[3].table,
+ NULL, 1, 1, FALSE))
+ goto end;
table->use_all_columns();
while (!(read_record_info.read_record(&read_record_info)))
{
ACL_PROXY_USER proxy;
- proxy.init(table, &mem);
+ proxy.init(table, &acl_memroot);
if (proxy.check_validity(check_no_resolve))
continue;
if (push_dynamic(&acl_proxy_users, (uchar*) &proxy))
@@ -1123,6 +1455,48 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables)
}
freeze_size(&acl_proxy_users);
+ if (tables[4].table)
+ {
+ if (init_read_record(&read_record_info, thd, table= tables[4].table,
+ NULL, 1, 1, FALSE))
+ goto end;
+ table->use_all_columns();
+ /* account for every role mapping */
+
+ (void) my_hash_init2(&acl_roles_mappings, 50, system_charset_info, 0, 0, 0,
+ (my_hash_get_key) acl_role_map_get_key, 0, 0, 0);
+ MEM_ROOT temp_root;
+ init_alloc_root(&temp_root, ACL_ALLOC_BLOCK_SIZE, 0, MYF(0));
+ while (!(read_record_info.read_record(&read_record_info)))
+ {
+ char *hostname= safe_str(get_field(&temp_root, table->field[0]));
+ char *username= safe_str(get_field(&temp_root, table->field[1]));
+ char *rolename= safe_str(get_field(&temp_root, table->field[2]));
+ bool with_grant_option= get_YN_as_bool(table->field[3]);
+
+ if (add_role_user_mapping(username, hostname, rolename)) {
+ sql_print_error("Invalid roles_mapping table entry user:'%s@%s', rolename:'%s'",
+ username, hostname, rolename);
+ continue;
+ }
+
+ ROLE_GRANT_PAIR *mapping= new (&acl_memroot) ROLE_GRANT_PAIR;
+
+ if (mapping->init(&acl_memroot, username, hostname, rolename, with_grant_option))
+ continue;
+
+ my_hash_insert(&acl_roles_mappings, (uchar*) mapping);
+ }
+
+ free_root(&temp_root, MYF(0));
+ end_read_record(&read_record_info);
+ }
+ else
+ {
+ sql_print_error("Missing system table mysql.roles_mapping; "
+ "please run mysql_upgrade to create it");
+ }
+
init_check_host();
initialized=1;
@@ -1136,13 +1510,15 @@ end:
void acl_free(bool end)
{
- free_root(&mem,MYF(0));
+ my_hash_free(&acl_roles);
+ free_root(&acl_memroot,MYF(0));
delete_dynamic(&acl_hosts);
- delete_dynamic(&acl_users);
+ delete_dynamic_with_callback(&acl_users, (FREE_FUNC) free_acl_user);
delete_dynamic(&acl_dbs);
delete_dynamic(&acl_wild_hosts);
delete_dynamic(&acl_proxy_users);
my_hash_free(&acl_check_hosts);
+ my_hash_free(&acl_roles_mappings);
plugin_unlock(0, native_password_plugin);
plugin_unlock(0, old_password_plugin);
if (!end)
@@ -1176,10 +1552,10 @@ void acl_free(bool end)
my_bool acl_reload(THD *thd)
{
- TABLE_LIST tables[4];
+ TABLE_LIST tables[5];
DYNAMIC_ARRAY old_acl_hosts, old_acl_users, old_acl_dbs, old_acl_proxy_users;
+ HASH old_acl_roles, old_acl_roles_mappings;
MEM_ROOT old_mem;
- bool old_initialized;
my_bool return_val= TRUE;
DBUG_ENTER("acl_reload");
@@ -1196,33 +1572,40 @@ my_bool acl_reload(THD *thd)
tables[3].init_one_table(C_STRING_WITH_LEN("mysql"),
C_STRING_WITH_LEN("proxies_priv"),
"proxies_priv", TL_READ);
+ tables[4].init_one_table(C_STRING_WITH_LEN("mysql"),
+ C_STRING_WITH_LEN("roles_mapping"),
+ "roles_mapping", TL_READ);
tables[0].next_local= tables[0].next_global= tables + 1;
tables[1].next_local= tables[1].next_global= tables + 2;
tables[2].next_local= tables[2].next_global= tables + 3;
+ tables[3].next_local= tables[3].next_global= tables + 4;
tables[0].open_type= tables[1].open_type= tables[2].open_type=
- tables[3].open_type= OT_BASE_ONLY;
- tables[3].open_strategy= TABLE_LIST::OPEN_IF_EXISTS;
-
+ tables[3].open_type= tables[4].open_type= OT_BASE_ONLY;
+ tables[0].open_strategy= tables[3].open_strategy=
+ tables[4].open_strategy= TABLE_LIST::OPEN_IF_EXISTS;
+
if (open_and_lock_tables(thd, tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT))
{
/*
Execution might have been interrupted; only print the error message
if an error condition has been raised.
*/
- if (thd->stmt_da->is_error())
+ if (thd->get_stmt_da()->is_error())
sql_print_error("Fatal error: Can't open and lock privilege tables: %s",
- thd->stmt_da->message());
+ thd->get_stmt_da()->message());
goto end;
}
- if ((old_initialized=initialized))
- mysql_mutex_lock(&acl_cache->lock);
+ acl_cache->clear(0);
+ mysql_mutex_lock(&acl_cache->lock);
old_acl_hosts= acl_hosts;
old_acl_users= acl_users;
+ old_acl_roles= acl_roles;
+ old_acl_roles_mappings= acl_roles_mappings;
old_acl_proxy_users= acl_proxy_users;
old_acl_dbs= acl_dbs;
- old_mem= mem;
+ old_mem= acl_memroot;
delete_dynamic(&acl_wild_hosts);
my_hash_free(&acl_check_hosts);
@@ -1232,27 +1615,29 @@ my_bool acl_reload(THD *thd)
acl_free(); /* purecov: inspected */
acl_hosts= old_acl_hosts;
acl_users= old_acl_users;
+ acl_roles= old_acl_roles;
+ acl_roles_mappings= old_acl_roles_mappings;
acl_proxy_users= old_acl_proxy_users;
acl_dbs= old_acl_dbs;
- mem= old_mem;
+ acl_memroot= old_mem;
init_check_host();
}
else
{
+ my_hash_free(&old_acl_roles);
free_root(&old_mem,MYF(0));
delete_dynamic(&old_acl_hosts);
- delete_dynamic(&old_acl_users);
+ delete_dynamic_with_callback(&old_acl_users, (FREE_FUNC) free_acl_user);
delete_dynamic(&old_acl_proxy_users);
delete_dynamic(&old_acl_dbs);
+ my_hash_free(&old_acl_roles_mappings);
}
- if (old_initialized)
- mysql_mutex_unlock(&acl_cache->lock);
+ mysql_mutex_unlock(&acl_cache->lock);
end:
close_mysql_tables(thd);
DBUG_RETURN(return_val);
}
-
/*
Get all access bits from table after fieldnr
@@ -1284,8 +1669,7 @@ static ulong get_access(TABLE *form, uint fieldnr, uint *next_field)
((Field_enum*) (*pos))->typelib->count == 2 ;
pos++, fieldnr++, bit<<=1)
{
- (*pos)->val_str(&res);
- if (my_toupper(&my_charset_latin1, res[0]) == 'Y')
+ if (get_YN_as_bool(*pos))
access_bits|= bit;
}
if (next_field)
@@ -1293,6 +1677,34 @@ static ulong get_access(TABLE *form, uint fieldnr, uint *next_field)
return access_bits;
}
+/*
+ Check if a user entry in the user table is marked as being a role entry
+
+ IMPLEMENTATION
+ Access the coresponding column and check the coresponding ENUM of the form
+ ENUM('N', 'Y')
+
+ SYNOPSIS
+ check_is_role()
+ form an open table to read the entry from.
+ The record should be already read in table->record[0]
+
+ RETURN VALUE
+ TRUE if the user is marked as a role
+ FALSE otherwise
+*/
+
+static bool check_is_role(TABLE *form)
+{
+ char buff[2];
+ String res(buff, sizeof(buff), &my_charset_latin1);
+ /* Table version does not support roles */
+ if (form->s->fields <= ROLE_ASSIGN_COLUMN_IDX)
+ return FALSE;
+
+ return get_YN_as_bool(form->field[ROLE_ASSIGN_COLUMN_IDX]);
+}
+
/*
Return a number which, if sorted 'desc', puts strings in this order:
@@ -1330,7 +1742,7 @@ static ulong get_sort(uint count,...)
chars= 128; // Marker that chars existed
}
}
- sort= (sort << 8) + (wild_pos ? min(wild_pos, 127) : chars);
+ sort= (sort << 8) + (wild_pos ? MY_MIN(wild_pos, 127U) : chars);
}
va_end(args);
return sort;
@@ -1372,12 +1784,11 @@ bool acl_getroot(Security_context *sctx, char *user, char *host,
DBUG_ENTER("acl_getroot");
DBUG_PRINT("enter", ("Host: '%s', Ip: '%s', User: '%s', db: '%s'",
- (host ? host : "(NULL)"), (ip ? ip : "(NULL)"),
- user, (db ? db : "(NULL)")));
+ host, ip, user, db));
sctx->user= user;
sctx->host= host;
sctx->ip= ip;
- sctx->host_or_ip= host ? host : (ip ? ip : "");
+ sctx->host_or_ip= host ? host : (safe_str(ip));
if (!initialized)
{
@@ -1392,63 +1803,168 @@ bool acl_getroot(Security_context *sctx, char *user, char *host,
sctx->master_access= 0;
sctx->db_access= 0;
- *sctx->priv_user= *sctx->priv_host= 0;
+ *sctx->priv_user= *sctx->priv_host= *sctx->priv_role= 0;
- /*
- Find acl entry in user database.
- This is specially tailored to suit the check we do for CALL of
- a stored procedure; user is set to what is actually a
- priv_user, which can be ''.
- */
- for (i=0 ; i < acl_users.elements ; i++)
+ if (host[0]) // User, not Role
{
- ACL_USER *acl_user_tmp= dynamic_element(&acl_users,i,ACL_USER*);
- if ((!acl_user_tmp->user && !user[0]) ||
- (acl_user_tmp->user && strcmp(user, acl_user_tmp->user) == 0))
+ acl_user= find_user_wild(host, user, ip);
+
+ if (acl_user)
{
- if (compare_hostname(&acl_user_tmp->host, host, ip))
+ res= 0;
+ for (i=0 ; i < acl_dbs.elements ; i++)
{
- acl_user= acl_user_tmp;
- res= 0;
- break;
+ ACL_DB *acl_db= dynamic_element(&acl_dbs, i, ACL_DB*);
+ if (!acl_db->user ||
+ (user && user[0] && !strcmp(user, acl_db->user)))
+ {
+ if (compare_hostname(&acl_db->host, host, ip))
+ {
+ if (!acl_db->db || (db && !wild_compare(db, acl_db->db, 0)))
+ {
+ sctx->db_access= acl_db->access;
+ break;
+ }
+ }
+ }
}
+ sctx->master_access= acl_user->access;
+
+ if (acl_user->user.str)
+ strmake_buf(sctx->priv_user, user);
+
+ if (acl_user->host.hostname)
+ strmake_buf(sctx->priv_host, acl_user->host.hostname);
}
}
-
- if (acl_user)
+ else // Role, not User
{
- for (i=0 ; i < acl_dbs.elements ; i++)
+ ACL_ROLE *acl_role= find_acl_role(user);
+ if (acl_role)
{
- ACL_DB *acl_db= dynamic_element(&acl_dbs, i, ACL_DB*);
- if (!acl_db->user ||
- (user && user[0] && !strcmp(user, acl_db->user)))
+ res= 0;
+ for (i=0 ; i < acl_dbs.elements ; i++)
{
- if (compare_hostname(&acl_db->host, host, ip))
- {
- if (!acl_db->db || (db && !wild_compare(db, acl_db->db, 0)))
- {
- sctx->db_access= acl_db->access;
- break;
- }
- }
+ ACL_DB *acl_db= dynamic_element(&acl_dbs, i, ACL_DB*);
+ if (!acl_db->user ||
+ (user && user[0] && !strcmp(user, acl_db->user)))
+ {
+ if (compare_hostname(&acl_db->host, "", ""))
+ {
+ if (!acl_db->db || (db && !wild_compare(db, acl_db->db, 0)))
+ {
+ sctx->db_access= acl_db->access;
+ break;
+ }
+ }
+ }
}
+ sctx->master_access= acl_role->access;
+
+ if (acl_role->user.str)
+ strmake_buf(sctx->priv_user, user);
+ sctx->priv_host[0]= 0;
}
- sctx->master_access= acl_user->access;
+ }
- if (acl_user->user)
- strmake_buf(sctx->priv_user, user);
- else
- *sctx->priv_user= 0;
+ mysql_mutex_unlock(&acl_cache->lock);
+ DBUG_RETURN(res);
+}
- if (acl_user->host.hostname)
- strmake_buf(sctx->priv_host, acl_user->host.hostname);
- else
- *sctx->priv_host= 0;
+int acl_check_setrole(THD *thd, char *rolename, ulonglong *access)
+{
+ ACL_ROLE *role;
+ ACL_USER_BASE *acl_user_base;
+ ACL_USER *UNINIT_VAR(acl_user);
+ bool is_granted= FALSE;
+ int result= 0;
+
+ /* clear role privileges */
+ mysql_mutex_lock(&acl_cache->lock);
+
+ if (!strcasecmp(rolename, "NONE"))
+ {
+ /* have to clear the privileges */
+ /* get the current user */
+ acl_user= find_user_exact(thd->security_ctx->priv_host,
+ thd->security_ctx->priv_user);
+ if (acl_user == NULL)
+ {
+ my_error(ER_INVALID_CURRENT_USER, MYF(0), rolename);
+ result= -1;
+ }
+ else if (access)
+ *access= acl_user->access;
+
+ goto end;
+ }
+
+ role= find_acl_role(rolename);
+
+ /* According to SQL standard, the same error message must be presented */
+ if (role == NULL) {
+ my_error(ER_INVALID_ROLE, MYF(0), rolename);
+ result= -1;
+ goto end;
+ }
+
+ for (uint i=0 ; i < role->parent_grantee.elements ; i++)
+ {
+ acl_user_base= *(dynamic_element(&role->parent_grantee, i, ACL_USER_BASE**));
+ if (acl_user_base->flags & IS_ROLE)
+ continue;
+
+ acl_user= (ACL_USER *)acl_user_base;
+ /* Yes! priv_user@host. Don't ask why - that's what check_access() does. */
+ if (acl_user->wild_eq(thd->security_ctx->priv_user,
+ thd->security_ctx->host, thd->security_ctx->ip))
+ {
+ is_granted= TRUE;
+ break;
+ }
+ }
+
+ /* According to SQL standard, the same error message must be presented */
+ if (!is_granted)
+ {
+ my_error(ER_INVALID_ROLE, MYF(0), rolename);
+ result= 1;
+ goto end;
+ }
+
+ if (access)
+ {
+ *access = acl_user->access | role->access;
}
+end:
mysql_mutex_unlock(&acl_cache->lock);
- DBUG_RETURN(res);
+ return result;
+}
+
+
+int acl_setrole(THD *thd, char *rolename, ulonglong access)
+{
+ /* merge the privileges */
+ Security_context *sctx= thd->security_ctx;
+ sctx->master_access= static_cast<ulong>(access);
+ if (thd->db)
+ sctx->db_access= acl_get(sctx->host, sctx->ip, sctx->user, thd->db, FALSE);
+
+ if (!strcasecmp(rolename, "NONE"))
+ {
+ thd->security_ctx->priv_role[0]= 0;
+ }
+ else
+ {
+ if (thd->db)
+ sctx->db_access|= acl_get("", "", rolename, thd->db, FALSE);
+ /* mark the current role */
+ strmake_buf(thd->security_ctx->priv_role, rolename);
+ }
+ return 0;
}
+
static uchar* check_get_key(ACL_USER *buff, size_t *length,
my_bool not_used __attribute__((unused)))
{
@@ -1457,6 +1973,14 @@ static uchar* check_get_key(ACL_USER *buff, size_t *length,
}
+static void acl_update_role(const char *rolename, ulong privileges)
+{
+ ACL_ROLE *role= find_acl_role(rolename);
+ if (role)
+ role->initial_role_access= role->access= privileges;
+}
+
+
static void acl_update_user(const char *user, const char *host,
const char *password, uint password_len,
enum SSL_type ssl_type,
@@ -1473,57 +1997,71 @@ static void acl_update_user(const char *user, const char *host,
for (uint i=0 ; i < acl_users.elements ; i++)
{
ACL_USER *acl_user=dynamic_element(&acl_users,i,ACL_USER*);
- if ((!acl_user->user && !user[0]) ||
- (acl_user->user && !strcmp(user,acl_user->user)))
+ if (acl_user->eq(user, host))
{
- if ((!acl_user->host.hostname && !host[0]) ||
- (acl_user->host.hostname &&
- !my_strcasecmp(system_charset_info, host, acl_user->host.hostname)))
+ if (plugin->str[0])
{
- if (plugin->str[0])
+ acl_user->plugin= *plugin;
+ acl_user->auth_string.str= auth->str ?
+ strmake_root(&acl_memroot, auth->str, auth->length) : const_cast<char*>("");
+ acl_user->auth_string.length= auth->length;
+ if (acl_user->plugin.str != native_password_plugin_name.str &&
+ acl_user->plugin.str != old_password_plugin_name.str)
+ acl_user->plugin.str= strmake_root(&acl_memroot, plugin->str, plugin->length);
+ else
+ set_user_salt(acl_user, acl_user->auth_string.str,
+ acl_user->auth_string.length);
+
+ }
+ else
+ if (password[0])
{
- acl_user->plugin= *plugin;
- acl_user->auth_string.str= auth->str ?
- strmake_root(&mem, auth->str, auth->length) : const_cast<char*>("");
- acl_user->auth_string.length= auth->length;
- if (fix_user_plugin_ptr(acl_user))
- acl_user->plugin.str= strmake_root(&mem, plugin->str, plugin->length);
+ acl_user->auth_string.str= strmake_root(&acl_memroot, password, password_len);
+ acl_user->auth_string.length= password_len;
+ set_user_salt(acl_user, password, password_len);
+ set_user_plugin(acl_user, password_len);
}
- else
- if (password[0])
- {
- acl_user->auth_string.str= strmake_root(&mem, password, password_len);
- acl_user->auth_string.length= password_len;
- set_user_salt(acl_user, password, password_len);
- set_user_plugin(acl_user, password_len);
- }
- acl_user->access=privileges;
- if (mqh->specified_limits & USER_RESOURCES::QUERIES_PER_HOUR)
- acl_user->user_resource.questions=mqh->questions;
- if (mqh->specified_limits & USER_RESOURCES::UPDATES_PER_HOUR)
- acl_user->user_resource.updates=mqh->updates;
- if (mqh->specified_limits & USER_RESOURCES::CONNECTIONS_PER_HOUR)
- acl_user->user_resource.conn_per_hour= mqh->conn_per_hour;
- if (mqh->specified_limits & USER_RESOURCES::USER_CONNECTIONS)
- acl_user->user_resource.user_conn= mqh->user_conn;
- if (ssl_type != SSL_TYPE_NOT_SPECIFIED)
- {
- acl_user->ssl_type= ssl_type;
- acl_user->ssl_cipher= (ssl_cipher ? strdup_root(&mem,ssl_cipher) :
- 0);
- acl_user->x509_issuer= (x509_issuer ? strdup_root(&mem,x509_issuer) :
- 0);
- acl_user->x509_subject= (x509_subject ?
- strdup_root(&mem,x509_subject) : 0);
- }
- /* search complete: */
- break;
+ acl_user->access=privileges;
+ if (mqh->specified_limits & USER_RESOURCES::QUERIES_PER_HOUR)
+ acl_user->user_resource.questions=mqh->questions;
+ if (mqh->specified_limits & USER_RESOURCES::UPDATES_PER_HOUR)
+ acl_user->user_resource.updates=mqh->updates;
+ if (mqh->specified_limits & USER_RESOURCES::CONNECTIONS_PER_HOUR)
+ acl_user->user_resource.conn_per_hour= mqh->conn_per_hour;
+ if (mqh->specified_limits & USER_RESOURCES::USER_CONNECTIONS)
+ acl_user->user_resource.user_conn= mqh->user_conn;
+ if (ssl_type != SSL_TYPE_NOT_SPECIFIED)
+ {
+ acl_user->ssl_type= ssl_type;
+ acl_user->ssl_cipher= (ssl_cipher ? strdup_root(&acl_memroot,ssl_cipher) :
+ 0);
+ acl_user->x509_issuer= (x509_issuer ? strdup_root(&acl_memroot,x509_issuer) :
+ 0);
+ acl_user->x509_subject= (x509_subject ?
+ strdup_root(&acl_memroot,x509_subject) : 0);
}
+ /* search complete: */
+ break;
}
}
}
+static void acl_insert_role(const char *rolename, ulong privileges)
+{
+ ACL_ROLE *entry;
+
+ mysql_mutex_assert_owner(&acl_cache->lock);
+ entry= new (&acl_memroot) ACL_ROLE(rolename, privileges, &acl_memroot);
+ (void) my_init_dynamic_array(&entry->parent_grantee,
+ sizeof(ACL_USER_BASE *), 8, 8, MYF(0));
+ (void) my_init_dynamic_array(&entry->role_grants,sizeof(ACL_ROLE *),
+ 8, 8, MYF(0));
+
+ my_hash_insert(&acl_roles, (uchar *)entry);
+}
+
+
static void acl_insert_user(const char *user, const char *host,
const char *password, uint password_len,
enum SSL_type ssl_type,
@@ -1539,34 +2077,42 @@ static void acl_insert_user(const char *user, const char *host,
mysql_mutex_assert_owner(&acl_cache->lock);
- acl_user.user=*user ? strdup_root(&mem,user) : 0;
- update_hostname(&acl_user.host, *host ? strdup_root(&mem, host): 0);
+ acl_user.user.str=*user ? strdup_root(&acl_memroot,user) : 0;
+ acl_user.user.length= strlen(user);
+ update_hostname(&acl_user.host, safe_strdup_root(&acl_memroot, host));
if (plugin->str[0])
{
acl_user.plugin= *plugin;
acl_user.auth_string.str= auth->str ?
- strmake_root(&mem, auth->str, auth->length) : const_cast<char*>("");
+ strmake_root(&acl_memroot, auth->str, auth->length) : const_cast<char*>("");
acl_user.auth_string.length= auth->length;
- if (fix_user_plugin_ptr(&acl_user))
- acl_user.plugin.str= strmake_root(&mem, plugin->str, plugin->length);
+ if (acl_user.plugin.str != native_password_plugin_name.str &&
+ acl_user.plugin.str != old_password_plugin_name.str)
+ acl_user.plugin.str= strmake_root(&acl_memroot, plugin->str, plugin->length);
+ else
+ set_user_salt(&acl_user, acl_user.auth_string.str,
+ acl_user.auth_string.length);
}
else
{
- acl_user.auth_string.str= strmake_root(&mem, password, password_len);
+ acl_user.auth_string.str= strmake_root(&acl_memroot, password, password_len);
acl_user.auth_string.length= password_len;
set_user_salt(&acl_user, password, password_len);
set_user_plugin(&acl_user, password_len);
}
+ acl_user.flags= 0;
acl_user.access=privileges;
acl_user.user_resource = *mqh;
- acl_user.sort=get_sort(2,acl_user.host.hostname,acl_user.user);
+ acl_user.sort=get_sort(2, acl_user.host.hostname, acl_user.user.str);
acl_user.hostname_length=(uint) strlen(host);
acl_user.ssl_type= (ssl_type != SSL_TYPE_NOT_SPECIFIED ?
ssl_type : SSL_TYPE_NONE);
- acl_user.ssl_cipher= ssl_cipher ? strdup_root(&mem,ssl_cipher) : 0;
- acl_user.x509_issuer= x509_issuer ? strdup_root(&mem,x509_issuer) : 0;
- acl_user.x509_subject=x509_subject ? strdup_root(&mem,x509_subject) : 0;
+ acl_user.ssl_cipher= ssl_cipher ? strdup_root(&acl_memroot,ssl_cipher) : 0;
+ acl_user.x509_issuer= x509_issuer ? strdup_root(&acl_memroot,x509_issuer) : 0;
+ acl_user.x509_subject=x509_subject ? strdup_root(&acl_memroot,x509_subject) : 0;
+ (void) my_init_dynamic_array(&acl_user.role_grants, sizeof(ACL_USER *),
+ 8, 8, MYF(0));
(void) push_dynamic(&acl_users,(uchar*) &acl_user);
if (!acl_user.host.hostname ||
@@ -1577,11 +2123,17 @@ static void acl_insert_user(const char *user, const char *host,
/* Rebuild 'acl_check_hosts' since 'acl_users' has been modified */
rebuild_check_host();
+
+ /*
+ Rebuild every user's role_grants since 'acl_users' has been sorted
+ and old pointers to ACL_USER elements are no longer valid
+ */
+ rebuild_role_grants();
}
static void acl_update_db(const char *user, const char *host, const char *db,
- ulong privileges)
+ ulong privileges)
{
mysql_mutex_assert_owner(&acl_cache->lock);
@@ -1601,7 +2153,10 @@ static void acl_update_db(const char *user, const char *host, const char *db,
{
if (privileges)
- acl_db->access=privileges;
+ {
+ acl_db->access= privileges;
+ acl_db->initial_access= acl_db->access;
+ }
else
delete_dynamic_element(&acl_dbs,i);
}
@@ -1626,14 +2181,14 @@ static void acl_update_db(const char *user, const char *host, const char *db,
*/
static void acl_insert_db(const char *user, const char *host, const char *db,
- ulong privileges)
+ ulong privileges)
{
ACL_DB acl_db;
mysql_mutex_assert_owner(&acl_cache->lock);
- acl_db.user=strdup_root(&mem,user);
- update_hostname(&acl_db.host, *host ? strdup_root(&mem,host) : 0);
- acl_db.db=strdup_root(&mem,db);
- acl_db.access=privileges;
+ acl_db.user=strdup_root(&acl_memroot,user);
+ update_hostname(&acl_db.host, safe_strdup_root(&acl_memroot, host));
+ acl_db.db=strdup_root(&acl_memroot,db);
+ acl_db.initial_access= acl_db.access= privileges;
acl_db.sort=get_sort(3,acl_db.host.hostname,acl_db.db,acl_db.user);
(void) push_dynamic(&acl_dbs,(uchar*) &acl_db);
my_qsort((uchar*) dynamic_element(&acl_dbs,0,ACL_DB*),acl_dbs.elements,
@@ -1641,7 +2196,6 @@ static void acl_insert_db(const char *user, const char *host, const char *db,
}
-
/*
Get privilege for a host, user and db combination
@@ -1659,7 +2213,7 @@ ulong acl_get(const char *host, const char *ip,
acl_entry *entry;
DBUG_ENTER("acl_get");
- tmp_db= strmov(strmov(key, ip ? ip : "") + 1, user) + 1;
+ tmp_db= strmov(strmov(key, safe_str(ip)) + 1, user) + 1;
end= strnmov(tmp_db, db, key + sizeof(key) - tmp_db);
if (end >= key + sizeof(key)) // db name was truncated
@@ -1673,8 +2227,7 @@ ulong acl_get(const char *host, const char *ip,
key_length= (size_t) (end-key);
mysql_mutex_lock(&acl_cache->lock);
- if (!db_is_pattern && (entry=(acl_entry*) acl_cache->search((uchar*) key,
- key_length)))
+ if (!db_is_pattern && (entry=acl_cache->search((uchar*) key, key_length)))
{
db_access=entry->access;
mysql_mutex_unlock(&acl_cache->lock);
@@ -1692,12 +2245,15 @@ ulong acl_get(const char *host, const char *ip,
{
if (compare_hostname(&acl_db->host,host,ip))
{
- if (!acl_db->db || !wild_compare(db,acl_db->db,db_is_pattern))
- {
- db_access=acl_db->access;
- if (acl_db->host.hostname)
- goto exit; // Fully specified. Take it
- break; /* purecov: tested */
+ if (!acl_db->db || !wild_compare(db,acl_db->db,db_is_pattern))
+ {
+ db_access=acl_db->access;
+ if (acl_db->host.hostname)
+ goto exit; // Fully specified. Take it
+ /* the host table is not used for roles */
+ if ((!host || !host[0]) && !acl_db->host.hostname && find_acl_role(user))
+ goto exit;
+ break; /* purecov: tested */
}
}
}
@@ -1748,7 +2304,7 @@ static void init_check_host(void)
{
DBUG_ENTER("init_check_host");
(void) my_init_dynamic_array(&acl_wild_hosts,sizeof(struct acl_host_and_ip),
- acl_users.elements,1);
+ acl_users.elements, 1, MYF(0));
(void) my_hash_init(&acl_check_hosts,system_charset_info,
acl_users.elements, 0, 0,
(my_hash_get_key) check_get_key, 0, 0);
@@ -1806,9 +2362,143 @@ void rebuild_check_host(void)
init_check_host();
}
+/*
+ Reset a role role_grants dynamic array.
+ Also, the role's access bits are reset to the ones present in the table.
+*/
+static my_bool acl_role_reset_role_arrays(void *ptr,
+ void * not_used __attribute__((unused)))
+{
+ ACL_ROLE *role= (ACL_ROLE *)ptr;
+ reset_dynamic(&role->role_grants);
+ reset_dynamic(&role->parent_grantee);
+ role->counter= 0;
+ return 0;
+}
+
+/*
+ Add a the coresponding pointers present in the mapping to the entries in
+ acl_users and acl_roles
+*/
+static bool add_role_user_mapping(ACL_USER_BASE *grantee, ACL_ROLE *role)
+{
+ return push_dynamic(&grantee->role_grants, (uchar*) &role)
+ || push_dynamic(&role->parent_grantee, (uchar*) &grantee);
+
+}
+
+/*
+ Revert the last add_role_user_mapping() action
+*/
+static void undo_add_role_user_mapping(ACL_USER_BASE *grantee, ACL_ROLE *role)
+{
+ void *pop __attribute__((unused));
+
+ pop= pop_dynamic(&grantee->role_grants);
+ DBUG_ASSERT(role == *(ACL_ROLE**)pop);
+
+ pop= pop_dynamic(&role->parent_grantee);
+ DBUG_ASSERT(grantee == *(ACL_USER_BASE**)pop);
+}
+
+/*
+ this helper is used when building role_grants and parent_grantee arrays
+ from scratch.
+
+ this happens either on initial loading of data from tables, in acl_load().
+ or in rebuild_role_grants after acl_role_reset_role_arrays().
+*/
+static bool add_role_user_mapping(const char *uname, const char *hname,
+ const char *rname)
+{
+ ACL_USER_BASE *grantee= find_acl_user_base(uname, hname);
+ ACL_ROLE *role= find_acl_role(rname);
+
+ if (grantee == NULL || role == NULL)
+ return 1;
+
+ /*
+ because all arrays are rebuilt completely, and counters were also reset,
+ we can increment them here, and after the rebuild all counters will
+ have correct values (equal to the number of roles granted).
+ */
+ if (grantee->flags & IS_ROLE)
+ ((ACL_ROLE*)grantee)->counter++;
+ return add_role_user_mapping(grantee, role);
+}
+
+/*
+ This helper function is used to removes roles and grantees
+ from the corresponding cross-reference arrays. see remove_role_user_mapping().
+ as such, it asserts that an element to delete is present in the array,
+ and is present only once.
+*/
+static void remove_ptr_from_dynarray(DYNAMIC_ARRAY *array, void *ptr)
+{
+ bool found __attribute__((unused))= false;
+ for (uint i= 0; i < array->elements; i++)
+ {
+ if (ptr == *dynamic_element(array, i, void**))
+ {
+ DBUG_ASSERT(!found);
+ delete_dynamic_element(array, i);
+ IF_DBUG(found= true, break);
+ }
+ }
+ DBUG_ASSERT(found);
+}
+
+static void remove_role_user_mapping(ACL_USER_BASE *grantee, ACL_ROLE *role,
+ int grantee_idx=-1, int role_idx=-1)
+{
+ remove_ptr_from_dynarray(&grantee->role_grants, role);
+ remove_ptr_from_dynarray(&role->parent_grantee, grantee);
+}
+
+
+static my_bool add_role_user_mapping_action(void *ptr, void *unused __attribute__((unused)))
+{
+ ROLE_GRANT_PAIR *pair= (ROLE_GRANT_PAIR*)ptr;
+ bool status __attribute__((unused));
+ status= add_role_user_mapping(pair->u_uname, pair->u_hname, pair->r_uname);
+ /*
+ The invariant chosen is that acl_roles_mappings should _always_
+ only contain valid entries, referencing correct user and role grants.
+ If add_role_user_mapping detects an invalid entry, it will not add
+ the mapping into the ACL_USER::role_grants array.
+ */
+ DBUG_ASSERT(status == 0);
+ return 0;
+}
-/* Return true if there is no users that can match the given host */
+/*
+ Rebuild the role grants every time the acl_users is modified
+
+ The role grants in the ACL_USER class need to be rebuilt, as they contain
+ pointers to elements of the acl_users array.
+*/
+
+static void rebuild_role_grants(void)
+{
+ DBUG_ENTER("rebuild_role_grants");
+ /*
+ Reset every user's and role's role_grants array
+ */
+ for (uint i=0; i < acl_users.elements; i++) {
+ ACL_USER *user= dynamic_element(&acl_users, i, ACL_USER *);
+ reset_dynamic(&user->role_grants);
+ }
+ my_hash_iterate(&acl_roles, acl_role_reset_role_arrays, NULL);
+
+ /* Rebuild the direct links between users and roles in ACL_USER::role_grants */
+ my_hash_iterate(&acl_roles_mappings, add_role_user_mapping_action, NULL);
+
+ DBUG_VOID_RETURN;
+}
+
+
+/* Return true if there is no users that can match the given host */
bool acl_check_host(const char *host, const char *ip)
{
if (allow_all_hosts)
@@ -1831,26 +2521,31 @@ bool acl_check_host(const char *host, const char *ip)
}
}
mysql_mutex_unlock(&acl_cache->lock);
+ if (ip != NULL)
+ {
+ /* Increment HOST_CACHE.COUNT_HOST_ACL_ERRORS. */
+ Host_errors errors;
+ errors.m_host_acl= 1;
+ inc_host_errors(ip, &errors);
+ }
return 1; // Host is not allowed
}
-/*
+/**
Check if the user is allowed to change password
- SYNOPSIS:
- check_change_password()
- thd THD
- host hostname for the user
- user user name
- new_password new password
+ @param thd THD
+ @param host Hostname for the user
+ @param user User name
+ @param new_password New password
+ @param new_password_len The length of the new password
- NOTE:
- new_password cannot be NULL
+ new_password cannot be NULL
- RETURN VALUE
- 0 OK
- 1 ERROR ; In this case the error is sent to the client.
+ @return Error status
+ @retval 0 OK
+ @retval 1 ERROR; In this case the error is sent to the client.
*/
int check_change_password(THD *thd, const char *host, const char *user,
@@ -1861,28 +2556,37 @@ int check_change_password(THD *thd, const char *host, const char *user,
my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
return(1);
}
+
+#ifdef WITH_WSREP
+ if ((!WSREP(thd) || !thd->wsrep_applier) &&
+ !thd->slave_thread && !thd->security_ctx->priv_user[0] &&
+ !in_bootstrap)
+#else
+ if (!thd->slave_thread && !thd->security_ctx->priv_user[0] &&
+ !in_bootstrap)
+#endif /* WITH_WSREP */
+ {
+ my_message(ER_PASSWORD_ANONYMOUS_USER, ER(ER_PASSWORD_ANONYMOUS_USER),
+ MYF(0));
+ return(1);
+ }
+ if (!host) // Role
+ {
+ my_error(ER_PASSWORD_NO_MATCH, MYF(0));
+ return 1;
+ }
+
if (!thd->slave_thread &&
#ifdef WITH_WSREP
(!WSREP(thd) || !thd->wsrep_applier) &&
#endif /* WITH_WSREP */
- (strcmp(thd->security_ctx->user, user) ||
+ (strcmp(thd->security_ctx->priv_user, user) ||
my_strcasecmp(system_charset_info, host,
thd->security_ctx->priv_host)))
{
if (check_access(thd, UPDATE_ACL, "mysql", NULL, NULL, 1, 0))
return(1);
}
-#ifdef WITH_WSREP
- if ((!WSREP(thd) || !thd->wsrep_applier) &&
- !thd->slave_thread && !thd->security_ctx->user[0])
-#else
- if (!thd->slave_thread && !thd->security_ctx->user[0])
-#endif /* WITH_WSREP */
- {
- my_message(ER_PASSWORD_ANONYMOUS_USER, ER(ER_PASSWORD_ANONYMOUS_USER),
- MYF(0));
- return(1);
- }
size_t len= strlen(new_password);
if (len && len != SCRAMBLED_PASSWORD_CHAR_LENGTH &&
len != SCRAMBLED_PASSWORD_CHAR_LENGTH_323)
@@ -1894,30 +2598,28 @@ int check_change_password(THD *thd, const char *host, const char *user,
}
-/*
- Change a password for a user
-
- SYNOPSIS
- change_password()
- thd Thread handle
- host Hostname
- user User name
- new_password New password for host@user
+/**
+ Change a password for a user.
- RETURN VALUES
- 0 ok
- 1 ERROR; In this case the error is sent to the client.
+ @param thd THD
+ @param host Hostname
+ @param user User name
+ @param new_password New password hash for host@user
+
+ @return Error code
+ @retval 0 ok
+ @retval 1 ERROR; In this case the error is sent to the client.
*/
-
bool change_password(THD *thd, const char *host, const char *user,
char *new_password)
{
TABLE_LIST tables;
TABLE *table;
+ Rpl_filter *rpl_filter;
/* Buffer should be extended when password length is extended. */
char buff[512];
ulong query_length=0;
- bool save_binlog_row_based;
+ enum_binlog_format save_binlog_format;
uint new_password_len= (uint) strlen(new_password);
bool result= 1;
#ifdef WITH_WSREP
@@ -1950,7 +2652,8 @@ bool change_password(THD *thd, const char *host, const char *user,
GRANT and REVOKE are applied the slave in/exclusion rules as they are
some kind of updates to the mysql.% tables.
*/
- if (thd->slave_thread && rpl_filter->is_on())
+ if (thd->slave_thread &&
+ (rpl_filter= thd->system_thread_info.rpl_sql_info->rpl_filter)->is_on())
{
/*
The tables must be marked "updating" so that tables_ok() takes them into
@@ -1969,13 +2672,14 @@ bool change_password(THD *thd, const char *host, const char *user,
This statement will be replicated as a statement, even when using
row-based replication. The flag will be reset at the end of the
statement.
+ This has to be handled here as it's called by set_var.cc, which is
+ not automaticly handled by sql_parse.cc
*/
- if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
- thd->clear_current_stmt_binlog_format_row();
+ save_binlog_format= thd->set_current_stmt_binlog_format_stmt();
mysql_mutex_lock(&acl_cache->lock);
ACL_USER *acl_user;
- if (!(acl_user= find_acl_user(host, user, TRUE)))
+ if (!(acl_user= find_user_exact(host, user)))
{
mysql_mutex_unlock(&acl_cache->lock);
my_message(ER_PASSWORD_NO_MATCH, ER(ER_PASSWORD_NO_MATCH), MYF(0));
@@ -1983,21 +2687,21 @@ bool change_password(THD *thd, const char *host, const char *user,
}
/* update loaded acl entry: */
- if (acl_user->plugin.str == native_password_plugin_name.str ||
+ if (acl_user->plugin.str == native_password_plugin_name.str ||
acl_user->plugin.str == old_password_plugin_name.str)
{
- acl_user->auth_string.str= strmake_root(&mem, new_password, new_password_len);
+ acl_user->auth_string.str= strmake_root(&acl_memroot, new_password, new_password_len);
acl_user->auth_string.length= new_password_len;
set_user_salt(acl_user, new_password, new_password_len);
set_user_plugin(acl_user, new_password_len);
}
else
- push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
+ push_warning(thd, Sql_condition::WARN_LEVEL_NOTE,
ER_SET_PASSWORD_AUTH_PLUGIN, ER(ER_SET_PASSWORD_AUTH_PLUGIN));
if (update_user_table(thd, table,
- acl_user->host.hostname ? acl_user->host.hostname : "",
- acl_user->user ? acl_user->user : "",
+ safe_str(acl_user->host.hostname),
+ safe_str(acl_user->user.str),
new_password, new_password_len))
{
mysql_mutex_unlock(&acl_cache->lock); /* purecov: deadcode */
@@ -2011,8 +2715,8 @@ bool change_password(THD *thd, const char *host, const char *user,
{
query_length=
sprintf(buff,"SET PASSWORD FOR '%-.120s'@'%-.120s'='%-.120s'",
- acl_user->user ? acl_user->user : "",
- acl_user->host.hostname ? acl_user->host.hostname : "",
+ safe_str(acl_user->user.str),
+ safe_str(acl_user->host.hostname),
new_password);
thd->clear_error();
result= thd->binlog_query(THD::STMT_QUERY_TYPE, buff, query_length,
@@ -2024,15 +2728,12 @@ end:
if (WSREP(thd) && !thd->wsrep_applier)
{
WSREP_TO_ISOLATION_END;
-
+
thd->query_string = query_save;
thd->wsrep_exec_mode = LOCAL_STATE;
}
#endif /* WITH_WSREP */
- /* Restore the state of binlog format */
- DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
- if (save_binlog_row_based)
- thd->set_current_stmt_binlog_format_row();
+ thd->restore_stmt_binlog_format(save_binlog_format);
DBUG_RETURN(result);
#ifdef WITH_WSREP
@@ -2053,7 +2754,7 @@ end:
RETURN
FALSE user not fond
- TRUE there are such user
+ TRUE there is such user
*/
bool is_acl_user(const char *host, const char *user)
@@ -2065,45 +2766,96 @@ bool is_acl_user(const char *host, const char *user)
return TRUE;
mysql_mutex_lock(&acl_cache->lock);
- res= find_acl_user(host, user, TRUE) != NULL;
+
+ if (*host) // User
+ res= find_user_exact(host, user) != NULL;
+ else // Role
+ res= find_acl_role(user) != NULL;
+
mysql_mutex_unlock(&acl_cache->lock);
return res;
}
/*
- Find first entry that matches the current user
+ unlike find_user_exact and find_user_wild,
+ this function finds anonymous users too, it's when a
+ user is not empty, but priv_user (acl_user->user) is empty.
*/
+static ACL_USER *find_user_or_anon(const char *host, const char *user, const char *ip)
+{
+ ACL_USER *result= NULL;
+ mysql_mutex_assert_owner(&acl_cache->lock);
+ for (uint i=0; i < acl_users.elements; i++)
+ {
+ ACL_USER *acl_user_tmp= dynamic_element(&acl_users, i, ACL_USER*);
+ if ((!acl_user_tmp->user.str ||
+ !strcmp(user, acl_user_tmp->user.str)) &&
+ compare_hostname(&acl_user_tmp->host, host, ip))
+ {
+ result= acl_user_tmp;
+ break;
+ }
+ }
+ return result;
+}
-static ACL_USER *
-find_acl_user(const char *host, const char *user, my_bool exact)
+
+/*
+ Find first entry that matches the specified user@host pair
+*/
+static ACL_USER * find_user_exact(const char *host, const char *user)
{
- DBUG_ENTER("find_acl_user");
- DBUG_PRINT("enter",("host: '%s' user: '%s'",host,user));
+ mysql_mutex_assert_owner(&acl_cache->lock);
+ for (uint i=0 ; i < acl_users.elements ; i++)
+ {
+ ACL_USER *acl_user=dynamic_element(&acl_users,i,ACL_USER*);
+ if (acl_user->eq(user, host))
+ return acl_user;
+ }
+ return 0;
+}
+
+/*
+ Find first entry that matches the specified user@host pair
+*/
+static ACL_USER * find_user_wild(const char *host, const char *user, const char *ip)
+{
mysql_mutex_assert_owner(&acl_cache->lock);
for (uint i=0 ; i < acl_users.elements ; i++)
{
ACL_USER *acl_user=dynamic_element(&acl_users,i,ACL_USER*);
- DBUG_PRINT("info",("strcmp('%s','%s'), compare_hostname('%s','%s'),",
- user, acl_user->user ? acl_user->user : "",
- host,
- acl_user->host.hostname ? acl_user->host.hostname :
- ""));
- if ((!acl_user->user && !user[0]) ||
- (acl_user->user && !strcmp(user,acl_user->user)))
- {
- if (exact ? !my_strcasecmp(system_charset_info, host,
- acl_user->host.hostname ?
- acl_user->host.hostname : "") :
- compare_hostname(&acl_user->host,host,host))
- {
- DBUG_RETURN(acl_user);
- }
- }
+ if (acl_user->wild_eq(user, host, ip))
+ return acl_user;
}
- DBUG_RETURN(0);
+ return 0;
+}
+
+/*
+ Find a role with the specified name
+*/
+static ACL_ROLE *find_acl_role(const char *role)
+{
+ DBUG_ENTER("find_acl_role");
+ DBUG_PRINT("enter",("role: '%s'", role));
+ DBUG_PRINT("info", ("Hash elements: %ld", acl_roles.records));
+
+ mysql_mutex_assert_owner(&acl_cache->lock);
+
+ ACL_ROLE *r= (ACL_ROLE *)my_hash_search(&acl_roles, (uchar *)role,
+ role ? strlen(role) : 0);
+ DBUG_RETURN(r);
+}
+
+
+static ACL_USER_BASE *find_acl_user_base(const char *user, const char *host)
+{
+ if (*host)
+ return find_user_exact(host, user);
+
+ return find_acl_role(user);
}
@@ -2140,10 +2892,11 @@ static const char *calc_ip(const char *ip, long *val, char end)
static void update_hostname(acl_host_and_ip *host, const char *hostname)
{
+ // fix historical undocumented convention that empty host is the same as '%'
+ hostname=const_cast<char*>(hostname ? hostname : host_not_specified.str);
host->hostname=(char*) hostname; // This will not be modified!
- if (!hostname ||
- (!(hostname=calc_ip(hostname,&host->ip,'/')) ||
- !(hostname=calc_ip(hostname+1,&host->ip_mask,'\0'))))
+ if (!(hostname= calc_ip(hostname,&host->ip,'/')) ||
+ !(hostname= calc_ip(hostname+1,&host->ip_mask,'\0')))
{
host->ip= host->ip_mask=0; // Not a masked ip
}
@@ -2244,17 +2997,17 @@ bool hostname_requires_resolving(const char *hostname)
}
-/*
+/**
Update record for user in mysql.user privilege table with new password.
- SYNOPSIS
- update_user_table()
- thd Thread handle
- table Pointer to TABLE object for open mysql.user table
- host/user Hostname/username pair identifying user for which
- new password should be set
- new_password New password
- new_password_len Length of new password
+ @param thd THD
+ @param table Pointer to TABLE object for open mysql.user table
+ @param host Hostname
+ @param user Username
+ @param new_password New password hash
+ @param new_password_len Length of new password hash
+
+ @see change_password
*/
static bool update_user_table(THD *thd, TABLE *table,
@@ -2302,9 +3055,9 @@ static bool update_user_table(THD *thd, TABLE *table,
static bool test_if_create_new_users(THD *thd)
{
Security_context *sctx= thd->security_ctx;
- bool create_new_users= test(sctx->master_access & INSERT_ACL) ||
+ bool create_new_users= MY_TEST(sctx->master_access & INSERT_ACL) ||
(!opt_safe_user_create &&
- test(sctx->master_access & CREATE_USER_ACL));
+ MY_TEST(sctx->master_access & CREATE_USER_ACL));
if (!create_new_users)
{
TABLE_LIST tl;
@@ -2315,6 +3068,8 @@ static bool test_if_create_new_users(THD *thd)
db_access=acl_get(sctx->host, sctx->ip,
sctx->priv_user, tl.db, 0);
+ if (sctx->priv_role[0])
+ db_access|= acl_get("", "", sctx->priv_role, tl.db, 0);
if (!(db_access & INSERT_ACL))
{
if (check_grant(thd, INSERT_ACL, &tl, FALSE, UINT_MAX, TRUE))
@@ -2331,28 +3086,41 @@ static bool test_if_create_new_users(THD *thd)
static int replace_user_table(THD *thd, TABLE *table, LEX_USER &combo,
ulong rights, bool revoke_grant,
- bool can_create_user, bool no_auto_create)
+ bool can_create_user, bool no_auto_create)
{
int error = -1;
bool old_row_exists=0;
char what= (revoke_grant) ? 'N' : 'Y';
uchar user_key[MAX_KEY_LENGTH];
+ bool handle_as_role= combo.is_role();
LEX *lex= thd->lex;
DBUG_ENTER("replace_user_table");
mysql_mutex_assert_owner(&acl_cache->lock);
+ size_t length_to_check = 0;
+ combo.password = combo.password.str ? combo.password : empty_lex_str;
if (combo.password.str && combo.password.str[0])
+ length_to_check = combo.password.length;
+ else if (!fix_user_plugin_ptr(&combo.plugin))
+ {
+ length_to_check = combo.auth.length;
+ }
+
+ if (!password_length_valid(length_to_check))
{
- if (combo.password.length != SCRAMBLED_PASSWORD_CHAR_LENGTH &&
- combo.password.length != SCRAMBLED_PASSWORD_CHAR_LENGTH_323)
- {
my_error(ER_PASSWD_LENGTH, MYF(0), SCRAMBLED_PASSWORD_CHAR_LENGTH);
DBUG_RETURN(-1);
- }
}
- else
- combo.password= empty_lex_str;
+
+ /* if the user table is not up to date, we can't handle role updates */
+ if (table->s->fields <= ROLE_ASSIGN_COLUMN_IDX && handle_as_role)
+ {
+ my_error(ER_COL_COUNT_DOESNT_MATCH_PLEASE_UPDATE, MYF(0),
+ table->alias.c_ptr(), ROLE_ASSIGN_COLUMN_IDX + 1, table->s->fields,
+ static_cast<int>(table->s->mysql_version), MYSQL_VERSION_ID);
+ DBUG_RETURN(-1);
+ }
table->use_all_columns();
table->field[0]->store(combo.host.str,combo.host.length,
@@ -2512,6 +3280,16 @@ static int replace_user_table(THD *thd, TABLE *table, LEX_USER &combo,
table->field[next_field + 1]->reset();
}
}
+
+ /* table format checked earlier */
+ if (handle_as_role)
+ {
+ if (old_row_exists && !check_is_role(table))
+ {
+ goto end;
+ }
+ table->field[ROLE_ASSIGN_COLUMN_IDX]->store("Y", 1, system_charset_info);
+ }
}
if (old_row_exists)
@@ -2525,10 +3303,10 @@ static int replace_user_table(THD *thd, TABLE *table, LEX_USER &combo,
if ((error=
table->file->ha_update_row(table->record[1],table->record[0])) &&
error != HA_ERR_RECORD_IS_THE_SAME)
- { // This should never happen
- table->file->print_error(error,MYF(0)); /* purecov: deadcode */
- error= -1; /* purecov: deadcode */
- goto end; /* purecov: deadcode */
+ { // This should never happen
+ table->file->print_error(error,MYF(0)); /* purecov: deadcode */
+ error= -1; /* purecov: deadcode */
+ goto end; /* purecov: deadcode */
}
else
error= 0;
@@ -2550,27 +3328,37 @@ end:
{
acl_cache->clear(1); // Clear privilege cache
if (old_row_exists)
- acl_update_user(combo.user.str, combo.host.str,
- combo.password.str, combo.password.length,
- lex->ssl_type,
- lex->ssl_cipher,
- lex->x509_issuer,
- lex->x509_subject,
- &lex->mqh,
- rights,
- &combo.plugin,
- &combo.auth);
+ {
+ if (handle_as_role)
+ acl_update_role(combo.user.str, rights);
+ else
+ acl_update_user(combo.user.str, combo.host.str,
+ combo.password.str, combo.password.length,
+ lex->ssl_type,
+ lex->ssl_cipher,
+ lex->x509_issuer,
+ lex->x509_subject,
+ &lex->mqh,
+ rights,
+ &combo.plugin,
+ &combo.auth);
+ }
else
- acl_insert_user(combo.user.str, combo.host.str,
- combo.password.str, combo.password.length,
- lex->ssl_type,
- lex->ssl_cipher,
- lex->x509_issuer,
- lex->x509_subject,
- &lex->mqh,
- rights,
- &combo.plugin,
- &combo.auth);
+ {
+ if (handle_as_role)
+ acl_insert_role(combo.user.str, rights);
+ else
+ acl_insert_user(combo.user.str, combo.host.str,
+ combo.password.str, combo.password.length,
+ lex->ssl_type,
+ lex->ssl_cipher,
+ lex->x509_issuer,
+ lex->x509_subject,
+ &lex->mqh,
+ rights,
+ &combo.plugin,
+ &combo.auth);
+ }
}
DBUG_RETURN(error);
}
@@ -2599,10 +3387,14 @@ static int replace_db_table(TABLE *table, const char *db,
}
/* Check if there is such a user in user table in memory? */
- if (!find_acl_user(combo.host.str,combo.user.str, FALSE))
+ if (!find_user_wild(combo.host.str,combo.user.str))
{
- my_message(ER_PASSWORD_NO_MATCH, ER(ER_PASSWORD_NO_MATCH), MYF(0));
- DBUG_RETURN(-1);
+ /* The user could be a role, check if the user is registered as a role */
+ if (!combo.host.length && !find_acl_role(combo.user.str))
+ {
+ my_message(ER_PASSWORD_NO_MATCH, ER(ER_PASSWORD_NO_MATCH), MYF(0));
+ DBUG_RETURN(-1);
+ }
}
table->use_all_columns();
@@ -2684,8 +3476,131 @@ abort:
DBUG_RETURN(-1);
}
+/**
+ Updates the mysql.roles_mapping table
+
+ @param table TABLE to update
+ @param user user name of the grantee
+ @param host host name of the grantee
+ @param role role name to grant
+ @param with_admin WITH ADMIN OPTION flag
+ @param existing the entry in the acl_roles_mappings hash or NULL.
+ it is never NULL if revoke_grant is true.
+ it is NULL when a new pair is added, it's not NULL
+ when an existing pair is updated.
+ @param revoke_grant true for REVOKE, false for GRANT
+*/
+static int
+replace_roles_mapping_table(TABLE *table, LEX_STRING *user, LEX_STRING *host,
+ LEX_STRING *role, bool with_admin,
+ ROLE_GRANT_PAIR *existing, bool revoke_grant)
+{
+ DBUG_ENTER("replace_roles_mapping_table");
-static void
+ uchar row_key[MAX_KEY_LENGTH];
+ int error;
+ table->use_all_columns();
+ restore_record(table, s->default_values);
+ table->field[0]->store(host->str, host->length, system_charset_info);
+ table->field[1]->store(user->str, user->length, system_charset_info);
+ table->field[2]->store(role->str, role->length, system_charset_info);
+
+ DBUG_ASSERT(!revoke_grant || existing);
+
+ if (existing) // delete or update
+ {
+ key_copy(row_key, table->record[0], table->key_info,
+ table->key_info->key_length);
+ if (table->file->ha_index_read_idx_map(table->record[1], 0, row_key,
+ HA_WHOLE_KEY, HA_READ_KEY_EXACT))
+ {
+ /* No match */
+ DBUG_RETURN(1);
+ }
+ if (revoke_grant && !with_admin)
+ {
+ if ((error= table->file->ha_delete_row(table->record[1])))
+ {
+ DBUG_PRINT("info", ("error deleting row '%s' '%s' '%s'",
+ host->str, user->str, role->str));
+ goto table_error;
+ }
+ }
+ else if (with_admin)
+ {
+ table->field[3]->store(!revoke_grant + 1);
+
+ if ((error= table->file->ha_update_row(table->record[1], table->record[0])))
+ {
+ DBUG_PRINT("info", ("error updating row '%s' '%s' '%s'",
+ host->str, user->str, role->str));
+ goto table_error;
+ }
+ }
+ DBUG_RETURN(0);
+ }
+
+ table->field[3]->store(with_admin + 1);
+
+ if ((error= table->file->ha_write_row(table->record[0])))
+ {
+ DBUG_PRINT("info", ("error inserting row '%s' '%s' '%s'",
+ host->str, user->str, role->str));
+ goto table_error;
+ }
+
+ /* all ok */
+ DBUG_RETURN(0);
+
+table_error:
+ DBUG_PRINT("info", ("table error"));
+ table->file->print_error(error, MYF(0));
+ DBUG_RETURN(1);
+}
+
+
+/**
+ Updates the acl_roles_mappings hash
+
+ @param user user name of the grantee
+ @param host host name of the grantee
+ @param role role name to grant
+ @param with_admin WITH ADMIN OPTION flag
+ @param existing the entry in the acl_roles_mappings hash or NULL.
+ it is never NULL if revoke_grant is true.
+ it is NULL when a new pair is added, it's not NULL
+ when an existing pair is updated.
+ @param revoke_grant true for REVOKE, false for GRANT
+*/
+static int
+update_role_mapping(LEX_STRING *user, LEX_STRING *host, LEX_STRING *role,
+ bool with_admin, ROLE_GRANT_PAIR *existing, bool revoke_grant)
+{
+ if (revoke_grant)
+ {
+ if (with_admin)
+ {
+ existing->with_admin= false;
+ return 0;
+ }
+ return my_hash_delete(&acl_roles_mappings, (uchar*)existing);
+ }
+
+ if (existing)
+ {
+ existing->with_admin|= with_admin;
+ return 0;
+ }
+
+ /* allocate a new entry that will go in the hash */
+ ROLE_GRANT_PAIR *hash_entry= new (&acl_memroot) ROLE_GRANT_PAIR;
+ if (hash_entry->init(&acl_memroot, user->str, host->str,
+ role->str, with_admin))
+ return 1;
+ return my_hash_insert(&acl_roles_mappings, (uchar*) hash_entry);
+}
+
+static void
acl_update_proxy_user(ACL_PROXY_USER *new_value, bool is_revoke)
{
mysql_mutex_assert_owner(&acl_cache->lock);
@@ -2693,7 +3608,7 @@ acl_update_proxy_user(ACL_PROXY_USER *new_value, bool is_revoke)
DBUG_ENTER("acl_update_proxy_user");
for (uint i= 0; i < acl_proxy_users.elements; i++)
{
- ACL_PROXY_USER *acl_user=
+ ACL_PROXY_USER *acl_user=
dynamic_element(&acl_proxy_users, i, ACL_PROXY_USER *);
if (acl_user->pk_equals(new_value))
@@ -2715,7 +3630,7 @@ acl_update_proxy_user(ACL_PROXY_USER *new_value, bool is_revoke)
}
-static void
+static void
acl_insert_proxy_user(ACL_PROXY_USER *new_value)
{
DBUG_ENTER("acl_insert_proxy_user");
@@ -2728,9 +3643,9 @@ acl_insert_proxy_user(ACL_PROXY_USER *new_value)
}
-static int
+static int
replace_proxies_priv_table(THD *thd, TABLE *table, const LEX_USER *user,
- const LEX_USER *proxied_user, bool with_grant_arg,
+ const LEX_USER *proxied_user, bool with_grant_arg,
bool revoke_grant)
{
bool old_row_exists= 0;
@@ -2748,14 +3663,14 @@ replace_proxies_priv_table(THD *thd, TABLE *table, const LEX_USER *user,
}
/* Check if there is such a user in user table in memory? */
- if (!find_acl_user(user->host.str,user->user.str, FALSE))
+ if (!find_user_wild(user->host.str,user->user.str))
{
my_message(ER_PASSWORD_NO_MATCH, ER(ER_PASSWORD_NO_MATCH), MYF(0));
DBUG_RETURN(-1);
}
table->use_all_columns();
- ACL_PROXY_USER::store_pk (table, &user->host, &user->user,
+ ACL_PROXY_USER::store_pk (table, &user->host, &user->user,
&proxied_user->host, &proxied_user->user);
key_copy(user_key, table->record[0], table->key_info,
@@ -2828,7 +3743,7 @@ replace_proxies_priv_table(THD *thd, TABLE *table, const LEX_USER *user,
}
else
{
- new_grant.init(&mem, user->host.str, user->user.str,
+ new_grant.init(&acl_memroot, user->host.str, user->user.str,
proxied_user->host.str, proxied_user->user.str,
with_grant_arg);
acl_insert_proxy_user(&new_grant);
@@ -2854,11 +3769,16 @@ class GRANT_COLUMN :public Sql_alloc
public:
char *column;
ulong rights;
+ ulong init_rights;
uint key_length;
- GRANT_COLUMN(String &c, ulong y) :rights (y)
+ GRANT_COLUMN(String &c, ulong y) :rights (y), init_rights(y)
{
- column= (char*) memdup_root(&memex,c.ptr(), key_length=c.length());
+ column= (char*) memdup_root(&grant_memroot,c.ptr(), key_length=c.length());
}
+
+ /* this constructor assumes thas source->column is allocated in grant_memroot */
+ GRANT_COLUMN(GRANT_COLUMN *source) : column(source->column),
+ rights (source->rights), init_rights(0), key_length(source->key_length) { }
};
@@ -2869,13 +3789,13 @@ static uchar* get_key_column(GRANT_COLUMN *buff, size_t *length,
return (uchar*) buff->column;
}
-
class GRANT_NAME :public Sql_alloc
{
public:
acl_host_and_ip host;
char *db, *user, *tname, *hash_key;
ulong privs;
+ ulong init_privs; /* privileges found in physical table */
ulong sort;
size_t key_length;
GRANT_NAME(const char *h, const char *d,const char *u,
@@ -2893,6 +3813,7 @@ class GRANT_TABLE :public GRANT_NAME
{
public:
ulong cols;
+ ulong init_cols; /* privileges found in physical table */
HASH hash_columns;
GRANT_TABLE(const char *h, const char *d,const char *u,
@@ -2900,6 +3821,11 @@ public:
GRANT_TABLE (TABLE *form, TABLE *col_privs);
~GRANT_TABLE();
bool ok() { return privs != 0 || cols != 0; }
+ void init_hash()
+ {
+ my_hash_init2(&hash_columns, 4, system_charset_info, 0, 0, 0,
+ (my_hash_get_key) get_key_column, 0, 0, 0);
+ }
};
@@ -2908,29 +3834,29 @@ void GRANT_NAME::set_user_details(const char *h, const char *d,
bool is_routine)
{
/* Host given by user */
- update_hostname(&host, strdup_root(&memex, h));
+ update_hostname(&host, strdup_root(&grant_memroot, h));
if (db != d)
{
- db= strdup_root(&memex, d);
+ db= strdup_root(&grant_memroot, d);
if (lower_case_table_names)
my_casedn_str(files_charset_info, db);
}
- user = strdup_root(&memex,u);
+ user = strdup_root(&grant_memroot,u);
sort= get_sort(3,host.hostname,db,user);
if (tname != t)
{
- tname= strdup_root(&memex, t);
+ tname= strdup_root(&grant_memroot, t);
if (lower_case_table_names || is_routine)
my_casedn_str(files_charset_info, tname);
}
key_length= strlen(d) + strlen(u)+ strlen(t)+3;
- hash_key= (char*) alloc_root(&memex,key_length);
+ hash_key= (char*) alloc_root(&grant_memroot,key_length);
strmov(strmov(strmov(hash_key,user)+1,db)+1,tname);
}
GRANT_NAME::GRANT_NAME(const char *h, const char *d,const char *u,
const char *t, ulong p, bool is_routine)
- :db(0), tname(0), privs(p)
+ :db(0), tname(0), privs(p), init_privs(p)
{
set_user_details(h, d, u, t, is_routine);
}
@@ -2939,20 +3865,27 @@ 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, FALSE), cols(c)
{
- (void) my_hash_init2(&hash_columns,4,system_charset_info,
- 0,0,0, (my_hash_get_key) get_key_column,0,0);
+ init_hash();
}
-
+/*
+ create a new GRANT_TABLE entry for role inheritance. init_* fields are set
+ to 0
+*/
GRANT_NAME::GRANT_NAME(TABLE *form, bool is_routine)
{
- update_hostname(&host, get_field(&memex, form->field[0]));
- db= get_field(&memex,form->field[1]);
- user= get_field(&memex,form->field[2]);
- if (!user)
- user= (char*) "";
+ user= safe_str(get_field(&grant_memroot,form->field[2]));
+
+ const char *hostname= get_field(&grant_memroot, form->field[0]);
+ mysql_mutex_lock(&acl_cache->lock);
+ if (!hostname && find_acl_role(user))
+ hostname= "";
+ mysql_mutex_unlock(&acl_cache->lock);
+ update_hostname(&host, hostname);
+
+ db= get_field(&grant_memroot,form->field[1]);
sort= get_sort(3, host.hostname, db, user);
- tname= get_field(&memex,form->field[3]);
+ tname= get_field(&grant_memroot,form->field[3]);
if (!db || !tname)
{
/* Wrong table row; Ignore it */
@@ -2968,10 +3901,11 @@ GRANT_NAME::GRANT_NAME(TABLE *form, bool is_routine)
my_casedn_str(files_charset_info, tname);
}
key_length= (strlen(db) + strlen(user) + strlen(tname) + 3);
- hash_key= (char*) alloc_root(&memex, key_length);
+ hash_key= (char*) alloc_root(&grant_memroot, key_length);
strmov(strmov(strmov(hash_key,user)+1,db)+1,tname);
privs = (ulong) form->field[6]->val_int();
privs = fix_rights_for_table(privs);
+ init_privs= privs;
}
@@ -2988,10 +3922,17 @@ GRANT_TABLE::GRANT_TABLE(TABLE *form, TABLE *col_privs)
return;
}
cols= (ulong) form->field[7]->val_int();
- cols = fix_rights_for_column(cols);
+ cols= fix_rights_for_column(cols);
+ /*
+ Initial columns privileges are the same as column privileges on creation.
+ In case of roles, the cols privilege bits can get inherited and thus
+ cause the cols field to change. The init_cols field is always the same
+ as the physical table entry
+ */
+ init_cols= cols;
+
+ init_hash();
- (void) my_hash_init2(&hash_columns,4,system_charset_info,
- 0,0,0, (my_hash_get_key) get_key_column,0,0);
if (cols)
{
uint key_prefix_len;
@@ -3014,6 +3955,7 @@ GRANT_TABLE::GRANT_TABLE(TABLE *form, TABLE *col_privs)
if (col_privs->file->ha_index_init(0, 1))
{
cols= 0;
+ init_cols= 0;
return;
}
@@ -3021,7 +3963,8 @@ GRANT_TABLE::GRANT_TABLE(TABLE *form, TABLE *col_privs)
(key_part_map)15,
HA_READ_KEY_EXACT))
{
- cols = 0; /* purecov: deadcode */
+ cols= 0; /* purecov: deadcode */
+ init_cols= 0;
col_privs->file->ha_index_end();
return;
}
@@ -3036,13 +3979,13 @@ GRANT_TABLE::GRANT_TABLE(TABLE *form, TABLE *col_privs)
fix_rights_for_column(priv))))
{
/* Don't use this entry */
- privs = cols = 0; /* purecov: deadcode */
+ privs= cols= init_privs= init_cols=0; /* purecov: deadcode */
return; /* purecov: deadcode */
}
if (my_hash_insert(&hash_columns, (uchar *) mem_check))
{
/* Invalidate this entry */
- privs= cols= 0;
+ privs= cols= init_privs= init_cols=0;
return;
}
} while (!col_privs->file->ha_index_next(col_privs->record[0]) &&
@@ -3066,9 +4009,9 @@ static uchar* get_grant_table(GRANT_NAME *buff, size_t *length,
}
-void free_grant_table(GRANT_TABLE *grant_table)
+static void free_grant_table(GRANT_TABLE *grant_table)
{
- my_hash_free(&grant_table->hash_columns);
+ grant_table->~GRANT_TABLE();
}
@@ -3123,7 +4066,7 @@ static GRANT_NAME *name_hash_search(HASH *name_hash,
}
-inline GRANT_NAME *
+static GRANT_NAME *
routine_hash_search(const char *host, const char *ip, const char *db,
const char *user, const char *tname, bool proc, bool exact)
{
@@ -3133,7 +4076,7 @@ routine_hash_search(const char *host, const char *ip, const char *db,
}
-inline GRANT_TABLE *
+static GRANT_TABLE *
table_hash_search(const char *host, const char *ip, const char *db,
const char *user, const char *tname, bool exact)
{
@@ -3142,7 +4085,7 @@ table_hash_search(const char *host, const char *ip, const char *db,
}
-inline GRANT_COLUMN *
+static GRANT_COLUMN *
column_hash_search(GRANT_TABLE *t, const char *cname, uint length)
{
return (GRANT_COLUMN*) my_hash_search(&t->hash_columns,
@@ -3324,7 +4267,10 @@ static int replace_column_table(GRANT_TABLE *g_t,
goto end; /* purecov: deadcode */
}
if (grant_column)
- grant_column->rights = privileges; // Update hash
+ {
+ grant_column->rights = privileges; // Update hash
+ grant_column->init_rights = privileges;
+ }
}
else
{
@@ -3381,11 +4327,14 @@ static int replace_table_table(THD *thd, GRANT_TABLE *grant_table,
The following should always succeed as new users are created before
this function is called!
*/
- if (!find_acl_user(combo.host.str,combo.user.str, FALSE))
+ if (!find_user_wild(combo.host.str,combo.user.str))
{
- my_message(ER_PASSWORD_NO_MATCH, ER(ER_PASSWORD_NO_MATCH),
- MYF(0)); /* purecov: deadcode */
- DBUG_RETURN(-1); /* purecov: deadcode */
+ if (!combo.host.length && !find_acl_role(combo.user.str))
+ {
+ my_message(ER_PASSWORD_NO_MATCH, ER(ER_PASSWORD_NO_MATCH),
+ MYF(0)); /* purecov: deadcode */
+ DBUG_RETURN(-1); /* purecov: deadcode */
+ }
}
table->use_all_columns();
@@ -3469,6 +4418,9 @@ static int replace_table_table(THD *thd, GRANT_TABLE *grant_table,
if (rights | col_rights)
{
+ grant_table->init_privs= rights;
+ grant_table->init_cols= col_rights;
+
grant_table->privs= rights;
grant_table->cols= col_rights;
}
@@ -3498,6 +4450,7 @@ static int replace_routine_table(THD *thd, GRANT_NAME *grant_name,
int old_row_exists= 1;
int error=0;
ulong store_proc_rights;
+ HASH *hash= is_proc ? &proc_priv_hash : &func_priv_hash;
DBUG_ENTER("replace_routine_table");
if (!initialized)
@@ -3506,6 +4459,12 @@ static int replace_routine_table(THD *thd, GRANT_NAME *grant_name,
DBUG_RETURN(-1);
}
+ if (revoke_grant && !grant_name->init_privs) // only inherited role privs
+ {
+ my_hash_delete(hash, (uchar*) grant_name);
+ DBUG_RETURN(0);
+ }
+
get_grantor(thd, grantor);
/*
New users are created before this function is called.
@@ -3535,6 +4494,9 @@ static int replace_routine_table(THD *thd, GRANT_NAME *grant_name,
The following should never happen as we first check the in memory
grant tables for the user. There is however always a small change that
the user has modified the grant tables directly.
+
+ Also, there is also a second posibility that this routine entry
+ is created for a role by being inherited from a granted role.
*/
if (revoke_grant)
{ // no row, no revoke
@@ -3589,12 +4551,12 @@ static int replace_routine_table(THD *thd, GRANT_NAME *grant_name,
if (rights)
{
+ grant_name->init_privs= rights;
grant_name->privs= rights;
}
else
{
- my_hash_delete(is_proc ? &proc_priv_hash : &func_priv_hash,(uchar*)
- grant_name);
+ my_hash_delete(hash, (uchar*) grant_name);
}
DBUG_RETURN(0);
@@ -3605,6 +4567,894 @@ table_error:
}
+/*****************************************************************
+ Role privilege propagation and graph traversal functionality
+
+ According to the SQL standard, a role can be granted to a role,
+ thus role grants can create an arbitrarily complex directed acyclic
+ graph (a standard explicitly specifies that cycles are not allowed).
+
+ When a privilege is granted to a role, it becomes available to all grantees.
+ The code below recursively traverses a DAG of role grants, propagating
+ privilege changes.
+
+ The traversal function can work both ways, from roles to grantees or
+ from grantees to roles. The first is used for privilege propagation,
+ the second - for SHOW GRANTS and I_S.APPLICABLE_ROLES
+
+ The role propagation code is smart enough to propagate only privilege
+ changes to one specific database, table, or routine, if only they
+ were changed (like in GRANT ... ON ... TO ...) or it can propagate
+ everything (on startup or after FLUSH PRIVILEGES).
+
+ It traverses only a subgraph that's accessible from the modified role,
+ only visiting roles that can be possibly affected by the GRANT statement.
+
+ Additionally, it stops traversal early, if this particular GRANT statement
+ didn't result in any changes of privileges (e.g. both role1 and role2
+ are granted to the role3, both role1 and role2 have SELECT privilege.
+ if SELECT is revoked from role1 it won't change role3 privileges,
+ so we won't traverse from role3 to its grantees).
+******************************************************************/
+struct PRIVS_TO_MERGE
+{
+ enum what { ALL, GLOBAL, DB, TABLE_COLUMN, PROC, FUNC } what;
+ const char *db, *name;
+};
+
+static int init_role_for_merging(ACL_ROLE *role, void *context)
+{
+ role->counter= 0;
+ return 0;
+}
+
+static int count_subgraph_nodes(ACL_ROLE *role, ACL_ROLE *grantee, void *context)
+{
+ grantee->counter++;
+ return 0;
+}
+
+static int merge_role_privileges(ACL_ROLE *, ACL_ROLE *, void *);
+
+/**
+ rebuild privileges of all affected roles
+
+ entry point into role privilege propagation. after privileges of the
+ 'role' were changed, this function rebuilds privileges of all affected roles
+ as necessary.
+*/
+static void propagate_role_grants(ACL_ROLE *role,
+ enum PRIVS_TO_MERGE::what what,
+ const char *db= 0, const char *name= 0)
+{
+
+ mysql_mutex_assert_owner(&acl_cache->lock);
+ PRIVS_TO_MERGE data= { what, db, name };
+
+ /*
+ Changing privileges of a role causes all other roles that had
+ this role granted to them to have their rights invalidated.
+
+ We need to rebuild all roles' related access bits.
+
+ This cannot be a simple depth-first search, instead we have to merge
+ privieges for all roles granted to a specific grantee, *before*
+ merging privileges for this grantee. In other words, we must visit all
+ parent nodes of a specific node, before descencing into this node.
+
+ For example, if role1 is granted to role2 and role3, and role3 is
+ granted to role2, after "GRANT ... role1", we cannot merge privileges
+ for role2, until role3 is merged. The counter will be 0 for role1, 2
+ for role2, 1 for role3. Traversal will start from role1, go to role2,
+ decrement the counter, backtrack, go to role3, merge it, go to role2
+ again, merge it.
+
+ And the counter is not just "all parent nodes", but only parent nodes
+ that are part of the subgraph we're interested in. For example, if
+ both roleA and roleB are granted to roleC, then roleC has two parent
+ nodes. But when granting a privilege to roleA, we're only looking at a
+ subgraph that includes roleA and roleC (roleB cannot be possibly
+ affected by that grant statement). In this subgraph roleC has only one
+ parent.
+
+ (on the other hand, in acl_load we want to update all roles, and
+ the counter is exactly equal to the number of all parent nodes)
+
+ Thus, we do two graph traversals here. First we only count parents
+ that are part of the subgraph. On the second traversal we decrement
+ the counter and actually merge privileges for a node when a counter
+ drops to zero.
+ */
+ traverse_role_graph_up(role, &data, init_role_for_merging, count_subgraph_nodes);
+ traverse_role_graph_up(role, &data, NULL, merge_role_privileges);
+}
+
+
+// State of a node during a Depth First Search exploration
+struct NODE_STATE
+{
+ ACL_USER_BASE *node_data; /* pointer to the node data */
+ uint neigh_idx; /* the neighbour that needs to be evaluated next */
+};
+
+/**
+ Traverse the role grant graph and invoke callbacks at the specified points.
+
+ @param user user or role to start traversal from
+ @param context opaque parameter to pass to callbacks
+ @param offset offset to ACL_ROLE::parent_grantee or to
+ ACL_USER_BASE::role_grants. Depending on this value,
+ traversal will go from roles to grantees or from
+ grantees to roles.
+ @param on_node called when a node is visited for the first time.
+ Returning a value <0 will abort the traversal.
+ @param on_edge called for every edge in the graph, when traversal
+ goes from a node to a neighbour node.
+ Returning <0 will abort the traversal. Returning >0
+ will make the traversal not to follow this edge.
+
+ @note
+ The traverse method is a DEPTH FIRST SEARCH, but callbacks can influence
+ that (on_edge returning >0 value).
+
+ @note
+ This function should not be called directly, use
+ traverse_role_graph_up() and traverse_role_graph_down() instead.
+
+ @retval 0 traversal finished successfully
+ @retval ROLE_CYCLE_FOUND traversal aborted, cycle detected
+ @retval <0 traversal was aborted, because a callback returned
+ this error code
+*/
+static int traverse_role_graph_impl(ACL_USER_BASE *user, void *context,
+ off_t offset,
+ int (*on_node) (ACL_USER_BASE *role, void *context),
+ int (*on_edge) (ACL_USER_BASE *current, ACL_ROLE *neighbour, void *context))
+{
+ DBUG_ENTER("traverse_role_graph_impl");
+ DBUG_ASSERT(user);
+ DBUG_PRINT("enter",("role: '%s'", user->user.str));
+ /*
+ The search operation should always leave the ROLE_ON_STACK and
+ ROLE_EXPLORED flags clean for all nodes involved in the search
+ */
+ DBUG_ASSERT(!(user->flags & ROLE_ON_STACK));
+ DBUG_ASSERT(!(user->flags & ROLE_EXPLORED));
+ mysql_mutex_assert_owner(&acl_cache->lock);
+
+ /*
+ Stack used to simulate the recursive calls of DFS.
+ It uses a Dynamic_array to reduce the number of
+ malloc calls to a minimum
+ */
+ Dynamic_array<NODE_STATE> stack(20,50);
+ Dynamic_array<ACL_USER_BASE *> to_clear(20,50);
+ NODE_STATE state; /* variable used to insert elements in the stack */
+ int result= 0;
+
+ state.neigh_idx= 0;
+ state.node_data= user;
+ user->flags|= ROLE_ON_STACK;
+
+ stack.push(state);
+ to_clear.push(user);
+
+ user->flags|= ROLE_OPENED;
+ if (on_node && ((result= on_node(user, context)) < 0))
+ goto end;
+
+ while (stack.elements())
+ {
+ NODE_STATE *curr_state= stack.back();
+
+ DBUG_ASSERT(curr_state->node_data->flags & ROLE_ON_STACK);
+
+ ACL_USER_BASE *current= curr_state->node_data;
+ ACL_USER_BASE *neighbour= NULL;
+ DBUG_PRINT("info", ("Examining role %s", current->user.str));
+ /*
+ Iterate through the neighbours until a first valid jump-to
+ neighbour is found
+ */
+ my_bool found= FALSE;
+ uint i;
+ DYNAMIC_ARRAY *array= (DYNAMIC_ARRAY *)(((char*)current) + offset);
+
+ DBUG_ASSERT(array == &current->role_grants || current->flags & IS_ROLE);
+ for (i= curr_state->neigh_idx; i < array->elements; i++)
+ {
+ neighbour= *(dynamic_element(array, i, ACL_ROLE**));
+ if (!(neighbour->flags & IS_ROLE))
+ continue;
+
+ DBUG_PRINT("info", ("Examining neighbour role %s", neighbour->user.str));
+
+ /* check if it forms a cycle */
+ if (neighbour->flags & ROLE_ON_STACK)
+ {
+ DBUG_PRINT("info", ("Found cycle"));
+ result= ROLE_CYCLE_FOUND;
+ goto end;
+ }
+
+ if (!(neighbour->flags & ROLE_OPENED))
+ {
+ neighbour->flags|= ROLE_OPENED;
+ to_clear.push(neighbour);
+ if (on_node && ((result= on_node(neighbour, context)) < 0))
+ goto end;
+ }
+
+ if (on_edge)
+ {
+ result= on_edge(current, (ACL_ROLE*)neighbour, context);
+ if (result < 0)
+ goto end;
+ if (result > 0)
+ continue;
+ }
+
+ /* Check if it was already explored, in that case, move on */
+ if (neighbour->flags & ROLE_EXPLORED)
+ continue;
+
+ found= TRUE;
+ break;
+ }
+
+ /* found states that we have found a node to jump next into */
+ if (found)
+ {
+ curr_state->neigh_idx= i + 1;
+
+ /* some sanity checks */
+ DBUG_ASSERT(!(neighbour->flags & ROLE_ON_STACK));
+
+ /* add the neighbour on the stack */
+ neighbour->flags|= ROLE_ON_STACK;
+ state.neigh_idx= 0;
+ state.node_data= neighbour;
+ stack.push(state);
+ }
+ else
+ {
+ /* Make sure we got a correct node */
+ DBUG_ASSERT(curr_state->node_data->flags & ROLE_ON_STACK);
+ /* Finished with exploring the current node, pop it off the stack */
+ curr_state= &stack.pop();
+ curr_state->node_data->flags&= ~ROLE_ON_STACK; /* clear the on-stack bit */
+ curr_state->node_data->flags|= ROLE_EXPLORED;
+ }
+ }
+
+end:
+ /* Cleanup */
+ for (uint i= 0; i < to_clear.elements(); i++)
+ {
+ ACL_USER_BASE *current= to_clear.at(i);
+ DBUG_ASSERT(current->flags & (ROLE_EXPLORED | ROLE_ON_STACK | ROLE_OPENED));
+ current->flags&= ~(ROLE_EXPLORED | ROLE_ON_STACK | ROLE_OPENED);
+ }
+ DBUG_RETURN(result);
+}
+
+/**
+ Traverse the role grant graph, going from a role to its grantees.
+
+ This is used to propagate changes in privileges, for example,
+ when GRANT or REVOKE is issued for a role.
+*/
+
+static int traverse_role_graph_up(ACL_ROLE *role, void *context,
+ int (*on_node) (ACL_ROLE *role, void *context),
+ int (*on_edge) (ACL_ROLE *current, ACL_ROLE *neighbour, void *context))
+{
+ return traverse_role_graph_impl(role, context,
+ my_offsetof(ACL_ROLE, parent_grantee),
+ (int (*)(ACL_USER_BASE *, void *))on_node,
+ (int (*)(ACL_USER_BASE *, ACL_ROLE *, void *))on_edge);
+}
+
+/**
+ Traverse the role grant graph, going from a user or a role to granted roles.
+
+ This is used, for example, to print all grants available to a user or a role
+ (as in SHOW GRANTS).
+*/
+
+static int traverse_role_graph_down(ACL_USER_BASE *user, void *context,
+ int (*on_node) (ACL_USER_BASE *role, void *context),
+ int (*on_edge) (ACL_USER_BASE *current, ACL_ROLE *neighbour, void *context))
+{
+ return traverse_role_graph_impl(user, context,
+ my_offsetof(ACL_USER_BASE, role_grants),
+ on_node, on_edge);
+}
+
+/*
+ To find all db/table/routine privilege for a specific role
+ we need to scan the array of privileges. It can be big.
+ But the set of privileges granted to a role in question (or
+ to roles directly granted to the role in question) is supposedly
+ much smaller.
+
+ We put a role and all roles directly granted to it in a hash, and iterate
+ the (suposedly long) array of privileges, filtering out "interesting"
+ entries using the role hash. We put all these "interesting"
+ entries in a (suposedly small) dynamic array and them use it for merging.
+*/
+static uchar* role_key(const ACL_ROLE *role, size_t *klen, my_bool)
+{
+ *klen= role->user.length;
+ return (uchar*) role->user.str;
+}
+typedef Hash_set<ACL_ROLE> role_hash_t;
+
+static bool merge_role_global_privileges(ACL_ROLE *grantee)
+{
+ ulong old= grantee->access;
+ grantee->access= grantee->initial_role_access;
+
+ DBUG_EXECUTE_IF("role_merge_stats", role_global_merges++;);
+
+ for (uint i= 0; i < grantee->role_grants.elements; i++)
+ {
+ ACL_ROLE *r= *dynamic_element(&grantee->role_grants, i, ACL_ROLE**);
+ grantee->access|= r->access;
+ }
+ return old != grantee->access;
+}
+
+static int db_name_sort(ACL_DB * const *db1, ACL_DB * const *db2)
+{
+ return strcmp((*db1)->db, (*db2)->db);
+}
+
+/**
+ update ACL_DB for given database and a given role with merged privileges
+
+ @param merged ACL_DB of the role in question (or NULL if it wasn't found)
+ @param first first ACL_DB in an array for the database in question
+ @param access new privileges for the given role on the gived database
+ @param role the name of the given role
+
+ @return a bitmap of
+ 1 - privileges were changed
+ 2 - ACL_DB was added
+ 4 - ACL_DB was deleted
+*/
+static int update_role_db(ACL_DB *merged, ACL_DB **first, ulong access, char *role)
+{
+ if (!first)
+ return 0;
+
+ DBUG_EXECUTE_IF("role_merge_stats", role_db_merges++;);
+
+ if (merged == NULL)
+ {
+ /*
+ there's no ACL_DB for this role (all db grants come from granted roles)
+ we need to create it
+
+ Note that we cannot use acl_insert_db() now:
+ 1. it'll sort elements in the acl_dbs, so the pointers will become invalid
+ 2. we may need many of them, no need to sort every time
+ */
+ DBUG_ASSERT(access);
+ ACL_DB acl_db;
+ acl_db.user= role;
+ acl_db.host.hostname= const_cast<char*>("");
+ acl_db.host.ip= acl_db.host.ip_mask= 0;
+ acl_db.db= first[0]->db;
+ acl_db.access= access;
+ acl_db.initial_access= 0;
+ acl_db.sort=get_sort(3, "", acl_db.db, role);
+ push_dynamic(&acl_dbs,(uchar*) &acl_db);
+ return 2;
+ }
+ else if (access == 0)
+ {
+ /*
+ there is ACL_DB but the role has no db privileges granted
+ (all privileges were coming from granted roles, and now those roles
+ were dropped or had their privileges revoked).
+ we need to remove this ACL_DB entry
+
+ Note, that we cannot delete now:
+ 1. it'll shift elements in the acl_dbs, so the pointers will become invalid
+ 2. it's O(N) operation, and we may need many of them
+ so we only mark elements deleted and will delete later.
+ */
+ merged->sort= 0; // lower than any valid ACL_DB sort value, will be sorted last
+ return 4;
+ }
+ else if (merged->access != access)
+ {
+ /* this is easy */
+ merged->access= access;
+ return 1;
+ }
+ return 0;
+}
+
+/**
+ merges db privileges from roles granted to the role 'grantee'.
+
+ @return true if database privileges of the 'grantee' were changed
+
+*/
+static bool merge_role_db_privileges(ACL_ROLE *grantee, const char *dbname,
+ role_hash_t *rhash)
+{
+ Dynamic_array<ACL_DB *> dbs;
+
+ /*
+ Supposedly acl_dbs can be huge, but only a handful of db grants
+ apply to grantee or roles directly granted to grantee.
+
+ Collect these applicable db grants.
+ */
+ for (uint i=0 ; i < acl_dbs.elements ; i++)
+ {
+ ACL_DB *db= dynamic_element(&acl_dbs,i,ACL_DB*);
+ if (db->host.hostname[0])
+ continue;
+ if (dbname && strcmp(db->db, dbname))
+ continue;
+ ACL_ROLE *r= rhash->find(db->user, strlen(db->user));
+ if (!r)
+ continue;
+ dbs.append(db);
+ }
+ dbs.sort(db_name_sort);
+
+ /*
+ Because dbs array is sorted by the db name, all grants for the same db
+ (that should be merged) are sorted together. The grantee's ACL_DB element
+ is not necessarily the first and may be not present at all.
+ */
+ ACL_DB **first= NULL, *UNINIT_VAR(merged);
+ ulong UNINIT_VAR(access), update_flags= 0;
+ for (ACL_DB **cur= dbs.front(); cur <= dbs.back(); cur++)
+ {
+ if (!first || (!dbname && strcmp(cur[0]->db, cur[-1]->db)))
+ { // new db name series
+ update_flags|= update_role_db(merged, first, access, grantee->user.str);
+ merged= NULL;
+ access= 0;
+ first= cur;
+ }
+ if (strcmp(cur[0]->user, grantee->user.str) == 0)
+ access|= (merged= cur[0])->initial_access;
+ else
+ access|= cur[0]->access;
+ }
+ update_flags|= update_role_db(merged, first, access, grantee->user.str);
+
+ /*
+ to make this code a bit simpler, we sort on deletes, to move
+ deleted elements to the end of the array. strictly speaking it's
+ unnecessary, it'd be faster to remove them in one O(N) array scan.
+
+ on the other hand, qsort on almost sorted array is pretty fast anyway...
+ */
+ if (update_flags & (2|4))
+ { // inserted or deleted, need to sort
+ my_qsort((uchar*) dynamic_element(&acl_dbs,0,ACL_DB*),acl_dbs.elements,
+ sizeof(ACL_DB),(qsort_cmp) acl_compare);
+ }
+ if (update_flags & 4)
+ { // deleted, trim the end
+ while (acl_dbs.elements &&
+ dynamic_element(&acl_dbs, acl_dbs.elements-1, ACL_DB*)->sort == 0)
+ acl_dbs.elements--;
+ }
+ return update_flags;
+}
+
+static int table_name_sort(GRANT_TABLE * const *tbl1, GRANT_TABLE * const *tbl2)
+{
+ int res = strcmp((*tbl1)->db, (*tbl2)->db);
+ if (res) return res;
+ return strcmp((*tbl1)->tname, (*tbl2)->tname);
+}
+
+/**
+ merges column privileges for the entry 'merged'
+
+ @param merged GRANT_TABLE to merge the privileges into
+ @param cur first entry in the array of GRANT_TABLE's for a given table
+ @param last last entry in the array of GRANT_TABLE's for a given table,
+ all entries between cur and last correspond to the *same* table
+
+ @return 1 if the _set of columns_ in 'merged' was changed
+ (not if the _set of privileges_ was changed).
+*/
+static int update_role_columns(GRANT_TABLE *merged,
+ GRANT_TABLE **cur, GRANT_TABLE **last)
+
+{
+ ulong rights __attribute__((unused))= 0;
+ int changed= 0;
+ if (!merged->cols)
+ {
+ changed= merged->hash_columns.records > 0;
+ my_hash_reset(&merged->hash_columns);
+ return changed;
+ }
+
+ DBUG_EXECUTE_IF("role_merge_stats", role_column_merges++;);
+
+ HASH *mh= &merged->hash_columns;
+ for (uint i=0 ; i < mh->records ; i++)
+ {
+ GRANT_COLUMN *col = (GRANT_COLUMN *)my_hash_element(mh, i);
+ col->rights= col->init_rights;
+ }
+
+ for (; cur < last; cur++)
+ {
+ if (*cur == merged)
+ continue;
+ HASH *ch= &cur[0]->hash_columns;
+ for (uint i=0 ; i < ch->records ; i++)
+ {
+ GRANT_COLUMN *ccol = (GRANT_COLUMN *)my_hash_element(ch, i);
+ GRANT_COLUMN *mcol = (GRANT_COLUMN *)my_hash_search(mh,
+ (uchar *)ccol->column, ccol->key_length);
+ if (mcol)
+ mcol->rights|= ccol->rights;
+ else
+ {
+ changed= 1;
+ my_hash_insert(mh, (uchar*)new (&grant_memroot) GRANT_COLUMN(ccol));
+ }
+ }
+ }
+
+ for (uint i=0 ; i < mh->records ; i++)
+ {
+ GRANT_COLUMN *col = (GRANT_COLUMN *)my_hash_element(mh, i);
+ rights|= col->rights;
+ if (!col->rights)
+ {
+ changed= 1;
+ my_hash_delete(mh, (uchar*)col);
+ }
+ }
+ DBUG_ASSERT(rights == merged->cols);
+ return changed;
+}
+
+/**
+ update GRANT_TABLE for a given table and a given role with merged privileges
+
+ @param merged GRANT_TABLE of the role in question (or NULL if it wasn't found)
+ @param first first GRANT_TABLE in an array for the table in question
+ @param last last entry in the array of GRANT_TABLE's for a given table,
+ all entries between first and last correspond to the *same* table
+ @param privs new table-level privileges for 'merged'
+ @param cols new OR-ed column-level privileges for 'merged'
+ @param role the name of the given role
+
+ @return a bitmap of
+ 1 - privileges were changed
+ 2 - GRANT_TABLE was added
+ 4 - GRANT_TABLE was deleted
+*/
+static int update_role_table_columns(GRANT_TABLE *merged,
+ GRANT_TABLE **first, GRANT_TABLE **last,
+ ulong privs, ulong cols, char *role)
+{
+ if (!first)
+ return 0;
+
+ DBUG_EXECUTE_IF("role_merge_stats", role_table_merges++;);
+
+ if (merged == NULL)
+ {
+ /*
+ there's no GRANT_TABLE for this role (all table grants come from granted
+ roles) we need to create it
+ */
+ DBUG_ASSERT(privs | cols);
+ merged= new (&grant_memroot) GRANT_TABLE("", first[0]->db, role, first[0]->tname,
+ privs, cols);
+ merged->init_privs= merged->init_cols= 0;
+ update_role_columns(merged, first, last);
+ my_hash_insert(&column_priv_hash,(uchar*) merged);
+ return 2;
+ }
+ else if ((privs | cols) == 0)
+ {
+ /*
+ there is GRANT_TABLE object but the role has no table or column
+ privileges granted (all privileges were coming from granted roles, and
+ now those roles were dropped or had their privileges revoked).
+ we need to remove this GRANT_TABLE
+ */
+ DBUG_EXECUTE_IF("role_merge_stats",
+ role_column_merges+= MY_TEST(merged->cols););
+ my_hash_delete(&column_priv_hash,(uchar*) merged);
+ return 4;
+ }
+ else
+ {
+ bool changed= merged->cols != cols || merged->privs != privs;
+ merged->cols= cols;
+ merged->privs= privs;
+ if (update_role_columns(merged, first, last))
+ changed= true;
+ return changed;
+ }
+}
+
+/**
+ merges table privileges from roles granted to the role 'grantee'.
+
+ @return true if table privileges of the 'grantee' were changed
+
+*/
+static bool merge_role_table_and_column_privileges(ACL_ROLE *grantee,
+ const char *db, const char *tname, role_hash_t *rhash)
+{
+ Dynamic_array<GRANT_TABLE *> grants;
+ DBUG_ASSERT(MY_TEST(db) == MY_TEST(tname)); // both must be set, or neither
+
+ /*
+ first, collect table/column privileges granted to
+ roles in question.
+ */
+ for (uint i=0 ; i < column_priv_hash.records ; i++)
+ {
+ GRANT_TABLE *grant= (GRANT_TABLE *) my_hash_element(&column_priv_hash, i);
+ if (grant->host.hostname[0])
+ continue;
+ if (tname && (strcmp(grant->db, db) || strcmp(grant->tname, tname)))
+ continue;
+ ACL_ROLE *r= rhash->find(grant->user, strlen(grant->user));
+ if (!r)
+ continue;
+ grants.append(grant);
+ }
+ grants.sort(table_name_sort);
+
+ GRANT_TABLE **first= NULL, *UNINIT_VAR(merged), **cur;
+ ulong UNINIT_VAR(privs), UNINIT_VAR(cols), update_flags= 0;
+ for (cur= grants.front(); cur <= grants.back(); cur++)
+ {
+ if (!first ||
+ (!tname && (strcmp(cur[0]->db, cur[-1]->db) ||
+ strcmp(cur[0]->tname, cur[-1]->tname))))
+ { // new db.tname series
+ update_flags|= update_role_table_columns(merged, first, cur,
+ privs, cols, grantee->user.str);
+ merged= NULL;
+ privs= cols= 0;
+ first= cur;
+ }
+ if (strcmp(cur[0]->user, grantee->user.str) == 0)
+ {
+ merged= cur[0];
+ cols|= cur[0]->init_cols;
+ privs|= cur[0]->init_privs;
+ }
+ else
+ {
+ cols|= cur[0]->cols;
+ privs|= cur[0]->privs;
+ }
+ }
+ update_flags|= update_role_table_columns(merged, first, cur,
+ privs, cols, grantee->user.str);
+
+ return update_flags;
+}
+
+static int routine_name_sort(GRANT_NAME * const *r1, GRANT_NAME * const *r2)
+{
+ int res= strcmp((*r1)->db, (*r2)->db);
+ if (res) return res;
+ return strcmp((*r1)->tname, (*r2)->tname);
+}
+
+/**
+ update GRANT_NAME for a given routine and a given role with merged privileges
+
+ @param merged GRANT_NAME of the role in question (or NULL if it wasn't found)
+ @param first first GRANT_NAME in an array for the routine in question
+ @param privs new routine-level privileges for 'merged'
+ @param role the name of the given role
+ @param hash proc_priv_hash or func_priv_hash
+
+ @return a bitmap of
+ 1 - privileges were changed
+ 2 - GRANT_NAME was added
+ 4 - GRANT_NAME was deleted
+*/
+static int update_role_routines(GRANT_NAME *merged, GRANT_NAME **first,
+ ulong privs, char *role, HASH *hash)
+{
+ if (!first)
+ return 0;
+
+ DBUG_EXECUTE_IF("role_merge_stats", role_routine_merges++;);
+
+ if (merged == NULL)
+ {
+ /*
+ there's no GRANT_NAME for this role (all routine grants come from granted
+ roles) we need to create it
+ */
+ DBUG_ASSERT(privs);
+ merged= new (&grant_memroot) GRANT_NAME("", first[0]->db, role, first[0]->tname,
+ privs, true);
+ merged->init_privs= 0; // all privs are inherited
+ my_hash_insert(hash, (uchar *)merged);
+ return 2;
+ }
+ else if (privs == 0)
+ {
+ /*
+ there is GRANT_NAME but the role has no privileges granted
+ (all privileges were coming from granted roles, and now those roles
+ were dropped or had their privileges revoked).
+ we need to remove this entry
+ */
+ my_hash_delete(hash, (uchar*)merged);
+ return 4;
+ }
+ else if (merged->privs != privs)
+ {
+ /* this is easy */
+ merged->privs= privs;
+ return 1;
+ }
+ return 0;
+}
+
+/**
+ merges routine privileges from roles granted to the role 'grantee'.
+
+ @return true if routine privileges of the 'grantee' were changed
+
+*/
+static bool merge_role_routine_grant_privileges(ACL_ROLE *grantee,
+ const char *db, const char *tname, role_hash_t *rhash, HASH *hash)
+{
+ ulong update_flags= 0;
+
+ DBUG_ASSERT(MY_TEST(db) == MY_TEST(tname)); // both must be set, or neither
+
+ Dynamic_array<GRANT_NAME *> grants;
+
+ /* first, collect routine privileges granted to roles in question */
+ for (uint i=0 ; i < hash->records ; i++)
+ {
+ GRANT_NAME *grant= (GRANT_NAME *) my_hash_element(hash, i);
+ if (grant->host.hostname[0])
+ continue;
+ if (tname && (strcmp(grant->db, db) || strcmp(grant->tname, tname)))
+ continue;
+ ACL_ROLE *r= rhash->find(grant->user, strlen(grant->user));
+ if (!r)
+ continue;
+ grants.append(grant);
+ }
+ grants.sort(routine_name_sort);
+
+ GRANT_NAME **first= NULL, *UNINIT_VAR(merged);
+ ulong UNINIT_VAR(privs);
+ for (GRANT_NAME **cur= grants.front(); cur <= grants.back(); cur++)
+ {
+ if (!first ||
+ (!tname && (strcmp(cur[0]->db, cur[-1]->db) ||
+ strcmp(cur[0]->tname, cur[-1]->tname))))
+ { // new db.tname series
+ update_flags|= update_role_routines(merged, first, privs,
+ grantee->user.str, hash);
+ merged= NULL;
+ privs= 0;
+ first= cur;
+ }
+ if (strcmp(cur[0]->user, grantee->user.str) == 0)
+ {
+ merged= cur[0];
+ privs|= cur[0]->init_privs;
+ }
+ else
+ {
+ privs|= cur[0]->privs;
+ }
+ }
+ update_flags|= update_role_routines(merged, first, privs,
+ grantee->user.str, hash);
+ return update_flags;
+}
+
+/**
+ update privileges of the 'grantee' from all roles, granted to it
+*/
+static int merge_role_privileges(ACL_ROLE *role __attribute__((unused)),
+ ACL_ROLE *grantee, void *context)
+{
+ PRIVS_TO_MERGE *data= (PRIVS_TO_MERGE *)context;
+
+ if (--grantee->counter)
+ return 1; // don't recurse into grantee just yet
+
+ /* if we'll do db/table/routine privileges, create a hash of role names */
+ role_hash_t role_hash(role_key);
+ if (data->what != PRIVS_TO_MERGE::GLOBAL)
+ {
+ role_hash.insert(grantee);
+ for (uint i= 0; i < grantee->role_grants.elements; i++)
+ role_hash.insert(*dynamic_element(&grantee->role_grants, i, ACL_ROLE**));
+ }
+
+ bool all= data->what == PRIVS_TO_MERGE::ALL;
+ bool changed= false;
+ if (all || data->what == PRIVS_TO_MERGE::GLOBAL)
+ changed|= merge_role_global_privileges(grantee);
+ if (all || data->what == PRIVS_TO_MERGE::DB)
+ changed|= merge_role_db_privileges(grantee, data->db, &role_hash);
+ if (all || data->what == PRIVS_TO_MERGE::TABLE_COLUMN)
+ changed|= merge_role_table_and_column_privileges(grantee,
+ data->db, data->name, &role_hash);
+ if (all || data->what == PRIVS_TO_MERGE::PROC)
+ changed|= merge_role_routine_grant_privileges(grantee,
+ data->db, data->name, &role_hash, &proc_priv_hash);
+ if (all || data->what == PRIVS_TO_MERGE::FUNC)
+ changed|= merge_role_routine_grant_privileges(grantee,
+ data->db, data->name, &role_hash, &func_priv_hash);
+
+ return !changed; // don't recurse into the subgraph if privs didn't change
+}
+
+static bool merge_one_role_privileges(ACL_ROLE *grantee)
+{
+ PRIVS_TO_MERGE data= { PRIVS_TO_MERGE::ALL, 0, 0 };
+ grantee->counter= 1;
+ return merge_role_privileges(0, grantee, &data);
+}
+
+/*****************************************************************
+ End of the role privilege propagation and graph traversal code
+******************************************************************/
+
+bool copy_and_check_auth(LEX_USER *to, LEX_USER *from, LEX *lex)
+{
+ if (to != from)
+ {
+ /* preserve authentication information, if LEX_USER was reallocated */
+ to->password= from->password;
+ to->plugin= from->plugin;
+ to->auth= from->auth;
+ }
+
+ /*
+ Note, that no password is null_lex_str, while no plugin is empty_lex_str.
+ See sql_yacc.yy
+ */
+ bool has_auth= to->password.str || to->plugin.length || to->auth.length ||
+ lex->ssl_type != SSL_TYPE_NOT_SPECIFIED || lex->ssl_cipher ||
+ lex->x509_issuer || lex->x509_subject ||
+ lex->mqh.specified_limits;
+
+ /*
+ Specifying authentication clauses forces the name to be interpreted
+ as a user, not a role. See also check_change_password()
+ */
+ if (to->is_role() && has_auth)
+ {
+ my_error(ER_PASSWORD_NO_MATCH, MYF(0));
+ return true;
+ }
+
+ return false;
+}
+
+
/*
Store table level and column level grants in the privilege tables
@@ -3633,7 +5483,7 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list,
TABLE_LIST tables[3];
bool create_new_users=0;
char *db_name, *table_name;
- bool save_binlog_row_based;
+ Rpl_filter *rpl_filter;
DBUG_ENTER("mysql_table_grant");
if (!initialized)
@@ -3684,12 +5534,7 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list,
{
if (!(rights & CREATE_ACL))
{
- char buf[FN_REFLEN + 1];
- build_table_filename(buf, sizeof(buf) - 1, table_list->db,
- table_list->table_name, reg_ext, 0);
- fn_format(buf, buf, "", "", MY_UNPACK_FILENAME | MY_RESOLVE_SYMLINKS |
- MY_RETURN_REAL_PATH | MY_APPEND_EXT);
- if (access(buf,F_OK))
+ if (!ha_table_exists(thd, table_list->db, table_list->table_name, 0))
{
my_error(ER_NO_SUCH_TABLE, MYF(0), table_list->db, table_list->alias);
DBUG_RETURN(TRUE);
@@ -3723,20 +5568,13 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list,
if (column_priv || (revoke_grant && ((rights & COL_ACLS) || columns.elements)))
tables[1].next_local= tables[1].next_global= tables+2;
- /*
- This statement will be replicated as a statement, even when using
- row-based replication. The flag will be reset at the end of the
- statement.
- */
- if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
- thd->clear_current_stmt_binlog_format_row();
-
#ifdef HAVE_REPLICATION
/*
GRANT and REVOKE are applied the slave in/exclusion rules as they are
some kind of updates to the mysql.% tables.
*/
- if (thd->slave_thread && rpl_filter->is_on())
+ if (thd->slave_thread &&
+ (rpl_filter= thd->system_thread_info.rpl_sql_info->rpl_filter)->is_on())
{
/*
The tables must be marked "updating" so that tables_ok() takes them into
@@ -3744,17 +5582,11 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list,
*/
tables[0].updating= tables[1].updating= tables[2].updating= 1;
if (!(thd->spcont || rpl_filter->tables_ok(0, tables)))
- {
- /* Restore the state of binlog format */
- DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
- if (save_binlog_row_based)
- thd->set_current_stmt_binlog_format_row();
DBUG_RETURN(FALSE);
- }
}
#endif
- /*
+ /*
The lock api is depending on the thd->lex variable which needs to be
re-initialized.
*/
@@ -3768,11 +5600,7 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list,
thd->lex->sql_command= backup.sql_command;
if (open_and_lock_tables(thd, tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT))
{ // Should never happen
- /* Restore the state of binlog format */
- DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
thd->lex->restore_backup_query_tables_list(&backup);
- if (save_binlog_row_based)
- thd->set_current_stmt_binlog_format_row();
DBUG_RETURN(TRUE); /* purecov: deadcode */
}
@@ -3782,23 +5610,26 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list,
mysql_rwlock_wrlock(&LOCK_grant);
mysql_mutex_lock(&acl_cache->lock);
MEM_ROOT *old_root= thd->mem_root;
- thd->mem_root= &memex;
+ thd->mem_root= &grant_memroot;
grant_version++;
while ((tmp_Str = str_list++))
{
int error;
GRANT_TABLE *grant_table;
- if (!(Str= get_current_user(thd, tmp_Str)))
+ if (!(Str= get_current_user(thd, tmp_Str, false)))
{
result= TRUE;
continue;
- }
+ }
/* Create user if needed */
- error=replace_user_table(thd, tables[0].table, *Str,
- 0, revoke_grant, create_new_users,
- test(thd->variables.sql_mode &
- MODE_NO_AUTO_CREATE_USER));
+ if (copy_and_check_auth(Str, tmp_Str, thd->lex))
+ error= -1;
+ else
+ error=replace_user_table(thd, tables[0].table, *Str,
+ 0, revoke_grant, create_new_users,
+ MY_TEST(thd->variables.sql_mode &
+ MODE_NO_AUTO_CREATE_USER));
if (error)
{
result= TRUE; // Remember error
@@ -3875,15 +5706,17 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list,
}
else if (tables[2].table)
{
- if ((replace_column_table(grant_table, tables[2].table, *Str,
- columns,
- db_name, table_name,
- rights, revoke_grant)))
+ if (replace_column_table(grant_table, tables[2].table, *Str, columns,
+ db_name, table_name, rights, revoke_grant))
{
result= TRUE;
}
}
+ if (Str->is_role())
+ propagate_role_grants(find_acl_role(Str->user.str),
+ PRIVS_TO_MERGE::TABLE_COLUMN, db_name, table_name);
}
+
thd->mem_root= old_root;
mysql_mutex_unlock(&acl_cache->lock);
@@ -3900,9 +5733,6 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list,
/* Tables are automatically closed */
thd->lex->restore_backup_query_tables_list(&backup);
/* Restore the state of binlog format */
- DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
- if (save_binlog_row_based)
- thd->set_current_stmt_binlog_format_row();
DBUG_RETURN(result);
}
@@ -3931,7 +5761,7 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc,
TABLE_LIST tables[2];
bool create_new_users=0, result=0;
char *db_name, *table_name;
- bool save_binlog_row_based;
+ Rpl_filter *rpl_filter;
DBUG_ENTER("mysql_routine_grant");
if (!initialized)
@@ -3961,20 +5791,13 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc,
C_STRING_WITH_LEN("procs_priv"), "procs_priv", TL_WRITE);
tables[0].next_local= tables[0].next_global= tables+1;
- /*
- This statement will be replicated as a statement, even when using
- row-based replication. The flag will be reset at the end of the
- statement.
- */
- if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
- thd->clear_current_stmt_binlog_format_row();
-
#ifdef HAVE_REPLICATION
/*
GRANT and REVOKE are applied the slave in/exclusion rules as they are
some kind of updates to the mysql.% tables.
*/
- if (thd->slave_thread && rpl_filter->is_on())
+ if (thd->slave_thread &&
+ (rpl_filter= thd->system_thread_info.rpl_sql_info->rpl_filter)->is_on())
{
/*
The tables must be marked "updating" so that tables_ok() takes them into
@@ -3983,30 +5806,22 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc,
tables[0].updating= tables[1].updating= 1;
if (!(thd->spcont || rpl_filter->tables_ok(0, tables)))
{
- /* Restore the state of binlog format */
- DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
- if (save_binlog_row_based)
- thd->set_current_stmt_binlog_format_row();
DBUG_RETURN(FALSE);
}
}
#endif
if (open_and_lock_tables(thd, tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT))
- { // Should never happen
- /* Restore the state of binlog format */
- DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
- if (save_binlog_row_based)
- thd->set_current_stmt_binlog_format_row();
DBUG_RETURN(TRUE);
- }
+
+ DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
if (!revoke_grant)
create_new_users= test_if_create_new_users(thd);
mysql_rwlock_wrlock(&LOCK_grant);
mysql_mutex_lock(&acl_cache->lock);
MEM_ROOT *old_root= thd->mem_root;
- thd->mem_root= &memex;
+ thd->mem_root= &grant_memroot;
DBUG_PRINT("info",("now time to iterate and add users"));
@@ -4014,16 +5829,16 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc,
{
int error;
GRANT_NAME *grant_name;
- if (!(Str= get_current_user(thd, tmp_Str)))
+ if (!(Str= get_current_user(thd, tmp_Str, false)))
{
result= TRUE;
continue;
- }
+ }
/* Create user if needed */
error=replace_user_table(thd, tables[0].table, *Str,
0, revoke_grant, create_new_users,
- test(thd->variables.sql_mode &
- MODE_NO_AUTO_CREATE_USER));
+ MY_TEST(thd->variables.sql_mode &
+ MODE_NO_AUTO_CREATE_USER));
if (error)
{
result= TRUE; // Remember error
@@ -4032,10 +5847,9 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc,
db_name= table_list->db;
table_name= table_list->table_name;
-
grant_name= routine_hash_search(Str->host.str, NullS, db_name,
Str->user.str, table_name, is_proc, 1);
- if (!grant_name)
+ if (!grant_name || !grant_name->init_privs)
{
if (revoke_grant)
{
@@ -4057,12 +5871,16 @@ 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,
+ db_name, table_name, is_proc, rights,
revoke_grant) != 0)
{
result= TRUE;
continue;
}
+ if (Str->is_role())
+ propagate_role_grants(find_acl_role(Str->user.str),
+ is_proc ? PRIVS_TO_MERGE::PROC : PRIVS_TO_MERGE::FUNC,
+ db_name, table_name);
}
thd->mem_root= old_root;
mysql_mutex_unlock(&acl_cache->lock);
@@ -4074,15 +5892,338 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc,
}
mysql_rwlock_unlock(&LOCK_grant);
- /* Restore the state of binlog format */
- DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
- if (save_binlog_row_based)
- thd->set_current_stmt_binlog_format_row();
/* Tables are automatically closed */
DBUG_RETURN(result);
}
+/**
+ append a user or role name to a buffer that will be later used as an error message
+*/
+static void append_user(THD *thd, String *str,
+ const LEX_STRING *u, const LEX_STRING *h)
+{
+ if (str->length())
+ str->append(',');
+ append_query_string(system_charset_info, str, u->str, u->length,
+ thd->variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES);
+ /* hostname part is not relevant for roles, it is always empty */
+ if (u->length == 0 || h->length != 0)
+ {
+ str->append('@');
+ append_query_string(system_charset_info, str, h->str, h->length,
+ thd->variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES);
+ }
+}
+
+static void append_user(THD *thd, String *str, LEX_USER *user)
+{
+ append_user(thd, str, & user->user, & user->host);
+}
+
+/**
+ append a string to a buffer that will be later used as an error message
+
+ @note
+ a string can be either CURRENT_USER or CURRENT_ROLE or NONE, it should be
+ neither quoted nor escaped.
+*/
+static void append_str(String *str, const char *s, size_t l)
+{
+ if (str->length())
+ str->append(',');
+ str->append(s, l);
+}
+
+static int can_grant_role_callback(ACL_USER_BASE *grantee,
+ ACL_ROLE *role, void *data)
+{
+ ROLE_GRANT_PAIR *pair;
+
+ if (role != (ACL_ROLE*)data)
+ return 0; // keep searching
+
+ if (grantee->flags & IS_ROLE)
+ pair= find_role_grant_pair(&grantee->user, &empty_lex_str, &role->user);
+ else
+ {
+ ACL_USER *user= (ACL_USER *)grantee;
+ LEX_STRING host= { user->host.hostname, user->hostname_length };
+ pair= find_role_grant_pair(&user->user, &host, &role->user);
+ }
+ if (!pair->with_admin)
+ return 0; // keep searching
+
+ return -1; // abort the traversal
+}
+
+
+/*
+ One can only grant a role if SELECT * FROM I_S.APPLICABLE_ROLES shows this
+ role as grantable.
+
+ What this really means - we need to traverse role graph for the current user
+ looking for our role being granted with the admin option.
+*/
+static bool can_grant_role(THD *thd, ACL_ROLE *role)
+{
+ Security_context *sctx= thd->security_ctx;
+
+ if (!sctx->user) // replication
+ return true;
+
+ ACL_USER *grantee= find_user_exact(sctx->priv_host, sctx->priv_user);
+ if (!grantee)
+ return false;
+
+ return traverse_role_graph_down(grantee, role, NULL,
+ can_grant_role_callback) == -1;
+}
+
+
+bool mysql_grant_role(THD *thd, List <LEX_USER> &list, bool revoke)
+{
+ DBUG_ENTER("mysql_grant_role");
+ /*
+ The first entry in the list is the granted role. Need at least two
+ entries for the command to be valid
+ */
+ DBUG_ASSERT(list.elements >= 2);
+ bool result= 0;
+ bool create_new_user, no_auto_create_user;
+ String wrong_users;
+ LEX_USER *user, *granted_role;
+ LEX_STRING rolename;
+ LEX_STRING username;
+ LEX_STRING hostname;
+ ACL_ROLE *role, *role_as_user;
+
+ List_iterator <LEX_USER> user_list(list);
+ granted_role= user_list++;
+ if (!(granted_role= get_current_user(thd, granted_role)))
+ DBUG_RETURN(TRUE);
+
+ DBUG_ASSERT(granted_role->is_role());
+ rolename= granted_role->user;
+
+ create_new_user= test_if_create_new_users(thd);
+ no_auto_create_user= MY_TEST(thd->variables.sql_mode &
+ MODE_NO_AUTO_CREATE_USER);
+
+ TABLE_LIST tables[2];
+ tables[0].init_one_table(C_STRING_WITH_LEN("mysql"),
+ C_STRING_WITH_LEN("roles_mapping"),
+ "roles_mapping", TL_WRITE);
+ tables[1].init_one_table(C_STRING_WITH_LEN("mysql"),
+ C_STRING_WITH_LEN("user"), "user", TL_WRITE);
+ tables[0].next_local= tables[0].next_global= tables+1;
+
+ if (open_and_lock_tables(thd, tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT))
+ DBUG_RETURN(TRUE); /* purecov: deadcode */
+
+ mysql_rwlock_wrlock(&LOCK_grant);
+ mysql_mutex_lock(&acl_cache->lock);
+ if (!(role= find_acl_role(rolename.str)))
+ {
+ mysql_mutex_unlock(&acl_cache->lock);
+ mysql_rwlock_unlock(&LOCK_grant);
+ my_error(ER_INVALID_ROLE, MYF(0), rolename.str);
+ DBUG_RETURN(TRUE);
+ }
+
+ if (!can_grant_role(thd, role))
+ {
+ mysql_mutex_unlock(&acl_cache->lock);
+ mysql_rwlock_unlock(&LOCK_grant);
+ my_error(ER_ACCESS_DENIED_NO_PASSWORD_ERROR, MYF(0),
+ thd->security_ctx->priv_user, thd->security_ctx->priv_host);
+ DBUG_RETURN(TRUE);
+ }
+
+ while ((user= user_list++))
+ {
+ role_as_user= NULL;
+ /* current_role is treated slightly different */
+ if (user->user.str == current_role.str)
+ {
+ /* current_role is NONE */
+ if (!thd->security_ctx->priv_role[0])
+ {
+ my_error(ER_INVALID_ROLE, MYF(0), "NONE");
+ append_str(&wrong_users, STRING_WITH_LEN("NONE"));
+ result= 1;
+ continue;
+ }
+ if (!(role_as_user= find_acl_role(thd->security_ctx->priv_role)))
+ {
+ LEX_STRING ls= { thd->security_ctx->priv_role,
+ strlen(thd->security_ctx->priv_role) };
+ append_user(thd, &wrong_users, &ls, &empty_lex_str);
+ result= 1;
+ continue;
+ }
+
+ /* can not grant current_role to current_role */
+ if (granted_role->user.str == current_role.str)
+ {
+ append_user(thd, &wrong_users, &role_as_user->user, &empty_lex_str);
+ result= 1;
+ continue;
+ }
+ username.str= thd->security_ctx->priv_role;
+ username.length= strlen(username.str);
+ hostname= empty_lex_str;
+ }
+ else if (user->user.str == current_user.str)
+ {
+ username.str= thd->security_ctx->priv_user;
+ username.length= strlen(username.str);
+ hostname.str= thd->security_ctx->priv_host;
+ hostname.length= strlen(hostname.str);
+ }
+ else
+ {
+ username= user->user;
+ if (user->host.str)
+ hostname= user->host;
+ else
+ if ((role_as_user= find_acl_role(user->user.str)))
+ hostname= empty_lex_str;
+ else
+ {
+ if (is_invalid_role_name(username.str))
+ {
+ append_user(thd, &wrong_users, &username, &empty_lex_str);
+ result= 1;
+ continue;
+ }
+ hostname= host_not_specified;
+ }
+ }
+
+ ROLE_GRANT_PAIR *hash_entry= find_role_grant_pair(&username, &hostname,
+ &rolename);
+ ACL_USER_BASE *grantee= role_as_user;
+
+ if (!grantee)
+ grantee= find_user_exact(hostname.str, username.str);
+
+ if (!grantee && !revoke)
+ {
+ LEX_USER user_combo = *user;
+ user_combo.host = hostname;
+ user_combo.user = username;
+
+ /* create the user if it does not exist */
+ if (replace_user_table(thd, tables[1].table, user_combo, 0,
+ false, create_new_user,
+ no_auto_create_user))
+ {
+ append_user(thd, &wrong_users, &username, &hostname);
+ result= 1;
+ continue;
+ }
+ grantee= find_user_exact(hostname.str, username.str);
+
+ /* either replace_user_table failed, or we've added the user */
+ DBUG_ASSERT(grantee);
+ }
+
+ if (!grantee)
+ {
+ append_user(thd, &wrong_users, &username, &hostname);
+ result= 1;
+ continue;
+ }
+
+ if (!revoke)
+ {
+ if (hash_entry)
+ {
+ // perhaps, updating an existing grant, adding WITH ADMIN OPTION
+ }
+ else
+ {
+ add_role_user_mapping(grantee, role);
+
+ /*
+ Check if this grant would cause a cycle. It only needs to be run
+ if we're granting a role to a role
+ */
+ if (role_as_user &&
+ traverse_role_graph_down(role, 0, 0, 0) == ROLE_CYCLE_FOUND)
+ {
+ append_user(thd, &wrong_users, &username, &empty_lex_str);
+ result= 1;
+ undo_add_role_user_mapping(grantee, role);
+ continue;
+ }
+ }
+ }
+ else
+ {
+ /* grant was already removed or never existed */
+ if (!hash_entry)
+ {
+ append_user(thd, &wrong_users, &username, &hostname);
+ result= 1;
+ continue;
+ }
+ if (thd->lex->with_admin_option)
+ {
+ // only revoking an admin option, not the complete grant
+ }
+ else
+ {
+ /* revoke a role grant */
+ remove_role_user_mapping(grantee, role);
+ }
+ }
+
+ /* write into the roles_mapping table */
+ if (replace_roles_mapping_table(tables[0].table,
+ &username, &hostname, &rolename,
+ thd->lex->with_admin_option,
+ hash_entry, revoke))
+ {
+ append_user(thd, &wrong_users, &username, &empty_lex_str);
+ result= 1;
+ if (!revoke)
+ {
+ /* need to remove the mapping added previously */
+ undo_add_role_user_mapping(grantee, role);
+ }
+ else
+ {
+ /* need to restore the mapping deleted previously */
+ add_role_user_mapping(grantee, role);
+ }
+ continue;
+ }
+ update_role_mapping(&username, &hostname, &rolename,
+ thd->lex->with_admin_option, hash_entry, revoke);
+
+ /*
+ Only need to propagate grants when granting/revoking a role to/from
+ a role
+ */
+ if (role_as_user && merge_one_role_privileges(role_as_user) == 0)
+ propagate_role_grants(role_as_user, PRIVS_TO_MERGE::ALL);
+ }
+
+ mysql_mutex_unlock(&acl_cache->lock);
+
+ if (result)
+ my_error(revoke ? ER_CANNOT_REVOKE_ROLE : ER_CANNOT_GRANT_ROLE, MYF(0),
+ rolename.str, wrong_users.c_ptr_safe());
+ else
+ result= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
+
+ mysql_rwlock_unlock(&LOCK_grant);
+
+ DBUG_RETURN(result);
+}
+
bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &list,
ulong rights, bool revoke_grant, bool is_proxy)
@@ -4092,8 +6233,9 @@ bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &list,
char tmp_db[SAFE_NAME_LEN+1];
bool create_new_users=0;
TABLE_LIST tables[2];
- bool save_binlog_row_based;
+ Rpl_filter *rpl_filter;
DBUG_ENTER("mysql_grant");
+
if (!initialized)
{
my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0),
@@ -4126,29 +6268,22 @@ bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &list,
tables[1].init_one_table(C_STRING_WITH_LEN("mysql"),
C_STRING_WITH_LEN("proxies_priv"),
- "proxies_priv",
+ "proxies_priv",
TL_WRITE);
else
tables[1].init_one_table(C_STRING_WITH_LEN("mysql"),
- C_STRING_WITH_LEN("db"),
- "db",
+ C_STRING_WITH_LEN("db"),
+ "db",
TL_WRITE);
tables[0].next_local= tables[0].next_global= tables+1;
- /*
- This statement will be replicated as a statement, even when using
- row-based replication. The flag will be reset at the end of the
- statement.
- */
- if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
- thd->clear_current_stmt_binlog_format_row();
-
#ifdef HAVE_REPLICATION
/*
GRANT and REVOKE are applied the slave in/exclusion rules as they are
some kind of updates to the mysql.% tables.
*/
- if (thd->slave_thread && rpl_filter->is_on())
+ if (thd->slave_thread &&
+ (rpl_filter= thd->system_thread_info.rpl_sql_info->rpl_filter)->is_on())
{
/*
The tables must be marked "updating" so that tables_ok() takes them into
@@ -4156,24 +6291,14 @@ bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &list,
*/
tables[0].updating= tables[1].updating= 1;
if (!(thd->spcont || rpl_filter->tables_ok(0, tables)))
- {
- /* Restore the state of binlog format */
- DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
- if (save_binlog_row_based)
- thd->set_current_stmt_binlog_format_row();
DBUG_RETURN(FALSE);
- }
}
#endif
if (open_and_lock_tables(thd, tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT))
- { // This should never happen
- /* Restore the state of binlog format */
- DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
- if (save_binlog_row_based)
- thd->set_current_stmt_binlog_format_row();
DBUG_RETURN(TRUE); /* purecov: deadcode */
- }
+
+ DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
if (!revoke_grant)
create_new_users= test_if_create_new_users(thd);
@@ -4183,25 +6308,29 @@ bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &list,
mysql_mutex_lock(&acl_cache->lock);
grant_version++;
+ if (proxied_user)
+ {
+ if (!(proxied_user= get_current_user(thd, proxied_user, false)))
+ DBUG_RETURN(TRUE);
+ DBUG_ASSERT(proxied_user->host.length); // not a Role
+ }
+
int result=0;
while ((tmp_Str = str_list++))
{
- if (!(Str= get_current_user(thd, tmp_Str)))
+ if (!(Str= get_current_user(thd, tmp_Str, false)))
{
result= TRUE;
continue;
}
- /*
- No User, but a password?
- They did GRANT ... TO CURRENT_USER() IDENTIFIED BY ... !
- Get the current user, and shallow-copy the new password to them!
- */
- if (!tmp_Str->user.str && tmp_Str->password.str)
- Str->password= tmp_Str->password;
+
+ if (copy_and_check_auth(Str, tmp_Str, thd->lex))
+ result= -1;
+ else
if (replace_user_table(thd, tables[0].table, *Str,
(!db ? rights : 0), revoke_grant, create_new_users,
- test(thd->variables.sql_mode &
- MODE_NO_AUTO_CREATE_USER)))
+ MY_TEST(thd->variables.sql_mode &
+ MODE_NO_AUTO_CREATE_USER)))
result= -1;
else if (db)
{
@@ -4221,10 +6350,14 @@ bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &list,
else if (is_proxy)
{
if (replace_proxies_priv_table (thd, tables[1].table, Str, proxied_user,
- rights & GRANT_ACL ? TRUE : FALSE,
+ rights & GRANT_ACL ? TRUE : FALSE,
revoke_grant))
result= -1;
}
+ if (Str->is_role())
+ propagate_role_grants(find_acl_role(Str->user.str),
+ db ? PRIVS_TO_MERGE::DB : PRIVS_TO_MERGE::GLOBAL,
+ db);
}
mysql_mutex_unlock(&acl_cache->lock);
@@ -4237,10 +6370,6 @@ bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &list,
if (!result)
my_ok(thd);
- /* Restore the state of binlog format */
- DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
- if (save_binlog_row_based)
- thd->set_current_stmt_binlog_format_row();
DBUG_RETURN(result);
}
@@ -4254,7 +6383,7 @@ void grant_free(void)
my_hash_free(&column_priv_hash);
my_hash_free(&proc_priv_hash);
my_hash_free(&func_priv_hash);
- free_root(&memex,MYF(0));
+ free_root(&grant_memroot,MYF(0));
DBUG_VOID_RETURN;
}
@@ -4280,107 +6409,6 @@ my_bool grant_init()
thd->store_globals();
return_val= grant_reload(thd);
delete thd;
- /* Remember that we don't have a THD */
- my_pthread_setspecific_ptr(THR_THD, 0);
- DBUG_RETURN(return_val);
-}
-
-
-/**
- @brief Helper function to grant_reload_procs_priv
-
- Reads the procs_priv table into memory hash.
-
- @param table A pointer to the procs_priv table structure.
-
- @see grant_reload
- @see grant_reload_procs_priv
-
- @return Error state
- @retval TRUE An error occurred
- @retval FALSE Success
-*/
-
-static my_bool grant_load_procs_priv(TABLE *p_table)
-{
- MEM_ROOT *memex_ptr;
- my_bool return_val= 1;
- bool check_no_resolve= specialflag & SPECIAL_NO_RESOLVE;
- MEM_ROOT **save_mem_root_ptr= my_pthread_getspecific_ptr(MEM_ROOT**,
- THR_MALLOC);
- DBUG_ENTER("grant_load_procs_priv");
- (void) my_hash_init(&proc_priv_hash, &my_charset_utf8_bin,
- 0,0,0, (my_hash_get_key) get_grant_table,
- 0,0);
- (void) my_hash_init(&func_priv_hash, &my_charset_utf8_bin,
- 0,0,0, (my_hash_get_key) get_grant_table,
- 0,0);
-
- if (p_table->file->ha_index_init(0, 1))
- DBUG_RETURN(TRUE);
-
- p_table->use_all_columns();
-
- if (!p_table->file->ha_index_first(p_table->record[0]))
- {
- memex_ptr= &memex;
- my_pthread_setspecific_ptr(THR_MALLOC, &memex_ptr);
- do
- {
- GRANT_NAME *mem_check;
- HASH *hash;
- if (!(mem_check=new (memex_ptr) GRANT_NAME(p_table, TRUE)))
- {
- /* This could only happen if we are out memory */
- goto end_unlock;
- }
-
- if (check_no_resolve)
- {
- if (hostname_requires_resolving(mem_check->host.hostname))
- {
- sql_print_warning("'procs_priv' entry '%s %s@%s' "
- "ignored in --skip-name-resolve mode.",
- mem_check->tname, mem_check->user,
- mem_check->host.hostname ?
- mem_check->host.hostname : "");
- continue;
- }
- }
- if (p_table->field[4]->val_int() == TYPE_ENUM_PROCEDURE)
- {
- hash= &proc_priv_hash;
- }
- else
- if (p_table->field[4]->val_int() == TYPE_ENUM_FUNCTION)
- {
- hash= &func_priv_hash;
- }
- else
- {
- sql_print_warning("'procs_priv' entry '%s' "
- "ignored, bad routine type",
- mem_check->tname);
- continue;
- }
-
- mem_check->privs= fix_rights_for_procedure(mem_check->privs);
- if (! mem_check->ok())
- delete mem_check;
- else if (my_hash_insert(hash, (uchar*) mem_check))
- {
- delete mem_check;
- goto end_unlock;
- }
- }
- while (!p_table->file->ha_index_next(p_table->record[0]));
- }
- /* Return ok */
- return_val= 0;
-
-end_unlock:
- p_table->file->ha_index_end();
- my_pthread_setspecific_ptr(THR_MALLOC, save_mem_root_ptr);
DBUG_RETURN(return_val);
}
@@ -4404,7 +6432,7 @@ static my_bool grant_load(THD *thd, TABLE_LIST *tables)
{
MEM_ROOT *memex_ptr;
my_bool return_val= 1;
- TABLE *t_table= 0, *c_table= 0;
+ TABLE *t_table, *c_table, *p_table;
bool check_no_resolve= specialflag & SPECIAL_NO_RESOLVE;
MEM_ROOT **save_mem_root_ptr= my_pthread_getspecific_ptr(MEM_ROOT**,
THR_MALLOC);
@@ -4416,9 +6444,15 @@ static my_bool grant_load(THD *thd, TABLE_LIST *tables)
(void) my_hash_init(&column_priv_hash, &my_charset_utf8_bin,
0,0,0, (my_hash_get_key) get_grant_table,
(my_hash_free_key) free_grant_table,0);
+ (void) my_hash_init(&proc_priv_hash, &my_charset_utf8_bin,
+ 0,0,0, (my_hash_get_key) get_grant_table, 0,0);
+ (void) my_hash_init(&func_priv_hash, &my_charset_utf8_bin,
+ 0,0,0, (my_hash_get_key) get_grant_table, 0,0);
+ init_sql_alloc(&grant_memroot, ACL_ALLOC_BLOCK_SIZE, 0, MYF(0));
- t_table = tables[0].table;
- c_table = tables[1].table;
+ t_table= tables[0].table;
+ c_table= tables[1].table;
+ p_table= tables[2].table; // this can be NULL
if (t_table->file->ha_index_init(0, 1))
goto end_index_init;
@@ -4426,10 +6460,11 @@ static my_bool grant_load(THD *thd, TABLE_LIST *tables)
t_table->use_all_columns();
c_table->use_all_columns();
+ memex_ptr= &grant_memroot;
+ my_pthread_setspecific_ptr(THR_MALLOC, &memex_ptr);
+
if (!t_table->file->ha_index_first(t_table->record[0]))
{
- memex_ptr= &memex;
- my_pthread_setspecific_ptr(THR_MALLOC, &memex_ptr);
do
{
GRANT_TABLE *mem_check;
@@ -4446,9 +6481,8 @@ static my_bool grant_load(THD *thd, TABLE_LIST *tables)
sql_print_warning("'tables_priv' entry '%s %s@%s' "
"ignored in --skip-name-resolve mode.",
mem_check->tname,
- mem_check->user ? mem_check->user : "",
- mem_check->host.hostname ?
- mem_check->host.hostname : "");
+ safe_str(mem_check->user),
+ safe_str(mem_check->host.hostname));
continue;
}
}
@@ -4464,8 +6498,72 @@ static my_bool grant_load(THD *thd, TABLE_LIST *tables)
while (!t_table->file->ha_index_next(t_table->record[0]));
}
- return_val=0; // Return ok
+ return_val= 0;
+
+ if (p_table)
+ {
+ if (p_table->file->ha_index_init(0, 1))
+ goto end_unlock;
+
+ p_table->use_all_columns();
+
+ if (!p_table->file->ha_index_first(p_table->record[0]))
+ {
+ do
+ {
+ GRANT_NAME *mem_check;
+ HASH *hash;
+ if (!(mem_check=new (memex_ptr) GRANT_NAME(p_table, TRUE)))
+ {
+ /* This could only happen if we are out memory */
+ goto end_unlock_p;
+ }
+
+ if (check_no_resolve)
+ {
+ if (hostname_requires_resolving(mem_check->host.hostname))
+ {
+ sql_print_warning("'procs_priv' entry '%s %s@%s' "
+ "ignored in --skip-name-resolve mode.",
+ mem_check->tname, mem_check->user,
+ safe_str(mem_check->host.hostname));
+ continue;
+ }
+ }
+ if (p_table->field[4]->val_int() == TYPE_ENUM_PROCEDURE)
+ {
+ hash= &proc_priv_hash;
+ }
+ else
+ if (p_table->field[4]->val_int() == TYPE_ENUM_FUNCTION)
+ {
+ hash= &func_priv_hash;
+ }
+ else
+ {
+ sql_print_warning("'procs_priv' entry '%s' "
+ "ignored, bad routine type",
+ mem_check->tname);
+ continue;
+ }
+
+ mem_check->privs= fix_rights_for_procedure(mem_check->privs);
+ mem_check->init_privs= mem_check->privs;
+ if (! mem_check->ok())
+ delete mem_check;
+ else if (my_hash_insert(hash, (uchar*) mem_check))
+ {
+ delete mem_check;
+ goto end_unlock_p;
+ }
+ }
+ while (!p_table->file->ha_index_next(p_table->record[0]));
+ }
+ }
+end_unlock_p:
+ if (p_table)
+ p_table->file->ha_index_end();
end_unlock:
t_table->file->ha_index_end();
my_pthread_setspecific_ptr(THR_MALLOC, save_mem_root_ptr);
@@ -4475,56 +6573,16 @@ end_index_init:
}
-/**
- @brief Helper function to grant_reload. Reloads procs_priv table is it
- exists.
-
- @param thd A pointer to the thread handler object.
-
- @see grant_reload
-
- @return Error state
- @retval FALSE Success
- @retval TRUE An error has occurred.
-*/
-
-static my_bool grant_reload_procs_priv(THD *thd)
+my_bool role_propagate_grants_action(void *ptr, void *unused __attribute__((unused)))
{
- HASH old_proc_priv_hash, old_func_priv_hash;
- TABLE_LIST table;
- my_bool return_val= FALSE;
- DBUG_ENTER("grant_reload_procs_priv");
-
- table.init_one_table("mysql", 5, "procs_priv",
- strlen("procs_priv"), "procs_priv",
- TL_READ);
- table.open_type= OT_BASE_ONLY;
-
- if (open_and_lock_tables(thd, &table, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT))
- DBUG_RETURN(TRUE);
-
- mysql_rwlock_wrlock(&LOCK_grant);
- /* Save a copy of the current hash if we need to undo the grant load */
- old_proc_priv_hash= proc_priv_hash;
- old_func_priv_hash= func_priv_hash;
-
- if ((return_val= grant_load_procs_priv(table.table)))
- {
- /* Error; Reverting to old hash */
- DBUG_PRINT("error",("Reverting to old privileges"));
- grant_free();
- proc_priv_hash= old_proc_priv_hash;
- func_priv_hash= old_func_priv_hash;
- }
- else
- {
- my_hash_free(&old_proc_priv_hash);
- my_hash_free(&old_func_priv_hash);
- }
- mysql_rwlock_unlock(&LOCK_grant);
+ ACL_ROLE *role= (ACL_ROLE *)ptr;
+ if (role->counter)
+ return 0;
- close_mysql_tables(thd);
- DBUG_RETURN(return_val);
+ mysql_mutex_assert_owner(&acl_cache->lock);
+ PRIVS_TO_MERGE data= { PRIVS_TO_MERGE::ALL, 0, 0 };
+ traverse_role_graph_up(role, &data, NULL, merge_role_privileges);
+ return 0;
}
@@ -4545,8 +6603,8 @@ static my_bool grant_reload_procs_priv(THD *thd)
my_bool grant_reload(THD *thd)
{
- TABLE_LIST tables[2];
- HASH old_column_priv_hash;
+ TABLE_LIST tables[3];
+ HASH old_column_priv_hash, old_proc_priv_hash, old_func_priv_hash;
MEM_ROOT old_mem;
my_bool return_val= 1;
DBUG_ENTER("grant_reload");
@@ -4561,8 +6619,13 @@ my_bool grant_reload(THD *thd)
tables[1].init_one_table(C_STRING_WITH_LEN("mysql"),
C_STRING_WITH_LEN("columns_priv"),
"columns_priv", TL_READ);
+ tables[2].init_one_table(C_STRING_WITH_LEN("mysql"),
+ C_STRING_WITH_LEN("procs_priv"),
+ "procs_priv", TL_READ);
tables[0].next_local= tables[0].next_global= tables+1;
- tables[0].open_type= tables[1].open_type= OT_BASE_ONLY;
+ tables[1].next_local= tables[1].next_global= tables+2;
+ tables[0].open_type= tables[1].open_type= tables[2].open_type= OT_BASE_ONLY;
+ tables[2].open_strategy= TABLE_LIST::OPEN_IF_EXISTS;
/*
To avoid deadlocks we should obtain table locks before
@@ -4572,41 +6635,42 @@ my_bool grant_reload(THD *thd)
goto end;
mysql_rwlock_wrlock(&LOCK_grant);
+ grant_version++;
old_column_priv_hash= column_priv_hash;
+ old_proc_priv_hash= proc_priv_hash;
+ old_func_priv_hash= func_priv_hash;
/*
Create a new memory pool but save the current memory pool to make an undo
opertion possible in case of failure.
*/
- old_mem= memex;
- init_sql_alloc(&memex, ACL_ALLOC_BLOCK_SIZE, 0);
+ old_mem= grant_memroot;
if ((return_val= grant_load(thd, tables)))
{ // Error. Revert to old hash
DBUG_PRINT("error",("Reverting to old privileges"));
grant_free(); /* purecov: deadcode */
column_priv_hash= old_column_priv_hash; /* purecov: deadcode */
- memex= old_mem; /* purecov: deadcode */
+ proc_priv_hash= old_proc_priv_hash;
+ func_priv_hash= old_func_priv_hash;
+ grant_memroot= old_mem; /* purecov: deadcode */
}
else
{
my_hash_free(&old_column_priv_hash);
+ my_hash_free(&old_proc_priv_hash);
+ my_hash_free(&old_func_priv_hash);
free_root(&old_mem,MYF(0));
}
- mysql_rwlock_unlock(&LOCK_grant);
- close_mysql_tables(thd);
- /*
- It is OK failing to load procs_priv table because we may be
- working with 4.1 privilege tables.
- */
- if (grant_reload_procs_priv(thd))
- return_val= 1;
+ mysql_mutex_lock(&acl_cache->lock);
+ my_hash_iterate(&acl_roles, role_propagate_grants_action, NULL);
+ mysql_mutex_unlock(&acl_cache->lock);
- mysql_rwlock_wrlock(&LOCK_grant);
- grant_version++;
mysql_rwlock_unlock(&LOCK_grant);
+ close_mysql_tables(thd);
+
end:
DBUG_RETURN(return_val);
}
@@ -4637,12 +6701,16 @@ end:
@see check_access
@see check_table_access
- @note This functions assumes that either number of tables to be inspected
+ @note
+ This functions assumes that either number of tables to be inspected
by it is limited explicitly (i.e. is is not UINT_MAX) or table list
used and thd->lex->query_tables_own_last value correspond to each
other (the latter should be either 0 or point to next_global member
of one of elements of this table list).
+ We delay locking of LOCK_grant until we really need it as we assume that
+ most privileges be resolved with user or db level accesses.
+
@return Access status
@retval FALSE Access granted; But column privileges might need to be
checked.
@@ -4659,6 +6727,9 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables,
Security_context *sctx= thd->security_ctx;
uint i;
ulong orig_want_access= want_access;
+ my_bool locked= 0;
+ GRANT_TABLE *grant_table;
+ GRANT_TABLE *grant_table_role= NULL;
DBUG_ENTER("check_grant");
DBUG_ASSERT(number > 0);
@@ -4682,16 +6753,13 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables,
*/
tl->grant.orig_want_privilege= (want_access & ~SHOW_VIEW_ACL);
}
+ number= i;
- mysql_rwlock_rdlock(&LOCK_grant);
- for (tl= tables;
- tl && number-- && tl != first_not_own_table;
- tl= tl->next_global)
+ for (tl= tables; number-- ; tl= tl->next_global)
{
TABLE_LIST *const t_ref=
tl->correspondent_table ? tl->correspondent_table : tl;
- sctx = test(t_ref->security_ctx) ? t_ref->security_ctx :
- thd->security_ctx;
+ sctx= t_ref->security_ctx ? t_ref->security_ctx : thd->security_ctx;
const ACL_internal_table_access *access=
get_cached_table_access(&t_ref->grant.m_internal,
@@ -4742,15 +6810,40 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables,
}
continue;
}
- GRANT_TABLE *grant_table= table_hash_search(sctx->host, sctx->ip,
- t_ref->get_db_name(),
- sctx->priv_user,
- t_ref->get_table_name(),
- FALSE);
- if (!grant_table)
+ if (is_temporary_table(t_ref))
{
- want_access &= ~t_ref->grant.privilege;
+ /*
+ If this table list element corresponds to a pre-opened temporary
+ table skip checking of all relevant table-level privileges for it.
+ Note that during creation of temporary table we still need to check
+ if user has CREATE_TMP_ACL.
+ */
+ t_ref->grant.privilege|= TMP_TABLE_ACLS;
+ t_ref->grant.want_privilege= 0;
+ continue;
+ }
+
+ if (!locked)
+ {
+ locked= 1;
+ mysql_rwlock_rdlock(&LOCK_grant);
+ }
+
+ grant_table= table_hash_search(sctx->host, sctx->ip,
+ t_ref->get_db_name(),
+ sctx->priv_user,
+ t_ref->get_table_name(),
+ FALSE);
+ if (sctx->priv_role[0])
+ grant_table_role= table_hash_search("", NULL, t_ref->get_db_name(),
+ sctx->priv_role,
+ t_ref->get_table_name(),
+ TRUE);
+
+ if (!grant_table && !grant_table_role)
+ {
+ want_access&= ~t_ref->grant.privilege;
goto err; // No grants
}
@@ -4761,25 +6854,30 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables,
if (any_combination_will_do)
continue;
- t_ref->grant.grant_table= grant_table; // Remember for column test
+ t_ref->grant.grant_table_user= grant_table; // Remember for column test
+ t_ref->grant.grant_table_role= grant_table_role;
t_ref->grant.version= grant_version;
- t_ref->grant.privilege|= grant_table->privs;
+ t_ref->grant.privilege|= grant_table ? grant_table->privs : 0;
+ t_ref->grant.privilege|= grant_table_role ? grant_table_role->privs : 0;
t_ref->grant.want_privilege= ((want_access & COL_ACLS) & ~t_ref->grant.privilege);
if (!(~t_ref->grant.privilege & want_access))
continue;
- if (want_access & ~(grant_table->cols | t_ref->grant.privilege))
+ if ((want_access&= ~((grant_table ? grant_table->cols : 0) |
+ (grant_table_role ? grant_table_role->cols : 0) |
+ t_ref->grant.privilege)))
{
- want_access &= ~(grant_table->cols | t_ref->grant.privilege);
- goto err; // impossible
+ goto err; // impossible
}
}
- mysql_rwlock_unlock(&LOCK_grant);
+ if (locked)
+ mysql_rwlock_unlock(&LOCK_grant);
DBUG_RETURN(FALSE);
err:
- mysql_rwlock_unlock(&LOCK_grant);
+ if (locked)
+ mysql_rwlock_unlock(&LOCK_grant);
if (!no_errors) // Not a silent skip of table
{
char command[128];
@@ -4819,6 +6917,7 @@ bool check_grant_column(THD *thd, GRANT_INFO *grant,
const char *name, uint length, Security_context *sctx)
{
GRANT_TABLE *grant_table;
+ GRANT_TABLE *grant_table_role;
GRANT_COLUMN *grant_column;
ulong want_access= grant->want_privilege & ~grant->privilege;
DBUG_ENTER("check_grant_column");
@@ -4833,17 +6932,40 @@ bool check_grant_column(THD *thd, GRANT_INFO *grant,
if (grant->version != grant_version)
{
- grant->grant_table=
+ grant->grant_table_user=
table_hash_search(sctx->host, sctx->ip, db_name,
sctx->priv_user,
table_name, 0); /* purecov: inspected */
+ grant->grant_table_role=
+ sctx->priv_role[0] ? table_hash_search("", NULL, db_name,
+ sctx->priv_role,
+ table_name, TRUE) : NULL;
grant->version= grant_version; /* purecov: inspected */
}
- if (!(grant_table= grant->grant_table))
- goto err; /* purecov: deadcode */
- grant_column=column_hash_search(grant_table, name, length);
- if (grant_column && !(~grant_column->rights & want_access))
+ grant_table= grant->grant_table_user;
+ grant_table_role= grant->grant_table_role;
+
+ if (!grant_table && !grant_table_role)
+ goto err;
+
+ if (grant_table)
+ {
+ grant_column= column_hash_search(grant_table, name, length);
+ if (grant_column)
+ {
+ want_access&= ~grant_column->rights;
+ }
+ }
+ if (grant_table_role)
+ {
+ grant_column= column_hash_search(grant_table_role, name, length);
+ if (grant_column)
+ {
+ want_access&= ~grant_column->rights;
+ }
+ }
+ if (!want_access)
{
mysql_rwlock_unlock(&LOCK_grant);
DBUG_RETURN(0);
@@ -4853,6 +6975,7 @@ err:
mysql_rwlock_unlock(&LOCK_grant);
char command[128];
get_privilege_desc(command, sizeof(command), want_access);
+ /* TODO perhaps error should print current rolename aswell */
my_error(ER_COLUMNACCESS_DENIED_ERROR, MYF(0),
command,
sctx->priv_user,
@@ -4891,7 +7014,7 @@ bool check_column_grant_in_table_ref(THD *thd, TABLE_LIST * table_ref,
GRANT_INFO *grant;
const char *db_name;
const char *table_name;
- Security_context *sctx= test(table_ref->security_ctx) ?
+ Security_context *sctx= MY_TEST(table_ref->security_ctx) ?
table_ref->security_ctx : thd->security_ctx;
if (table_ref->view || table_ref->field_translation)
@@ -4901,7 +7024,7 @@ bool check_column_grant_in_table_ref(THD *thd, TABLE_LIST * table_ref,
grant= &(table_ref->grant);
db_name= table_ref->view_db.str;
table_name= table_ref->view_name.str;
- if (table_ref->belong_to_view &&
+ if (table_ref->belong_to_view &&
thd->lex->sql_command == SQLCOM_SHOW_FIELDS)
{
view_privs= get_column_grant(thd, grant, db_name, table_name, name);
@@ -4933,7 +7056,7 @@ bool check_column_grant_in_table_ref(THD *thd, TABLE_LIST * table_ref,
}
-/**
+/**
@brief check if a query can access a set of columns
@param thd the current thread
@@ -4942,24 +7065,23 @@ bool check_column_grant_in_table_ref(THD *thd, TABLE_LIST * table_ref,
@return Operation status
@retval 0 Success
@retval 1 Falure
- @details This function walks over the columns of a table reference
+ @details This function walks over the columns of a table reference
The columns may originate from different tables, depending on the kind of
table reference, e.g. join, view.
For each table it will retrieve the grant information and will use it
to check the required access privileges for the fields requested from it.
-*/
-bool check_grant_all_columns(THD *thd, ulong want_access_arg,
+*/
+bool check_grant_all_columns(THD *thd, ulong want_access_arg,
Field_iterator_table_ref *fields)
{
Security_context *sctx= thd->security_ctx;
- ulong want_access= want_access_arg;
+ ulong UNINIT_VAR(want_access);
const char *table_name= NULL;
-
- const char* db_name;
+ const char* db_name;
GRANT_INFO *grant;
- /* Initialized only to make gcc happy */
- GRANT_TABLE *grant_table= NULL;
- /*
+ GRANT_TABLE *UNINIT_VAR(grant_table);
+ GRANT_TABLE *UNINIT_VAR(grant_table_role);
+ /*
Flag that gets set if privilege checking has to be performed on column
level.
*/
@@ -4983,26 +7105,46 @@ bool check_grant_all_columns(THD *thd, ulong want_access_arg,
/* reload table if someone has modified any grants */
if (grant->version != grant_version)
{
- grant->grant_table=
+ grant->grant_table_user=
table_hash_search(sctx->host, sctx->ip, db_name,
sctx->priv_user,
table_name, 0); /* purecov: inspected */
+ grant->grant_table_role=
+ sctx->priv_role[0] ? table_hash_search("", NULL, db_name,
+ sctx->priv_role,
+ table_name, TRUE) : NULL;
grant->version= grant_version; /* purecov: inspected */
}
- grant_table= grant->grant_table;
- DBUG_ASSERT (grant_table);
+ grant_table= grant->grant_table_user;
+ grant_table_role= grant->grant_table_role;
+ DBUG_ASSERT (grant_table || grant_table_role);
}
}
if (want_access)
{
- GRANT_COLUMN *grant_column=
- column_hash_search(grant_table, field_name,
- (uint) strlen(field_name));
- if (grant_column)
+ ulong have_access= 0;
+ if (grant_table)
+ {
+ GRANT_COLUMN *grant_column=
+ column_hash_search(grant_table, field_name,
+ (uint) strlen(field_name));
+ if (grant_column)
+ have_access= grant_column->rights;
+ }
+ if (grant_table_role)
+ {
+ GRANT_COLUMN *grant_column=
+ column_hash_search(grant_table_role, field_name,
+ (uint) strlen(field_name));
+ if (grant_column)
+ have_access|= grant_column->rights;
+ }
+
+ if (have_access)
using_column_privileges= TRUE;
- if (!grant_column || (~grant_column->rights & want_access))
+ if (want_access & ~have_access)
goto err;
}
}
@@ -5021,7 +7163,7 @@ err:
if (using_column_privileges)
my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0),
command, sctx->priv_user,
- sctx->host_or_ip, table_name);
+ sctx->host_or_ip, table_name);
else
my_error(ER_COLUMNACCESS_DENIED_ERROR, MYF(0),
command,
@@ -5047,6 +7189,12 @@ static bool check_grant_db_routine(THD *thd, const char *db, HASH *hash)
{
return FALSE;
}
+ if (sctx->priv_role[0] && strcmp(item->user, sctx->priv_role) == 0 &&
+ strcmp(item->db, db) == 0 &&
+ (!item->host.hostname || !item->host.hostname[0]))
+ {
+ return FALSE; /* Found current role match */
+ }
}
return TRUE;
@@ -5059,21 +7207,40 @@ static bool check_grant_db_routine(THD *thd, const char *db, HASH *hash)
Return 1 if access is denied
*/
-bool check_grant_db(THD *thd,const char *db)
+bool check_grant_db(THD *thd, const char *db)
{
Security_context *sctx= thd->security_ctx;
char helping [SAFE_NAME_LEN + USERNAME_LENGTH+2], *end;
- uint len;
+ char helping2 [SAFE_NAME_LEN + USERNAME_LENGTH+2], *tmp_db;
+ uint len, UNINIT_VAR(len2);
bool error= TRUE;
- end= strmov(helping, sctx->priv_user) + 1;
- end= strnmov(end, db, helping + sizeof(helping) - end);
+ tmp_db= strmov(helping, sctx->priv_user) + 1;
+ end= strnmov(tmp_db, db, helping + sizeof(helping) - tmp_db);
if (end >= helping + sizeof(helping)) // db name was truncated
return 1; // no privileges for an invalid db name
+ if (lower_case_table_names)
+ {
+ end = tmp_db + my_casedn_str(files_charset_info, tmp_db);
+ db=tmp_db;
+ }
+
len= (uint) (end - helping) + 1;
+ /*
+ If a role is set, we need to check for privileges
+ here aswell
+ */
+ if (sctx->priv_role[0])
+ {
+ end= strmov(helping2, sctx->priv_role) + 1;
+ end= strnmov(end, db, helping2 + sizeof(helping2) - end);
+ len2= (uint) (end - helping2) + 1;
+ }
+
+
mysql_rwlock_rdlock(&LOCK_grant);
for (uint idx=0 ; idx < column_priv_hash.records ; idx++)
@@ -5088,6 +7255,14 @@ bool check_grant_db(THD *thd,const char *db)
error= FALSE; /* Found match. */
break;
}
+ if (sctx->priv_role[0] &&
+ len2 < grant_table->key_length &&
+ !memcmp(grant_table->hash_key,helping2,len) &&
+ (!grant_table->host.hostname || !grant_table->host.hostname[0]))
+ {
+ error= FALSE; /* Found role match */
+ break;
+ }
}
if (error)
@@ -5124,6 +7299,7 @@ bool check_grant_routine(THD *thd, ulong want_access,
Security_context *sctx= thd->security_ctx;
char *user= sctx->priv_user;
char *host= sctx->priv_host;
+ char *role= sctx->priv_role;
DBUG_ENTER("check_grant_routine");
want_access&= ~sctx->master_access;
@@ -5137,6 +7313,12 @@ bool check_grant_routine(THD *thd, ulong want_access,
if ((grant_proc= routine_hash_search(host, sctx->ip, table->db, user,
table->table_name, is_proc, 0)))
table->grant.privilege|= grant_proc->privs;
+ if (role[0]) /* current role set check */
+ {
+ if ((grant_proc= routine_hash_search("", NULL, table->db, role,
+ table->table_name, is_proc, 0)))
+ table->grant.privilege|= grant_proc->privs;
+ }
if (want_access & ~table->grant.privilege)
{
@@ -5168,9 +7350,9 @@ err:
/*
- Check if routine has any of the
+ Check if routine has any of the
routine level grants
-
+
SYNPOSIS
bool check_routine_level_acl()
thd Thread handler
@@ -5178,11 +7360,11 @@ err:
name Routine name
RETURN
- 0 Ok
+ 0 Ok
1 error
*/
-bool check_routine_level_acl(THD *thd, const char *db, const char *name,
+bool check_routine_level_acl(THD *thd, const char *db, const char *name,
bool is_proc)
{
bool no_routine_acl= 1;
@@ -5194,6 +7376,15 @@ bool check_routine_level_acl(THD *thd, const char *db, const char *name,
sctx->priv_user,
name, is_proc, 0)))
no_routine_acl= !(grant_proc->privs & SHOW_PROC_ACLS);
+
+ if (no_routine_acl && sctx->priv_role[0]) /* current set role check */
+ {
+ if ((grant_proc= routine_hash_search("",
+ NULL, db,
+ sctx->priv_role,
+ name, is_proc, 0)))
+ no_routine_acl= !(grant_proc->privs & SHOW_PROC_ACLS);
+ }
mysql_rwlock_unlock(&LOCK_grant);
return no_routine_acl;
}
@@ -5209,18 +7400,26 @@ ulong get_table_grant(THD *thd, TABLE_LIST *table)
Security_context *sctx= thd->security_ctx;
const char *db = table->db ? table->db : thd->db;
GRANT_TABLE *grant_table;
+ GRANT_TABLE *grant_table_role= NULL;
mysql_rwlock_rdlock(&LOCK_grant);
#ifdef EMBEDDED_LIBRARY
grant_table= NULL;
+ grant_table_role= NULL;
#else
grant_table= table_hash_search(sctx->host, sctx->ip, db, sctx->priv_user,
table->table_name, 0);
+ if (sctx->priv_role[0])
+ grant_table_role= table_hash_search("", "", db, sctx->priv_role,
+ table->table_name, 0);
#endif
- table->grant.grant_table=grant_table; // Remember for column test
+ table->grant.grant_table_user= grant_table; // Remember for column test
+ table->grant.grant_table_role= grant_table_role;
table->grant.version=grant_version;
if (grant_table)
table->grant.privilege|= grant_table->privs;
+ if (grant_table_role)
+ table->grant.privilege|= grant_table_role->privs;
privilege= table->grant.privilege;
mysql_rwlock_unlock(&LOCK_grant);
return privilege;
@@ -5250,31 +7449,52 @@ ulong get_column_grant(THD *thd, GRANT_INFO *grant,
const char *field_name)
{
GRANT_TABLE *grant_table;
+ GRANT_TABLE *grant_table_role;
GRANT_COLUMN *grant_column;
- ulong priv;
+ ulong priv= 0;
mysql_rwlock_rdlock(&LOCK_grant);
/* reload table if someone has modified any grants */
if (grant->version != grant_version)
{
Security_context *sctx= thd->security_ctx;
- grant->grant_table=
+ grant->grant_table_user=
table_hash_search(sctx->host, sctx->ip,
db_name, sctx->priv_user,
- table_name, 0); /* purecov: inspected */
+ table_name, 0); /* purecov: inspected */
+ grant->grant_table_role=
+ sctx->priv_role[0] ? table_hash_search("", "", db_name,
+ sctx->priv_role,
+ table_name, TRUE) : NULL;
grant->version= grant_version; /* purecov: inspected */
}
- if (!(grant_table= grant->grant_table))
+ grant_table= grant->grant_table_user;
+ grant_table_role= grant->grant_table_role;
+
+ if (!grant_table && !grant_table_role)
priv= grant->privilege;
else
{
- grant_column= column_hash_search(grant_table, field_name,
- (uint) strlen(field_name));
- if (!grant_column)
- priv= (grant->privilege | grant_table->privs);
- else
- priv= (grant->privilege | grant_table->privs | grant_column->rights);
+ if (grant_table)
+ {
+ grant_column= column_hash_search(grant_table, field_name,
+ (uint) strlen(field_name));
+ if (!grant_column)
+ priv= (grant->privilege | grant_table->privs);
+ else
+ priv= (grant->privilege | grant_table->privs | grant_column->rights);
+ }
+
+ if (grant_table_role)
+ {
+ grant_column= column_hash_search(grant_table_role, field_name,
+ (uint) strlen(field_name));
+ if (!grant_column)
+ priv|= (grant->privilege | grant_table_role->privs);
+ else
+ priv|= (grant->privilege | grant_table->privs | grant_column->rights);
+ }
}
mysql_rwlock_unlock(&LOCK_grant);
return priv;
@@ -5314,9 +7534,43 @@ static uint command_lengths[]=
};
-static int show_routine_grants(THD *thd, LEX_USER *lex_user, HASH *hash,
- const char *type, int typelen,
- char *buff, int buffsize);
+static bool print_grants_for_role(THD *thd, ACL_ROLE * role)
+{
+ char buff[1024];
+
+ if (show_role_grants(thd, role->user.str, "", role, buff, sizeof(buff)))
+ return TRUE;
+
+ if (show_global_privileges(thd, role, TRUE, buff, sizeof(buff)))
+ return TRUE;
+
+ if (show_database_privileges(thd, role->user.str, "", buff, sizeof(buff)))
+ return TRUE;
+
+ if (show_table_and_column_privileges(thd, role->user.str, "", buff, sizeof(buff)))
+ return TRUE;
+
+ if (show_routine_grants(thd, role->user.str, "", &proc_priv_hash,
+ STRING_WITH_LEN("PROCEDURE"), buff, sizeof(buff)))
+ return TRUE;
+
+ if (show_routine_grants(thd, role->user.str, "", &func_priv_hash,
+ STRING_WITH_LEN("FUNCTION"), buff, sizeof(buff)))
+ return TRUE;
+
+ return FALSE;
+
+}
+
+
+static int show_grants_callback(ACL_USER_BASE *role, void *data)
+{
+ THD *thd= (THD *)data;
+ DBUG_ASSERT(role->flags & IS_ROLE);
+ if (print_grants_for_role(thd, (ACL_ROLE *)role))
+ return -1;
+ return 0;
+}
/*
@@ -5326,18 +7580,18 @@ static int show_routine_grants(THD *thd, LEX_USER *lex_user, HASH *hash,
Send to client grant-like strings depicting user@host privileges
*/
-bool mysql_show_grants(THD *thd,LEX_USER *lex_user)
+bool mysql_show_grants(THD *thd, LEX_USER *lex_user)
{
- ulong want_access;
- uint counter,index;
- int error = 0;
- ACL_USER *acl_user;
- ACL_DB *acl_db;
+ int error = -1;
+ ACL_USER *UNINIT_VAR(acl_user);
+ ACL_ROLE *acl_role= NULL;
char buff[1024];
Protocol *protocol= thd->protocol;
+ char *username= NULL;
+ char *hostname= NULL;
+ char *rolename= NULL;
DBUG_ENTER("mysql_show_grants");
- LINT_INIT(acl_user);
if (!initialized)
{
my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
@@ -5347,26 +7601,54 @@ bool mysql_show_grants(THD *thd,LEX_USER *lex_user)
mysql_rwlock_rdlock(&LOCK_grant);
mysql_mutex_lock(&acl_cache->lock);
- acl_user= find_acl_user(lex_user->host.str, lex_user->user.str, TRUE);
- if (!acl_user)
+ if (lex_user->user.str == current_user.str)
{
- mysql_mutex_unlock(&acl_cache->lock);
- mysql_rwlock_unlock(&LOCK_grant);
+ username= thd->security_ctx->priv_user;
+ hostname= thd->security_ctx->priv_host;
+ }
+ else if (lex_user->user.str == current_role.str)
+ {
+ rolename= thd->security_ctx->priv_role;
+ }
+ else if (lex_user->user.str == current_user_and_current_role.str)
+ {
+ username= thd->security_ctx->priv_user;
+ hostname= thd->security_ctx->priv_host;
+ rolename= thd->security_ctx->priv_role;
+ }
+ else
+ {
+ lex_user= get_current_user(thd, lex_user, false);
+ if (!lex_user)
+ {
+ mysql_mutex_unlock(&acl_cache->lock);
+ mysql_rwlock_unlock(&LOCK_grant);
+ DBUG_RETURN(TRUE);
+ }
- my_error(ER_NONEXISTING_GRANT, MYF(0),
- lex_user->user.str, lex_user->host.str);
- DBUG_RETURN(TRUE);
+ if (lex_user->is_role())
+ {
+ rolename= lex_user->user.str;
+ }
+ else
+ {
+ username= lex_user->user.str;
+ hostname= lex_user->host.str;
+ }
}
+ DBUG_ASSERT(rolename || username);
- Item_string *field=new Item_string("",0,&my_charset_latin1);
+ Item_string *field=new Item_string_ascii("", 0);
List<Item> field_list;
field->name=buff;
field->max_length=1024;
- strxmov(buff,"Grants for ",lex_user->user.str,"@",
- lex_user->host.str,NullS);
+ if (!username)
+ strxmov(buff,"Grants for ",rolename, NullS);
+ else
+ strxmov(buff,"Grants for ",username,"@",hostname, NullS);
field_list.push_back(field);
if (protocol->send_result_set_metadata(&field_list,
- Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
+ Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
{
mysql_mutex_unlock(&acl_cache->lock);
mysql_rwlock_unlock(&LOCK_grant);
@@ -5374,39 +7656,188 @@ bool mysql_show_grants(THD *thd,LEX_USER *lex_user)
DBUG_RETURN(TRUE);
}
- /* Add first global access grants */
+ if (username)
{
- String global(buff,sizeof(buff),system_charset_info);
- global.length(0);
- global.append(STRING_WITH_LEN("GRANT "));
+ acl_user= find_user_exact(hostname, username);
+ if (!acl_user)
+ {
+ mysql_mutex_unlock(&acl_cache->lock);
+ mysql_rwlock_unlock(&LOCK_grant);
- want_access= acl_user->access;
- if (test_all_bits(want_access, (GLOBAL_ACLS & ~ GRANT_ACL)))
- global.append(STRING_WITH_LEN("ALL PRIVILEGES"));
- else if (!(want_access & ~GRANT_ACL))
- global.append(STRING_WITH_LEN("USAGE"));
+ my_error(ER_NONEXISTING_GRANT, MYF(0),
+ username, hostname);
+ DBUG_RETURN(TRUE);
+ }
+
+ /* Show granted roles to acl_user */
+ if (show_role_grants(thd, username, hostname, acl_user, buff, sizeof(buff)))
+ goto end;
+
+ /* Add first global access grants */
+ if (show_global_privileges(thd, acl_user, FALSE, buff, sizeof(buff)))
+ goto end;
+
+ /* Add database access */
+ if (show_database_privileges(thd, username, hostname, buff, sizeof(buff)))
+ goto end;
+
+ /* Add table & column access */
+ if (show_table_and_column_privileges(thd, username, hostname, buff, sizeof(buff)))
+ goto end;
+
+ if (show_routine_grants(thd, username, hostname, &proc_priv_hash,
+ STRING_WITH_LEN("PROCEDURE"), buff, sizeof(buff)))
+ goto end;
+
+ if (show_routine_grants(thd, username, hostname, &func_priv_hash,
+ STRING_WITH_LEN("FUNCTION"), buff, sizeof(buff)))
+ goto end;
+
+ if (show_proxy_grants(thd, username, hostname, buff, sizeof(buff)))
+ goto end;
+ }
+
+ if (rolename)
+ {
+ acl_role= find_acl_role(rolename);
+ if (acl_role)
+ {
+ /* get a list of all inherited roles */
+ traverse_role_graph_down(acl_role, thd, show_grants_callback, NULL);
+ }
else
{
- bool found=0;
- ulong j,test_access= want_access & ~GRANT_ACL;
- for (counter=0, j = SELECT_ACL;j <= GLOBAL_ACLS;counter++,j <<= 1)
+ if (lex_user->user.str == current_role.str)
{
- if (test_access & j)
- {
- if (found)
- global.append(STRING_WITH_LEN(", "));
- found=1;
- global.append(command_array[counter],command_lengths[counter]);
- }
+ mysql_mutex_unlock(&acl_cache->lock);
+ mysql_rwlock_unlock(&LOCK_grant);
+ my_error(ER_NONEXISTING_GRANT, MYF(0),
+ thd->security_ctx->priv_user,
+ thd->security_ctx->priv_host);
+ DBUG_RETURN(TRUE);
+ }
+ }
+ }
+
+ error= 0;
+end:
+ mysql_mutex_unlock(&acl_cache->lock);
+ mysql_rwlock_unlock(&LOCK_grant);
+
+ my_eof(thd);
+ DBUG_RETURN(error);
+}
+
+static ROLE_GRANT_PAIR *find_role_grant_pair(const LEX_STRING *u,
+ const LEX_STRING *h,
+ const LEX_STRING *r)
+{
+ char buf[1024];
+ String pair_key(buf, sizeof(buf), &my_charset_bin);
+
+ size_t key_length= u->length + h->length + r->length + 3;
+ pair_key.alloc(key_length);
+
+ strmov(strmov(strmov(const_cast<char*>(pair_key.ptr()),
+ safe_str(u->str)) + 1, h->str) + 1, r->str);
+
+ return (ROLE_GRANT_PAIR *)
+ my_hash_search(&acl_roles_mappings, (uchar*)pair_key.ptr(), key_length);
+}
+
+static bool show_role_grants(THD *thd, const char *username,
+ const char *hostname, ACL_USER_BASE *acl_entry,
+ char *buff, size_t buffsize)
+{
+ uint counter;
+ Protocol *protocol= thd->protocol;
+ LEX_STRING host= {const_cast<char*>(hostname), strlen(hostname)};
+
+ String grant(buff,sizeof(buff),system_charset_info);
+ for (counter= 0; counter < acl_entry->role_grants.elements; counter++)
+ {
+ grant.length(0);
+ grant.append(STRING_WITH_LEN("GRANT "));
+ ACL_ROLE *acl_role= *(dynamic_element(&acl_entry->role_grants, counter,
+ ACL_ROLE**));
+ grant.append(acl_role->user.str, acl_role->user.length,
+ system_charset_info);
+ grant.append(STRING_WITH_LEN(" TO '"));
+ grant.append(acl_entry->user.str, acl_entry->user.length,
+ system_charset_info);
+ if (!(acl_entry->flags & IS_ROLE))
+ {
+ grant.append(STRING_WITH_LEN("'@'"));
+ grant.append(&host);
+ }
+ grant.append('\'');
+
+ ROLE_GRANT_PAIR *pair=
+ find_role_grant_pair(&acl_entry->user, &host, &acl_role->user);
+ DBUG_ASSERT(pair);
+
+ if (pair->with_admin)
+ grant.append(STRING_WITH_LEN(" WITH ADMIN OPTION"));
+
+ protocol->prepare_for_resend();
+ protocol->store(grant.ptr(),grant.length(),grant.charset());
+ if (protocol->write())
+ {
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+static bool show_global_privileges(THD *thd, ACL_USER_BASE *acl_entry,
+ bool handle_as_role,
+ char *buff, size_t buffsize)
+{
+ uint counter;
+ ulong want_access;
+ Protocol *protocol= thd->protocol;
+
+ String global(buff,sizeof(buff),system_charset_info);
+ global.length(0);
+ global.append(STRING_WITH_LEN("GRANT "));
+
+ if (handle_as_role)
+ want_access= ((ACL_ROLE *)acl_entry)->initial_role_access;
+ else
+ want_access= acl_entry->access;
+ if (test_all_bits(want_access, (GLOBAL_ACLS & ~ GRANT_ACL)))
+ global.append(STRING_WITH_LEN("ALL PRIVILEGES"));
+ else if (!(want_access & ~GRANT_ACL))
+ global.append(STRING_WITH_LEN("USAGE"));
+ else
+ {
+ bool found=0;
+ ulong j,test_access= want_access & ~GRANT_ACL;
+ for (counter=0, j = SELECT_ACL;j <= GLOBAL_ACLS;counter++,j <<= 1)
+ {
+ if (test_access & j)
+ {
+ if (found)
+ global.append(STRING_WITH_LEN(", "));
+ found=1;
+ global.append(command_array[counter],command_lengths[counter]);
}
}
- global.append (STRING_WITH_LEN(" ON *.* TO '"));
- global.append(lex_user->user.str, lex_user->user.length,
- system_charset_info);
- global.append (STRING_WITH_LEN("'@'"));
- global.append(lex_user->host.str,lex_user->host.length,
- system_charset_info);
+ }
+ global.append (STRING_WITH_LEN(" ON *.* TO '"));
+ global.append(acl_entry->user.str, acl_entry->user.length,
+ system_charset_info);
+ global.append('\'');
+
+ if (!handle_as_role)
+ {
+ ACL_USER *acl_user= (ACL_USER *)acl_entry;
+
+ global.append (STRING_WITH_LEN("@'"));
+ global.append(acl_user->host.hostname, acl_user->hostname_length,
+ system_charset_info);
global.append ('\'');
+
if (acl_user->plugin.str == native_password_plugin_name.str ||
acl_user->plugin.str == old_password_plugin_name.str)
{
@@ -5420,14 +7851,14 @@ bool mysql_show_grants(THD *thd,LEX_USER *lex_user)
}
else
{
- global.append(STRING_WITH_LEN(" IDENTIFIED VIA "));
- global.append(acl_user->plugin.str, acl_user->plugin.length);
- if (acl_user->auth_string.length)
- {
- global.append(STRING_WITH_LEN(" USING '"));
- global.append(acl_user->auth_string.str, acl_user->auth_string.length);
- global.append('\'');
- }
+ global.append(STRING_WITH_LEN(" IDENTIFIED VIA "));
+ global.append(acl_user->plugin.str, acl_user->plugin.length);
+ if (acl_user->auth_string.length)
+ {
+ global.append(STRING_WITH_LEN(" USING '"));
+ global.append(acl_user->auth_string.str, acl_user->auth_string.length);
+ global.append('\'');
+ }
}
/* "show grants" SSL related stuff */
if (acl_user->ssl_type == SSL_TYPE_ANY)
@@ -5440,67 +7871,75 @@ bool mysql_show_grants(THD *thd,LEX_USER *lex_user)
global.append(STRING_WITH_LEN(" REQUIRE "));
if (acl_user->x509_issuer)
{
- ssl_options++;
- global.append(STRING_WITH_LEN("ISSUER \'"));
- global.append(acl_user->x509_issuer,strlen(acl_user->x509_issuer));
- global.append('\'');
+ ssl_options++;
+ global.append(STRING_WITH_LEN("ISSUER \'"));
+ global.append(acl_user->x509_issuer,strlen(acl_user->x509_issuer));
+ global.append('\'');
}
if (acl_user->x509_subject)
{
- if (ssl_options++)
- global.append(' ');
- global.append(STRING_WITH_LEN("SUBJECT \'"));
- global.append(acl_user->x509_subject,strlen(acl_user->x509_subject),
+ if (ssl_options++)
+ global.append(' ');
+ global.append(STRING_WITH_LEN("SUBJECT \'"));
+ global.append(acl_user->x509_subject,strlen(acl_user->x509_subject),
system_charset_info);
- global.append('\'');
+ global.append('\'');
}
if (acl_user->ssl_cipher)
{
- if (ssl_options++)
- global.append(' ');
- global.append(STRING_WITH_LEN("CIPHER '"));
- global.append(acl_user->ssl_cipher,strlen(acl_user->ssl_cipher),
+ if (ssl_options++)
+ global.append(' ');
+ global.append(STRING_WITH_LEN("CIPHER '"));
+ global.append(acl_user->ssl_cipher,strlen(acl_user->ssl_cipher),
system_charset_info);
- global.append('\'');
+ global.append('\'');
}
}
if ((want_access & GRANT_ACL) ||
- (acl_user->user_resource.questions ||
+ (acl_user->user_resource.questions ||
acl_user->user_resource.updates ||
acl_user->user_resource.conn_per_hour ||
acl_user->user_resource.user_conn))
{
global.append(STRING_WITH_LEN(" WITH"));
if (want_access & GRANT_ACL)
- global.append(STRING_WITH_LEN(" GRANT OPTION"));
+ global.append(STRING_WITH_LEN(" GRANT OPTION"));
add_user_option(&global, acl_user->user_resource.questions,
- "MAX_QUERIES_PER_HOUR", 0);
+ "MAX_QUERIES_PER_HOUR", 0);
add_user_option(&global, acl_user->user_resource.updates,
- "MAX_UPDATES_PER_HOUR", 0);
+ "MAX_UPDATES_PER_HOUR", 0);
add_user_option(&global, acl_user->user_resource.conn_per_hour,
- "MAX_CONNECTIONS_PER_HOUR", 0);
+ "MAX_CONNECTIONS_PER_HOUR", 0);
add_user_option(&global, acl_user->user_resource.user_conn,
- "MAX_USER_CONNECTIONS", 1);
- }
- protocol->prepare_for_resend();
- protocol->store(global.ptr(),global.length(),global.charset());
- if (protocol->write())
- {
- error= -1;
- goto end;
+ "MAX_USER_CONNECTIONS", 1);
}
}
- /* Add database access */
+ protocol->prepare_for_resend();
+ protocol->store(global.ptr(),global.length(),global.charset());
+ if (protocol->write())
+ return TRUE;
+
+ return FALSE;
+
+}
+
+static bool show_database_privileges(THD *thd, const char *username,
+ const char *hostname,
+ char *buff, size_t buffsize)
+{
+ ACL_DB *acl_db;
+ ulong want_access;
+ uint counter;
+ Protocol *protocol= thd->protocol;
+
for (counter=0 ; counter < acl_dbs.elements ; counter++)
{
const char *user, *host;
acl_db=dynamic_element(&acl_dbs,counter,ACL_DB*);
- if (!(user=acl_db->user))
- user= "";
- if (!(host=acl_db->host.hostname))
- host= "";
+ user= safe_str(acl_db->user);
+ host=acl_db->host.hostname;
/*
We do not make SHOW GRANTS case-sensitive here (like REVOKE),
@@ -5509,68 +7948,84 @@ bool mysql_show_grants(THD *thd,LEX_USER *lex_user)
would be wrong from a security point of view.
*/
- if (!strcmp(lex_user->user.str,user) &&
- !my_strcasecmp(system_charset_info, lex_user->host.str, host))
+ if (!strcmp(username, user) &&
+ !my_strcasecmp(system_charset_info, hostname, host))
{
- want_access=acl_db->access;
+ /*
+ do not print inherited access bits for roles,
+ the role bits present in the table are what matters
+ */
+ if (*hostname) // User
+ want_access=acl_db->access;
+ else // Role
+ want_access=acl_db->initial_access;
if (want_access)
{
- String db(buff,sizeof(buff),system_charset_info);
- db.length(0);
- db.append(STRING_WITH_LEN("GRANT "));
-
- if (test_all_bits(want_access,(DB_ACLS & ~GRANT_ACL)))
- db.append(STRING_WITH_LEN("ALL PRIVILEGES"));
- else if (!(want_access & ~GRANT_ACL))
- db.append(STRING_WITH_LEN("USAGE"));
- else
- {
- int found=0, cnt;
- ulong j,test_access= want_access & ~GRANT_ACL;
- for (cnt=0, j = SELECT_ACL; j <= DB_ACLS; cnt++,j <<= 1)
- {
- if (test_access & j)
- {
- if (found)
- db.append(STRING_WITH_LEN(", "));
- found = 1;
- db.append(command_array[cnt],command_lengths[cnt]);
- }
- }
- }
- db.append (STRING_WITH_LEN(" ON "));
- append_identifier(thd, &db, acl_db->db, strlen(acl_db->db));
- db.append (STRING_WITH_LEN(".* TO '"));
- db.append(lex_user->user.str, lex_user->user.length,
- system_charset_info);
- db.append (STRING_WITH_LEN("'@'"));
- // host and lex_user->host are equal except for case
- db.append(host, strlen(host), system_charset_info);
- db.append ('\'');
- if (want_access & GRANT_ACL)
- db.append(STRING_WITH_LEN(" WITH GRANT OPTION"));
- protocol->prepare_for_resend();
- protocol->store(db.ptr(),db.length(),db.charset());
- if (protocol->write())
- {
- error= -1;
- goto end;
- }
+ String db(buff,sizeof(buff),system_charset_info);
+ db.length(0);
+ db.append(STRING_WITH_LEN("GRANT "));
+
+ if (test_all_bits(want_access,(DB_ACLS & ~GRANT_ACL)))
+ db.append(STRING_WITH_LEN("ALL PRIVILEGES"));
+ else if (!(want_access & ~GRANT_ACL))
+ db.append(STRING_WITH_LEN("USAGE"));
+ else
+ {
+ int found=0, cnt;
+ ulong j,test_access= want_access & ~GRANT_ACL;
+ for (cnt=0, j = SELECT_ACL; j <= DB_ACLS; cnt++,j <<= 1)
+ {
+ if (test_access & j)
+ {
+ if (found)
+ db.append(STRING_WITH_LEN(", "));
+ found = 1;
+ db.append(command_array[cnt],command_lengths[cnt]);
+ }
+ }
+ }
+ db.append (STRING_WITH_LEN(" ON "));
+ append_identifier(thd, &db, acl_db->db, strlen(acl_db->db));
+ db.append (STRING_WITH_LEN(".* TO '"));
+ db.append(username, strlen(username),
+ system_charset_info);
+ if (*hostname)
+ {
+ db.append (STRING_WITH_LEN("'@'"));
+ // host and lex_user->host are equal except for case
+ db.append(host, strlen(host), system_charset_info);
+ }
+ db.append ('\'');
+ if (want_access & GRANT_ACL)
+ db.append(STRING_WITH_LEN(" WITH GRANT OPTION"));
+ protocol->prepare_for_resend();
+ protocol->store(db.ptr(),db.length(),db.charset());
+ if (protocol->write())
+ {
+ return TRUE;
+ }
}
}
}
+ return FALSE;
+
+}
+
+static bool show_table_and_column_privileges(THD *thd, const char *username,
+ const char *hostname,
+ char *buff, size_t buffsize)
+{
+ uint counter, index;
+ Protocol *protocol= thd->protocol;
- /* Add table & column access */
for (index=0 ; index < column_priv_hash.records ; index++)
{
const char *user, *host;
GRANT_TABLE *grant_table= (GRANT_TABLE*)
my_hash_element(&column_priv_hash, index);
- if (!(user=grant_table->user))
- user= "";
- if (!(host= grant_table->host.hostname))
- host= "";
+ user= safe_str(grant_table->user);
+ host= grant_table->host.hostname;
/*
We do not make SHOW GRANTS case-sensitive here (like REVOKE),
@@ -5579,132 +8034,126 @@ bool mysql_show_grants(THD *thd,LEX_USER *lex_user)
would be wrong from a security point of view.
*/
- if (!strcmp(lex_user->user.str,user) &&
- !my_strcasecmp(system_charset_info, lex_user->host.str, host))
+ if (!strcmp(username,user) &&
+ !my_strcasecmp(system_charset_info, hostname, host))
{
- ulong table_access= grant_table->privs;
- if ((table_access | grant_table->cols) != 0)
+ ulong table_access;
+ ulong cols_access;
+ if (*hostname) // User
{
- String global(buff, sizeof(buff), system_charset_info);
- ulong test_access= (table_access | grant_table->cols) & ~GRANT_ACL;
+ table_access= grant_table->privs;
+ cols_access= grant_table->cols;
+ }
+ else // Role
+ {
+ table_access= grant_table->init_privs;
+ cols_access= grant_table->init_cols;
+ }
- global.length(0);
- global.append(STRING_WITH_LEN("GRANT "));
+ if ((table_access | cols_access) != 0)
+ {
+ String global(buff, sizeof(buff), system_charset_info);
+ ulong test_access= (table_access | cols_access) & ~GRANT_ACL;
- if (test_all_bits(table_access, (TABLE_ACLS & ~GRANT_ACL)))
- global.append(STRING_WITH_LEN("ALL PRIVILEGES"));
- else if (!test_access)
- global.append(STRING_WITH_LEN("USAGE"));
- else
- {
+ global.length(0);
+ global.append(STRING_WITH_LEN("GRANT "));
+
+ if (test_all_bits(table_access, (TABLE_ACLS & ~GRANT_ACL)))
+ global.append(STRING_WITH_LEN("ALL PRIVILEGES"));
+ else if (!test_access)
+ global.append(STRING_WITH_LEN("USAGE"));
+ else
+ {
/* Add specific column access */
- int found= 0;
- ulong j;
+ int found= 0;
+ ulong j;
- for (counter= 0, j= SELECT_ACL; j <= TABLE_ACLS; counter++, j<<= 1)
- {
- if (test_access & j)
- {
- if (found)
- global.append(STRING_WITH_LEN(", "));
- found= 1;
- global.append(command_array[counter],command_lengths[counter]);
+ for (counter= 0, j= SELECT_ACL; j <= TABLE_ACLS; counter++, j<<= 1)
+ {
+ if (test_access & j)
+ {
+ if (found)
+ global.append(STRING_WITH_LEN(", "));
+ found= 1;
+ global.append(command_array[counter],command_lengths[counter]);
- if (grant_table->cols)
- {
- uint found_col= 0;
- for (uint col_index=0 ;
- col_index < grant_table->hash_columns.records ;
- col_index++)
- {
- GRANT_COLUMN *grant_column = (GRANT_COLUMN*)
- my_hash_element(&grant_table->hash_columns,col_index);
- if (grant_column->rights & j)
- {
- if (!found_col)
- {
- found_col= 1;
- /*
- If we have a duplicated table level privilege, we
- must write the access privilege name again.
- */
- if (table_access & j)
- {
- global.append(STRING_WITH_LEN(", "));
- global.append(command_array[counter],
- command_lengths[counter]);
- }
- global.append(STRING_WITH_LEN(" ("));
- }
- else
- global.append(STRING_WITH_LEN(", "));
- global.append(grant_column->column,
- grant_column->key_length,
- system_charset_info);
- }
- }
- if (found_col)
- global.append(')');
- }
- }
- }
- }
- global.append(STRING_WITH_LEN(" ON "));
- append_identifier(thd, &global, grant_table->db,
- strlen(grant_table->db));
- global.append('.');
- append_identifier(thd, &global, grant_table->tname,
- strlen(grant_table->tname));
- global.append(STRING_WITH_LEN(" TO '"));
- global.append(lex_user->user.str, lex_user->user.length,
- system_charset_info);
- global.append(STRING_WITH_LEN("'@'"));
- // host and lex_user->host are equal except for case
- global.append(host, strlen(host), system_charset_info);
- global.append('\'');
- if (table_access & GRANT_ACL)
- global.append(STRING_WITH_LEN(" WITH GRANT OPTION"));
- protocol->prepare_for_resend();
- protocol->store(global.ptr(),global.length(),global.charset());
- if (protocol->write())
- {
- error= -1;
- break;
- }
+ if (grant_table->cols)
+ {
+ uint found_col= 0;
+ HASH *hash_columns;
+ hash_columns= &grant_table->hash_columns;
+
+ for (uint col_index=0 ;
+ col_index < hash_columns->records ;
+ col_index++)
+ {
+ GRANT_COLUMN *grant_column = (GRANT_COLUMN*)
+ my_hash_element(hash_columns,col_index);
+ if (j & (*hostname ? grant_column->rights // User
+ : grant_column->init_rights)) // Role
+ {
+ if (!found_col)
+ {
+ found_col= 1;
+ /*
+ If we have a duplicated table level privilege, we
+ must write the access privilege name again.
+ */
+ if (table_access & j)
+ {
+ global.append(STRING_WITH_LEN(", "));
+ global.append(command_array[counter],
+ command_lengths[counter]);
+ }
+ global.append(STRING_WITH_LEN(" ("));
+ }
+ else
+ global.append(STRING_WITH_LEN(", "));
+ global.append(grant_column->column,
+ grant_column->key_length,
+ system_charset_info);
+ }
+ }
+ if (found_col)
+ global.append(')');
+ }
+ }
+ }
+ }
+ global.append(STRING_WITH_LEN(" ON "));
+ append_identifier(thd, &global, grant_table->db,
+ strlen(grant_table->db));
+ global.append('.');
+ append_identifier(thd, &global, grant_table->tname,
+ strlen(grant_table->tname));
+ global.append(STRING_WITH_LEN(" TO '"));
+ global.append(username, strlen(username),
+ system_charset_info);
+ if (*hostname)
+ {
+ global.append(STRING_WITH_LEN("'@'"));
+ // host and lex_user->host are equal except for case
+ global.append(host, strlen(host), system_charset_info);
+ }
+ global.append('\'');
+ if (table_access & GRANT_ACL)
+ global.append(STRING_WITH_LEN(" WITH GRANT OPTION"));
+ protocol->prepare_for_resend();
+ protocol->store(global.ptr(),global.length(),global.charset());
+ if (protocol->write())
+ {
+ return TRUE;
+ }
}
}
}
+ return FALSE;
- if (show_routine_grants(thd, lex_user, &proc_priv_hash,
- STRING_WITH_LEN("PROCEDURE"), buff, sizeof(buff)))
- {
- error= -1;
- goto end;
- }
-
- if (show_routine_grants(thd, lex_user, &func_priv_hash,
- STRING_WITH_LEN("FUNCTION"), buff, sizeof(buff)))
- {
- error= -1;
- goto end;
- }
-
- if (show_proxy_grants(thd, lex_user, buff, sizeof(buff)))
- {
- error= -1;
- goto end;
- }
-
-end:
- mysql_mutex_unlock(&acl_cache->lock);
- mysql_rwlock_unlock(&LOCK_grant);
-
- my_eof(thd);
- DBUG_RETURN(error);
}
-static int show_routine_grants(THD* thd, LEX_USER *lex_user, HASH *hash,
- const char *type, int typelen,
+static int show_routine_grants(THD* thd,
+ const char *username, const char *hostname,
+ HASH *hash, const char *type, int typelen,
char *buff, int buffsize)
{
uint counter, index;
@@ -5716,10 +8165,8 @@ static int show_routine_grants(THD* thd, LEX_USER *lex_user, HASH *hash,
const char *user, *host;
GRANT_NAME *grant_proc= (GRANT_NAME*) my_hash_element(hash, index);
- if (!(user=grant_proc->user))
- user= "";
- if (!(host= grant_proc->host.hostname))
- host= "";
+ user= safe_str(grant_proc->user);
+ host= grant_proc->host.hostname;
/*
We do not make SHOW GRANTS case-sensitive here (like REVOKE),
@@ -5728,10 +8175,15 @@ static int show_routine_grants(THD* thd, LEX_USER *lex_user, HASH *hash,
would be wrong from a security point of view.
*/
- if (!strcmp(lex_user->user.str,user) &&
- !my_strcasecmp(system_charset_info, lex_user->host.str, host))
+ if (!strcmp(username, user) &&
+ !my_strcasecmp(system_charset_info, hostname, host))
{
- ulong proc_access= grant_proc->privs;
+ ulong proc_access;
+ if (*hostname) // User
+ proc_access= grant_proc->privs;
+ else // Role
+ proc_access= grant_proc->init_privs;
+
if (proc_access != 0)
{
String global(buff, buffsize, system_charset_info);
@@ -5768,11 +8220,14 @@ static int show_routine_grants(THD* thd, LEX_USER *lex_user, HASH *hash,
append_identifier(thd, &global, grant_proc->tname,
strlen(grant_proc->tname));
global.append(STRING_WITH_LEN(" TO '"));
- global.append(lex_user->user.str, lex_user->user.length,
+ global.append(username, strlen(username),
system_charset_info);
- global.append(STRING_WITH_LEN("'@'"));
- // host and lex_user->host are equal except for case
- global.append(host, strlen(host), system_charset_info);
+ if (*hostname)
+ {
+ global.append(STRING_WITH_LEN("'@'"));
+ // host and lex_user->host are equal except for case
+ global.append(host, strlen(host), system_charset_info);
+ }
global.append('\'');
if (proc_access & GRANT_ACL)
global.append(STRING_WITH_LEN(" WITH GRANT OPTION"));
@@ -5789,6 +8244,7 @@ static int show_routine_grants(THD* thd, LEX_USER *lex_user, HASH *hash,
return error;
}
+
/*
Make a clear-text version of the requested privilege.
*/
@@ -5797,7 +8253,7 @@ void get_privilege_desc(char *to, uint max_length, ulong access)
{
uint pos;
char *start=to;
- DBUG_ASSERT(max_length >= 30); // For end ',' removal
+ DBUG_ASSERT(max_length >= 30); // For end ', ' removal
if (access)
{
@@ -5808,9 +8264,11 @@ void get_privilege_desc(char *to, uint max_length, ulong access)
command_lengths[pos] + (uint) (to-start) < max_length)
{
to= strmov(to, command_array[pos]);
- *to++=',';
+ *to++= ',';
+ *to++= ' ';
}
}
+ to--; // Remove end ' '
to--; // Remove end ','
}
*to=0;
@@ -5823,7 +8281,7 @@ void get_mqh(const char *user, const char *host, USER_CONN *uc)
mysql_mutex_lock(&acl_cache->lock);
- if (initialized && (acl_user= find_acl_user(host,user, FALSE)))
+ if (initialized && (acl_user= find_user_wild(host,user)))
uc->user_resources= acl_user->user_resource;
else
bzero((char*) &uc->user_resources, sizeof(uc->user_resources));
@@ -5837,7 +8295,7 @@ void get_mqh(const char *user, const char *host, USER_CONN *uc)
SYNOPSIS
open_grant_tables()
thd The current thread.
- tables (out) The 4 elements array for the opened tables.
+ tables (out) The 7 elements array for the opened tables.
DESCRIPTION
Tables are numbered as follows:
@@ -5845,6 +8303,9 @@ void get_mqh(const char *user, const char *host, USER_CONN *uc)
1 db
2 tables_priv
3 columns_priv
+ 4 procs_priv
+ 5 proxies_priv
+ 6 roles_mapping
RETURN
1 Skip GRANT handling during replication.
@@ -5852,9 +8313,10 @@ void get_mqh(const char *user, const char *host, USER_CONN *uc)
< 0 Error.
*/
-#define GRANT_TABLES 6
-int open_grant_tables(THD *thd, TABLE_LIST *tables)
+#define GRANT_TABLES 7
+static int open_grant_tables(THD *thd, TABLE_LIST *tables)
{
+ Rpl_filter *rpl_filter;
DBUG_ENTER("open_grant_tables");
if (!initialized)
@@ -5879,31 +8341,40 @@ int open_grant_tables(THD *thd, TABLE_LIST *tables)
(tables+5)->init_one_table(C_STRING_WITH_LEN("mysql"),
C_STRING_WITH_LEN("proxies_priv"),
"proxies_priv", TL_WRITE);
- tables[5].open_strategy= TABLE_LIST::OPEN_IF_EXISTS;
+ (tables+5)->open_strategy= TABLE_LIST::OPEN_IF_EXISTS;
+ (tables+6)->init_one_table(C_STRING_WITH_LEN("mysql"),
+ C_STRING_WITH_LEN("roles_mapping"),
+ "roles_mapping", TL_WRITE);
+ (tables+6)->open_strategy= TABLE_LIST::OPEN_IF_EXISTS;
+
tables->next_local= tables->next_global= tables + 1;
(tables+1)->next_local= (tables+1)->next_global= tables + 2;
(tables+2)->next_local= (tables+2)->next_global= tables + 3;
(tables+3)->next_local= (tables+3)->next_global= tables + 4;
(tables+4)->next_local= (tables+4)->next_global= tables + 5;
+ (tables+5)->next_local= (tables+5)->next_global= tables + 6;
#ifdef HAVE_REPLICATION
/*
GRANT and REVOKE are applied the slave in/exclusion rules as they are
some kind of updates to the mysql.% tables.
*/
- if (thd->slave_thread && rpl_filter->is_on())
+ if (thd->slave_thread &&
+ (rpl_filter= thd->system_thread_info.rpl_sql_info->rpl_filter)->is_on())
{
/*
The tables must be marked "updating" so that tables_ok() takes them into
account in tests.
*/
tables[0].updating= tables[1].updating= tables[2].updating=
- tables[3].updating= tables[4].updating= tables[5].updating= 1;
+ tables[3].updating= tables[4].updating= tables[5].updating=
+ tables[6].updating= 1;
if (!(thd->spcont || rpl_filter->tables_ok(0, tables)))
DBUG_RETURN(1);
tables[0].updating= tables[1].updating= tables[2].updating=
- tables[3].updating= tables[4].updating= tables[5].updating= 0;
+ tables[3].updating= tables[4].updating= tables[5].updating=
+ tables[6].updating= 0;
}
#endif
@@ -5915,8 +8386,7 @@ int open_grant_tables(THD *thd, TABLE_LIST *tables)
DBUG_RETURN(0);
}
-ACL_USER *check_acl_user(LEX_USER *user_name,
- uint *acl_acl_userdx)
+ACL_USER *check_acl_user(LEX_USER *user_name, uint *acl_acl_userdx)
{
ACL_USER *acl_user= 0;
uint counter;
@@ -5925,14 +8395,8 @@ ACL_USER *check_acl_user(LEX_USER *user_name,
for (counter= 0 ; counter < acl_users.elements ; counter++)
{
- const char *user,*host;
acl_user= dynamic_element(&acl_users, counter, ACL_USER*);
- if (!(user=acl_user->user))
- user= "";
- if (!(host=acl_user->host.hostname))
- host= "";
- if (!strcmp(user_name->user.str,user) &&
- !my_strcasecmp(system_charset_info, user_name->host.str, host))
+ if(acl_user->eq(user_name->user.str, user_name->host.str))
break;
}
if (counter == acl_users.elements)
@@ -5976,7 +8440,7 @@ static int modify_grant_table(TABLE *table, Field *host_field,
system_charset_info);
user_field->store(user_to->user.str, user_to->user.length,
system_charset_info);
- if ((error= table->file->ha_update_row(table->record[1],
+ if ((error= table->file->ha_update_row(table->record[1],
table->record[0])) &&
error != HA_ERR_RECORD_IS_THE_SAME)
table->file->print_error(error, MYF(0));
@@ -5994,6 +8458,89 @@ static int modify_grant_table(TABLE *table, Field *host_field,
}
/*
+ Handle the roles_mappings privilege table
+*/
+static int handle_roles_mappings_table(TABLE *table, bool drop,
+ LEX_USER *user_from, LEX_USER *user_to)
+{
+ /*
+ All entries (Host, User) that match user_from will be renamed,
+ as well as all Role entries that match if user_from.host.str == ""
+
+ Otherwise, only matching (Host, User) will be renamed.
+ */
+ DBUG_ENTER("handle_roles_mappings_table");
+
+ int error;
+ int result= 0;
+ THD *thd= current_thd;
+ const char *host, *user, *role;
+ Field *host_field= table->field[0];
+ Field *user_field= table->field[1];
+ Field *role_field= table->field[2];
+
+ DBUG_PRINT("info", ("Rewriting entry in roles_mappings table: %s@%s",
+ user_from->user.str, user_from->host.str));
+ table->use_all_columns();
+ if ((error= table->file->ha_rnd_init(1)))
+ {
+ table->file->print_error(error, MYF(0));
+ result= -1;
+ }
+ else
+ {
+ while((error= table->file->ha_rnd_next(table->record[0])) !=
+ HA_ERR_END_OF_FILE)
+ {
+ if (error)
+ {
+ DBUG_PRINT("info", ("scan error: %d", error));
+ continue;
+ }
+
+ host= safe_str(get_field(thd->mem_root, host_field));
+ user= safe_str(get_field(thd->mem_root, user_field));
+
+ if (!(strcmp(user_from->user.str, user) ||
+ my_strcasecmp(system_charset_info, user_from->host.str, host)))
+ result= ((drop || user_to) &&
+ modify_grant_table(table, host_field, user_field, user_to)) ?
+ -1 : result ? result : 1; /* Error or keep result or found. */
+ else
+ {
+ role= safe_str(get_field(thd->mem_root, role_field));
+
+ if (!user_from->is_role() || strcmp(user_from->user.str, role))
+ continue;
+
+ error= 0;
+
+ if (drop) /* drop if requested */
+ {
+ if ((error= table->file->ha_delete_row(table->record[0])))
+ table->file->print_error(error, MYF(0));
+ }
+ else if (user_to)
+ {
+ store_record(table, record[1]);
+ role_field->store(user_to->user.str, user_to->user.length,
+ system_charset_info);
+ if ((error= table->file->ha_update_row(table->record[1],
+ table->record[0])) &&
+ error != HA_ERR_RECORD_IS_THE_SAME)
+ table->file->print_error(error, MYF(0));
+ }
+
+ /* Error or keep result or found. */
+ result= error ? -1 : result ? result : 1;
+ }
+ }
+ table->file->ha_rnd_end();
+ }
+ DBUG_RETURN(result);
+}
+
+/*
Handle a privilege table.
SYNOPSIS
@@ -6018,6 +8565,8 @@ static int modify_grant_table(TABLE *table, Field *host_field,
2 tables_priv
3 columns_priv
4 procs_priv
+ 5 proxies_priv
+ 6 roles_mapping
RETURN
> 0 At least one record matched.
@@ -6033,8 +8582,8 @@ static int handle_grant_table(TABLE_LIST *tables, uint table_no, bool drop,
TABLE *table= tables[table_no].table;
Field *host_field= table->field[0];
Field *user_field= table->field[table_no && table_no != 5 ? 2 : 1];
- char *host_str= user_from->host.str;
- char *user_str= user_from->user.str;
+ const char *host_str= user_from->host.str;
+ const char *user_str= user_from->user.str;
const char *host;
const char *user;
uchar user_key[MAX_KEY_LENGTH];
@@ -6042,6 +8591,12 @@ static int handle_grant_table(TABLE_LIST *tables, uint table_no, bool drop,
DBUG_ENTER("handle_grant_table");
THD *thd= current_thd;
+ if (table_no == 6)
+ {
+ result= handle_roles_mappings_table(table, drop, user_from, user_to);
+ DBUG_RETURN(result);
+ }
+
table->use_all_columns();
if (! table_no) // mysql.user table
{
@@ -6063,9 +8618,15 @@ static int handle_grant_table(TABLE_LIST *tables, uint table_no, bool drop,
table->key_info->key_part[1].store_length);
key_copy(user_key, table->record[0], table->key_info, key_prefix_length);
- if ((error= table->file->ha_index_read_idx_map(table->record[0], 0,
- user_key, (key_part_map)3,
- HA_READ_KEY_EXACT)))
+ error= table->file->ha_index_read_idx_map(table->record[0], 0,
+ user_key, (key_part_map)3,
+ HA_READ_KEY_EXACT);
+ if (!error && !*host_str)
+ { // verify that we got a role or a user, as needed
+ if (check_is_role(table) != user_from->is_role())
+ error= HA_ERR_KEY_NOT_FOUND;
+ }
+ if (error)
{
if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE)
{
@@ -6100,7 +8661,7 @@ static int handle_grant_table(TABLE_LIST *tables, uint table_no, bool drop,
DBUG_PRINT("info",("scan table: '%s' search: '%s'@'%s'",
table->s->table_name.str, user_str, host_str));
#endif
- while ((error= table->file->ha_rnd_next(table->record[0])) !=
+ while ((error= table->file->ha_rnd_next(table->record[0])) !=
HA_ERR_END_OF_FILE)
{
if (error)
@@ -6109,10 +8670,8 @@ static int handle_grant_table(TABLE_LIST *tables, uint table_no, bool drop,
DBUG_PRINT("info",("scan error: %d", error));
continue;
}
- if (! (host= get_field(thd->mem_root, host_field)))
- host= "";
- if (! (user= get_field(thd->mem_root, user_field)))
- user= "";
+ host= safe_str(get_field(thd->mem_root, host_field));
+ user= safe_str(get_field(thd->mem_root, user_field));
#ifdef EXTRA_DEBUG
if (table_no != 5)
@@ -6149,7 +8708,7 @@ static int handle_grant_table(TABLE_LIST *tables, uint table_no, bool drop,
/**
Handle an in-memory privilege structure.
- @param struct_no The number of the structure to handle (0..5).
+ @param struct_no The number of the structure to handle (0..6).
@param drop If user_from is to be dropped.
@param user_from The the user to be searched/dropped/renamed.
@param user_to The new name for the user if to be renamed, NULL otherwise.
@@ -6160,13 +8719,6 @@ static int handle_grant_table(TABLE_LIST *tables, uint table_no, bool drop,
Delete from grant structure if drop is true.
Update in grant structure if drop is false and user_to is not NULL.
Search in grant structure if drop is false and user_to is NULL.
- Structures are enumerated as follows:
- 0 ACL_USER
- 1 ACL_DB
- 2 COLUMN_PRIVILEGES_HASH
- 3 PROC_PRIVILEGES_HASH
- 4 FUNC_PRIVILEGES_HASH
- 5 PROXY_USERS_ACL
@retval > 0 At least one element matched.
@retval 0 OK, but no element matched.
@@ -6178,22 +8730,74 @@ static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop,
int result= 0;
int idx;
int elements;
- const char *user;
- const char *host;
+ const char *UNINIT_VAR(user);
+ const char *UNINIT_VAR(host);
ACL_USER *acl_user= NULL;
+ ACL_ROLE *acl_role= NULL;
ACL_DB *acl_db= NULL;
ACL_PROXY_USER *acl_proxy_user= NULL;
GRANT_NAME *grant_name= NULL;
+ ROLE_GRANT_PAIR *UNINIT_VAR(role_grant_pair);
HASH *grant_name_hash= NULL;
+ HASH *roles_mappings_hash= NULL;
DBUG_ENTER("handle_grant_struct");
DBUG_PRINT("info",("scan struct: %u search: '%s'@'%s'",
struct_no, user_from->user.str, user_from->host.str));
- LINT_INIT(user);
- LINT_INIT(host);
-
mysql_mutex_assert_owner(&acl_cache->lock);
+ /* No point in querying ROLE ACL if user_from is not a role */
+ if (struct_no == ROLE_ACL && user_from->host.length)
+ DBUG_RETURN(0);
+
+ /* same. no roles in PROXY_USERS_ACL */
+ if (struct_no == PROXY_USERS_ACL && user_from->is_role())
+ DBUG_RETURN(0);
+
+ if (struct_no == ROLE_ACL) //no need to scan the structures in this case
+ {
+ acl_role= find_acl_role(user_from->user.str);
+ if (!acl_role)
+ DBUG_RETURN(0);
+
+ if (!drop && !user_to) //role was found
+ DBUG_RETURN(1);
+
+ /* this calls for a role update */
+ char *old_key= acl_role->user.str;
+ size_t old_key_length= acl_role->user.length;
+ if (drop)
+ {
+ /* all grants must be revoked from this role by now. propagate this */
+ propagate_role_grants(acl_role, PRIVS_TO_MERGE::ALL);
+
+ // delete the role from cross-reference arrays
+ for (uint i=0; i < acl_role->role_grants.elements; i++)
+ {
+ ACL_ROLE *grant= *dynamic_element(&acl_role->role_grants,
+ i, ACL_ROLE**);
+ remove_ptr_from_dynarray(&grant->parent_grantee, acl_role);
+ }
+
+ for (uint i=0; i < acl_role->parent_grantee.elements; i++)
+ {
+ ACL_USER_BASE *grantee= *dynamic_element(&acl_role->parent_grantee,
+ i, ACL_USER_BASE**);
+ remove_ptr_from_dynarray(&grantee->role_grants, acl_role);
+ }
+
+ my_hash_delete(&acl_roles, (uchar*) acl_role);
+ DBUG_RETURN(1);
+ }
+ acl_role->user.str= strdup_root(&acl_memroot, user_to->user.str);
+ acl_role->user.length= user_to->user.length;
+
+ my_hash_update(&acl_roles, (uchar*) acl_role, (uchar*) old_key,
+ old_key_length);
+ DBUG_RETURN(1);
+
+ }
+
/* Get the number of elements in the in-memory structure. */
switch (struct_no) {
case USER_ACL:
@@ -6217,9 +8821,13 @@ static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop,
case PROXY_USERS_ACL:
elements= acl_proxy_users.elements;
break;
+ case ROLES_MAPPINGS_HASH:
+ roles_mappings_hash= &acl_roles_mappings;
+ elements= roles_mappings_hash->records;
+ break;
default:
DBUG_ASSERT(0);
- return -1;
+ DBUG_RETURN(-1);
}
#ifdef EXTRA_DEBUG
@@ -6235,7 +8843,7 @@ static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop,
switch (struct_no) {
case USER_ACL:
acl_user= dynamic_element(&acl_users, idx, ACL_USER*);
- user= acl_user->user;
+ user= acl_user->user.str;
host= acl_user->host.hostname;
break;
@@ -6259,6 +8867,12 @@ static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop,
host= acl_proxy_user->get_host();
break;
+ case ROLES_MAPPINGS_HASH:
+ role_grant_pair= (ROLE_GRANT_PAIR *) my_hash_element(roles_mappings_hash, idx);
+ user= role_grant_pair->u_uname;
+ host= role_grant_pair->u_hname;
+ break;
+
default:
DBUG_ASSERT(0);
}
@@ -6271,9 +8885,41 @@ static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop,
DBUG_PRINT("loop",("scan struct: %u index: %u user: '%s' host: '%s'",
struct_no, idx, user, host));
#endif
- if (strcmp(user_from->user.str, user) ||
- my_strcasecmp(system_charset_info, user_from->host.str, host))
- continue;
+
+ if (struct_no == ROLES_MAPPINGS_HASH)
+ {
+ const char* role= role_grant_pair->r_uname? role_grant_pair->r_uname: "";
+ if (user_from->is_role())
+ {
+ /* When searching for roles within the ROLES_MAPPINGS_HASH, we have
+ to check both the user field as well as the role field for a match.
+
+ It is possible to have a role granted to a role. If we are going
+ to modify the mapping entry, it needs to be done on either on the
+ "user" end (here represented by a role) or the "role" end. At least
+ one part must match.
+
+ If the "user" end has a not-empty host string, it can never match
+ as we are searching for a role here. A role always has an empty host
+ string.
+ */
+ if ((*host || strcmp(user_from->user.str, user)) &&
+ strcmp(user_from->user.str, role))
+ continue;
+ }
+ else
+ {
+ if (strcmp(user_from->user.str, user) ||
+ my_strcasecmp(system_charset_info, user_from->host.str, host))
+ continue;
+ }
+ }
+ else
+ {
+ if (strcmp(user_from->user.str, user) ||
+ my_strcasecmp(system_charset_info, user_from->host.str, host))
+ continue;
+ }
result= 1; /* At least one element found. */
if ( drop )
@@ -6281,6 +8927,7 @@ static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop,
elements--;
switch ( struct_no ) {
case USER_ACL:
+ free_acl_user(dynamic_element(&acl_users, idx, ACL_USER*));
delete_dynamic_element(&acl_users, idx);
break;
@@ -6307,19 +8954,30 @@ static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop,
delete_dynamic_element(&acl_proxy_users, idx);
break;
+ case ROLES_MAPPINGS_HASH:
+ my_hash_delete(roles_mappings_hash, (uchar*) role_grant_pair);
+ if (idx != elements)
+ idx++;
+ break;
+
+ default:
+ DBUG_ASSERT(0);
+ break;
}
}
else if ( user_to )
{
switch ( struct_no ) {
case USER_ACL:
- acl_user->user= strdup_root(&mem, user_to->user.str);
- acl_user->host.hostname= strdup_root(&mem, user_to->host.str);
+ acl_user->user.str= strdup_root(&acl_memroot, user_to->user.str);
+ acl_user->user.length= user_to->user.length;
+ acl_user->host.hostname= strdup_root(&acl_memroot, user_to->host.str);
+ acl_user->hostname_length= user_to->host.length;
break;
case DB_ACL:
- acl_db->user= strdup_root(&mem, user_to->user.str);
- acl_db->host.hostname= strdup_root(&mem, user_to->host.str);
+ acl_db->user= strdup_root(&acl_memroot, user_to->user.str);
+ acl_db->host.hostname= strdup_root(&acl_memroot, user_to->host.str);
break;
case COLUMN_PRIVILEGES_HASH:
@@ -6327,7 +8985,7 @@ static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop,
case FUNC_PRIVILEGES_HASH:
{
/*
- Save old hash key and its length to be able properly update
+ Save old hash key and its length to be able to properly update
element position in hash.
*/
char *old_key= grant_name->hash_key;
@@ -6355,16 +9013,47 @@ static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop,
So we need to examine the current element once again, but
we don't need to restart the search from the beginning.
*/
- if (idx != elements)
- idx++;
+ idx++;
break;
}
case PROXY_USERS_ACL:
- acl_proxy_user->set_user (&mem, user_to->user.str);
- acl_proxy_user->set_host (&mem, user_to->host.str);
+ acl_proxy_user->set_user (&acl_memroot, user_to->user.str);
+ acl_proxy_user->set_host (&acl_memroot, user_to->host.str);
+ break;
+
+ case ROLES_MAPPINGS_HASH:
+ {
+ /*
+ Save old hash key and its length to be able to properly update
+ element position in hash.
+ */
+ char *old_key= role_grant_pair->hashkey.str;
+ size_t old_key_length= role_grant_pair->hashkey.length;
+ bool oom;
+
+ if (user_to->is_role())
+ oom= role_grant_pair->init(&acl_memroot, role_grant_pair->u_uname,
+ role_grant_pair->u_hname,
+ user_to->user.str, false);
+ else
+ oom= role_grant_pair->init(&acl_memroot, user_to->user.str,
+ user_to->host.str,
+ role_grant_pair->r_uname, false);
+ if (oom)
+ DBUG_RETURN(-1);
+
+ my_hash_update(roles_mappings_hash, (uchar*) role_grant_pair,
+ (uchar*) old_key, old_key_length);
+ idx++; // see the comment above
+ break;
+ }
+
+ default:
+ DBUG_ASSERT(0);
break;
}
+
}
else
{
@@ -6409,24 +9098,21 @@ static int handle_grant_data(TABLE_LIST *tables, bool drop,
{
int result= 0;
int found;
+ bool handle_as_role= user_from->is_role();
+ bool search_only= !drop && !user_to;
DBUG_ENTER("handle_grant_data");
- /* Handle user table. */
- if ((found= handle_grant_table(tables, 0, drop, user_from, user_to)) < 0)
- {
- /* Handle of table failed, don't touch the in-memory array. */
- result= -1;
- }
- else
+ if (user_to)
+ DBUG_ASSERT(handle_as_role == user_to->is_role());
+
+ if (search_only)
{
- /* Handle user array. */
- if ((handle_grant_struct(USER_ACL, drop, user_from, user_to)) || found)
- {
- result= 1; /* At least one record/element found. */
- /* If search is requested, we do not need to search further. */
- if (! drop && ! user_to)
- goto end;
- }
+ /* quickly search in-memory structures first */
+ if (handle_as_role && find_acl_role(user_from->user.str))
+ DBUG_RETURN(1); // found
+
+ if (!handle_as_role && find_user_exact(user_from->host.str, user_from->user.str))
+ DBUG_RETURN(1); // found
}
/* Handle db table. */
@@ -6438,13 +9124,14 @@ static int handle_grant_data(TABLE_LIST *tables, bool drop,
else
{
/* Handle db array. */
- if (((handle_grant_struct(DB_ACL, drop, user_from, user_to) && ! result) ||
- found) && ! result)
+ if ((handle_grant_struct(DB_ACL, drop, user_from, user_to) || found)
+ && ! result)
{
result= 1; /* At least one record/element found. */
/* If search is requested, we do not need to search further. */
- if (! drop && ! user_to)
+ if (search_only)
goto end;
+ acl_cache->clear(1);
}
}
@@ -6457,21 +9144,21 @@ static int handle_grant_data(TABLE_LIST *tables, bool drop,
else
{
/* Handle procs array. */
- if (((handle_grant_struct(PROC_PRIVILEGES_HASH, drop, user_from, user_to) && ! result) ||
- found) && ! result)
+ if ((handle_grant_struct(PROC_PRIVILEGES_HASH, drop, user_from, user_to) || found)
+ && ! result)
{
result= 1; /* At least one record/element found. */
/* If search is requested, we do not need to search further. */
- if (! drop && ! user_to)
+ if (search_only)
goto end;
}
/* Handle funcs array. */
- if (((handle_grant_struct(FUNC_PRIVILEGES_HASH, drop, user_from, user_to) && ! result) ||
- found) && ! result)
+ if ((handle_grant_struct(FUNC_PRIVILEGES_HASH, drop, user_from, user_to) || found)
+ && ! result)
{
result= 1; /* At least one record/element found. */
/* If search is requested, we do not need to search further. */
- if (! drop && ! user_to)
+ if (search_only)
goto end;
}
}
@@ -6488,7 +9175,7 @@ static int handle_grant_data(TABLE_LIST *tables, bool drop,
{
result= 1; /* At least one record found. */
/* If search is requested, we do not need to search further. */
- if (! drop && ! user_to)
+ if (search_only)
goto end;
}
@@ -6501,9 +9188,11 @@ static int handle_grant_data(TABLE_LIST *tables, bool drop,
else
{
/* Handle columns hash. */
- if (((handle_grant_struct(COLUMN_PRIVILEGES_HASH, drop, user_from, user_to) && ! result) ||
- found) && ! result)
+ if ((handle_grant_struct(COLUMN_PRIVILEGES_HASH, drop, user_from, user_to) || found)
+ && ! result)
result= 1; /* At least one record/element found. */
+ if (search_only)
+ goto end;
}
}
@@ -6518,27 +9207,52 @@ static int handle_grant_data(TABLE_LIST *tables, bool drop,
else
{
/* Handle proxies_priv array. */
- if ((handle_grant_struct(PROXY_USERS_ACL, drop, user_from, user_to) && !result) ||
- found)
+ if ((handle_grant_struct(PROXY_USERS_ACL, drop, user_from, user_to) || found)
+ && ! result)
result= 1; /* At least one record/element found. */
+ if (search_only)
+ goto end;
}
}
- end:
- DBUG_RETURN(result);
-}
+ /* Handle roles_mappings table. */
+ if (tables[6].table)
+ {
+ if ((found= handle_grant_table(tables, 6, drop, user_from, user_to)) < 0)
+ {
+ /* Handle of table failed, don't touch the in-memory array. */
+ result= -1;
+ }
+ else
+ {
+ /* Handle acl_roles_mappings array */
+ if ((handle_grant_struct(ROLES_MAPPINGS_HASH, drop, user_from, user_to) || found)
+ && ! result)
+ result= 1; /* At least one record/element found */
+ if (search_only)
+ goto end;
+ }
+ }
-static void append_user(String *str, LEX_USER *user)
-{
- if (str->length())
- str->append(',');
- str->append('\'');
- str->append(user->user.str);
- str->append(STRING_WITH_LEN("'@'"));
- str->append(user->host.str);
- str->append('\'');
-}
+ /* Handle user table. */
+ if ((found= handle_grant_table(tables, 0, drop, user_from, user_to)) < 0)
+ {
+ /* Handle of table failed, don't touch the in-memory array. */
+ result= -1;
+ }
+ else
+ {
+ enum enum_acl_lists what= handle_as_role ? ROLE_ACL : USER_ACL;
+ if (((handle_grant_struct(what, drop, user_from, user_to)) || found) && !result)
+ {
+ result= 1; /* At least one record/element found. */
+ DBUG_ASSERT(! search_only);
+ }
+ }
+end:
+ DBUG_RETURN(result);
+}
/*
Create a list of users.
@@ -6547,59 +9261,68 @@ static void append_user(String *str, LEX_USER *user)
mysql_create_user()
thd The current thread.
list The users to create.
+ handle_as_role Handle the user list as roles if true
RETURN
FALSE OK.
TRUE Error.
*/
-bool mysql_create_user(THD *thd, List <LEX_USER> &list)
+bool mysql_create_user(THD *thd, List <LEX_USER> &list, bool handle_as_role)
{
int result;
String wrong_users;
- LEX_USER *user_name, *tmp_user_name;
+ LEX_USER *user_name;
List_iterator <LEX_USER> user_list(list);
TABLE_LIST tables[GRANT_TABLES];
bool some_users_created= FALSE;
- bool save_binlog_row_based;
DBUG_ENTER("mysql_create_user");
+ DBUG_PRINT("entry", ("Handle as %s", handle_as_role ? "role" : "user"));
- /*
- This statement will be replicated as a statement, even when using
- row-based replication. The flag will be reset at the end of the
- statement.
- */
- if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
- thd->clear_current_stmt_binlog_format_row();
+ if (handle_as_role && sp_process_definer(thd))
+ DBUG_RETURN(TRUE);
/* CREATE USER may be skipped on replication client. */
if ((result= open_grant_tables(thd, tables)))
- {
- /* Restore the state of binlog format */
- DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
- if (save_binlog_row_based)
- thd->set_current_stmt_binlog_format_row();
DBUG_RETURN(result != 1);
- }
mysql_rwlock_wrlock(&LOCK_grant);
mysql_mutex_lock(&acl_cache->lock);
- while ((tmp_user_name= user_list++))
+ while ((user_name= user_list++))
{
- if (!(user_name= get_current_user(thd, tmp_user_name)))
+ if (user_name->user.str == current_user.str)
{
+ append_str(&wrong_users, STRING_WITH_LEN("CURRENT_USER"));
result= TRUE;
continue;
}
+ if (user_name->user.str == current_role.str)
+ {
+ append_str(&wrong_users, STRING_WITH_LEN("CURRENT_ROLE"));
+ result= TRUE;
+ continue;
+ }
+
+ if (handle_as_role && is_invalid_role_name(user_name->user.str))
+ {
+ append_user(thd, &wrong_users, user_name);
+ result= TRUE;
+ continue;
+ }
+
+ if (!user_name->host.str)
+ user_name->host= host_not_specified;
+
/*
Search all in-memory structures and grant tables
- for a mention of the new user name.
+ for a mention of the new user/role name.
*/
if (handle_grant_data(tables, 0, user_name, NULL))
{
- append_user(&wrong_users, user_name);
+ append_user(thd, &wrong_users, user_name);
+
result= TRUE;
continue;
}
@@ -6607,28 +9330,57 @@ bool mysql_create_user(THD *thd, List <LEX_USER> &list)
some_users_created= TRUE;
if (replace_user_table(thd, tables[0].table, *user_name, 0, 0, 1, 0))
{
- append_user(&wrong_users, user_name);
+ append_user(thd, &wrong_users, user_name);
result= TRUE;
+ continue;
+ }
+
+ // every created role is automatically granted to its creator-admin
+ if (handle_as_role)
+ {
+ ACL_USER_BASE *grantee= find_acl_user_base(thd->lex->definer->user.str,
+ thd->lex->definer->host.str);
+ ACL_ROLE *role= find_acl_role(user_name->user.str);
+
+ /*
+ just like with routines, views, triggers, and events we allow
+ non-existant definers here with a warning (see sp_process_definer())
+ */
+ if (grantee)
+ add_role_user_mapping(grantee, role);
+
+ if (replace_roles_mapping_table(tables[6].table,
+ &thd->lex->definer->user,
+ &thd->lex->definer->host,
+ &user_name->user, true,
+ NULL, false))
+ {
+ append_user(thd, &wrong_users, user_name);
+ if (grantee)
+ undo_add_role_user_mapping(grantee, role);
+ result= TRUE;
+ }
+ else if (grantee)
+ update_role_mapping(&thd->lex->definer->user,
+ &thd->lex->definer->host,
+ &user_name->user, true, NULL, false);
}
}
mysql_mutex_unlock(&acl_cache->lock);
if (result)
- my_error(ER_CANNOT_USER, MYF(0), "CREATE USER", wrong_users.c_ptr_safe());
+ my_error(ER_CANNOT_USER, MYF(0),
+ (handle_as_role) ? "CREATE ROLE" : "CREATE USER",
+ wrong_users.c_ptr_safe());
if (some_users_created)
result |= write_bin_log(thd, FALSE, thd->query(), thd->query_length());
mysql_rwlock_unlock(&LOCK_grant);
- /* Restore the state of binlog format */
- DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
- if (save_binlog_row_based)
- thd->set_current_stmt_binlog_format_row();
DBUG_RETURN(result);
}
-
/*
Drop a list of users and all their privileges.
@@ -6642,7 +9394,7 @@ bool mysql_create_user(THD *thd, List <LEX_USER> &list)
TRUE Error.
*/
-bool mysql_drop_user(THD *thd, List <LEX_USER> &list)
+bool mysql_drop_user(THD *thd, List <LEX_USER> &list, bool handle_as_role)
{
int result;
String wrong_users;
@@ -6651,26 +9403,12 @@ bool mysql_drop_user(THD *thd, List <LEX_USER> &list)
TABLE_LIST tables[GRANT_TABLES];
bool some_users_deleted= FALSE;
ulonglong old_sql_mode= thd->variables.sql_mode;
- bool save_binlog_row_based;
DBUG_ENTER("mysql_drop_user");
-
- /*
- This statement will be replicated as a statement, even when using
- row-based replication. The flag will be reset at the end of the
- statement.
- */
- if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
- thd->clear_current_stmt_binlog_format_row();
+ DBUG_PRINT("entry", ("Handle as %s", handle_as_role ? "role" : "user"));
/* DROP USER may be skipped on replication client. */
if ((result= open_grant_tables(thd, tables)))
- {
- /* Restore the state of binlog format */
- DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
- if (save_binlog_row_based)
- thd->set_current_stmt_binlog_format_row();
DBUG_RETURN(result != 1);
- }
thd->variables.sql_mode&= ~MODE_PAD_CHAR_TO_FULL_LENGTH;
@@ -6679,41 +9417,59 @@ bool mysql_drop_user(THD *thd, List <LEX_USER> &list)
while ((tmp_user_name= user_list++))
{
- if (!(user_name= get_current_user(thd, tmp_user_name)))
+ user_name= get_current_user(thd, tmp_user_name, false);
+ if (!user_name)
+ {
+ thd->clear_error();
+ append_str(&wrong_users, STRING_WITH_LEN("CURRENT_ROLE"));
+ result= TRUE;
+ continue;
+ }
+
+ if (handle_as_role != user_name->is_role())
{
+ append_user(thd, &wrong_users, user_name);
result= TRUE;
continue;
- }
+ }
+
if (handle_grant_data(tables, 1, user_name, NULL) <= 0)
{
- append_user(&wrong_users, user_name);
+ append_user(thd, &wrong_users, user_name);
result= TRUE;
continue;
}
+
some_users_deleted= TRUE;
}
- /* Rebuild 'acl_check_hosts' since 'acl_users' has been modified */
- rebuild_check_host();
+ if (!handle_as_role)
+ {
+ /* Rebuild 'acl_check_hosts' since 'acl_users' has been modified */
+ rebuild_check_host();
+
+ /*
+ Rebuild every user's role_grants since 'acl_users' has been sorted
+ and old pointers to ACL_USER elements are no longer valid
+ */
+ rebuild_role_grants();
+ }
mysql_mutex_unlock(&acl_cache->lock);
if (result)
- my_error(ER_CANNOT_USER, MYF(0), "DROP USER", wrong_users.c_ptr_safe());
+ my_error(ER_CANNOT_USER, MYF(0),
+ (handle_as_role) ? "DROP ROLE" : "DROP USER",
+ wrong_users.c_ptr_safe());
if (some_users_deleted)
result |= write_bin_log(thd, FALSE, thd->query(), thd->query_length());
mysql_rwlock_unlock(&LOCK_grant);
thd->variables.sql_mode= old_sql_mode;
- /* Restore the state of binlog format */
- DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
- if (save_binlog_row_based)
- thd->set_current_stmt_binlog_format_row();
DBUG_RETURN(result);
}
-
/*
Rename a user.
@@ -6736,44 +9492,34 @@ bool mysql_rename_user(THD *thd, List <LEX_USER> &list)
List_iterator <LEX_USER> user_list(list);
TABLE_LIST tables[GRANT_TABLES];
bool some_users_renamed= FALSE;
- bool save_binlog_row_based;
DBUG_ENTER("mysql_rename_user");
- /*
- This statement will be replicated as a statement, even when using
- row-based replication. The flag will be reset at the end of the
- statement.
- */
- if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
- thd->clear_current_stmt_binlog_format_row();
-
/* RENAME USER may be skipped on replication client. */
if ((result= open_grant_tables(thd, tables)))
- {
- /* Restore the state of binlog format */
- DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
- if (save_binlog_row_based)
- thd->set_current_stmt_binlog_format_row();
DBUG_RETURN(result != 1);
- }
+
+ DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
mysql_rwlock_wrlock(&LOCK_grant);
mysql_mutex_lock(&acl_cache->lock);
while ((tmp_user_from= user_list++))
{
- if (!(user_from= get_current_user(thd, tmp_user_from)))
+ tmp_user_to= user_list++;
+ if (!(user_from= get_current_user(thd, tmp_user_from, false)))
{
+ append_user(thd, &wrong_users, user_from);
result= TRUE;
continue;
- }
- tmp_user_to= user_list++;
- if (!(user_to= get_current_user(thd, tmp_user_to)))
+ }
+ if (!(user_to= get_current_user(thd, tmp_user_to, false)))
{
+ append_user(thd, &wrong_users, user_to);
result= TRUE;
continue;
- }
- DBUG_ASSERT(user_to != 0); /* Syntax enforces pairs of users. */
+ }
+ DBUG_ASSERT(!user_from->is_role());
+ DBUG_ASSERT(!user_to->is_role());
/*
Search all in-memory structures and grant tables
@@ -6782,29 +9528,32 @@ bool mysql_rename_user(THD *thd, List <LEX_USER> &list)
if (handle_grant_data(tables, 0, user_to, NULL) ||
handle_grant_data(tables, 0, user_from, user_to) <= 0)
{
- append_user(&wrong_users, user_from);
+ /* NOTE TODO renaming roles is not yet implemented */
+ append_user(thd, &wrong_users, user_from);
result= TRUE;
continue;
}
some_users_renamed= TRUE;
}
-
+
/* Rebuild 'acl_check_hosts' since 'acl_users' has been modified */
rebuild_check_host();
+ /*
+ Rebuild every user's role_grants since 'acl_users' has been sorted
+ and old pointers to ACL_USER elements are no longer valid
+ */
+ rebuild_role_grants();
+
mysql_mutex_unlock(&acl_cache->lock);
if (result)
my_error(ER_CANNOT_USER, MYF(0), "RENAME USER", wrong_users.c_ptr_safe());
-
+
if (some_users_renamed && mysql_bin_log.is_open())
result |= write_bin_log(thd, FALSE, thd->query(), thd->query_length());
mysql_rwlock_unlock(&LOCK_grant);
- /* Restore the state of binlog format */
- DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
- if (save_binlog_row_based)
- thd->set_current_stmt_binlog_format_row();
DBUG_RETURN(result);
}
@@ -6829,25 +9578,12 @@ bool mysql_revoke_all(THD *thd, List <LEX_USER> &list)
int result;
ACL_DB *acl_db;
TABLE_LIST tables[GRANT_TABLES];
- bool save_binlog_row_based;
DBUG_ENTER("mysql_revoke_all");
- /*
- This statement will be replicated as a statement, even when using
- row-based replication. The flag will be reset at the end of the
- statement.
- */
- if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
- thd->clear_current_stmt_binlog_format_row();
-
if ((result= open_grant_tables(thd, tables)))
- {
- /* Restore the state of binlog format */
- DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
- if (save_binlog_row_based)
- thd->set_current_stmt_binlog_format_row();
DBUG_RETURN(result != 1);
- }
+
+ DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
mysql_rwlock_wrlock(&LOCK_grant);
mysql_mutex_lock(&acl_cache->lock);
@@ -6856,19 +9592,21 @@ bool mysql_revoke_all(THD *thd, List <LEX_USER> &list)
List_iterator <LEX_USER> user_list(list);
while ((tmp_lex_user= user_list++))
{
- if (!(lex_user= get_current_user(thd, tmp_lex_user)))
+ if (!(lex_user= get_current_user(thd, tmp_lex_user, false)))
{
result= -1;
continue;
- }
- if (!find_acl_user(lex_user->host.str, lex_user->user.str, TRUE))
+ }
+
+ /* This is not a role and the user could not be found */
+ if (!lex_user->is_role() &&
+ !find_user_exact(lex_user->host.str, lex_user->user.str))
{
result= -1;
continue;
}
- if (replace_user_table(thd, tables[0].table,
- *lex_user, ~(ulong)0, 1, 0, 0))
+ if (replace_user_table(thd, tables[0].table, *lex_user, ~(ulong)0, 1, 0, 0))
{
result= -1;
continue;
@@ -6887,12 +9625,11 @@ bool mysql_revoke_all(THD *thd, List <LEX_USER> &list)
const char *user,*host;
acl_db=dynamic_element(&acl_dbs,counter,ACL_DB*);
- if (!(user=acl_db->user))
- user= "";
- if (!(host=acl_db->host.hostname))
- host= "";
- if (!strcmp(lex_user->user.str,user) &&
+ user= safe_str(acl_db->user);
+ host= safe_str(acl_db->host.hostname);
+
+ if (!strcmp(lex_user->user.str, user) &&
!strcmp(lex_user->host.str, host))
{
if (!replace_db_table(tables[1].table, acl_db->db, *lex_user,
@@ -6919,10 +9656,8 @@ bool mysql_revoke_all(THD *thd, List <LEX_USER> &list)
const char *user,*host;
GRANT_TABLE *grant_table=
(GRANT_TABLE*) my_hash_element(&column_priv_hash, counter);
- if (!(user=grant_table->user))
- user= "";
- if (!(host=grant_table->host.hostname))
- host= "";
+ user= safe_str(grant_table->user);
+ host= safe_str(grant_table->host.hostname);
if (!strcmp(lex_user->user.str,user) &&
!strcmp(lex_user->host.str, host))
@@ -6965,10 +9700,8 @@ bool mysql_revoke_all(THD *thd, List <LEX_USER> &list)
{
const char *user,*host;
GRANT_NAME *grant_proc= (GRANT_NAME*) my_hash_element(hash, counter);
- if (!(user=grant_proc->user))
- user= "";
- if (!(host=grant_proc->host.hostname))
- host= "";
+ user= safe_str(grant_proc->user);
+ host= safe_str(grant_proc->host.hostname);
if (!strcmp(lex_user->user.str,user) &&
!strcmp(lex_user->host.str, host))
@@ -6987,6 +9720,61 @@ bool mysql_revoke_all(THD *thd, List <LEX_USER> &list)
counter++;
}
} while (revoked);
+
+ ACL_USER_BASE *user_or_role;
+ /* remove role grants */
+ if (lex_user->is_role())
+ {
+ /* this can not fail due to get_current_user already having searched for it */
+ user_or_role= find_acl_role(lex_user->user.str);
+ }
+ else
+ {
+ user_or_role= find_user_exact(lex_user->host.str, lex_user->user.str);
+ }
+ /*
+ Find every role grant pair matching the role_grants array and remove it,
+ both from the acl_roles_mappings and the roles_mapping table
+ */
+ for (counter= 0; counter < user_or_role->role_grants.elements; counter++)
+ {
+ ACL_ROLE *role_grant= *dynamic_element(&user_or_role->role_grants,
+ counter, ACL_ROLE**);
+ ROLE_GRANT_PAIR *pair = find_role_grant_pair(&lex_user->user,
+ &lex_user->host,
+ &role_grant->user);
+ if (replace_roles_mapping_table(tables[6].table,
+ &lex_user->user,
+ &lex_user->host,
+ &role_grant->user, false, pair, true))
+ {
+ result= -1; //Something went wrong
+ }
+ update_role_mapping(&lex_user->user, &lex_user->host,
+ &role_grant->user, false, pair, true);
+ /*
+ Delete from the parent_grantee array of the roles granted,
+ the entry pointing to this user_or_role
+ */
+ remove_ptr_from_dynarray(&role_grant->parent_grantee, user_or_role);
+ }
+ /* TODO
+ How to handle an error in the replace_roles_mapping_table, in
+ regards to the privileges held in memory
+ */
+
+ /* Finally, clear the role_grants array */
+ if (counter == user_or_role->role_grants.elements)
+ {
+ reset_dynamic(&user_or_role->role_grants);
+ }
+ /*
+ If we are revoking from a role, we need to update all the parent grantees
+ */
+ if (lex_user->is_role())
+ {
+ propagate_role_grants((ACL_ROLE *)user_or_role, PRIVS_TO_MERGE::ALL);
+ }
}
mysql_mutex_unlock(&acl_cache->lock);
@@ -6998,10 +9786,6 @@ bool mysql_revoke_all(THD *thd, List <LEX_USER> &list)
write_bin_log(thd, FALSE, thd->query(), thd->query_length());
mysql_rwlock_unlock(&LOCK_grant);
- /* Restore the state of binlog format */
- DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
- if (save_binlog_row_based)
- thd->set_current_stmt_binlog_format_row();
DBUG_RETURN(result);
}
@@ -7030,9 +9814,9 @@ public:
virtual bool handle_condition(THD *thd,
uint sql_errno,
const char* sqlstate,
- MYSQL_ERROR::enum_warning_level level,
+ Sql_condition::enum_warning_level level,
const char* msg,
- MYSQL_ERROR ** cond_hdl);
+ Sql_condition ** cond_hdl);
bool has_errors() { return is_grave; }
@@ -7045,18 +9829,18 @@ Silence_routine_definer_errors::handle_condition(
THD *thd,
uint sql_errno,
const char*,
- MYSQL_ERROR::enum_warning_level level,
+ Sql_condition::enum_warning_level level,
const char* msg,
- MYSQL_ERROR ** cond_hdl)
+ Sql_condition ** cond_hdl)
{
*cond_hdl= NULL;
- if (level == MYSQL_ERROR::WARN_LEVEL_ERROR)
+ if (level == Sql_condition::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,
+ push_warning(thd, Sql_condition::WARN_LEVEL_WARN,
sql_errno, msg);
return TRUE;
default:
@@ -7093,26 +9877,19 @@ bool sp_revoke_privileges(THD *thd, const char *sp_db, const char *sp_name,
TABLE_LIST tables[GRANT_TABLES];
HASH *hash= is_proc ? &proc_priv_hash : &func_priv_hash;
Silence_routine_definer_errors error_handler;
- bool save_binlog_row_based;
DBUG_ENTER("sp_revoke_privileges");
if ((result= open_grant_tables(thd, tables)))
DBUG_RETURN(result != 1);
+ DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
+
/* Be sure to pop this before exiting this scope! */
thd->push_internal_handler(&error_handler);
mysql_rwlock_wrlock(&LOCK_grant);
mysql_mutex_lock(&acl_cache->lock);
- /*
- This statement will be replicated as a statement, even when using
- row-based replication. The flag will be reset at the end of the
- statement.
- */
- if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
- thd->clear_current_stmt_binlog_format_row();
-
/* Remove procedure access */
do
{
@@ -7125,11 +9902,8 @@ bool sp_revoke_privileges(THD *thd, const char *sp_db, const char *sp_name,
LEX_USER lex_user;
lex_user.user.str= grant_proc->user;
lex_user.user.length= strlen(grant_proc->user);
- lex_user.host.str= grant_proc->host.hostname ?
- grant_proc->host.hostname : (char*)"";
- lex_user.host.length= grant_proc->host.hostname ?
- strlen(grant_proc->host.hostname) : 0;
-
+ lex_user.host.str= safe_str(grant_proc->host.hostname);
+ lex_user.host.length= strlen(lex_user.host.str);
if (replace_routine_table(thd,grant_proc,tables[4].table,lex_user,
grant_proc->db, grant_proc->tname,
is_proc, ~(ulong)0, 1) == 0)
@@ -7146,10 +9920,6 @@ bool sp_revoke_privileges(THD *thd, const char *sp_db, const char *sp_name,
mysql_rwlock_unlock(&LOCK_grant);
thd->pop_internal_handler();
- /* Restore the state of binlog format */
- DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
- if (save_binlog_row_based)
- thd->set_current_stmt_binlog_format_row();
DBUG_RETURN(error_handler.has_errors());
}
@@ -7177,7 +9947,6 @@ bool sp_grant_privileges(THD *thd, const char *sp_db, const char *sp_name,
List<LEX_USER> user_list;
bool result;
ACL_USER *au;
- char passwd_buff[SCRAMBLED_PASSWORD_CHAR_LENGTH+1];
Dummy_error_handler error_handler;
DBUG_ENTER("sp_grant_privileges");
@@ -7188,13 +9957,13 @@ bool sp_grant_privileges(THD *thd, const char *sp_db, const char *sp_name,
mysql_mutex_lock(&acl_cache->lock);
- if ((au= find_acl_user(combo->host.str=(char*)sctx->host_or_ip,combo->user.str,FALSE)))
+ if ((au= find_user_wild(combo->host.str=(char*)sctx->host_or_ip, combo->user.str)))
goto found_acl;
- if ((au= find_acl_user(combo->host.str=(char*)sctx->host, combo->user.str,FALSE)))
+ if ((au= find_user_wild(combo->host.str=(char*)sctx->host, combo->user.str)))
goto found_acl;
- if ((au= find_acl_user(combo->host.str=(char*)sctx->ip, combo->user.str,FALSE)))
+ if ((au= find_user_wild(combo->host.str=(char*)sctx->ip, combo->user.str)))
goto found_acl;
- if((au= find_acl_user(combo->host.str=(char*)"%", combo->user.str, FALSE)))
+ if ((au= find_user_wild(combo->host.str=(char*)"%", combo->user.str)))
goto found_acl;
mysql_mutex_unlock(&acl_cache->lock);
@@ -7209,10 +9978,8 @@ bool sp_grant_privileges(THD *thd, const char *sp_db, const char *sp_name,
tables->db= (char*)sp_db;
tables->table_name= tables->alias= (char*)sp_name;
- thd->make_lex_string(&combo->user,
- combo->user.str, strlen(combo->user.str), 0);
- thd->make_lex_string(&combo->host,
- combo->host.str, strlen(combo->host.str), 0);
+ thd->make_lex_string(&combo->user, combo->user.str, strlen(combo->user.str));
+ thd->make_lex_string(&combo->host, combo->host.str, strlen(combo->host.str));
combo->password= empty_lex_str;
combo->plugin= empty_lex_str;
@@ -7220,33 +9987,10 @@ bool sp_grant_privileges(THD *thd, const char *sp_db, const char *sp_name,
if(au)
{
- if (au->salt_len)
- {
- if (au->salt_len == SCRAMBLE_LENGTH)
- {
- make_password_from_salt(passwd_buff, au->salt);
- combo->password.length= SCRAMBLED_PASSWORD_CHAR_LENGTH;
- }
- else if (au->salt_len == SCRAMBLE_LENGTH_323)
- {
- make_password_from_salt_323(passwd_buff, (ulong *) au->salt);
- combo->password.length= SCRAMBLED_PASSWORD_CHAR_LENGTH_323;
- }
- else
- {
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_PASSWD_LENGTH,
- ER(ER_PASSWD_LENGTH), SCRAMBLED_PASSWORD_CHAR_LENGTH);
- return TRUE;
- }
- combo->password.str= passwd_buff;
- }
-
if (au->plugin.str != native_password_plugin_name.str &&
au->plugin.str != old_password_plugin_name.str)
- {
combo->plugin= au->plugin;
- combo->auth= au->auth_string;
- }
+ combo->auth= au->auth_string;
}
if (user_list.push_back(combo))
@@ -7268,23 +10012,12 @@ bool sp_grant_privileges(THD *thd, const char *sp_db, const char *sp_name,
}
-/*****************************************************************************
- Instantiate used templates
-*****************************************************************************/
-
-#ifdef HAVE_EXPLICIT_TEMPLATE_INSTANTIATION
-template class List_iterator<LEX_COLUMN>;
-template class List_iterator<LEX_USER>;
-template class List<LEX_COLUMN>;
-template class List<LEX_USER>;
-#endif
-
/**
Validate if a user can proxy as another user
@thd current thread
@param user the logged in user (proxy user)
- @param authenticated_as the effective user a plugin is trying to
+ @param authenticated_as the effective user a plugin is trying to
impersonate as (proxied user)
@return proxy user definition
@retval NULL proxy user definition not found or not applicable
@@ -7292,7 +10025,7 @@ template class List<LEX_USER>;
*/
static ACL_PROXY_USER *
-acl_find_proxy_user(const char *user, const char *host, const char *ip,
+acl_find_proxy_user(const char *user, const char *host, const char *ip,
const char *authenticated_as, bool *proxy_used)
{
uint i;
@@ -7307,10 +10040,10 @@ acl_find_proxy_user(const char *user, const char *host, const char *ip,
DBUG_RETURN (NULL);
}
- *proxy_used= TRUE;
+ *proxy_used= TRUE;
for (i=0; i < acl_proxy_users.elements; i++)
{
- ACL_PROXY_USER *proxy= dynamic_element(&acl_proxy_users, i,
+ ACL_PROXY_USER *proxy= dynamic_element(&acl_proxy_users, i,
ACL_PROXY_USER *);
if (proxy->matches(host, user, ip, authenticated_as))
DBUG_RETURN(proxy);
@@ -7325,7 +10058,7 @@ acl_check_proxy_grant_access(THD *thd, const char *host, const char *user,
bool with_grant)
{
DBUG_ENTER("acl_check_proxy_grant_access");
- DBUG_PRINT("info", ("user=%s host=%s with_grant=%d", user, host,
+ DBUG_PRINT("info", ("user=%s host=%s with_grant=%d", user, host,
(int) with_grant));
if (!initialized)
{
@@ -7356,7 +10089,7 @@ acl_check_proxy_grant_access(THD *thd, const char *host, const char *user,
!my_strcasecmp(system_charset_info, host,
thd->security_ctx->priv_host))
{
- DBUG_PRINT("info", ("strcmp (%s, %s) my_casestrcmp (%s, %s) equal",
+ DBUG_PRINT("info", ("strcmp (%s, %s) my_casestrcmp (%s, %s) equal",
thd->security_ctx->priv_user, user,
host, thd->security_ctx->priv_host));
DBUG_RETURN(FALSE);
@@ -7367,7 +10100,7 @@ acl_check_proxy_grant_access(THD *thd, const char *host, const char *user,
/* check for matching WITH PROXY rights */
for (uint i=0; i < acl_proxy_users.elements; i++)
{
- ACL_PROXY_USER *proxy= dynamic_element(&acl_proxy_users, i,
+ ACL_PROXY_USER *proxy= dynamic_element(&acl_proxy_users, i,
ACL_PROXY_USER *);
if (proxy->matches(thd->security_ctx->host,
thd->security_ctx->user,
@@ -7390,7 +10123,8 @@ acl_check_proxy_grant_access(THD *thd, const char *host, const char *user,
static bool
-show_proxy_grants(THD *thd, LEX_USER *user, char *buff, size_t buffsize)
+show_proxy_grants(THD *thd, const char *username, const char *hostname,
+ char *buff, size_t buffsize)
{
Protocol *protocol= thd->protocol;
int error= 0;
@@ -7399,7 +10133,7 @@ show_proxy_grants(THD *thd, LEX_USER *user, char *buff, size_t buffsize)
{
ACL_PROXY_USER *proxy= dynamic_element(&acl_proxy_users, i,
ACL_PROXY_USER *);
- if (proxy->granted_on(user->host.str, user->user.str))
+ if (proxy->granted_on(hostname, username))
{
String global(buff, buffsize, system_charset_info);
global.length(0);
@@ -7416,9 +10150,119 @@ show_proxy_grants(THD *thd, LEX_USER *user, char *buff, size_t buffsize)
return error;
}
+static int enabled_roles_insert(ACL_USER_BASE *role, void *context_data)
+{
+ TABLE *table= (TABLE*) context_data;
+ DBUG_ASSERT(role->flags & IS_ROLE);
+
+ restore_record(table, s->default_values);
+ table->field[0]->set_notnull();
+ table->field[0]->store(role->user.str, role->user.length,
+ system_charset_info);
+ if (schema_table_store_record(table->in_use, table))
+ return -1;
+ return 0;
+}
+
+struct APPLICABLE_ROLES_DATA
+{
+ TABLE *table;
+ const LEX_STRING host;
+ const LEX_STRING user_and_host;
+ ACL_USER_BASE *user;
+};
+
+static int
+applicable_roles_insert(ACL_USER_BASE *grantee, ACL_ROLE *role, void *ptr)
+{
+ APPLICABLE_ROLES_DATA *data= (APPLICABLE_ROLES_DATA *)ptr;
+ CHARSET_INFO *cs= system_charset_info;
+ TABLE *table= data->table;
+ bool is_role= grantee != data->user;
+ const LEX_STRING *user_and_host= is_role ? &grantee->user
+ : &data->user_and_host;
+ const LEX_STRING *host= is_role ? &empty_lex_str : &data->host;
+
+ restore_record(table, s->default_values);
+ table->field[0]->store(user_and_host->str, user_and_host->length, cs);
+ table->field[1]->store(role->user.str, role->user.length, cs);
+
+ ROLE_GRANT_PAIR *pair=
+ find_role_grant_pair(&grantee->user, host, &role->user);
+ DBUG_ASSERT(pair);
+
+ if (pair->with_admin)
+ table->field[2]->store(STRING_WITH_LEN("YES"), cs);
+ else
+ table->field[2]->store(STRING_WITH_LEN("NO"), cs);
+
+ if (schema_table_store_record(table->in_use, table))
+ return -1;
+ return 0;
+}
#endif /*NO_EMBEDDED_ACCESS_CHECKS */
+int fill_schema_enabled_roles(THD *thd, TABLE_LIST *tables, COND *cond)
+{
+ TABLE *table= tables->table;
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ if (thd->security_ctx->priv_role[0])
+ {
+ mysql_rwlock_rdlock(&LOCK_grant);
+ mysql_mutex_lock(&acl_cache->lock);
+ ACL_ROLE *acl_role= find_acl_role(thd->security_ctx->priv_role);
+ if (acl_role)
+ traverse_role_graph_down(acl_role, table, enabled_roles_insert, NULL);
+ mysql_mutex_unlock(&acl_cache->lock);
+ mysql_rwlock_unlock(&LOCK_grant);
+ if (acl_role)
+ return 0;
+ }
+#endif
+
+ restore_record(table, s->default_values);
+ table->field[0]->set_null();
+ return schema_table_store_record(table->in_use, table);
+}
+
+
+/*
+ This shows all roles granted to current user
+ and recursively all roles granted to those roles
+*/
+int fill_schema_applicable_roles(THD *thd, TABLE_LIST *tables, COND *cond)
+{
+ int res= 0;
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ if (initialized)
+ {
+ TABLE *table= tables->table;
+ Security_context *sctx= thd->security_ctx;
+ mysql_rwlock_rdlock(&LOCK_grant);
+ mysql_mutex_lock(&acl_cache->lock);
+ ACL_USER *user= find_user_exact(sctx->priv_host, sctx->priv_user);
+ if (user)
+ {
+ char buff[USER_HOST_BUFF_SIZE+10];
+ DBUG_ASSERT(user->user.length + user->hostname_length +2 < sizeof(buff));
+ char *end= strxmov(buff, user->user.str, "@", user->host.hostname, NULL);
+ APPLICABLE_ROLES_DATA data= { table,
+ { user->host.hostname, user->hostname_length },
+ { buff, (size_t)(end - buff) }, user
+ };
+
+ res= traverse_role_graph_down(user, &data, 0, applicable_roles_insert);
+ }
+
+ mysql_mutex_unlock(&acl_cache->lock);
+ mysql_rwlock_unlock(&LOCK_grant);
+ }
+#endif
+
+ return res;
+}
+
int wild_case_compare(CHARSET_INFO *cs, const char *str,const char *wildstr)
{
@@ -7511,16 +10355,14 @@ int fill_schema_user_privileges(THD *thd, TABLE_LIST *tables, COND *cond)
{
const char *user,*host, *is_grantable="YES";
acl_user=dynamic_element(&acl_users,counter,ACL_USER*);
- if (!(user=acl_user->user))
- user= "";
- if (!(host=acl_user->host.hostname))
- host= "";
+ user= safe_str(acl_user->user.str);
+ host= safe_str(acl_user->host.hostname);
if (no_global_access &&
(strcmp(thd->security_ctx->priv_user, user) ||
my_strcasecmp(system_charset_info, curr_host, host)))
continue;
-
+
want_access= acl_user->access;
if (!(want_access & GRANT_ACL))
is_grantable= "NO";
@@ -7543,7 +10385,7 @@ int fill_schema_user_privileges(THD *thd, TABLE_LIST *tables, COND *cond)
{
if (test_access & j)
{
- if (update_schema_privilege(thd, table, buff, 0, 0, 0, 0,
+ if (update_schema_privilege(thd, table, buff, 0, 0, 0, 0,
command_array[priv_id],
command_lengths[priv_id], is_grantable))
{
@@ -7587,10 +10429,8 @@ int fill_schema_schema_privileges(THD *thd, TABLE_LIST *tables, COND *cond)
const char *user, *host, *is_grantable="YES";
acl_db=dynamic_element(&acl_dbs,counter,ACL_DB*);
- if (!(user=acl_db->user))
- user= "";
- if (!(host=acl_db->host.hostname))
- host= "";
+ user= safe_str(acl_db->user);
+ host= safe_str(acl_db->host.hostname);
if (no_global_access &&
(strcmp(thd->security_ctx->priv_user, user) ||
@@ -7660,11 +10500,9 @@ int fill_schema_table_privileges(THD *thd, TABLE_LIST *tables, COND *cond)
{
const char *user, *host, *is_grantable= "YES";
GRANT_TABLE *grant_table= (GRANT_TABLE*) my_hash_element(&column_priv_hash,
- index);
- if (!(user=grant_table->user))
- user= "";
- if (!(host= grant_table->host.hostname))
- host= "";
+ index);
+ user= safe_str(grant_table->user);
+ host= safe_str(grant_table->host.hostname);
if (no_global_access &&
(strcmp(thd->security_ctx->priv_user, user) ||
@@ -7714,7 +10552,7 @@ int fill_schema_table_privileges(THD *thd, TABLE_LIST *tables, COND *cond)
}
}
}
- }
+ }
}
err:
mysql_rwlock_unlock(&LOCK_grant);
@@ -7744,11 +10582,9 @@ int fill_schema_column_privileges(THD *thd, TABLE_LIST *tables, COND *cond)
{
const char *user, *host, *is_grantable= "YES";
GRANT_TABLE *grant_table= (GRANT_TABLE*) my_hash_element(&column_priv_hash,
- index);
- if (!(user=grant_table->user))
- user= "";
- if (!(host= grant_table->host.hostname))
- host= "";
+ index);
+ user= safe_str(grant_table->user);
+ host= safe_str(grant_table->host.hostname);
if (no_global_access &&
(strcmp(thd->security_ctx->priv_user, user) ||
@@ -7826,9 +10662,7 @@ void fill_effective_table_privileges(THD *thd, GRANT_INFO *grant,
Security_context *sctx= thd->security_ctx;
DBUG_ENTER("fill_effective_table_privileges");
DBUG_PRINT("enter", ("Host: '%s', Ip: '%s', User: '%s', table: `%s`.`%s`",
- sctx->priv_host, (sctx->ip ? sctx->ip : "(NULL)"),
- (sctx->priv_user ? sctx->priv_user : "(NULL)"),
- db, table));
+ sctx->priv_host, sctx->ip, sctx->priv_user, db, table));
/* --skip-grants */
if (!initialized)
{
@@ -7847,22 +10681,40 @@ void fill_effective_table_privileges(THD *thd, GRANT_INFO *grant,
DBUG_VOID_RETURN; // it is slave
}
- /* db privileges */
- grant->privilege|= acl_get(sctx->host, sctx->ip, sctx->priv_user, db, 0);
+ if (!thd->db || strcmp(db, thd->db))
+ {
+ /* db privileges */
+ grant->privilege|= acl_get(sctx->host, sctx->ip, sctx->priv_user, db, 0);
+ /* db privileges for role */
+ if (sctx->priv_role[0])
+ grant->privilege|= acl_get("", "", sctx->priv_role, db, 0);
+ }
+ else
+ {
+ grant->privilege|= sctx->db_access;
+ }
/* table privileges */
mysql_rwlock_rdlock(&LOCK_grant);
if (grant->version != grant_version)
{
- grant->grant_table=
+ grant->grant_table_user=
table_hash_search(sctx->host, sctx->ip, db,
- sctx->priv_user,
- table, 0); /* purecov: inspected */
+ sctx->priv_user,
+ table, 0); /* purecov: inspected */
+ grant->grant_table_role=
+ sctx->priv_role[0] ? table_hash_search("", "", db,
+ sctx->priv_role,
+ table, TRUE) : NULL;
grant->version= grant_version; /* purecov: inspected */
}
- if (grant->grant_table != 0)
+ if (grant->grant_table_user != 0)
{
- grant->privilege|= grant->grant_table->privs;
+ grant->privilege|= grant->grant_table_user->privs;
+ }
+ if (grant->grant_table_role != 0)
+ {
+ grant->privilege|= grant->grant_table_role->privs;
}
mysql_rwlock_unlock(&LOCK_grant);
@@ -7884,6 +10736,54 @@ bool check_routine_level_acl(THD *thd, const char *db, const char *name,
#endif
+/**
+ Return information about user or current user.
+
+ @param[in] thd thread handler
+ @param[in] user user
+ @param[in] lock whether &acl_cache->lock mutex needs to be locked
+
+ @return
+ - On success, return a valid pointer to initialized
+ LEX_USER, which contains user information.
+ - On error, return 0.
+*/
+
+LEX_USER *get_current_user(THD *thd, LEX_USER *user, bool lock)
+{
+ if (user->user.str == current_user.str) // current_user
+ return create_default_definer(thd, false);
+
+ if (user->user.str == current_role.str) // current_role
+ return create_default_definer(thd, true);
+
+ if (user->host.str == NULL) // Possibly a role
+ {
+ // to be reexecution friendly we have to make a copy
+ LEX_USER *dup= (LEX_USER*) thd->memdup(user, sizeof(*user));
+ if (!dup)
+ return 0;
+
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ if (is_invalid_role_name(user->user.str))
+ return 0;
+
+ if (lock)
+ mysql_mutex_lock(&acl_cache->lock);
+ if (find_acl_role(dup->user.str))
+ dup->host= empty_lex_str;
+ else
+ dup->host= host_not_specified;
+ if (lock)
+ mysql_mutex_unlock(&acl_cache->lock);
+#endif
+
+ return dup;
+ }
+
+ return user;
+}
+
struct ACL_internal_schema_registry_entry
{
const LEX_STRING *m_name;
@@ -8027,7 +10927,6 @@ struct MPVIO_EXT :public MYSQL_PLUGIN_VIO
uint pkt_len;
} cached_server_packet;
int packets_read, packets_written; ///< counters for send/received packets
- uint connect_errors; ///< if there were connect errors for this host
bool make_it_fail;
/** when plugin returns a failure this tells us what really happened */
enum { SUCCESS, FAILURE, RESTART } status;
@@ -8048,9 +10947,9 @@ static void login_failed_error(THD *thd)
thd->main_security_ctx.host_or_ip,
thd->password ? ER(ER_YES) : ER(ER_NO));
status_var_increment(thd->status_var.access_denied_errors);
- /*
+ /*
Log access denied messages to the error log when log-warnings = 2
- so that the overhead of the general query log is not required to track
+ so that the overhead of the general query log is not required to track
failed connections.
*/
if (global_system_variables.log_warnings > 1)
@@ -8058,7 +10957,7 @@ static void login_failed_error(THD *thd)
sql_print_warning(ER(access_denied_error_code(thd->password)),
thd->main_security_ctx.user,
thd->main_security_ctx.host_or_ip,
- thd->password ? ER(ER_YES) : ER(ER_NO));
+ thd->password ? ER(ER_YES) : ER(ER_NO));
}
}
@@ -8067,7 +10966,7 @@ static void login_failed_error(THD *thd)
after the connection was established
Packet format:
-
+
Bytes Content
----- ----
1 protocol version (always 10)
@@ -8149,7 +11048,7 @@ static bool send_server_handshake_packet(MPVIO_EXT *mpvio,
data_len= SCRAMBLE_LENGTH;
}
- end= strnmov(end, server_version, SERVER_VERSION_LENGTH) + 1;
+ end= strxnmov(end, SERVER_VERSION_LENGTH, RPL_VERSION_HACK, server_version, NullS) + 1;
int4store((uchar*) end, mpvio->thd->thread_id);
end+= 4;
@@ -8161,7 +11060,7 @@ static bool send_server_handshake_packet(MPVIO_EXT *mpvio,
end= (char*) memcpy(end, data, SCRAMBLE_LENGTH_323);
end+= SCRAMBLE_LENGTH_323;
*end++= 0;
-
+
int2store(end, thd->client_capabilities);
/* write server characteristics: up to 16 bytes allowed */
end[2]= (char) default_charset_info->number;
@@ -8191,7 +11090,7 @@ static bool secure_auth(THD *thd)
return 0;
/*
- If the server is running in secure auth mode, short scrambles are
+ If the server is running in secure auth mode, short scrambles are
forbidden. Extra juggling to report the same error as the old code.
*/
if (thd->client_capabilities & CLIENT_PROTOCOL_41)
@@ -8216,7 +11115,7 @@ static bool secure_auth(THD *thd)
using a different authentication plugin
Packet format:
-
+
Bytes Content
----- ----
1 byte with the value 254
@@ -8282,7 +11181,7 @@ static bool send_plugin_request_packet(MPVIO_EXT *mpvio,
DBUG_RETURN (1);
}
- DBUG_PRINT("info", ("requesting client to use the %s plugin",
+ DBUG_PRINT("info", ("requesting client to use the %s plugin",
client_auth_plugin));
DBUG_RETURN(net_write_command(net, switch_plugin_request_buf[0],
(uchar*) client_auth_plugin,
@@ -8293,13 +11192,10 @@ static bool send_plugin_request_packet(MPVIO_EXT *mpvio,
#ifndef NO_EMBEDDED_ACCESS_CHECKS
/**
Finds acl entry in user database for authentication purposes.
-
+
Finds a user and copies it into mpvio. Creates a fake user
if no matching user account is found.
- @note find_acl_user is not the same, because it doesn't take into
- account the case when user is not empty, but acl_user->user is empty
-
@retval 0 found
@retval 1 error
*/
@@ -8310,16 +11206,11 @@ static bool find_mpvio_user(MPVIO_EXT *mpvio)
DBUG_ASSERT(mpvio->acl_user == 0);
mysql_mutex_lock(&acl_cache->lock);
- for (uint i=0; i < acl_users.elements; i++)
- {
- ACL_USER *acl_user_tmp= dynamic_element(&acl_users, i, ACL_USER*);
- if ((!acl_user_tmp->user || !strcmp(sctx->user, acl_user_tmp->user)) &&
- compare_hostname(&acl_user_tmp->host, sctx->host, sctx->ip))
- {
- mpvio->acl_user= acl_user_tmp->copy(mpvio->thd->mem_root);
- break;
- }
- }
+
+ ACL_USER *user= find_user_or_anon(sctx->host, sctx->user, sctx->ip);
+ if (user)
+ mpvio->acl_user= user->copy(mpvio->thd->mem_root);
+
mysql_mutex_unlock(&acl_cache->lock);
if (!mpvio->acl_user)
@@ -8371,8 +11262,7 @@ static bool find_mpvio_user(MPVIO_EXT *mpvio)
mpvio->auth_info.user_name_length= strlen(sctx->user);
mpvio->auth_info.auth_string= mpvio->acl_user->auth_string.str;
mpvio->auth_info.auth_string_length= (unsigned long) mpvio->acl_user->auth_string.length;
- strmake_buf(mpvio->auth_info.authenticated_as, mpvio->acl_user->user ?
- mpvio->acl_user->user : "");
+ strmake_buf(mpvio->auth_info.authenticated_as, safe_str(mpvio->acl_user->user.str));
DBUG_PRINT("info", ("exit: user=%s, auth_string=%s, authenticated as=%s"
"plugin=%s",
@@ -8382,6 +11272,41 @@ static bool find_mpvio_user(MPVIO_EXT *mpvio)
mpvio->acl_user->plugin.str));
DBUG_RETURN(0);
}
+
+static bool
+read_client_connect_attrs(char **ptr, char *end,
+ const CHARSET_INFO *from_cs)
+{
+ size_t length;
+ char *ptr_save= *ptr;
+
+ /* not enough bytes to hold the length */
+ if (ptr_save >= end)
+ return true;
+
+ length= safe_net_field_length_ll((uchar **) ptr, end - ptr_save);
+
+ /* cannot even read the length */
+ if (*ptr == NULL)
+ return true;
+
+ /* length says there're more data than can fit into the packet */
+ if (*ptr + length > end)
+ return true;
+
+ /* impose an artificial length limit of 64k */
+ if (length > 65535)
+ return true;
+
+#ifdef HAVE_PSI_THREAD_INTERFACE
+ if (PSI_THREAD_CALL(set_thread_connect_attrs)(*ptr, length, from_cs) &&
+ current_thd->variables.log_warnings)
+ sql_print_warning("Connection attributes of length %lu were truncated",
+ (unsigned long) length);
+#endif
+ return false;
+}
+
#endif
/* the packet format is described in send_change_user_packet() */
@@ -8434,13 +11359,14 @@ static bool parse_com_change_user_packet(MPVIO_EXT *mpvio, uint packet_length)
uint db_len= strlen(db);
- char *ptr= db + db_len + 1;
+ char *next_field= db + db_len + 1;
- if (ptr + 1 < end)
+ if (next_field + 1 < end)
{
- if (thd_init_client_charset(thd, uint2korr(ptr)))
+ if (thd_init_client_charset(thd, uint2korr(next_field)))
DBUG_RETURN(1);
thd->update_charset();
+ next_field+= 2;
}
/* Convert database and user names to utf8 */
@@ -8458,7 +11384,7 @@ static bool parse_com_change_user_packet(MPVIO_EXT *mpvio, uint packet_length)
thd->user_connect= 0;
strmake_buf(sctx->priv_user, sctx->user);
- if (thd->make_lex_string(&mpvio->db, db_buff, db_len, 0) == 0)
+ if (thd->make_lex_string(&mpvio->db, db_buff, db_len) == 0)
DBUG_RETURN(1); /* The error is set by make_lex_string(). */
/*
@@ -8483,13 +11409,13 @@ static bool parse_com_change_user_packet(MPVIO_EXT *mpvio, uint packet_length)
char *client_plugin;
if (thd->client_capabilities & CLIENT_PLUGIN_AUTH)
{
- client_plugin= ptr + 2;
- if (client_plugin >= end)
+ if (next_field >= end)
{
my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
DBUG_RETURN(1);
}
- client_plugin= fix_plugin_ptr(client_plugin);
+ client_plugin= fix_plugin_ptr(next_field);
+ next_field+= strlen(next_field) + 1;
}
else
{
@@ -8501,7 +11427,7 @@ static bool parse_com_change_user_packet(MPVIO_EXT *mpvio, uint packet_length)
/*
For a passwordless accounts we use native_password_plugin.
But when an old 4.0 client connects to it, we change it to
- old_password_plugin, otherwise MySQL will think that server
+ old_password_plugin, otherwise MySQL will think that server
and client plugins don't match.
*/
if (mpvio->acl_user->auth_string.length == 0)
@@ -8509,10 +11435,18 @@ static bool parse_com_change_user_packet(MPVIO_EXT *mpvio, uint packet_length)
}
}
+ if ((mpvio->thd->client_capabilities & CLIENT_CONNECT_ATTRS) &&
+ read_client_connect_attrs(&next_field, end,
+ mpvio->thd->charset()))
+ {
+ my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
+ DBUG_RETURN(packet_error);
+ }
+
DBUG_PRINT("info", ("client_plugin=%s, restart", client_plugin));
- /*
- Remember the data part of the packet, to present it to plugin in
- read_packet()
+ /*
+ Remember the data part of the packet, to present it to plugin in
+ read_packet()
*/
mpvio->cached_client_reply.pkt= passwd;
mpvio->cached_client_reply.pkt_len= passwd_len;
@@ -8543,9 +11477,6 @@ static ulong parse_client_handshake_packet(MPVIO_EXT *mpvio,
*/
DBUG_ASSERT(net->read_pos[pkt_len] == 0);
- if (mpvio->connect_errors)
- reset_host_errors(thd->main_security_ctx.ip);
-
ulong client_capabilities= uint2korr(net->read_pos);
if (client_capabilities & CLIENT_PROTOCOL_41)
{
@@ -8635,19 +11566,27 @@ static ulong parse_client_handshake_packet(MPVIO_EXT *mpvio,
Cast *passwd to an unsigned char, so that it doesn't extend the sign for
*passwd > 127 and become 2**32-127+ after casting to uint.
*/
- uint passwd_len= thd->client_capabilities & CLIENT_SECURE_CONNECTION ?
- (uchar)(*passwd++) : strlen(passwd);
-
+ uint passwd_len;
+ if (!(thd->client_capabilities & CLIENT_SECURE_CONNECTION))
+ passwd_len= strlen(passwd);
+ else if (!(thd->client_capabilities & CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA))
+ passwd_len= (uchar)(*passwd++);
+ else
+ passwd_len= safe_net_field_length_ll((uchar**)&passwd,
+ net->read_pos + pkt_len - (uchar*)passwd);
+
db= thd->client_capabilities & CLIENT_CONNECT_WITH_DB ?
db + passwd_len + 1 : 0;
- if (passwd + passwd_len + test(db) > (char *)net->read_pos + pkt_len)
+ if (passwd == NULL ||
+ passwd + passwd_len + MY_TEST(db) > (char*) net->read_pos + pkt_len)
return packet_error;
/* strlen() can't be easily deleted without changing protocol */
db_len= db ? strlen(db) : 0;
- char *client_plugin= passwd + passwd_len + (db ? db_len + 1 : 0);
+ char *next_field;
+ char *client_plugin= next_field= passwd + passwd_len + (db ? db_len + 1 : 0);
/* Since 4.1 all database names are stored in utf8 */
if (db)
@@ -8685,7 +11624,7 @@ static ulong parse_client_handshake_packet(MPVIO_EXT *mpvio,
Security_context *sctx= thd->security_ctx;
- if (thd->make_lex_string(&mpvio->db, db, db_len, 0) == 0)
+ if (thd->make_lex_string(&mpvio->db, db, db_len) == 0)
return packet_error; /* The error is set by make_lex_string(). */
my_free(sctx->user);
if (!(sctx->user= my_strndup(user, user_len, MYF(MY_WME))))
@@ -8714,6 +11653,7 @@ static ulong parse_client_handshake_packet(MPVIO_EXT *mpvio,
(client_plugin < (char *)net->read_pos + pkt_len))
{
client_plugin= fix_plugin_ptr(client_plugin);
+ next_field+= strlen(next_field) + 1;
}
else
{
@@ -8728,14 +11668,19 @@ static ulong parse_client_handshake_packet(MPVIO_EXT *mpvio,
/*
For a passwordless accounts we use native_password_plugin.
But when an old 4.0 client connects to it, we change it to
- old_password_plugin, otherwise MySQL will think that server
+ old_password_plugin, otherwise MySQL will think that server
and client plugins don't match.
*/
if (mpvio->acl_user->auth_string.length == 0)
mpvio->acl_user->plugin= old_password_plugin_name;
}
}
-
+
+ if ((thd->client_capabilities & CLIENT_CONNECT_ATTRS) &&
+ read_client_connect_attrs(&next_field, ((char *)net->read_pos) + pkt_len,
+ mpvio->thd->charset()))
+ return packet_error;
+
/*
if the acl_user needs a different plugin to authenticate
(specified in GRANT ... AUTHENTICATED VIA plugin_name ..)
@@ -8877,9 +11822,6 @@ static int server_mpvio_read_packet(MYSQL_PLUGIN_VIO *param, uchar **buf)
mpvio->cached_client_reply.pkt= 0;
mpvio->packets_read++;
- if (mpvio->make_it_fail)
- goto err;
-
DBUG_RETURN ((int) mpvio->cached_client_reply.pkt_len);
}
@@ -8914,22 +11856,13 @@ static int server_mpvio_read_packet(MYSQL_PLUGIN_VIO *param, uchar **buf)
else
*buf= mpvio->thd->net.read_pos;
- if (mpvio->make_it_fail)
- goto err;
-
DBUG_RETURN((int)pkt_len);
err:
if (mpvio->status == MPVIO_EXT::FAILURE)
{
- inc_host_errors(mpvio->thd->security_ctx->ip);
if (!mpvio->thd->is_error())
- {
- if (mpvio->make_it_fail)
- login_failed_error(mpvio->thd);
- else
- my_error(ER_HANDSHAKE_ERROR, MYF(0));
- }
+ my_error(ER_HANDSHAKE_ERROR, MYF(0));
}
DBUG_RETURN(-1);
}
@@ -9041,7 +11974,7 @@ static bool acl_check_ssl(THD *thd, const ACL_USER *acl_user)
#else /* HAVE_OPENSSL */
default:
/*
- If we don't have SSL but SSL is required for this user the
+ If we don't have SSL but SSL is required for this user the
authentication should fail.
*/
return 1;
@@ -9095,6 +12028,9 @@ static int do_auth_once(THD *thd, const LEX_STRING *auth_plugin_name,
else
{
/* Server cannot load the required plugin. */
+ Host_errors errors;
+ errors.m_no_auth_plugin= 1;
+ inc_host_errors(mpvio->thd->security_ctx->ip, &errors);
my_error(ER_PLUGIN_IS_NOT_LOADED, MYF(0), auth_plugin_name->str);
res= CR_ERROR;
}
@@ -9118,8 +12054,6 @@ static int do_auth_once(THD *thd, const LEX_STRING *auth_plugin_name,
Perform the handshake, authorize the client and update thd sctx variables.
@param thd thread handle
- @param connect_errors number of previous failed connect attemps
- from this host
@param com_change_user_pkt_len size of the COM_CHANGE_USER packet
(without the first, command, byte) or 0
if it's not a COM_CHANGE_USER (that is, if
@@ -9128,8 +12062,7 @@ static int do_auth_once(THD *thd, const LEX_STRING *auth_plugin_name,
@retval 0 success, thd is updated.
@retval 1 error
*/
-bool acl_authenticate(THD *thd, uint connect_errors,
- uint com_change_user_pkt_len)
+bool acl_authenticate(THD *thd, uint com_change_user_pkt_len)
{
int res= CR_OK;
MPVIO_EXT mpvio;
@@ -9143,11 +12076,10 @@ bool acl_authenticate(THD *thd, uint connect_errors,
mpvio.write_packet= server_mpvio_write_packet;
mpvio.info= server_mpvio_info;
mpvio.thd= thd;
- mpvio.connect_errors= connect_errors;
mpvio.status= MPVIO_EXT::FAILURE;
mpvio.make_it_fail= false;
mpvio.auth_info.host_or_ip= thd->security_ctx->host_or_ip;
- mpvio.auth_info.host_or_ip_length=
+ mpvio.auth_info.host_or_ip_length=
(unsigned int) strlen(thd->security_ctx->host_or_ip);
DBUG_PRINT("info", ("com_change_user_pkt_len=%u", com_change_user_pkt_len));
@@ -9175,7 +12107,7 @@ bool acl_authenticate(THD *thd, uint connect_errors,
the correct plugin.
*/
- res= do_auth_once(thd, auth_plugin_name, &mpvio);
+ res= do_auth_once(thd, auth_plugin_name, &mpvio);
}
/*
@@ -9191,7 +12123,7 @@ bool acl_authenticate(THD *thd, uint connect_errors,
auth_plugin_name= &mpvio.acl_user->plugin;
res= do_auth_once(thd, auth_plugin_name, &mpvio);
}
- if (mpvio.make_it_fail)
+ if (mpvio.make_it_fail && res == CR_OK)
{
mpvio.status= MPVIO_EXT::FAILURE;
res= CR_ERROR;
@@ -9200,7 +12132,7 @@ bool acl_authenticate(THD *thd, uint connect_errors,
Security_context *sctx= thd->security_ctx;
const ACL_USER *acl_user= mpvio.acl_user;
- thd->password= mpvio.auth_info.password_used; // remember for error messages
+ thd->password= mpvio.auth_info.password_used; // remember for error messages
/*
Log the command here so that the user can check the log
@@ -9215,18 +12147,36 @@ bool acl_authenticate(THD *thd, uint connect_errors,
general_log_print(thd, command, "%s@%s as %s on %s",
sctx->user, sctx->host_or_ip,
sctx->priv_user[0] ? sctx->priv_user : "anonymous",
- mpvio.db.str ? mpvio.db.str : (char*) "");
+ safe_str(mpvio.db.str));
}
else
general_log_print(thd, command, (char*) "%s@%s on %s",
sctx->user, sctx->host_or_ip,
- mpvio.db.str ? mpvio.db.str : (char*) "");
+ safe_str(mpvio.db.str));
}
if (res > CR_OK && mpvio.status != MPVIO_EXT::SUCCESS)
{
+ Host_errors errors;
DBUG_ASSERT(mpvio.status == MPVIO_EXT::FAILURE);
-
+ switch (res)
+ {
+ case CR_AUTH_PLUGIN_ERROR:
+ errors.m_auth_plugin= 1;
+ break;
+ case CR_AUTH_HANDSHAKE:
+ errors.m_handshake= 1;
+ break;
+ case CR_AUTH_USER_CREDENTIALS:
+ errors.m_authentication= 1;
+ break;
+ case CR_ERROR:
+ default:
+ /* Unknown of unspecified auth plugin error. */
+ errors.m_auth_plugin= 1;
+ break;
+ }
+ inc_host_errors(mpvio.thd->security_ctx->ip, &errors);
if (!thd->is_error())
login_failed_error(thd);
DBUG_RETURN(1);
@@ -9238,7 +12188,7 @@ bool acl_authenticate(THD *thd, uint connect_errors,
{
#ifndef NO_EMBEDDED_ACCESS_CHECKS
bool is_proxy_user= FALSE;
- const char *auth_user = acl_user->user ? acl_user->user : "";
+ const char *auth_user = safe_str(acl_user->user.str);
ACL_PROXY_USER *proxy_user;
/* check if the user is allowed to proxy as another user */
proxy_user= acl_find_proxy_user(auth_user, sctx->host, sctx->ip,
@@ -9251,6 +12201,9 @@ bool acl_authenticate(THD *thd, uint connect_errors,
/* we need to find the proxy user, but there was none */
if (!proxy_user)
{
+ Host_errors errors;
+ errors.m_proxy_user= 1;
+ inc_host_errors(mpvio.thd->security_ctx->ip, &errors);
if (!thd->is_error())
login_failed_error(thd);
DBUG_RETURN(1);
@@ -9258,16 +12211,19 @@ bool acl_authenticate(THD *thd, uint connect_errors,
my_snprintf(sctx->proxy_user, sizeof(sctx->proxy_user) - 1,
"'%s'@'%s'", auth_user,
- acl_user->host.hostname ? acl_user->host.hostname : "");
+ safe_str(acl_user->host.hostname));
/* we're proxying : find the proxy user definition */
mysql_mutex_lock(&acl_cache->lock);
- acl_proxy_user= find_acl_user(proxy_user->get_proxied_host() ?
- proxy_user->get_proxied_host() : "",
- mpvio.auth_info.authenticated_as, TRUE);
+ acl_proxy_user= find_user_exact(safe_str(proxy_user->get_proxied_host()),
+ mpvio.auth_info.authenticated_as);
if (!acl_proxy_user)
{
mysql_mutex_unlock(&acl_cache->lock);
+
+ Host_errors errors;
+ errors.m_proxy_user_acl= 1;
+ inc_host_errors(mpvio.thd->security_ctx->ip, &errors);
if (!thd->is_error())
login_failed_error(thd);
DBUG_RETURN(1);
@@ -9278,8 +12234,8 @@ bool acl_authenticate(THD *thd, uint connect_errors,
#endif
sctx->master_access= acl_user->access;
- if (acl_user->user)
- strmake_buf(sctx->priv_user, acl_user->user);
+ if (acl_user->user.str)
+ strmake_buf(sctx->priv_user, acl_user->user.str);
else
*sctx->priv_user= 0;
@@ -9295,6 +12251,9 @@ bool acl_authenticate(THD *thd, uint connect_errors,
*/
if (acl_check_ssl(thd, acl_user))
{
+ Host_errors errors;
+ errors.m_ssl= 1;
+ inc_host_errors(mpvio.thd->security_ctx->ip, &errors);
login_failed_error(thd);
DBUG_RETURN(1);
}
@@ -9309,10 +12268,10 @@ bool acl_authenticate(THD *thd, uint connect_errors,
acl_user->user_resource.updates ||
acl_user->user_resource.conn_per_hour ||
acl_user->user_resource.user_conn || max_user_connections_checking) &&
- get_or_create_user_conn(thd,
- (opt_old_style_user_limits ? sctx->user : sctx->priv_user),
- (opt_old_style_user_limits ? sctx->host_or_ip : sctx->priv_host),
- &acl_user->user_resource))
+ get_or_create_user_conn(thd,
+ (opt_old_style_user_limits ? sctx->user : sctx->priv_user),
+ (opt_old_style_user_limits ? sctx->host_or_ip : sctx->priv_host),
+ &acl_user->user_resource))
DBUG_RETURN(1); // The error is set by get_or_create_user_conn()
}
else
@@ -9322,7 +12281,7 @@ bool acl_authenticate(THD *thd, uint connect_errors,
(thd->user_connect->user_resources.conn_per_hour ||
thd->user_connect->user_resources.user_conn ||
max_user_connections_checking) &&
- check_for_max_user_connections(thd, thd->user_connect))
+ check_for_max_user_connections(thd, thd->user_connect))
{
/* Ensure we don't decrement thd->user_connections->connections twice */
thd->user_connect= 0;
@@ -9377,10 +12336,16 @@ bool acl_authenticate(THD *thd, uint connect_errors,
sctx->external_user= my_strdup(mpvio.auth_info.external_user, MYF(0));
if (res == CR_OK_HANDSHAKE_COMPLETE)
- thd->stmt_da->disable_status();
+ thd->get_stmt_da()->disable_status();
else
my_ok(thd);
+#ifdef HAVE_PSI_THREAD_INTERFACE
+ PSI_THREAD_CALL(set_thread_user_host)
+ (thd->main_security_ctx.user, strlen(thd->main_security_ctx.user),
+ thd->main_security_ctx.host_or_ip, strlen(thd->main_security_ctx.host_or_ip));
+#endif
+
/* Ready to handle queries */
DBUG_RETURN(0);
}
@@ -9408,7 +12373,7 @@ static int native_password_authenticate(MYSQL_PLUGIN_VIO *vio,
create_random_string(thd->scramble, SCRAMBLE_LENGTH, &thd->rand);
/* and send it to the client */
if (mpvio->write_packet(mpvio, (uchar*)thd->scramble, SCRAMBLE_LENGTH + 1))
- DBUG_RETURN(CR_ERROR);
+ DBUG_RETURN(CR_AUTH_HANDSHAKE);
}
/* reply and authenticate */
@@ -9450,34 +12415,35 @@ static int native_password_authenticate(MYSQL_PLUGIN_VIO *vio,
/* read the reply with the encrypted password */
if ((pkt_len= mpvio->read_packet(mpvio, &pkt)) < 0)
- DBUG_RETURN(CR_ERROR);
+ DBUG_RETURN(CR_AUTH_HANDSHAKE);
DBUG_PRINT("info", ("reply read : pkt_len=%d", pkt_len));
#ifdef NO_EMBEDDED_ACCESS_CHECKS
DBUG_RETURN(CR_OK);
#endif
+ DBUG_EXECUTE_IF("native_password_bad_reply", { pkt_len= 12; });
+
if (pkt_len == 0) /* no password */
- DBUG_RETURN(info->auth_string[0] ? CR_ERROR : CR_OK);
+ DBUG_RETURN(mpvio->acl_user->salt_len != 0 ? CR_AUTH_USER_CREDENTIALS : CR_OK);
info->password_used= PASSWORD_USED_YES;
if (pkt_len == SCRAMBLE_LENGTH)
{
if (!mpvio->acl_user->salt_len)
- DBUG_RETURN(CR_ERROR);
+ DBUG_RETURN(CR_AUTH_USER_CREDENTIALS);
if (check_scramble(pkt, thd->scramble, mpvio->acl_user->salt))
- DBUG_RETURN(CR_ERROR);
+ DBUG_RETURN(CR_AUTH_USER_CREDENTIALS);
else
DBUG_RETURN(CR_OK);
}
- inc_host_errors(mpvio->thd->security_ctx->ip);
my_error(ER_HANDSHAKE_ERROR, MYF(0));
- DBUG_RETURN(CR_ERROR);
+ DBUG_RETURN(CR_AUTH_HANDSHAKE);
}
-static int old_password_authenticate(MYSQL_PLUGIN_VIO *vio,
+static int old_password_authenticate(MYSQL_PLUGIN_VIO *vio,
MYSQL_SERVER_AUTH_INFO *info)
{
uchar *pkt;
@@ -9491,12 +12457,12 @@ static int old_password_authenticate(MYSQL_PLUGIN_VIO *vio,
create_random_string(thd->scramble, SCRAMBLE_LENGTH, &thd->rand);
/* and send it to the client */
if (mpvio->write_packet(mpvio, (uchar*)thd->scramble, SCRAMBLE_LENGTH + 1))
- return CR_ERROR;
+ return CR_AUTH_HANDSHAKE;
}
/* read the reply and authenticate */
if ((pkt_len= mpvio->read_packet(mpvio, &pkt)) < 0)
- return CR_ERROR;
+ return CR_AUTH_HANDSHAKE;
#ifdef NO_EMBEDDED_ACCESS_CHECKS
return CR_OK;
@@ -9511,26 +12477,25 @@ static int old_password_authenticate(MYSQL_PLUGIN_VIO *vio,
pkt_len= strnlen((char*)pkt, pkt_len);
if (pkt_len == 0) /* no password */
- return info->auth_string[0] ? CR_ERROR : CR_OK;
+ return info->auth_string[0] ? CR_AUTH_USER_CREDENTIALS : CR_OK;
if (secure_auth(thd))
- return CR_ERROR;
+ return CR_AUTH_HANDSHAKE;
info->password_used= PASSWORD_USED_YES;
if (pkt_len == SCRAMBLE_LENGTH_323)
{
if (!mpvio->acl_user->salt_len)
- return CR_ERROR;
+ return CR_AUTH_USER_CREDENTIALS;
return check_scramble_323(pkt, thd->scramble,
(ulong *) mpvio->acl_user->salt) ?
- CR_ERROR : CR_OK;
+ CR_AUTH_USER_CREDENTIALS : CR_OK;
}
- inc_host_errors(mpvio->thd->security_ctx->ip);
my_error(ER_HANDSHAKE_ERROR, MYF(0));
- return CR_ERROR;
+ return CR_AUTH_HANDSHAKE;
}
static struct st_mysql_auth native_password_handler=
@@ -9561,7 +12526,7 @@ maria_declare_plugin(mysql_password)
NULL, /* status variables */
NULL, /* system variables */
"1.0", /* String version */
- MariaDB_PLUGIN_MATURITY_BETA /* Maturity */
+ MariaDB_PLUGIN_MATURITY_STABLE /* Maturity */
},
{
MYSQL_AUTHENTICATION_PLUGIN, /* type constant */
@@ -9576,6 +12541,13 @@ maria_declare_plugin(mysql_password)
NULL, /* status variables */
NULL, /* system variables */
"1.0", /* String version */
- MariaDB_PLUGIN_MATURITY_BETA /* Maturity */
+ MariaDB_PLUGIN_MATURITY_STABLE /* Maturity */
}
maria_declare_plugin_end;
+
+
+/* called when new user is created or exsisting password is changed */
+int check_password_policy(String *password)
+{
+ return (0);
+}
diff --git a/sql/sql_acl.h b/sql/sql_acl.h
index 3169746419c..1aeb123153e 100644
--- a/sql/sql_acl.h
+++ b/sql/sql_acl.h
@@ -20,30 +20,30 @@
#include "violite.h" /* SSL_type */
#include "sql_class.h" /* LEX_COLUMN */
-#define SELECT_ACL (1L << 0)
-#define INSERT_ACL (1L << 1)
-#define UPDATE_ACL (1L << 2)
-#define DELETE_ACL (1L << 3)
-#define CREATE_ACL (1L << 4)
-#define DROP_ACL (1L << 5)
-#define RELOAD_ACL (1L << 6)
-#define SHUTDOWN_ACL (1L << 7)
-#define PROCESS_ACL (1L << 8)
-#define FILE_ACL (1L << 9)
-#define GRANT_ACL (1L << 10)
-#define REFERENCES_ACL (1L << 11)
-#define INDEX_ACL (1L << 12)
-#define ALTER_ACL (1L << 13)
-#define SHOW_DB_ACL (1L << 14)
-#define SUPER_ACL (1L << 15)
-#define CREATE_TMP_ACL (1L << 16)
-#define LOCK_TABLES_ACL (1L << 17)
-#define EXECUTE_ACL (1L << 18)
-#define REPL_SLAVE_ACL (1L << 19)
-#define REPL_CLIENT_ACL (1L << 20)
-#define CREATE_VIEW_ACL (1L << 21)
-#define SHOW_VIEW_ACL (1L << 22)
-#define CREATE_PROC_ACL (1L << 23)
+#define SELECT_ACL (1L << 0)
+#define INSERT_ACL (1L << 1)
+#define UPDATE_ACL (1L << 2)
+#define DELETE_ACL (1L << 3)
+#define CREATE_ACL (1L << 4)
+#define DROP_ACL (1L << 5)
+#define RELOAD_ACL (1L << 6)
+#define SHUTDOWN_ACL (1L << 7)
+#define PROCESS_ACL (1L << 8)
+#define FILE_ACL (1L << 9)
+#define GRANT_ACL (1L << 10)
+#define REFERENCES_ACL (1L << 11)
+#define INDEX_ACL (1L << 12)
+#define ALTER_ACL (1L << 13)
+#define SHOW_DB_ACL (1L << 14)
+#define SUPER_ACL (1L << 15)
+#define CREATE_TMP_ACL (1L << 16)
+#define LOCK_TABLES_ACL (1L << 17)
+#define EXECUTE_ACL (1L << 18)
+#define REPL_SLAVE_ACL (1L << 19)
+#define REPL_CLIENT_ACL (1L << 20)
+#define CREATE_VIEW_ACL (1L << 21)
+#define SHOW_VIEW_ACL (1L << 22)
+#define CREATE_PROC_ACL (1L << 23)
#define ALTER_PROC_ACL (1L << 24)
#define CREATE_USER_ACL (1L << 25)
#define EVENT_ACL (1L << 26)
@@ -57,7 +57,7 @@
4. acl_init() or whatever - to define behaviour for old privilege tables
5. sql_yacc.yy - for GRANT/REVOKE to work
*/
-#define NO_ACCESS (1L << 30)
+#define NO_ACCESS (1L << 30)
#define DB_ACLS \
(UPDATE_ACL | SELECT_ACL | INSERT_ACL | DELETE_ACL | CREATE_ACL | DROP_ACL | \
GRANT_ACL | REFERENCES_ACL | INDEX_ACL | ALTER_ACL | CREATE_TMP_ACL | \
@@ -95,6 +95,14 @@
CREATE_ACL | DROP_ACL | ALTER_ACL | INDEX_ACL | \
TRIGGER_ACL | REFERENCES_ACL | GRANT_ACL | CREATE_VIEW_ACL | SHOW_VIEW_ACL)
+/**
+ Table-level privileges which are automatically "granted" to everyone on
+ existing temporary tables (CREATE_ACL is necessary for ALTER ... RENAME).
+*/
+#define TMP_TABLE_ACLS \
+(SELECT_ACL | INSERT_ACL | UPDATE_ACL | DELETE_ACL | CREATE_ACL | DROP_ACL | \
+ INDEX_ACL | ALTER_ACL)
+
/*
Defines to change the above bits to how things are stored in tables
This is needed as the 'host' and 'db' table is missing a few privileges
@@ -106,21 +114,21 @@
#define DB_CHUNK1 (GRANT_ACL | REFERENCES_ACL | INDEX_ACL | ALTER_ACL)
#define DB_CHUNK2 (CREATE_TMP_ACL | LOCK_TABLES_ACL)
#define DB_CHUNK3 (CREATE_VIEW_ACL | SHOW_VIEW_ACL | \
- CREATE_PROC_ACL | ALTER_PROC_ACL )
+ CREATE_PROC_ACL | ALTER_PROC_ACL )
#define DB_CHUNK4 (EXECUTE_ACL)
#define DB_CHUNK5 (EVENT_ACL | TRIGGER_ACL)
#define fix_rights_for_db(A) (((A) & DB_CHUNK0) | \
- (((A) << 4) & DB_CHUNK1) | \
- (((A) << 6) & DB_CHUNK2) | \
- (((A) << 9) & DB_CHUNK3) | \
- (((A) << 2) & DB_CHUNK4))| \
+ (((A) << 4) & DB_CHUNK1) | \
+ (((A) << 6) & DB_CHUNK2) | \
+ (((A) << 9) & DB_CHUNK3) | \
+ (((A) << 2) & DB_CHUNK4))| \
(((A) << 9) & DB_CHUNK5)
#define get_rights_for_db(A) (((A) & DB_CHUNK0) | \
- (((A) & DB_CHUNK1) >> 4) | \
- (((A) & DB_CHUNK2) >> 6) | \
- (((A) & DB_CHUNK3) >> 9) | \
- (((A) & DB_CHUNK4) >> 2))| \
+ (((A) & DB_CHUNK1) >> 4) | \
+ (((A) & DB_CHUNK2) >> 6) | \
+ (((A) & DB_CHUNK3) >> 9) | \
+ (((A) & DB_CHUNK4) >> 2))| \
(((A) & DB_CHUNK5) >> 9)
#define TBL_CHUNK0 DB_CHUNK0
#define TBL_CHUNK1 DB_CHUNK1
@@ -137,11 +145,11 @@
#define fix_rights_for_column(A) (((A) & 7) | (((A) & ~7) << 8))
#define get_rights_for_column(A) (((A) & 7) | ((A) >> 8))
#define fix_rights_for_procedure(A) ((((A) << 18) & EXECUTE_ACL) | \
- (((A) << 23) & ALTER_PROC_ACL) | \
- (((A) << 8) & GRANT_ACL))
+ (((A) << 23) & ALTER_PROC_ACL) | \
+ (((A) << 8) & GRANT_ACL))
#define get_rights_for_procedure(A) ((((A) & EXECUTE_ACL) >> 18) | \
- (((A) & ALTER_PROC_ACL) >> 23) | \
- (((A) & GRANT_ACL) >> 8))
+ (((A) & ALTER_PROC_ACL) >> 23) | \
+ (((A) & GRANT_ACL) >> 8))
enum mysql_db_table_field
{
@@ -173,6 +181,11 @@ enum mysql_db_table_field
extern const TABLE_FIELD_DEF mysql_db_table_def;
extern bool mysql_user_table_is_in_short_password_format;
+extern LEX_STRING host_not_specified;
+extern LEX_STRING current_user;
+extern LEX_STRING current_role;
+extern LEX_STRING current_user_and_current_role;
+
static inline int access_denied_error_code(int passwd_used)
{
@@ -188,47 +201,51 @@ my_bool acl_init(bool dont_read_acl_tables);
my_bool acl_reload(THD *thd);
void acl_free(bool end=0);
ulong acl_get(const char *host, const char *ip,
- const char *user, const char *db, my_bool db_is_pattern);
-bool acl_authenticate(THD *thd, uint connect_errors, uint com_change_user_pkt_len);
+ const char *user, const char *db, my_bool db_is_pattern);
+bool acl_authenticate(THD *thd, uint com_change_user_pkt_len);
bool acl_getroot(Security_context *sctx, char *user, char *host,
char *ip, char *db);
bool acl_check_host(const char *host, const char *ip);
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);
+ char *password);
+
+bool mysql_grant_role(THD *thd, List<LEX_USER> &user_list, bool revoke);
bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &user_list,
ulong rights, bool revoke, bool is_proxy);
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,
- List <LEX_USER> &user_list, ulong rights,
- bool revoke, bool write_to_binlog);
+ List <LEX_USER> &user_list, ulong rights,
+ bool revoke, bool write_to_binlog);
my_bool grant_init();
void grant_free(void);
my_bool grant_reload(THD *thd);
bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables,
bool any_combination_will_do, uint number, bool no_errors);
bool check_grant_column (THD *thd, GRANT_INFO *grant,
- const char *db_name, const char *table_name,
- const char *name, uint length, Security_context *sctx);
+ const char *db_name, const char *table_name,
+ const char *name, uint length, Security_context *sctx);
bool check_column_grant_in_table_ref(THD *thd, TABLE_LIST * table_ref,
const char *name, uint length);
-bool check_grant_all_columns(THD *thd, ulong want_access,
+bool check_grant_all_columns(THD *thd, ulong want_access,
Field_iterator_table_ref *fields);
bool check_grant_routine(THD *thd, ulong want_access,
- TABLE_LIST *procs, bool is_proc, bool no_error);
+ TABLE_LIST *procs, bool is_proc, bool no_error);
bool check_grant_db(THD *thd,const char *db);
ulong get_table_grant(THD *thd, TABLE_LIST *table);
ulong get_column_grant(THD *thd, GRANT_INFO *grant,
const char *db_name, const char *table_name,
const char *field_name);
bool mysql_show_grants(THD *thd, LEX_USER *user);
+int fill_schema_enabled_roles(THD *thd, TABLE_LIST *tables, COND *cond);
+int fill_schema_applicable_roles(THD *thd, TABLE_LIST *tables, COND *cond);
void get_privilege_desc(char *to, uint max_length, ulong access);
void get_mqh(const char *user, const char *host, USER_CONN *uc);
-bool mysql_create_user(THD *thd, List <LEX_USER> &list);
-bool mysql_drop_user(THD *thd, List <LEX_USER> &list);
+bool mysql_create_user(THD *thd, List <LEX_USER> &list, bool handle_as_role);
+bool mysql_drop_user(THD *thd, List <LEX_USER> &list, bool handle_as_role);
bool mysql_rename_user(THD *thd, List <LEX_USER> &list);
bool mysql_revoke_all(THD *thd, List <LEX_USER> &list);
void fill_effective_table_privileges(THD *thd, GRANT_INFO *grant,
@@ -245,7 +262,7 @@ int fill_schema_schema_privileges(THD *thd, TABLE_LIST *tables, COND *cond);
int fill_schema_table_privileges(THD *thd, TABLE_LIST *tables, COND *cond);
int fill_schema_column_privileges(THD *thd, TABLE_LIST *tables, COND *cond);
int wild_case_compare(CHARSET_INFO *cs, const char *str,const char *wildstr);
-
+int check_password_policy(String *password);
#ifdef NO_EMBEDDED_ACCESS_CHECKS
#define check_grant(A,B,C,D,E,F) 0
#define check_grant_db(A,B) 0
@@ -382,4 +399,11 @@ get_cached_table_access(GRANT_INTERNAL_INFO *grant_internal_info,
bool acl_check_proxy_grant_access (THD *thd, const char *host, const char *user,
bool with_grant);
+int acl_setrole(THD *thd, char *rolename, ulonglong access);
+int acl_check_setrole(THD *thd, char *rolename, ulonglong *access);
+
+#ifndef DBUG_OFF
+extern ulong role_global_merges, role_db_merges, role_table_merges,
+ role_column_merges, role_routine_merges;
+#endif
#endif /* SQL_ACL_INCLUDED */
diff --git a/sql/sql_admin.cc b/sql/sql_admin.cc
index b6f337aade5..d32b213a838 100644
--- a/sql/sql_admin.cc
+++ b/sql/sql_admin.cc
@@ -14,7 +14,7 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
-#include "sql_class.h" // THD
+#include "sql_class.h" // THD and my_global.h
#include "keycaches.h" // get_key_cache
#include "sql_base.h" // Open_table_context
#include "lock.h" // MYSQL_OPEN_*
@@ -28,7 +28,9 @@
#include "sql_acl.h" // *_ACL
#include "sp.h" // Sroutine_hash_entry
#include "sql_parse.h" // check_table_access
+#include "strfunc.h"
#include "sql_admin.h"
+#include "sql_statistics.h"
/* Prepare, run and cleanup for mysql_recreate_table() */
@@ -41,9 +43,19 @@ static bool admin_recreate_table(THD *thd, TABLE_LIST *table_list)
trans_rollback(thd);
close_thread_tables(thd);
thd->mdl_context.release_transactional_locks();
+
+ /*
+ table_list->table has been closed and freed. Do not reference
+ uninitialized data. open_tables() could fail.
+ */
+ table_list->table= NULL;
+ /* Same applies to MDL ticket. */
+ table_list->mdl_request.ticket= NULL;
+
DEBUG_SYNC(thd, "ha_admin_try_alter");
tmp_disable_binlog(thd); // binlogging is done by caller if wanted
- result_code= mysql_recreate_table(thd, table_list);
+ result_code= (open_temporary_tables(thd, table_list) ||
+ mysql_recreate_table(thd, table_list, false));
reenable_binlog(thd);
/*
mysql_recreate_table() can push OK or ERROR.
@@ -51,8 +63,8 @@ static bool admin_recreate_table(THD *thd, TABLE_LIST *table_list)
we will store the error message in a result set row
and then clear.
*/
- if (thd->stmt_da->is_ok())
- thd->stmt_da->reset_diagnostics_area();
+ if (thd->get_stmt_da()->is_ok())
+ thd->get_stmt_da()->reset_diagnostics_area();
table_list->table= NULL;
result_code= result_code ? HA_ADMIN_FAILED : HA_ADMIN_OK;
DBUG_RETURN(result_code);
@@ -88,7 +100,6 @@ static int prepare_for_repair(THD *thd, TABLE_LIST *table_list,
const char **ext;
MY_STAT stat_info;
Open_table_context ot_ctx(thd, (MYSQL_OPEN_IGNORE_FLUSH |
- MYSQL_OPEN_FOR_REPAIR |
MYSQL_OPEN_HAS_MDL_LOCK |
MYSQL_LOCK_IGNORE_TIMEOUT));
DBUG_ENTER("prepare_for_repair");
@@ -98,8 +109,6 @@ static int prepare_for_repair(THD *thd, TABLE_LIST *table_list,
if (!(table= table_list->table))
{
- char key[MAX_DBKEY_LENGTH];
- uint key_length;
/*
If the table didn't exist, we have a shared metadata lock
on it that is left from mysql_admin_table()'s attempt to
@@ -112,32 +121,23 @@ static int prepare_for_repair(THD *thd, TABLE_LIST *table_list,
Attempt to do full-blown table open in mysql_admin_table() has failed.
Let us try to open at least a .FRM for this table.
*/
- my_hash_value_type hash_value;
- key_length= create_table_def_key(thd, key, table_list, 0);
table_list->mdl_request.init(MDL_key::TABLE,
table_list->db, table_list->table_name,
MDL_EXCLUSIVE, MDL_TRANSACTION);
if (lock_table_names(thd, table_list, table_list->next_global,
- thd->variables.lock_wait_timeout,
- MYSQL_OPEN_SKIP_TEMPORARY))
+ thd->variables.lock_wait_timeout, 0))
DBUG_RETURN(0);
has_mdl_lock= TRUE;
- hash_value= my_calc_hash(&table_def_cache, (uchar*) key, key_length);
- mysql_mutex_lock(&LOCK_open);
- share= get_table_share(thd, table_list, key, key_length, 0,
- &error, hash_value);
- mysql_mutex_unlock(&LOCK_open);
+ share= tdc_acquire_share_shortlived(thd, table_list, GTS_TABLE);
if (share == NULL)
DBUG_RETURN(0); // Can't open frm file
if (open_table_from_share(thd, share, "", 0, 0, 0, &tmp_table, FALSE))
{
- mysql_mutex_lock(&LOCK_open);
- release_table_share(share);
- mysql_mutex_unlock(&LOCK_open);
+ tdc_release_share(share);
DBUG_RETURN(0); // Out of memory
}
table= &tmp_table;
@@ -199,13 +199,11 @@ static int prepare_for_repair(THD *thd, TABLE_LIST *table_list,
to close it, but leave it protected by exclusive metadata lock.
*/
pos_in_locked_tables= table->pos_in_locked_tables;
- if (wait_while_table_is_used(thd, table,
- HA_EXTRA_PREPARE_FOR_FORCED_CLOSE,
- TDC_RT_REMOVE_NOT_OWN_AND_MARK_NOT_USABLE))
+ if (wait_while_table_is_used(thd, table, HA_EXTRA_PREPARE_FOR_FORCED_CLOSE))
goto end;
/* Close table but don't remove from locked list */
close_all_tables_for_name(thd, table_list->table->s,
- HA_EXTRA_NOT_USED);
+ HA_EXTRA_NOT_USED, NULL);
table_list->table= 0;
}
/*
@@ -263,11 +261,7 @@ static int prepare_for_repair(THD *thd, TABLE_LIST *table_list,
end:
thd->locked_tables_list.unlink_all_closed_tables(thd, NULL, 0);
if (table == &tmp_table)
- {
- mysql_mutex_lock(&LOCK_open);
closefrm(table, 1); // Free allocated memory
- mysql_mutex_unlock(&LOCK_open);
- }
/* In case of a temporary table there will be no metadata lock. */
if (error && has_mdl_lock)
thd->mdl_context.release_transactional_locks();
@@ -326,7 +320,10 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
Protocol *protocol= thd->protocol;
LEX *lex= thd->lex;
int result_code;
+ int compl_result_code;
bool need_repair_or_alter= 0;
+ wait_for_commit* suspended_wfc;
+
DBUG_ENTER("mysql_admin_table");
DBUG_PRINT("enter", ("extra_open_options: %u", extra_open_options));
@@ -343,8 +340,23 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(TRUE);
+ /*
+ This function calls trans_commit() during its operation, but that does not
+ imply that the operation is complete or binlogged. So we have to suspend
+ temporarily the wakeup_subsequent_commits() calls (if used).
+ */
+ suspended_wfc= thd->suspend_subsequent_commits();
+
mysql_ha_rm_tables(thd, tables);
+ /*
+ Close all temporary tables which were pre-open to simplify
+ privilege checking. Clear all references to closed tables.
+ */
+ close_thread_tables(thd);
+ for (table= tables; table; table= table->next_local)
+ table->table= NULL;
+
for (table= tables; table; table= table->next_local)
{
char table_name[SAFE_NAME_LEN*2+2];
@@ -387,7 +399,7 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
to differentiate from ALTER TABLE...CHECK PARTITION on which view is not
allowed.
*/
- if (lex->alter_info.flags & ALTER_ADMIN_PARTITION ||
+ if (lex->alter_info.flags & Alter_info::ALTER_ADMIN_PARTITION ||
view_operator_func == NULL)
{
table->required_type=FRMTYPE_TABLE;
@@ -418,14 +430,15 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
because it's already known that the table is badly damaged.
*/
- Warning_info wi(thd->query_id, false);
- Warning_info *wi_saved= thd->warning_info;
+ Diagnostics_area *da= thd->get_stmt_da();
+ Warning_info tmp_wi(thd->query_id, false, true);
- thd->warning_info= &wi;
+ da->push_warning_info(&tmp_wi);
- open_error= open_and_lock_tables(thd, table, TRUE, 0);
+ open_error= (open_temporary_tables(thd, table) ||
+ open_and_lock_tables(thd, table, TRUE, 0));
- thd->warning_info= wi_saved;
+ da->pop_warning_info();
}
else
{
@@ -437,7 +450,8 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
mode. It does make sense for the user to see such errors.
*/
- open_error= open_and_lock_tables(thd, table, TRUE, 0);
+ open_error= (open_temporary_tables(thd, table) ||
+ open_and_lock_tables(thd, table, TRUE, 0));
}
thd->prepare_derived_at_open= FALSE;
@@ -472,12 +486,12 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
*/
Alter_info *alter_info= &lex->alter_info;
- if (alter_info->flags & ALTER_ADMIN_PARTITION)
+ if (alter_info->flags & Alter_info::ALTER_ADMIN_PARTITION)
{
if (!table->table->part_info)
{
my_error(ER_PARTITION_MGMT_ON_NONPARTITIONED, MYF(0));
- DBUG_RETURN(TRUE);
+ goto err2;
}
if (set_part_state(alter_info, table->table->part_info, PART_ADMIN))
{
@@ -536,16 +550,16 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
if (!table->table)
{
DBUG_PRINT("admin", ("open table failed"));
- if (thd->warning_info->is_empty())
- push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ if (thd->get_stmt_da()->is_warning_info_empty())
+ push_warning(thd, Sql_condition::WARN_LEVEL_WARN,
ER_CHECK_NO_SUCH_TABLE, ER(ER_CHECK_NO_SUCH_TABLE));
/* if it was a view will check md5 sum */
if (table->view &&
view_check(thd, table, check_opt) == HA_ADMIN_WRONG_CHECKSUM)
- push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning(thd, Sql_condition::WARN_LEVEL_WARN,
ER_VIEW_CHECKSUM, ER(ER_VIEW_CHECKSUM));
- if (thd->stmt_da->is_error() &&
- table_not_corrupt_error(thd->stmt_da->sql_errno()))
+ if (thd->get_stmt_da()->is_error() &&
+ table_not_corrupt_error(thd->get_stmt_da()->sql_errno()))
result_code= HA_ADMIN_FAILED;
else
/* Default failure code is corrupt table */
@@ -593,7 +607,7 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
table->table=0; // For query cache
if (protocol->write())
goto err;
- thd->stmt_da->reset_diagnostics_area();
+ thd->get_stmt_da()->reset_diagnostics_area();
continue;
/* purecov: end */
}
@@ -613,10 +627,8 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
*/
if (lock_type == TL_WRITE && !table->table->s->tmp_table)
{
- table->table->s->protect_against_usage();
if (wait_while_table_is_used(thd, table->table,
- HA_EXTRA_PREPARE_FOR_RENAME,
- TDC_RT_REMOVE_NOT_OWN_AND_MARK_NOT_USABLE))
+ HA_EXTRA_PREPARE_FOR_RENAME))
goto err;
DEBUG_SYNC(thd, "after_admin_flush");
/* Flush entries in the query cache involving this table. */
@@ -666,11 +678,105 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
}
}
- DBUG_PRINT("admin", ("calling operator_func '%s'", operator_name));
- thd_proc_info(thd, "executing");
- result_code = (table->table->file->*operator_func)(thd, check_opt);
- thd_proc_info(thd, "Sending data");
- DBUG_PRINT("admin", ("operator_func returned: %d", result_code));
+ result_code= compl_result_code= HA_ADMIN_OK;
+
+ if (operator_func == &handler::ha_analyze)
+ {
+ TABLE *tab= table->table;
+ Field **field_ptr= tab->field;
+
+ if (lex->with_persistent_for_clause &&
+ tab->s->table_category != TABLE_CATEGORY_USER)
+ {
+ compl_result_code= result_code= HA_ADMIN_INVALID;
+ }
+
+ if (!lex->column_list)
+ {
+ uint fields= 0;
+ for ( ; *field_ptr; field_ptr++, fields++) ;
+ bitmap_set_prefix(tab->read_set, fields);
+ }
+ else
+ {
+ int pos;
+ LEX_STRING *column_name;
+ List_iterator_fast<LEX_STRING> it(*lex->column_list);
+
+ bitmap_clear_all(tab->read_set);
+ while ((column_name= it++))
+ {
+ if (tab->s->fieldnames.type_names == 0 ||
+ (pos= find_type(&tab->s->fieldnames, column_name->str,
+ column_name->length, 1)) <= 0)
+ {
+ compl_result_code= result_code= HA_ADMIN_INVALID;
+ break;
+ }
+ bitmap_set_bit(tab->read_set, pos-1);
+ }
+ tab->file->column_bitmaps_signal();
+ }
+
+ if (!lex->index_list)
+ {
+ tab->keys_in_use_for_query.init(tab->s->keys);
+ }
+ else
+ {
+ int pos;
+ LEX_STRING *index_name;
+ List_iterator_fast<LEX_STRING> it(*lex->index_list);
+
+ tab->keys_in_use_for_query.clear_all();
+ while ((index_name= it++))
+ {
+ if (tab->s->keynames.type_names == 0 ||
+ (pos= find_type(&tab->s->keynames, index_name->str,
+ index_name->length, 1)) <= 0)
+ {
+ compl_result_code= result_code= HA_ADMIN_INVALID;
+ break;
+ }
+ tab->keys_in_use_for_query.set_bit(--pos);
+ }
+ }
+ }
+
+ if (result_code == HA_ADMIN_OK)
+ {
+ DBUG_PRINT("admin", ("calling operator_func '%s'", operator_name));
+ THD_STAGE_INFO(thd, stage_executing);
+ result_code = (table->table->file->*operator_func)(thd, check_opt);
+ THD_STAGE_INFO(thd, stage_sending_data);
+ DBUG_PRINT("admin", ("operator_func returned: %d", result_code));
+ }
+
+ if (compl_result_code == HA_ADMIN_OK &&
+ operator_func == &handler::ha_analyze &&
+ table->table->s->table_category == TABLE_CATEGORY_USER &&
+ (get_use_stat_tables_mode(thd) > NEVER ||
+ lex->with_persistent_for_clause))
+ {
+ if (!(compl_result_code=
+ alloc_statistics_for_table(thd, table->table)) &&
+ !(compl_result_code=
+ collect_statistics_for_table(thd, table->table)))
+ compl_result_code= update_statistics_for_table(thd, table->table);
+ if (compl_result_code)
+ result_code= HA_ADMIN_FAILED;
+ else
+ {
+ protocol->prepare_for_resend();
+ protocol->store(table_name, system_charset_info);
+ protocol->store(operator_name, system_charset_info);
+ protocol->store(STRING_WITH_LEN("status"), system_charset_info);
+ protocol->store(STRING_WITH_LEN("Engine-independent statistics collected"),
+ system_charset_info);
+ if (protocol->write())
+ goto err;
+ }
+ }
if (result_code == HA_ADMIN_NOT_IMPLEMENTED && need_repair_or_alter)
{
@@ -685,8 +791,9 @@ send_result:
lex->cleanup_after_one_table_open();
thd->clear_error(); // these errors shouldn't get client
{
- List_iterator_fast<MYSQL_ERROR> it(thd->warning_info->warn_list());
- MYSQL_ERROR *err;
+ Diagnostics_area::Sql_condition_iterator it=
+ thd->get_stmt_da()->sql_conditions();
+ const Sql_condition *err;
while ((err= it++))
{
protocol->prepare_for_resend();
@@ -699,7 +806,7 @@ send_result:
if (protocol->write())
goto err;
}
- thd->warning_info->clear_warning_info(thd->query_id);
+ thd->get_stmt_da()->clear_warning_info(thd->query_id);
}
protocol->prepare_for_resend();
protocol->store(table_name, system_charset_info);
@@ -767,19 +874,10 @@ send_result_message:
case HA_ADMIN_TRY_ALTER:
{
- uint save_flags;
Alter_info *alter_info= &lex->alter_info;
- /* Store the original value of alter_info->flags */
- save_flags= alter_info->flags;
- /*
- This is currently used only by InnoDB. ha_innobase::optimize() answers
- "try with alter", so here we close the table, do an ALTER TABLE,
- reopen the table and do ha_innobase::analyze() on it.
- We have to end the row, so analyze could return more rows.
- */
protocol->store(STRING_WITH_LEN("note"), system_charset_info);
- if(alter_info->flags & ALTER_ADMIN_PARTITION)
+ if (alter_info->flags & Alter_info::ALTER_ADMIN_PARTITION)
{
protocol->store(STRING_WITH_LEN(
"Table does not support optimize on partitions. All partitions "
@@ -793,17 +891,21 @@ send_result_message:
}
if (protocol->write())
goto err;
- thd_proc_info(thd, "recreating table");
+ THD_STAGE_INFO(thd, stage_recreating_table);
DBUG_PRINT("info", ("HA_ADMIN_TRY_ALTER, trying analyze..."));
TABLE_LIST *save_next_local= table->next_local,
*save_next_global= table->next_global;
table->next_local= table->next_global= 0;
- result_code= admin_recreate_table(thd, table);
+ tmp_disable_binlog(thd); // binlogging is done by caller if wanted
+ result_code= admin_recreate_table(thd, table);
+ reenable_binlog(thd);
trans_commit_stmt(thd);
trans_commit(thd);
close_thread_tables(thd);
thd->mdl_context.release_transactional_locks();
+ /* Clear references to TABLE and MDL_ticket after releasing them. */
+ table->mdl_request.ticket= NULL;
if (!result_code) // recreation went ok
{
@@ -811,22 +913,27 @@ send_result_message:
table->mdl_request.ticket= NULL;
DEBUG_SYNC(thd, "ha_admin_open_ltable");
table->mdl_request.set_type(MDL_SHARED_WRITE);
- /*
- Reset the ALTER_ADMIN_PARTITION bit in alter_info->flags
- to force analyze on all partitions.
- */
- alter_info->flags &= ~(ALTER_ADMIN_PARTITION);
- if ((table->table= open_ltable(thd, table, lock_type, 0)))
+ if (!open_temporary_tables(thd, table) &&
+ (table->table= open_ltable(thd, table, lock_type, 0)))
{
+ uint save_flags;
+ /* Store the original value of alter_info->flags */
+ save_flags= alter_info->flags;
+
+ /*
+ Reset the ALTER_ADMIN_PARTITION bit in alter_info->flags
+ to force analyze on all partitions.
+ */
+ alter_info->flags &= ~(Alter_info::ALTER_ADMIN_PARTITION);
result_code= table->table->file->ha_analyze(thd, check_opt);
if (result_code == HA_ADMIN_ALREADY_DONE)
result_code= HA_ADMIN_OK;
else if (result_code) // analyze failed
table->table->file->print_error(result_code, MYF(0));
+ alter_info->flags= save_flags;
}
else
result_code= -1; // open failed
- alter_info->flags= save_flags;
}
/* Start a new row for the final status row */
protocol->prepare_for_resend();
@@ -834,10 +941,10 @@ send_result_message:
protocol->store(operator_name, system_charset_info);
if (result_code) // either mysql_recreate_table or analyze failed
{
- DBUG_ASSERT(thd->is_error() || thd->killed);
+ DBUG_ASSERT(thd->is_error());
if (thd->is_error())
{
- const char *err_msg= thd->stmt_da->message();
+ const char *err_msg= thd->get_stmt_da()->message();
if (!thd->vio_ok())
{
sql_print_error("%s", err_msg);
@@ -856,6 +963,9 @@ send_result_message:
}
thd->clear_error();
}
+ /* Make sure this table instance is not reused after the operation. */
+ if (table->table)
+ table->table->m_needs_reopen= true;
}
result_code= result_code ? HA_ADMIN_FAILED : HA_ADMIN_OK;
table->next_local= save_next_local;
@@ -971,18 +1081,23 @@ send_result_message:
}
my_eof(thd);
+ thd->resume_subsequent_commits(suspended_wfc);
+ DBUG_EXECUTE_IF("inject_analyze_table_sleep", my_sleep(500000););
DBUG_RETURN(FALSE);
err:
/* Make sure this table instance is not reused after the failure. */
- if (table && table->table)
- table->table->m_needs_reopen= true;
trans_rollback_stmt(thd);
trans_rollback(thd);
+ if (table && table->table)
+ {
+ table->table->m_needs_reopen= true;
+ table->table= 0;
+ }
close_thread_tables(thd); // Shouldn't be needed
thd->mdl_context.release_transactional_locks();
- if (table)
- table->table=0;
+err2:
+ thd->resume_subsequent_commits(suspended_wfc);
DBUG_RETURN(TRUE);
}
@@ -1007,7 +1122,7 @@ bool mysql_assign_to_keycache(THD* thd, TABLE_LIST* tables,
KEY_CACHE *key_cache;
DBUG_ENTER("mysql_assign_to_keycache");
- thd_proc_info(thd, "Finding key cache");
+ THD_STAGE_INFO(thd, stage_finding_key_cache);
check_opt.init();
mysql_mutex_lock(&LOCK_global_system_variables);
if (!(key_cache= get_key_cache(key_cache_name)))
@@ -1056,16 +1171,18 @@ bool mysql_preload_keys(THD* thd, TABLE_LIST* tables)
}
-bool Analyze_table_statement::execute(THD *thd)
+bool Sql_cmd_analyze_table::execute(THD *thd)
{
+ LEX *m_lex= thd->lex;
TABLE_LIST *first_table= m_lex->select_lex.table_list.first;
bool res= TRUE;
thr_lock_type lock_type = TL_READ_NO_INSERT;
- DBUG_ENTER("Analyze_table_statement::execute");
+ DBUG_ENTER("Sql_cmd_analyze_table::execute");
if (check_table_access(thd, SELECT_ACL | INSERT_ACL, first_table,
FALSE, UINT_MAX, FALSE))
goto error;
+ WSREP_TO_ISOLATION_BEGIN_WRTCHK(NULL, NULL, first_table);
thd->enable_slow_log= opt_log_slow_admin_statements;
res= mysql_admin_table(thd, first_table, &m_lex->check_opt,
"analyze", lock_type, 1, 0, 0, 0,
@@ -1086,12 +1203,13 @@ error:
}
-bool Check_table_statement::execute(THD *thd)
+bool Sql_cmd_check_table::execute(THD *thd)
{
+ LEX *m_lex= thd->lex;
TABLE_LIST *first_table= m_lex->select_lex.table_list.first;
thr_lock_type lock_type = TL_READ_NO_INSERT;
bool res= TRUE;
- DBUG_ENTER("Check_table_statement::execute");
+ DBUG_ENTER("Sql_cmd_check_table::execute");
if (check_table_access(thd, SELECT_ACL, first_table,
TRUE, UINT_MAX, FALSE))
@@ -1110,20 +1228,20 @@ error:
}
-bool Optimize_table_statement::execute(THD *thd)
+bool Sql_cmd_optimize_table::execute(THD *thd)
{
+ LEX *m_lex= thd->lex;
TABLE_LIST *first_table= m_lex->select_lex.table_list.first;
bool res= TRUE;
- DBUG_ENTER("Optimize_table_statement::execute");
+ DBUG_ENTER("Sql_cmd_optimize_table::execute");
if (check_table_access(thd, SELECT_ACL | INSERT_ACL, first_table,
FALSE, UINT_MAX, FALSE))
goto error; /* purecov: inspected */
+ WSREP_TO_ISOLATION_BEGIN_WRTCHK(NULL, NULL, first_table);
thd->enable_slow_log= opt_log_slow_admin_statements;
-
- WSREP_TO_ISOLATION_BEGIN(first_table->db, first_table->table_name, NULL)
- res= (specialflag & (SPECIAL_SAFE_MODE | SPECIAL_NO_NEW_FUNC)) ?
- mysql_recreate_table(thd, first_table) :
+ res= (specialflag & SPECIAL_NO_NEW_FUNC) ?
+ mysql_recreate_table(thd, first_table, true) :
mysql_admin_table(thd, first_table, &m_lex->check_opt,
"optimize", TL_WRITE, 1, 0, 0, 0,
&handler::ha_optimize, 0);
@@ -1143,20 +1261,21 @@ error:
}
-bool Repair_table_statement::execute(THD *thd)
+bool Sql_cmd_repair_table::execute(THD *thd)
{
+ LEX *m_lex= thd->lex;
TABLE_LIST *first_table= m_lex->select_lex.table_list.first;
bool res= TRUE;
- DBUG_ENTER("Repair_table_statement::execute");
+ DBUG_ENTER("Sql_cmd_repair_table::execute");
if (check_table_access(thd, SELECT_ACL | INSERT_ACL, first_table,
FALSE, UINT_MAX, FALSE))
goto error; /* purecov: inspected */
thd->enable_slow_log= opt_log_slow_admin_statements;
- WSREP_TO_ISOLATION_BEGIN(first_table->db, first_table->table_name, NULL)
+ WSREP_TO_ISOLATION_BEGIN_WRTCHK(NULL, NULL, first_table);
res= mysql_admin_table(thd, first_table, &m_lex->check_opt, "repair",
TL_WRITE, 1,
- test(m_lex->check_opt.sql_flags & TT_USEFRM),
+ MY_TEST(m_lex->check_opt.sql_flags & TT_USEFRM),
HA_OPEN_FOR_REPAIR, &prepare_for_repair,
&handler::ha_repair, &view_repair);
diff --git a/sql/sql_admin.h b/sql/sql_admin.h
index 43d8f70c6f4..96594fad0cb 100644
--- a/sql/sql_admin.h
+++ b/sql/sql_admin.h
@@ -17,7 +17,7 @@
#define SQL_TABLE_MAINTENANCE_H
/* Must be able to hold ALTER TABLE t PARTITION BY ... KEY ALGORITHM = 1 ... */
-#define SQL_ADMIN_MSG_TEXT_SIZE 128 * 1024
+#define SQL_ADMIN_MSG_TEXT_SIZE (128 * 1024)
bool mysql_assign_to_keycache(THD* thd, TABLE_LIST* table_list,
LEX_STRING *key_cache_name);
@@ -26,109 +26,100 @@ int reassign_keycache_tables(THD* thd, KEY_CACHE *src_cache,
KEY_CACHE *dst_cache);
/**
- Analyze_statement represents the ANALYZE TABLE statement.
+ Sql_cmd_analyze_table represents the ANALYZE TABLE statement.
*/
-class Analyze_table_statement : public Sql_statement
+class Sql_cmd_analyze_table : public Sql_cmd
{
public:
/**
Constructor, used to represent a ANALYZE TABLE statement.
- @param lex the LEX structure for this statement.
*/
- Analyze_table_statement(LEX *lex)
- : Sql_statement(lex)
+ Sql_cmd_analyze_table()
{}
- ~Analyze_table_statement()
+ ~Sql_cmd_analyze_table()
{}
- /**
- Execute a ANALYZE TABLE statement at runtime.
- @param thd the current thread.
- @return false on success.
- */
bool execute(THD *thd);
+
+ virtual enum_sql_command sql_command_code() const
+ {
+ return SQLCOM_ANALYZE;
+ }
};
/**
- Check_table_statement represents the CHECK TABLE statement.
+ Sql_cmd_check_table represents the CHECK TABLE statement.
*/
-class Check_table_statement : public Sql_statement
+class Sql_cmd_check_table : public Sql_cmd
{
public:
/**
Constructor, used to represent a CHECK TABLE statement.
- @param lex the LEX structure for this statement.
*/
- Check_table_statement(LEX *lex)
- : Sql_statement(lex)
+ Sql_cmd_check_table()
{}
- ~Check_table_statement()
+ ~Sql_cmd_check_table()
{}
- /**
- Execute a CHECK TABLE statement at runtime.
- @param thd the current thread.
- @return false on success.
- */
bool execute(THD *thd);
-};
+ virtual enum_sql_command sql_command_code() const
+ {
+ return SQLCOM_CHECK;
+ }
+};
/**
- Optimize_table_statement represents the OPTIMIZE TABLE statement.
+ Sql_cmd_optimize_table represents the OPTIMIZE TABLE statement.
*/
-class Optimize_table_statement : public Sql_statement
+class Sql_cmd_optimize_table : public Sql_cmd
{
public:
/**
Constructor, used to represent a OPTIMIZE TABLE statement.
- @param lex the LEX structure for this statement.
*/
- Optimize_table_statement(LEX *lex)
- : Sql_statement(lex)
+ Sql_cmd_optimize_table()
{}
- ~Optimize_table_statement()
+ ~Sql_cmd_optimize_table()
{}
- /**
- Execute a OPTIMIZE TABLE statement at runtime.
- @param thd the current thread.
- @return false on success.
- */
bool execute(THD *thd);
+
+ virtual enum_sql_command sql_command_code() const
+ {
+ return SQLCOM_OPTIMIZE;
+ }
};
/**
- Repair_table_statement represents the REPAIR TABLE statement.
+ Sql_cmd_repair_table represents the REPAIR TABLE statement.
*/
-class Repair_table_statement : public Sql_statement
+class Sql_cmd_repair_table : public Sql_cmd
{
public:
/**
Constructor, used to represent a REPAIR TABLE statement.
- @param lex the LEX structure for this statement.
*/
- Repair_table_statement(LEX *lex)
- : Sql_statement(lex)
+ Sql_cmd_repair_table()
{}
- ~Repair_table_statement()
+ ~Sql_cmd_repair_table()
{}
- /**
- Execute a REPAIR TABLE statement at runtime.
- @param thd the current thread.
- @return false on success.
- */
bool execute(THD *thd);
+
+ virtual enum_sql_command sql_command_code() const
+ {
+ return SQLCOM_REPAIR;
+ }
};
#endif
diff --git a/sql/sql_alter.cc b/sql/sql_alter.cc
index 0b4636c1f0f..a39f07ae35d 100644
--- a/sql/sql_alter.cc
+++ b/sql/sql_alter.cc
@@ -16,11 +16,178 @@
#include "sql_parse.h" // check_access
#include "sql_table.h" // mysql_alter_table,
// mysql_exchange_partition
+#include "sql_base.h" // open_temporary_tables
#include "sql_alter.h"
#ifdef WITH_WSREP
#include "wsrep_mysqld.h"
#endif /* WITH_WSREP */
-bool Alter_table_statement::execute(THD *thd)
+Alter_info::Alter_info(const Alter_info &rhs, MEM_ROOT *mem_root)
+ :drop_list(rhs.drop_list, mem_root),
+ alter_list(rhs.alter_list, mem_root),
+ key_list(rhs.key_list, mem_root),
+ create_list(rhs.create_list, mem_root),
+ flags(rhs.flags),
+ keys_onoff(rhs.keys_onoff),
+ partition_names(rhs.partition_names, mem_root),
+ num_parts(rhs.num_parts),
+ requested_algorithm(rhs.requested_algorithm),
+ requested_lock(rhs.requested_lock)
+{
+ /*
+ Make deep copies of used objects.
+ This is not a fully deep copy - clone() implementations
+ of Alter_drop, Alter_column, Key, foreign_key, Key_part_spec
+ do not copy string constants. At the same length the only
+ reason we make a copy currently is that ALTER/CREATE TABLE
+ code changes input Alter_info definitions, but string
+ constants never change.
+ */
+ list_copy_and_replace_each_value(drop_list, mem_root);
+ list_copy_and_replace_each_value(alter_list, mem_root);
+ list_copy_and_replace_each_value(key_list, mem_root);
+ list_copy_and_replace_each_value(create_list, mem_root);
+ /* partition_names are not deeply copied currently */
+}
+
+
+bool Alter_info::set_requested_algorithm(const LEX_STRING *str)
+{
+ // To avoid adding new keywords to the grammar, we match strings here.
+ if (!my_strcasecmp(system_charset_info, str->str, "INPLACE"))
+ requested_algorithm= ALTER_TABLE_ALGORITHM_INPLACE;
+ else if (!my_strcasecmp(system_charset_info, str->str, "COPY"))
+ requested_algorithm= ALTER_TABLE_ALGORITHM_COPY;
+ else if (!my_strcasecmp(system_charset_info, str->str, "DEFAULT"))
+ requested_algorithm= ALTER_TABLE_ALGORITHM_DEFAULT;
+ else
+ return true;
+ return false;
+}
+
+
+bool Alter_info::set_requested_lock(const LEX_STRING *str)
+{
+ // To avoid adding new keywords to the grammar, we match strings here.
+ if (!my_strcasecmp(system_charset_info, str->str, "NONE"))
+ requested_lock= ALTER_TABLE_LOCK_NONE;
+ else if (!my_strcasecmp(system_charset_info, str->str, "SHARED"))
+ requested_lock= ALTER_TABLE_LOCK_SHARED;
+ else if (!my_strcasecmp(system_charset_info, str->str, "EXCLUSIVE"))
+ requested_lock= ALTER_TABLE_LOCK_EXCLUSIVE;
+ else if (!my_strcasecmp(system_charset_info, str->str, "DEFAULT"))
+ requested_lock= ALTER_TABLE_LOCK_DEFAULT;
+ else
+ return true;
+ return false;
+}
+
+
+Alter_table_ctx::Alter_table_ctx()
+ : datetime_field(NULL), error_if_not_empty(false),
+ tables_opened(0),
+ db(NULL), table_name(NULL), alias(NULL),
+ new_db(NULL), new_name(NULL), new_alias(NULL),
+ fk_error_if_delete_row(false), fk_error_id(NULL),
+ fk_error_table(NULL)
+#ifndef DBUG_OFF
+ , tmp_table(false)
+#endif
+{
+}
+
+
+Alter_table_ctx::Alter_table_ctx(THD *thd, TABLE_LIST *table_list,
+ uint tables_opened_arg,
+ char *new_db_arg, char *new_name_arg)
+ : datetime_field(NULL), error_if_not_empty(false),
+ tables_opened(tables_opened_arg),
+ new_db(new_db_arg), new_name(new_name_arg),
+ fk_error_if_delete_row(false), fk_error_id(NULL),
+ fk_error_table(NULL)
+#ifndef DBUG_OFF
+ , tmp_table(false)
+#endif
+{
+ /*
+ Assign members db, table_name, new_db and new_name
+ to simplify further comparisions: we want to see if it's a RENAME
+ later just by comparing the pointers, avoiding the need for strcmp.
+ */
+ db= table_list->db;
+ table_name= table_list->table_name;
+ alias= (lower_case_table_names == 2) ? table_list->alias : table_name;
+
+ if (!new_db || !my_strcasecmp(table_alias_charset, new_db, db))
+ new_db= db;
+
+ if (new_name)
+ {
+ DBUG_PRINT("info", ("new_db.new_name: '%s'.'%s'", new_db, new_name));
+
+ if (lower_case_table_names == 1) // Convert new_name/new_alias to lower case
+ {
+ my_casedn_str(files_charset_info, new_name);
+ new_alias= new_name;
+ }
+ else if (lower_case_table_names == 2) // Convert new_name to lower case
+ {
+ strmov(new_alias= new_alias_buff, new_name);
+ my_casedn_str(files_charset_info, new_name);
+ }
+ else
+ new_alias= new_name; // LCTN=0 => case sensitive + case preserving
+
+ if (!is_database_changed() &&
+ !my_strcasecmp(table_alias_charset, new_name, table_name))
+ {
+ /*
+ Source and destination table names are equal:
+ make is_table_renamed() more efficient.
+ */
+ new_alias= table_name;
+ new_name= table_name;
+ }
+ }
+ else
+ {
+ new_alias= alias;
+ new_name= table_name;
+ }
+
+ my_snprintf(tmp_name, sizeof(tmp_name), "%s-%lx_%lx", tmp_file_prefix,
+ current_pid, thd->thread_id);
+ /* Safety fix for InnoDB */
+ if (lower_case_table_names)
+ my_casedn_str(files_charset_info, tmp_name);
+
+ if (table_list->table->s->tmp_table == NO_TMP_TABLE)
+ {
+ build_table_filename(path, sizeof(path) - 1, db, table_name, "", 0);
+
+ build_table_filename(new_path, sizeof(new_path) - 1, new_db, new_name, "", 0);
+
+ build_table_filename(new_filename, sizeof(new_filename) - 1,
+ new_db, new_name, reg_ext, 0);
+
+ build_table_filename(tmp_path, sizeof(tmp_path) - 1, new_db, tmp_name, "",
+ FN_IS_TMP);
+ }
+ else
+ {
+ /*
+ We are not filling path, new_path and new_filename members if
+ we are altering temporary table as these members are not used in
+ this case. This fact is enforced with assert.
+ */
+ build_tmptable_filename(thd, tmp_path, sizeof(tmp_path));
+#ifndef DBUG_OFF
+ tmp_table= true;
+#endif
+ }
+}
+
+
+bool Sql_cmd_alter_table::execute(THD *thd)
{
LEX *lex= thd->lex;
/* first SELECT_LEX (have special meaning for many of non-SELECTcommands) */
@@ -40,7 +207,7 @@ bool Alter_table_statement::execute(THD *thd)
ulong priv_needed= ALTER_ACL;
bool result;
- DBUG_ENTER("Alter_table_statement::execute");
+ DBUG_ENTER("Sql_cmd_alter_table::execute");
if (thd->is_fatal_error) /* out of memory creating a copy of alter_info */
DBUG_RETURN(TRUE);
@@ -48,12 +215,14 @@ bool Alter_table_statement::execute(THD *thd)
We also require DROP priv for ALTER TABLE ... DROP PARTITION, as well
as for RENAME TO, as being done by SQLCOM_RENAME_TABLE
*/
- if (alter_info.flags & (ALTER_DROP_PARTITION | ALTER_RENAME))
+ if (alter_info.flags & (Alter_info::ALTER_DROP_PARTITION |
+ Alter_info::ALTER_RENAME))
priv_needed|= DROP_ACL;
/* Must be set in the parser */
DBUG_ASSERT(select_lex->db);
- DBUG_ASSERT(!(alter_info.flags & ALTER_ADMIN_PARTITION));
+ DBUG_ASSERT(!(alter_info.flags & Alter_info::ALTER_EXCHANGE_PARTITION));
+ DBUG_ASSERT(!(alter_info.flags & Alter_info::ALTER_ADMIN_PARTITION));
if (check_access(thd, priv_needed, first_table->db,
&first_table->grant.privilege,
&first_table->grant.m_internal,
@@ -65,10 +234,47 @@ bool Alter_table_statement::execute(THD *thd)
DBUG_RETURN(TRUE); /* purecov: inspected */
/* If it is a merge table, check privileges for merge children. */
- if (create_info.merge_list.first &&
- check_table_access(thd, SELECT_ACL | UPDATE_ACL | DELETE_ACL,
- create_info.merge_list.first, FALSE, UINT_MAX, FALSE))
- DBUG_RETURN(TRUE);
+ if (create_info.merge_list.first)
+ {
+ /*
+ The user must have (SELECT_ACL | UPDATE_ACL | DELETE_ACL) on the
+ underlying base tables, even if there are temporary tables with the same
+ names.
+
+ From user's point of view, it might look as if the user must have these
+ privileges on temporary tables to create a merge table over them. This is
+ one of two cases when a set of privileges is required for operations on
+ temporary tables (see also CREATE TABLE).
+
+ The reason for this behavior stems from the following facts:
+
+ - For merge tables, the underlying table privileges are checked only
+ at CREATE TABLE / ALTER TABLE time.
+
+ In other words, once a merge table is created, the privileges of
+ the underlying tables can be revoked, but the user will still have
+ access to the merge table (provided that the user has privileges on
+ the merge table itself).
+
+ - Temporary tables shadow base tables.
+
+ I.e. there might be temporary and base tables with the same name, and
+ the temporary table takes the precedence in all operations.
+
+ - For temporary MERGE tables we do not track if their child tables are
+ base or temporary. As result we can't guarantee that privilege check
+ which was done in presence of temporary child will stay relevant later
+ as this temporary table might be removed.
+
+ If SELECT_ACL | UPDATE_ACL | DELETE_ACL privileges were not checked for
+ the underlying *base* tables, it would create a security breach as in
+ Bug#12771903.
+ */
+
+ if (check_table_access(thd, SELECT_ACL | UPDATE_ACL | DELETE_ACL,
+ create_info.merge_list.first, FALSE, UINT_MAX, FALSE))
+ DBUG_RETURN(TRUE);
+ }
if (check_grant(thd, priv_needed, first_table, FALSE, UINT_MAX, FALSE))
DBUG_RETURN(TRUE); /* purecov: inspected */
@@ -77,7 +283,7 @@ bool Alter_table_statement::execute(THD *thd)
{
// Rename of table
TABLE_LIST tmp_table;
- bzero((char*) &tmp_table,sizeof(tmp_table));
+ memset(&tmp_table, 0, sizeof(tmp_table));
tmp_table.table_name= lex->name.str;
tmp_table.db= select_lex->db;
tmp_table.grant.privilege= priv;
@@ -88,11 +294,11 @@ bool Alter_table_statement::execute(THD *thd)
/* Don't yet allow changing of symlinks with ALTER TABLE */
if (create_info.data_file_name)
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
WARN_OPTION_IGNORED, ER(WARN_OPTION_IGNORED),
"DATA DIRECTORY");
if (create_info.index_file_name)
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
WARN_OPTION_IGNORED, ER(WARN_OPTION_IGNORED),
"INDEX DIRECTORY");
create_info.data_file_name= create_info.index_file_name= NULL;
@@ -102,21 +308,12 @@ bool Alter_table_statement::execute(THD *thd)
#ifdef WITH_WSREP
TABLE *find_temporary_table(THD *thd, const TABLE_LIST *tl);
- if (WSREP(thd) &&
- (!thd->is_current_stmt_binlog_format_row() ||
+ if ((!thd->is_current_stmt_binlog_format_row() ||
!find_temporary_table(thd, first_table)))
{
- if (wsrep_to_isolation_begin(thd,
- lex->name.str ? select_lex->db : NULL,
- lex->name.str ? lex->name.str : NULL,
- first_table))
- {
- WSREP_WARN("ALTER TABLE isolation failure");
- DBUG_RETURN(TRUE);
- }
-
- thd->variables.auto_increment_offset = 1;
- thd->variables.auto_increment_increment = 1;
+ WSREP_TO_ISOLATION_BEGIN(((lex->name.str) ? select_lex->db : NULL),
+ ((lex->name.str) ? lex->name.str : NULL),
+ first_table);
}
#endif /* WITH_WSREP */
result= mysql_alter_table(thd, select_lex->db, lex->name.str,
@@ -125,9 +322,45 @@ bool Alter_table_statement::execute(THD *thd)
&alter_info,
select_lex->order_list.elements,
select_lex->order_list.first,
- lex->ignore, lex->online);
+ lex->ignore);
+
+ DBUG_RETURN(result);
#ifdef WITH_WSREP
+error:
+ WSREP_WARN("ALTER TABLE isolation failure");
+ DBUG_RETURN(TRUE);
#endif /* WITH_WSREP */
- DBUG_RETURN(result);
+}
+
+bool Sql_cmd_discard_import_tablespace::execute(THD *thd)
+{
+ /* first SELECT_LEX (have special meaning for many of non-SELECTcommands) */
+ SELECT_LEX *select_lex= &thd->lex->select_lex;
+ /* first table of first SELECT_LEX */
+ TABLE_LIST *table_list= (TABLE_LIST*) select_lex->table_list.first;
+
+ if (check_access(thd, ALTER_ACL, table_list->db,
+ &table_list->grant.privilege,
+ &table_list->grant.m_internal,
+ 0, 0))
+ return true;
+
+ if (check_grant(thd, ALTER_ACL, table_list, false, UINT_MAX, false))
+ return true;
+
+ thd->enable_slow_log= opt_log_slow_admin_statements;
+
+ /*
+ Check if we attempt to alter mysql.slow_log or
+ mysql.general_log table and return an error if
+ it is the case.
+ TODO: this design is obsolete and will be removed.
+ */
+ if (check_if_log_table(table_list, TRUE, "ALTER"))
+ return true;
+
+ return
+ mysql_discard_or_import_tablespace(thd, table_list,
+ m_tablespace_op == DISCARD_TABLESPACE);
}
diff --git a/sql/sql_alter.h b/sql/sql_alter.h
index 6660748f666..526442e83e2 100644
--- a/sql/sql_alter.h
+++ b/sql/sql_alter.h
@@ -1,4 +1,5 @@
-/* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+/* Copyright (c) 2010, 2014, Oracle and/or its affiliates.
+ Copyright (c) 2013, 2014, Monty Program Ab.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -16,51 +17,413 @@
#ifndef SQL_ALTER_TABLE_H
#define SQL_ALTER_TABLE_H
+class Alter_drop;
+class Alter_column;
+class Key;
+
+/**
+ Data describing the table being created by CREATE TABLE or
+ altered by ALTER TABLE.
+*/
+
+class Alter_info
+{
+public:
+ /*
+ These flags are set by the parser and describes the type of
+ operation(s) specified by the ALTER TABLE statement.
+
+ They do *not* describe the type operation(s) to be executed
+ by the storage engine. For example, we don't yet know the
+ type of index to be added/dropped.
+ */
+
+ // Set for ADD [COLUMN]
+ static const uint ALTER_ADD_COLUMN = 1L << 0;
+
+ // Set for DROP [COLUMN]
+ static const uint ALTER_DROP_COLUMN = 1L << 1;
+
+ // Set for CHANGE [COLUMN] | MODIFY [CHANGE]
+ // Set by mysql_recreate_table()
+ static const uint ALTER_CHANGE_COLUMN = 1L << 2;
+
+ // Set for ADD INDEX | ADD KEY | ADD PRIMARY KEY | ADD UNIQUE KEY |
+ // ADD UNIQUE INDEX | ALTER ADD [COLUMN]
+ static const uint ALTER_ADD_INDEX = 1L << 3;
+
+ // Set for DROP PRIMARY KEY | DROP FOREIGN KEY | DROP KEY | DROP INDEX
+ static const uint ALTER_DROP_INDEX = 1L << 4;
+
+ // Set for RENAME [TO]
+ static const uint ALTER_RENAME = 1L << 5;
+
+ // Set for ORDER BY
+ static const uint ALTER_ORDER = 1L << 6;
+
+ // Set for table_options
+ static const uint ALTER_OPTIONS = 1L << 7;
+
+ // Set for ALTER [COLUMN] ... SET DEFAULT ... | DROP DEFAULT
+ static const uint ALTER_CHANGE_COLUMN_DEFAULT = 1L << 8;
+
+ // Set for DISABLE KEYS | ENABLE KEYS
+ static const uint ALTER_KEYS_ONOFF = 1L << 9;
+
+ // Set for CONVERT TO CHARACTER SET
+ static const uint ALTER_CONVERT = 1L << 10;
+
+ // Set for FORCE
+ // Set for ENGINE(same engine)
+ // Set by mysql_recreate_table()
+ static const uint ALTER_RECREATE = 1L << 11;
+
+ // Set for ADD PARTITION
+ static const uint ALTER_ADD_PARTITION = 1L << 12;
+
+ // Set for DROP PARTITION
+ static const uint ALTER_DROP_PARTITION = 1L << 13;
+
+ // Set for COALESCE PARTITION
+ static const uint ALTER_COALESCE_PARTITION = 1L << 14;
+
+ // Set for REORGANIZE PARTITION ... INTO
+ static const uint ALTER_REORGANIZE_PARTITION = 1L << 15;
+
+ // Set for partition_options
+ static const uint ALTER_PARTITION = 1L << 16;
+
+ // Set for LOAD INDEX INTO CACHE ... PARTITION
+ // Set for CACHE INDEX ... PARTITION
+ static const uint ALTER_ADMIN_PARTITION = 1L << 17;
+
+ // Set for REORGANIZE PARTITION
+ static const uint ALTER_TABLE_REORG = 1L << 18;
+
+ // Set for REBUILD PARTITION
+ static const uint ALTER_REBUILD_PARTITION = 1L << 19;
+
+ // Set for partitioning operations specifying ALL keyword
+ static const uint ALTER_ALL_PARTITION = 1L << 20;
+
+ // Set for REMOVE PARTITIONING
+ static const uint ALTER_REMOVE_PARTITIONING = 1L << 21;
+
+ // Set for ADD FOREIGN KEY
+ static const uint ADD_FOREIGN_KEY = 1L << 22;
+
+ // Set for DROP FOREIGN KEY
+ static const uint DROP_FOREIGN_KEY = 1L << 23;
+
+ // Set for EXCHANGE PARITION
+ static const uint ALTER_EXCHANGE_PARTITION = 1L << 24;
+
+ // Set by Sql_cmd_alter_table_truncate_partition::execute()
+ static const uint ALTER_TRUNCATE_PARTITION = 1L << 25;
+
+ // Set for ADD [COLUMN] FIRST | AFTER
+ static const uint ALTER_COLUMN_ORDER = 1L << 26;
+
+
+ enum enum_enable_or_disable { LEAVE_AS_IS, ENABLE, DISABLE };
+
+ /**
+ The different values of the ALGORITHM clause.
+ Describes which algorithm to use when altering the table.
+ */
+ enum enum_alter_table_algorithm
+ {
+ // In-place if supported, copy otherwise.
+ ALTER_TABLE_ALGORITHM_DEFAULT,
+
+ // In-place if supported, error otherwise.
+ ALTER_TABLE_ALGORITHM_INPLACE,
+
+ // Copy if supported, error otherwise.
+ ALTER_TABLE_ALGORITHM_COPY
+ };
+
+
+ /**
+ The different values of the LOCK clause.
+ Describes the level of concurrency during ALTER TABLE.
+ */
+ enum enum_alter_table_lock
+ {
+ // Maximum supported level of concurency for the given operation.
+ ALTER_TABLE_LOCK_DEFAULT,
+
+ // Allow concurrent reads & writes. If not supported, give erorr.
+ ALTER_TABLE_LOCK_NONE,
+
+ // Allow concurrent reads only. If not supported, give error.
+ ALTER_TABLE_LOCK_SHARED,
+
+ // Block reads and writes.
+ ALTER_TABLE_LOCK_EXCLUSIVE
+ };
+
+
+ // Columns and keys to be dropped.
+ List<Alter_drop> drop_list;
+ // Columns for ALTER_COLUMN_CHANGE_DEFAULT.
+ List<Alter_column> alter_list;
+ // List of keys, used by both CREATE and ALTER TABLE.
+ List<Key> key_list;
+ // List of columns, used by both CREATE and ALTER TABLE.
+ List<Create_field> create_list;
+ // Type of ALTER TABLE operation.
+ uint flags;
+ // Enable or disable keys.
+ enum_enable_or_disable keys_onoff;
+ // List of partitions.
+ List<char> partition_names;
+ // Number of partitions.
+ uint num_parts;
+ // Type of ALTER TABLE algorithm.
+ enum_alter_table_algorithm requested_algorithm;
+ // Type of ALTER TABLE lock.
+ enum_alter_table_lock requested_lock;
+
+
+ Alter_info() :
+ flags(0),
+ keys_onoff(LEAVE_AS_IS),
+ num_parts(0),
+ requested_algorithm(ALTER_TABLE_ALGORITHM_DEFAULT),
+ requested_lock(ALTER_TABLE_LOCK_DEFAULT)
+ {}
+
+ void reset()
+ {
+ drop_list.empty();
+ alter_list.empty();
+ key_list.empty();
+ create_list.empty();
+ flags= 0;
+ keys_onoff= LEAVE_AS_IS;
+ num_parts= 0;
+ partition_names.empty();
+ requested_algorithm= ALTER_TABLE_ALGORITHM_DEFAULT;
+ requested_lock= ALTER_TABLE_LOCK_DEFAULT;
+ }
+
+
+ /**
+ Construct a copy of this object to be used for mysql_alter_table
+ and mysql_create_table.
+
+ Historically, these two functions modify their Alter_info
+ arguments. This behaviour breaks re-execution of prepared
+ statements and stored procedures and is compensated by always
+ supplying a copy of Alter_info to these functions.
+
+ @param rhs Alter_info to make copy of
+ @param mem_root Mem_root for new Alter_info
+
+ @note You need to use check the error in THD for out
+ of memory condition after calling this function.
+ */
+ Alter_info(const Alter_info &rhs, MEM_ROOT *mem_root);
+
+
+ /**
+ Parses the given string and sets requested_algorithm
+ if the string value matches a supported value.
+ Supported values: INPLACE, COPY, DEFAULT
+
+ @param str String containing the supplied value
+ @retval false Supported value found, state updated
+ @retval true Not supported value, no changes made
+ */
+ bool set_requested_algorithm(const LEX_STRING *str);
+
+
+ /**
+ Parses the given string and sets requested_lock
+ if the string value matches a supported value.
+ Supported values: NONE, SHARED, EXCLUSIVE, DEFAULT
+
+ @param str String containing the supplied value
+ @retval false Supported value found, state updated
+ @retval true Not supported value, no changes made
+ */
+
+ bool set_requested_lock(const LEX_STRING *str);
+
+private:
+ Alter_info &operator=(const Alter_info &rhs); // not implemented
+ Alter_info(const Alter_info &rhs); // not implemented
+};
+
+
+/** Runtime context for ALTER TABLE. */
+class Alter_table_ctx
+{
+public:
+ Alter_table_ctx();
+
+ Alter_table_ctx(THD *thd, TABLE_LIST *table_list, uint tables_opened_arg,
+ char *new_db_arg, char *new_name_arg);
+
+ /**
+ @return true if the table is moved to another database, false otherwise.
+ */
+ bool is_database_changed() const
+ { return (new_db != db); };
+
+ /**
+ @return true if the table is renamed, false otherwise.
+ */
+ bool is_table_renamed() const
+ { return (is_database_changed() || new_name != table_name); };
+
+ /**
+ @return filename (including .frm) for the new table.
+ */
+ const char *get_new_filename() const
+ {
+ DBUG_ASSERT(!tmp_table);
+ return new_filename;
+ }
+
+ /**
+ @return path to the original table.
+ */
+ const char *get_path() const
+ {
+ DBUG_ASSERT(!tmp_table);
+ return path;
+ }
+
+ /**
+ @return path to the new table.
+ */
+ const char *get_new_path() const
+ {
+ DBUG_ASSERT(!tmp_table);
+ return new_path;
+ }
+
+ /**
+ @return path to the temporary table created during ALTER TABLE.
+ */
+ const char *get_tmp_path() const
+ { return tmp_path; }
+
+ /**
+ Mark ALTER TABLE as needing to produce foreign key error if
+ it deletes a row from the table being changed.
+ */
+ void set_fk_error_if_delete_row(FOREIGN_KEY_INFO *fk)
+ {
+ fk_error_if_delete_row= true;
+ fk_error_id= fk->foreign_id->str;
+ fk_error_table= fk->foreign_table->str;
+ }
+
+public:
+ Create_field *datetime_field;
+ bool error_if_not_empty;
+ uint tables_opened;
+ char *db;
+ char *table_name;
+ char *alias;
+ char *new_db;
+ char *new_name;
+ char *new_alias;
+ char tmp_name[80];
+ /**
+ Indicates that if a row is deleted during copying of data from old version
+ of table to the new version ER_FK_CANNOT_DELETE_PARENT error should be
+ emitted.
+ */
+ bool fk_error_if_delete_row;
+ /** Name of foreign key for the above error. */
+ const char *fk_error_id;
+ /** Name of table for the above error. */
+ const char *fk_error_table;
+
+private:
+ char new_filename[FN_REFLEN + 1];
+ char new_alias_buff[FN_REFLEN + 1];
+ char path[FN_REFLEN + 1];
+ char new_path[FN_REFLEN + 1];
+ char tmp_path[FN_REFLEN + 1];
+
+#ifndef DBUG_OFF
+ /** Indicates that we are altering temporary table. Used only in asserts. */
+ bool tmp_table;
+#endif
+
+ Alter_table_ctx &operator=(const Alter_table_ctx &rhs); // not implemented
+ Alter_table_ctx(const Alter_table_ctx &rhs); // not implemented
+};
+
+
/**
- Alter_table_common represents the common properties of the ALTER TABLE
+ Sql_cmd_common_alter_table represents the common properties of the ALTER TABLE
statements.
@todo move Alter_info and other ALTER generic structures from Lex here.
*/
-class Alter_table_common : public Sql_statement
+class Sql_cmd_common_alter_table : public Sql_cmd
{
protected:
/**
Constructor.
- @param lex the LEX structure for this statement.
*/
- Alter_table_common(LEX *lex)
- : Sql_statement(lex)
+ Sql_cmd_common_alter_table()
{}
- virtual ~Alter_table_common()
+ virtual ~Sql_cmd_common_alter_table()
{}
+ virtual enum_sql_command sql_command_code() const
+ {
+ return SQLCOM_ALTER_TABLE;
+ }
};
/**
- Alter_table_statement represents the generic ALTER TABLE statement.
+ Sql_cmd_alter_table represents the generic ALTER TABLE statement.
@todo move Alter_info and other ALTER specific structures from Lex here.
*/
-class Alter_table_statement : public Alter_table_common
+class Sql_cmd_alter_table : public Sql_cmd_common_alter_table
{
public:
/**
Constructor, used to represent a ALTER TABLE statement.
- @param lex the LEX structure for this statement.
*/
- Alter_table_statement(LEX *lex)
- : Alter_table_common(lex)
+ Sql_cmd_alter_table()
{}
- ~Alter_table_statement()
+ ~Sql_cmd_alter_table()
{}
- /**
- Execute a ALTER TABLE statement at runtime.
- @param thd the current thread.
- @return false on success.
- */
bool execute(THD *thd);
};
+
+/**
+ Sql_cmd_alter_table_tablespace represents ALTER TABLE
+ IMPORT/DISCARD TABLESPACE statements.
+*/
+class Sql_cmd_discard_import_tablespace : public Sql_cmd_common_alter_table
+{
+public:
+ enum enum_tablespace_op_type
+ {
+ DISCARD_TABLESPACE, IMPORT_TABLESPACE
+ };
+
+ Sql_cmd_discard_import_tablespace(enum_tablespace_op_type tablespace_op_arg)
+ : m_tablespace_op(tablespace_op_arg)
+ {}
+
+ bool execute(THD *thd);
+
+private:
+ const enum_tablespace_op_type m_tablespace_op;
+};
+
#endif
diff --git a/sql/sql_analyse.cc b/sql/sql_analyse.cc
index 36026a39616..32b447797cf 100644
--- a/sql/sql_analyse.cc
+++ b/sql/sql_analyse.cc
@@ -29,6 +29,7 @@
#define MYSQL_LEX 1
+#include <my_global.h>
#include "sql_priv.h"
#include "procedure.h"
#include "sql_analyse.h"
@@ -282,16 +283,16 @@ bool get_ev_num_info(EV_NUM_INFO *ev_info, NUM_INFO *info, const char *num)
{
if (((longlong) info->ullval) < 0)
return 0; // Impossible to store as a negative number
- ev_info->llval = -(longlong) max((ulonglong) -ev_info->llval,
+ ev_info->llval = -(longlong) MY_MAX((ulonglong) -ev_info->llval,
info->ullval);
- ev_info->min_dval = (double) -max(-ev_info->min_dval, info->dval);
+ ev_info->min_dval = (double) -MY_MAX(-ev_info->min_dval, info->dval);
}
else // ulonglong is as big as bigint in MySQL
{
if ((check_ulonglong(num, info->integers) == DECIMAL_NUM))
return 0;
- ev_info->ullval = (ulonglong) max(ev_info->ullval, info->ullval);
- ev_info->max_dval = (double) max(ev_info->max_dval, info->dval);
+ ev_info->ullval = (ulonglong) MY_MAX(ev_info->ullval, info->ullval);
+ ev_info->max_dval = (double) MY_MAX(ev_info->max_dval, info->dval);
}
return 1;
} // get_ev_num_info
@@ -1040,7 +1041,7 @@ String *field_decimal::avg(String *s, ha_rows rows)
my_decimal_div(E_DEC_FATAL_ERROR, &avg_val, sum+cur_sum, &num, prec_increment);
/* TODO remove this after decimal_div returns proper frac */
my_decimal_round(E_DEC_FATAL_ERROR, &avg_val,
- min(sum[cur_sum].frac + prec_increment, DECIMAL_MAX_SCALE),
+ MY_MIN(sum[cur_sum].frac + prec_increment, DECIMAL_MAX_SCALE),
FALSE,&rounded_avg);
my_decimal2string(E_DEC_FATAL_ERROR, &rounded_avg, 0, 0, '0', s);
return s;
@@ -1065,7 +1066,7 @@ String *field_decimal::std(String *s, ha_rows rows)
my_decimal_div(E_DEC_FATAL_ERROR, &tmp, &sum2, &num, prec_increment);
my_decimal2double(E_DEC_FATAL_ERROR, &tmp, &std_sqr);
s->set_real(((double) std_sqr <= 0.0 ? 0.0 : sqrt(std_sqr)),
- min(item->decimals + prec_increment, NOT_FIXED_DEC), my_thd_charset);
+ MY_MIN(item->decimals + prec_increment, NOT_FIXED_DEC), my_thd_charset);
return s;
}
@@ -1080,7 +1081,7 @@ int collect_string(String *element,
else
info->found = 1;
info->str->append('\'');
- if (append_escaped(info->str, element))
+ if (info->str->append_for_single_quote(element))
return 1;
info->str->append('\'');
return 0;
@@ -1182,7 +1183,7 @@ bool analyse::change_columns(List<Item> &field_list)
func_items[8] = new Item_proc_string("Std", 255);
func_items[8]->maybe_null = 1;
func_items[9] = new Item_proc_string("Optimal_fieldtype",
- max(64, output_str_length));
+ MY_MAX(64, output_str_length));
for (uint i = 0; i < array_elements(func_items); i++)
field_list.push_back(func_items[i]);
@@ -1239,56 +1240,3 @@ uint check_ulonglong(const char *str, uint length)
return ((uchar) str[-1] <= (uchar) cmp[-1]) ? smaller : bigger;
} /* check_ulonlong */
-
-/*
- Quote special characters in a string.
-
- SYNOPSIS
- append_escaped(to_str, from_str)
- to_str (in) A pointer to a String.
- from_str (to) A pointer to an allocated string
-
- DESCRIPTION
- append_escaped() takes a String type variable, where it appends
- escaped the second argument. Only characters that require escaping
- will be escaped.
-
- RETURN VALUES
- 0 Success
- 1 Out of memory
-*/
-
-bool append_escaped(String *to_str, String *from_str)
-{
- char *from, *end, c;
-
- if (to_str->realloc(to_str->length() + from_str->length()))
- return 1;
-
- from= (char*) from_str->ptr();
- end= from + from_str->length();
- for (; from < end; from++)
- {
- c= *from;
- switch (c) {
- case '\0':
- c= '0';
- break;
- case '\032':
- c= 'Z';
- break;
- case '\\':
- case '\'':
- break;
- default:
- goto normal_character;
- }
- if (to_str->append('\\'))
- return 1;
-
- normal_character:
- if (to_str->append(c))
- return 1;
- }
- return 0;
-}
diff --git a/sql/sql_analyse.h b/sql/sql_analyse.h
index 34c8e2da3d4..3d3662c3f4f 100644
--- a/sql/sql_analyse.h
+++ b/sql/sql_analyse.h
@@ -121,7 +121,8 @@ public:
must_be_blob(0), was_zero_fill(0),
was_maybe_zerofill(0), can_be_still_num(1)
{ init_tree(&tree, 0, 0, sizeof(String), (qsort_cmp2) sortcmp2,
- 0, (tree_element_free) free_string, NULL); };
+ (tree_element_free) free_string, NULL,
+ MYF(MY_THREAD_SPECIFIC)); };
void add();
void get_opt_type(String*, ha_rows);
@@ -162,7 +163,7 @@ public:
{
bin_size= my_decimal_get_binary_size(a->max_length, a->decimals);
init_tree(&tree, 0, 0, bin_size, (qsort_cmp2)compare_decimal2,
- 0, 0, (void *)&bin_size);
+ 0, (void *)&bin_size, MYF(MY_THREAD_SPECIFIC));
};
void add();
@@ -190,7 +191,8 @@ public:
field_real(Item* a, analyse* b) :field_info(a,b),
min_arg(0), max_arg(0), sum(0), sum_sqr(0), max_notzero_dec_len(0)
{ init_tree(&tree, 0, 0, sizeof(double),
- (qsort_cmp2) compare_double2, 0, NULL, NULL); }
+ (qsort_cmp2) compare_double2, NULL, NULL,
+ MYF(MY_THREAD_SPECIFIC)); }
void add();
void get_opt_type(String*, ha_rows);
@@ -244,7 +246,8 @@ public:
field_longlong(Item* a, analyse* b) :field_info(a,b),
min_arg(0), max_arg(0), sum(0), sum_sqr(0)
{ init_tree(&tree, 0, 0, sizeof(longlong),
- (qsort_cmp2) compare_longlong2, 0, NULL, NULL); }
+ (qsort_cmp2) compare_longlong2, NULL, NULL,
+ MYF(MY_THREAD_SPECIFIC)); }
void add();
void get_opt_type(String*, ha_rows);
@@ -289,7 +292,8 @@ public:
field_ulonglong(Item* a, analyse * b) :field_info(a,b),
min_arg(0), max_arg(0), sum(0),sum_sqr(0)
{ init_tree(&tree, 0, 0, sizeof(ulonglong),
- (qsort_cmp2) compare_ulonglong2, 0, NULL, NULL); }
+ (qsort_cmp2) compare_ulonglong2, NULL, NULL,
+ MYF(MY_THREAD_SPECIFIC)); }
void add();
void get_opt_type(String*, ha_rows);
String *get_min_arg(String *s) { s->set(min_arg,my_thd_charset); return s; }
@@ -361,6 +365,4 @@ public:
List<Item> &field_list);
};
-bool append_escaped(String *to_str, String *from_str);
-
#endif /* SQL_ANALYSE_INCLUDED */
diff --git a/sql/sql_array.h b/sql/sql_array.h
index 67f1f1c2ad7..8202e94ce41 100644
--- a/sql/sql_array.h
+++ b/sql/sql_array.h
@@ -19,8 +19,81 @@
#include <my_sys.h>
+/**
+ A wrapper class which provides array bounds checking.
+ We do *not* own the array, we simply have a pointer to the first element,
+ and a length.
+
+ @remark
+ We want the compiler-generated versions of:
+ - the copy CTOR (memberwise initialization)
+ - the assignment operator (memberwise assignment)
+
+ @param Element_type The type of the elements of the container.
+ */
+template <typename Element_type> class Bounds_checked_array
+{
+public:
+ Bounds_checked_array() : m_array(NULL), m_size(0) {}
+
+ Bounds_checked_array(Element_type *el, size_t size)
+ : m_array(el), m_size(size)
+ {}
+
+ void reset() { m_array= NULL; m_size= 0; }
+
+ void reset(Element_type *array, size_t size)
+ {
+ m_array= array;
+ m_size= size;
+ }
+
+ /**
+ Set a new bound on the array. Does not resize the underlying
+ array, so the new size must be smaller than or equal to the
+ current size.
+ */
+ void resize(size_t new_size)
+ {
+ DBUG_ASSERT(new_size <= m_size);
+ m_size= new_size;
+ }
+
+ Element_type &operator[](size_t n)
+ {
+ DBUG_ASSERT(n < m_size);
+ return m_array[n];
+ }
+
+ const Element_type &operator[](size_t n) const
+ {
+ DBUG_ASSERT(n < m_size);
+ return m_array[n];
+ }
+
+ size_t element_size() const { return sizeof(Element_type); }
+ size_t size() const { return m_size; }
+
+ bool is_null() const { return m_array == NULL; }
+
+ void pop_front()
+ {
+ DBUG_ASSERT(m_size > 0);
+ m_array+= 1;
+ m_size-= 1;
+ }
+
+ Element_type *array() const { return m_array; }
+
+private:
+ Element_type *m_array;
+ size_t m_size;
+};
+
/*
A typesafe wrapper around DYNAMIC_ARRAY
+
+ TODO: Change creator to take a THREAD_SPECIFIC option.
*/
template <class Elem> class Dynamic_array
@@ -29,114 +102,138 @@ template <class Elem> class Dynamic_array
public:
Dynamic_array(uint prealloc=16, uint increment=16)
{
- my_init_dynamic_array(&array, sizeof(Elem), prealloc, increment);
+ init(prealloc, increment);
+ }
+
+ void init(uint prealloc=16, uint increment=16)
+ {
+ my_init_dynamic_array(&array, sizeof(Elem), prealloc, increment,
+ MYF(0));
}
- Elem& at(int idx)
+ /**
+ @note Though formally this could be declared "const" it would be
+ misleading at it returns a non-const pointer to array's data.
+ */
+ Elem& at(size_t idx)
+ {
+ DBUG_ASSERT(idx < array.elements);
+ return *(((Elem*)array.buffer) + idx);
+ }
+ /// Const variant of at(), which cannot change data
+ const Elem& at(size_t idx) const
{
return *(((Elem*)array.buffer) + idx);
}
+ /// @returns pointer to first element
Elem *front()
{
return (Elem*)array.buffer;
}
- Elem *back()
+ /// @returns pointer to first element
+ const Elem *front() const
{
- return ((Elem*)array.buffer) + array.elements;
+ return (const Elem*)array.buffer;
}
- bool append(Elem &el)
+ /// @returns pointer to last element
+ Elem *back()
{
- return (insert_dynamic(&array, (uchar*)&el));
+ return ((Elem*)array.buffer) + array.elements - 1;
}
- int elements()
+ /// @returns pointer to last element
+ const Elem *back() const
{
- return array.elements;
+ return ((const Elem*)array.buffer) + array.elements - 1;
}
- ~Dynamic_array()
+ /**
+ @retval false ok
+ @retval true OOM, @c my_error() has been called.
+ */
+ bool append(const Elem &el)
{
- delete_dynamic(&array);
+ return insert_dynamic(&array, &el);
}
- typedef int (*CMP_FUNC)(const Elem *el1, const Elem *el2);
+ bool append_val(Elem el)
+ {
+ return (insert_dynamic(&array, (uchar*)&el));
+ }
- void sort(CMP_FUNC cmp_func)
+ bool push(Elem &el)
{
- my_qsort(array.buffer, array.elements, sizeof(Elem), (qsort_cmp)cmp_func);
+ return append(el);
}
-};
-/*
- Array of pointers to Elem that uses memory from MEM_ROOT
+ /// Pops the last element. Does nothing if array is empty.
+ Elem& pop()
+ {
+ return *((Elem*)pop_dynamic(&array));
+ }
- MEM_ROOT has no realloc() so this is supposed to be used for cases when
- reallocations are rare.
-*/
+ void del(uint idx)
+ {
+ delete_dynamic_element(&array, idx);
+ }
-template <class Elem> class Array
-{
- enum {alloc_increment = 16};
- Elem **buffer;
- uint n_elements, max_element;
-public:
- Array(MEM_ROOT *mem_root, uint prealloc=16)
+ size_t elements() const
{
- buffer= (Elem**)alloc_root(mem_root, prealloc * sizeof(Elem**));
- max_element = buffer? prealloc : 0;
- n_elements= 0;
+ return array.elements;
}
- Elem& at(int idx)
+ void elements(size_t num_elements)
{
- return *(((Elem*)buffer) + idx);
+ DBUG_ASSERT(num_elements <= array.max_element);
+ array.elements= num_elements;
}
- Elem **front()
+ void clear()
{
- return buffer;
+ elements(0);
}
- Elem **back()
+ void set(uint idx, const Elem &el)
{
- return buffer + n_elements;
+ set_dynamic(&array, &el, idx);
}
- bool append(MEM_ROOT *mem_root, Elem *el)
+ bool resize(size_t new_size, Elem default_val)
{
- if (n_elements == max_element)
+ size_t old_size= elements();
+ if (allocate_dynamic(&array, new_size))
+ return true;
+
+ if (new_size > old_size)
{
- Elem **newbuf;
- if (!(newbuf= (Elem**)alloc_root(mem_root, (n_elements + alloc_increment)*
- sizeof(Elem**))))
+ set_dynamic(&array, (uchar*)&default_val, new_size - 1);
+ /*for (size_t i= old_size; i != new_size; i++)
{
- return FALSE;
- }
- memcpy(newbuf, buffer, n_elements*sizeof(Elem*));
- buffer= newbuf;
+ at(i)= default_val;
+ }*/
}
- buffer[n_elements++]= el;
- return FALSE;
+ return false;
}
- int elements()
+ ~Dynamic_array()
{
- return n_elements;
+ delete_dynamic(&array);
}
- void clear()
+ typedef int (*CMP_FUNC)(const Elem *el1, const Elem *el2);
+
+ void sort(CMP_FUNC cmp_func)
{
- n_elements= 0;
+ my_qsort(array.buffer, array.elements, sizeof(Elem), (qsort_cmp)cmp_func);
}
- typedef int (*CMP_FUNC)(Elem * const *el1, Elem *const *el2);
-
- void sort(CMP_FUNC cmp_func)
+ typedef int (*CMP_FUNC2)(const Elem *el1, const Elem *el2, void *);
+ void sort(CMP_FUNC2 cmp_func, void *data)
{
- my_qsort(buffer, n_elements, sizeof(Elem*), (qsort_cmp)cmp_func);
+ my_qsort2(array.buffer, array.elements, sizeof(Elem), (qsort2_cmp)cmp_func, data);
}
};
diff --git a/sql/sql_audit.cc b/sql/sql_audit.cc
index 108b74cac5f..b659054a50b 100644
--- a/sql/sql_audit.cc
+++ b/sql/sql_audit.cc
@@ -13,6 +13,7 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+#include <my_global.h>
#include "sql_priv.h"
#include "sql_audit.h"
@@ -183,7 +184,7 @@ static my_bool acquire_plugins(THD *thd, plugin_ref plugin, void *arg)
{
/* specify some reasonable initialization defaults */
my_init_dynamic_array(&thd->audit_class_plugins,
- sizeof(plugin_ref), 16, 16);
+ sizeof(plugin_ref), 16, 16, MYF(0));
}
/* lock the plugin and add it to the list */
diff --git a/sql/sql_audit.h b/sql/sql_audit.h
index 3f3f97a2730..68106f099cc 100644
--- a/sql/sql_audit.h
+++ b/sql/sql_audit.h
@@ -43,6 +43,11 @@ static inline bool mysql_audit_general_enabled()
return mysql_global_audit_mask[0] & MYSQL_AUDIT_GENERAL_CLASSMASK;
}
+static inline bool mysql_audit_connection_enabled()
+{
+ return mysql_global_audit_mask[0] & MYSQL_AUDIT_CONNECTION_CLASSMASK;
+}
+
static inline bool mysql_audit_table_enabled()
{
return mysql_global_audit_mask[0] & MYSQL_AUDIT_TABLE_CLASSMASK;
@@ -52,6 +57,7 @@ static inline bool mysql_audit_table_enabled()
static inline void mysql_audit_notify(THD *thd, uint event_class,
uint event_subtype, ...) { }
#define mysql_audit_general_enabled() 0
+#define mysql_audit_connection_enabled() 0
#define mysql_audit_table_enabled() 0
#endif
extern void mysql_audit_release(THD *thd);
@@ -133,7 +139,7 @@ void mysql_audit_general(THD *thd, uint event_subtype,
query= thd->query_string;
user= user_buff;
userlen= make_user_name(thd, user_buff);
- rows= thd->warning_info->current_row_for_warning();
+ rows= thd->get_stmt_da()->current_row_for_warning();
db= thd->db;
db_length= thd->db_length;
}
@@ -153,46 +159,69 @@ void mysql_audit_general(THD *thd, uint event_subtype,
}
}
-#define MYSQL_AUDIT_NOTIFY_CONNECTION_CONNECT(thd) mysql_audit_notify(\
- (thd), MYSQL_AUDIT_CONNECTION_CLASS, MYSQL_AUDIT_CONNECTION_CONNECT,\
- (thd)->stmt_da->is_error() ? (thd)->stmt_da->sql_errno() : 0,\
- (thd)->thread_id, (thd)->security_ctx->user,\
- (thd)->security_ctx->user ? strlen((thd)->security_ctx->user) : 0,\
- (thd)->security_ctx->priv_user, strlen((thd)->security_ctx->priv_user),\
- (thd)->security_ctx->external_user,\
- (thd)->security_ctx->external_user ?\
- strlen((thd)->security_ctx->external_user) : 0,\
- (thd)->security_ctx->proxy_user, strlen((thd)->security_ctx->proxy_user),\
- (thd)->security_ctx->host,\
- (thd)->security_ctx->host ? strlen((thd)->security_ctx->host) : 0,\
- (thd)->security_ctx->ip,\
- (thd)->security_ctx->ip ? strlen((thd)->security_ctx->ip) : 0,\
- (thd)->db, (thd)->db ? strlen((thd)->db) : 0)
-
-#define MYSQL_AUDIT_NOTIFY_CONNECTION_DISCONNECT(thd, errcode)\
- mysql_audit_notify(\
- (thd), MYSQL_AUDIT_CONNECTION_CLASS, MYSQL_AUDIT_CONNECTION_DISCONNECT,\
- (errcode), (thd)->thread_id, (thd)->security_ctx->user,\
- (thd)->security_ctx->user ? strlen((thd)->security_ctx->user) : 0,\
- 0, 0, 0, 0, 0, 0, (thd)->security_ctx->host,\
- (thd)->security_ctx->host ? strlen((thd)->security_ctx->host) : 0,\
- 0, 0, 0, 0)
-
-#define MYSQL_AUDIT_NOTIFY_CONNECTION_CHANGE_USER(thd) mysql_audit_notify(\
- (thd), MYSQL_AUDIT_CONNECTION_CLASS, MYSQL_AUDIT_CONNECTION_CHANGE_USER,\
- (thd)->stmt_da->is_error() ? (thd)->stmt_da->sql_errno() : 0,\
- (thd)->thread_id, (thd)->security_ctx->user,\
- (thd)->security_ctx->user ? strlen((thd)->security_ctx->user) : 0,\
- (thd)->security_ctx->priv_user, strlen((thd)->security_ctx->priv_user),\
- (thd)->security_ctx->external_user,\
- (thd)->security_ctx->external_user ?\
- strlen((thd)->security_ctx->external_user) : 0,\
- (thd)->security_ctx->proxy_user, strlen((thd)->security_ctx->proxy_user),\
- (thd)->security_ctx->host,\
- (thd)->security_ctx->host ? strlen((thd)->security_ctx->host) : 0,\
- (thd)->security_ctx->ip,\
- (thd)->security_ctx->ip ? strlen((thd)->security_ctx->ip) : 0,\
- (thd)->db, (thd)->db ? strlen((thd)->db) : 0)
+static inline
+void mysql_audit_notify_connection_connect(THD *thd)
+{
+ if (mysql_audit_connection_enabled())
+ {
+ const Security_context *sctx= thd->security_ctx;
+ Diagnostics_area *da= thd->get_stmt_da();
+ mysql_audit_notify(thd, MYSQL_AUDIT_CONNECTION_CLASS,
+ MYSQL_AUDIT_CONNECTION_CONNECT,
+ da->is_error() ? da->sql_errno() : 0,
+ thd->thread_id,
+ sctx->user, sctx->user ? strlen(sctx->user) : 0,
+ sctx->priv_user, strlen(sctx->priv_user),
+ sctx->external_user,
+ sctx->external_user ? strlen(sctx->external_user) : 0,
+ sctx->proxy_user, strlen(sctx->proxy_user),
+ sctx->host, sctx->host ? strlen(sctx->host) : 0,
+ sctx->ip, sctx->ip ? strlen(sctx->ip) : 0,
+ thd->db, thd->db ? strlen(thd->db) : 0);
+ }
+}
+
+static inline
+void mysql_audit_notify_connection_disconnect(THD *thd, int errcode)
+{
+ if (mysql_audit_connection_enabled())
+ {
+ const Security_context *sctx= thd->security_ctx;
+ mysql_audit_notify(thd, MYSQL_AUDIT_CONNECTION_CLASS,
+ MYSQL_AUDIT_CONNECTION_DISCONNECT,
+ errcode, thd->thread_id,
+ sctx->user, sctx->user ? strlen(sctx->user) : 0,
+ sctx->priv_user, strlen(sctx->priv_user),
+ sctx->external_user,
+ sctx->external_user ? strlen(sctx->external_user) : 0,
+ sctx->proxy_user, strlen(sctx->proxy_user),
+ sctx->host, sctx->host ? strlen(sctx->host) : 0,
+ sctx->ip, sctx->ip ? strlen(sctx->ip) : 0,
+ thd->db, thd->db ? strlen(thd->db) : 0);
+ }
+}
+
+static inline
+void mysql_audit_notify_connection_change_user(THD *thd)
+{
+ if (mysql_audit_connection_enabled())
+ {
+ const Security_context *sctx= thd->security_ctx;
+ Diagnostics_area *da= thd->get_stmt_da();
+ mysql_audit_notify(thd, MYSQL_AUDIT_CONNECTION_CLASS,
+ MYSQL_AUDIT_CONNECTION_CHANGE_USER,
+ da->is_error() ? da->sql_errno() : 0,
+ thd->thread_id,
+ sctx->user, sctx->user ? strlen(sctx->user) : 0,
+ sctx->priv_user, strlen(sctx->priv_user),
+ sctx->external_user,
+ sctx->external_user ? strlen(sctx->external_user) : 0,
+ sctx->proxy_user, strlen(sctx->proxy_user),
+ sctx->host, sctx->host ? strlen(sctx->host) : 0,
+ sctx->ip, sctx->ip ? strlen(sctx->ip) : 0,
+ thd->db, thd->db ? strlen(thd->db) : 0);
+ }
+}
static inline
void mysql_audit_external_lock(THD *thd, TABLE_SHARE *share, int lock)
diff --git a/sql/sql_base.cc b/sql/sql_base.cc
index 402905f6af0..d71fc8a439e 100644
--- a/sql/sql_base.cc
+++ b/sql/sql_base.cc
@@ -17,8 +17,8 @@
/* Basic functions needed by many modules */
+#include <my_global.h>
#include "sql_base.h" // setup_table_map
-#include "my_global.h" /* NO_EMBEDDED_ACCESS_CHECKS */
#include "sql_priv.h"
#include "unireg.h"
#include "debug_sync.h"
@@ -49,16 +49,19 @@
#include "sql_trigger.h"
#include "transaction.h"
#include "sql_prepare.h"
+#include "sql_statistics.h"
#include <m_ctype.h>
#include <my_dir.h>
#include <hash.h>
#include "rpl_filter.h"
#include "sql_table.h" // build_table_filename
-#include "datadict.h" // dd_frm_type()
+#include "datadict.h" // dd_frm_is_view()
#include "sql_hset.h" // Hash_set
+#include "rpl_rli.h" // rpl_group_info
#ifdef __WIN__
#include <io.h>
#endif
+
#ifdef WITH_WSREP
#include "wsrep_mysqld.h"
#include "wsrep_thd.h"
@@ -68,9 +71,9 @@ bool
No_such_table_error_handler::handle_condition(THD *,
uint sql_errno,
const char*,
- MYSQL_ERROR::enum_warning_level level,
+ Sql_condition::enum_warning_level level,
const char*,
- MYSQL_ERROR ** cond_hdl)
+ Sql_condition ** cond_hdl)
{
*cond_hdl= NULL;
if (sql_errno == ER_NO_SUCH_TABLE || sql_errno == ER_NO_SUCH_TABLE_IN_ENGINE)
@@ -79,7 +82,7 @@ No_such_table_error_handler::handle_condition(THD *,
return TRUE;
}
- if (level == MYSQL_ERROR::WARN_LEVEL_ERROR)
+ if (level == Sql_condition::WARN_LEVEL_ERROR)
m_unhandled_errors++;
return FALSE;
}
@@ -112,9 +115,9 @@ public:
bool handle_condition(THD *thd,
uint sql_errno,
const char* sqlstate,
- MYSQL_ERROR::enum_warning_level level,
+ Sql_condition::enum_warning_level level,
const char* msg,
- MYSQL_ERROR ** cond_hdl);
+ Sql_condition ** cond_hdl);
/**
Returns TRUE if there were ER_NO_SUCH_/WRONG_MRG_TABLE and there
@@ -142,9 +145,9 @@ bool
Repair_mrg_table_error_handler::handle_condition(THD *,
uint sql_errno,
const char*,
- MYSQL_ERROR::enum_warning_level level,
+ Sql_condition::enum_warning_level level,
const char*,
- MYSQL_ERROR ** cond_hdl)
+ Sql_condition ** cond_hdl)
{
*cond_hdl= NULL;
if (sql_errno == ER_NO_SUCH_TABLE ||
@@ -165,679 +168,87 @@ Repair_mrg_table_error_handler::handle_condition(THD *,
@{
*/
-/**
- Protects table_def_hash, used and unused lists in the
- TABLE_SHARE object, LRU lists of used TABLEs and used
- TABLE_SHAREs, refresh_version and the table id counter.
-*/
-mysql_mutex_t LOCK_open;
-
-#ifdef HAVE_PSI_INTERFACE
-static PSI_mutex_key key_LOCK_open;
-static PSI_mutex_info all_tdc_mutexes[]= {
- { &key_LOCK_open, "LOCK_open", PSI_FLAG_GLOBAL }
-};
-
-/**
- Initialize performance schema instrumentation points
- used by the table cache.
-*/
-
-static void init_tdc_psi_keys(void)
-{
- const char *category= "sql";
- int count;
-
- if (PSI_server == NULL)
- return;
-
- count= array_elements(all_tdc_mutexes);
- PSI_server->register_mutex(category, all_tdc_mutexes, count);
-}
-#endif /* HAVE_PSI_INTERFACE */
-
-
-/**
- Total number of TABLE instances for tables in the table definition cache
- (both in use by threads and not in use). This value is accessible to user
- as "Open_tables" status variable.
-*/
-uint table_cache_count= 0;
-/**
- List that contains all TABLE instances for tables in the table definition
- cache that are not in use by any thread. Recently used TABLE instances are
- appended to the end of the list. Thus the beginning of the list contains
- tables which have been least recently used.
-*/
-TABLE *unused_tables;
-HASH table_def_cache;
-static TABLE_SHARE *oldest_unused_share, end_of_unused_share;
-static bool table_def_inited= 0;
-static bool table_def_shutdown_in_progress= 0;
-
static bool check_and_update_table_version(THD *thd, TABLE_LIST *tables,
TABLE_SHARE *table_share);
static bool open_table_entry_fini(THD *thd, TABLE_SHARE *share, TABLE *entry);
static bool auto_repair_table(THD *thd, TABLE_LIST *table_list);
-static void free_cache_entry(TABLE *entry);
-
-uint cached_open_tables(void)
-{
- return table_cache_count;
-}
-#ifdef EXTRA_DEBUG
-static void check_unused(THD *thd)
-{
- uint count= 0, open_files= 0, idx= 0;
- TABLE *cur_link, *start_link, *entry;
- TABLE_SHARE *share;
- DBUG_ENTER("check_unused");
-
- if ((start_link=cur_link=unused_tables))
- {
- do
- {
- if (cur_link != cur_link->next->prev || cur_link != cur_link->prev->next)
- {
- DBUG_PRINT("error",("Unused_links aren't linked properly")); /* purecov: inspected */
- DBUG_VOID_RETURN; /* purecov: inspected */
- }
- } while (count++ < table_cache_count &&
- (cur_link=cur_link->next) != start_link);
- if (cur_link != start_link)
- {
- DBUG_PRINT("error",("Unused_links aren't connected")); /* purecov: inspected */
- }
- }
- for (idx=0 ; idx < table_def_cache.records ; idx++)
- {
- share= (TABLE_SHARE*) my_hash_element(&table_def_cache, idx);
-
- I_P_List_iterator<TABLE, TABLE_share> it(share->free_tables);
- while ((entry= it++))
- {
- /*
- We must not have TABLEs in the free list that have their file closed.
- */
- DBUG_ASSERT(entry->db_stat && entry->file);
- /* Merge children should be detached from a merge parent */
- if (entry->in_use)
- {
- DBUG_PRINT("error",("Used table is in share's list of unused tables")); /* purecov: inspected */
- }
- /* extra() may assume that in_use is set */
- entry->in_use= thd;
- DBUG_ASSERT(! entry->file->extra(HA_EXTRA_IS_ATTACHED_CHILDREN));
- entry->in_use= 0;
-
- count--;
- open_files++;
- }
- it.init(share->used_tables);
- while ((entry= it++))
- {
- if (!entry->in_use)
- {
- DBUG_PRINT("error",("Unused table is in share's list of used tables")); /* purecov: inspected */
- }
- open_files++;
- }
- }
- if (count != 0)
- {
- DBUG_PRINT("error",("Unused_links doesn't match open_cache: diff: %d", /* purecov: inspected */
- count)); /* purecov: inspected */
- }
- DBUG_VOID_RETURN;
-}
-#else
-#define check_unused(A)
-#endif
-
-
-/*
- Create a table cache key
+/**
+ Create a table cache/table definition cache key
- SYNOPSIS
- create_table_def_key()
- thd Thread handler
- key Create key here (must be of size MAX_DBKEY_LENGTH)
- table_list Table definition
- tmp_table Set if table is a tmp table
+ @param thd Thread context
+ @param key Buffer for the key to be created (must be of
+ size MAX_DBKEY_LENGTH).
+ @param db_name Database name.
+ @param table_name Table name.
- IMPLEMENTATION
+ @note
The table cache_key is created from:
db_name + \0
table_name + \0
- if the table is a tmp table, we add the following to make each tmp table
+ additionally we add the following to make each tmp table
unique on the slave:
4 bytes for master thread id
4 bytes pseudo thread id
- RETURN
- Length of key
+ @return Length of key.
*/
-uint create_table_def_key(THD *thd, char *key,
- const TABLE_LIST *table_list,
- bool tmp_table)
+uint create_tmp_table_def_key(THD *thd, char *key,
+ const char *db, const char *table_name)
{
- uint key_length= create_table_def_key(key, table_list->db,
- table_list->table_name);
-
- if (tmp_table)
- {
- int4store(key + key_length, thd->server_id);
- int4store(key + key_length + 4, thd->variables.pseudo_thread_id);
- key_length+= TMP_TABLE_KEY_EXTRA;
- }
+ uint key_length= tdc_create_key(key, db, table_name);
+ int4store(key + key_length, thd->variables.server_id);
+ int4store(key + key_length + 4, thd->variables.pseudo_thread_id);
+ key_length+= TMP_TABLE_KEY_EXTRA;
return key_length;
}
-
-/*****************************************************************************
- Functions to handle table definition cach (TABLE_SHARE)
-*****************************************************************************/
-
-extern "C" uchar *table_def_key(const uchar *record, size_t *length,
- my_bool not_used __attribute__((unused)))
-{
- TABLE_SHARE *entry=(TABLE_SHARE*) record;
- *length= entry->table_cache_key.length;
- return (uchar*) entry->table_cache_key.str;
-}
-
-
-static void table_def_free_entry(TABLE_SHARE *share)
-{
- DBUG_ENTER("table_def_free_entry");
- mysql_mutex_assert_owner(&LOCK_open);
- if (share->prev)
- {
- /* remove from old_unused_share list */
- *share->prev= share->next;
- share->next->prev= share->prev;
- }
- free_table_share(share);
- DBUG_VOID_RETURN;
-}
-
-
-bool table_def_init(void)
-{
- table_def_inited= 1;
-#ifdef HAVE_PSI_INTERFACE
- init_tdc_psi_keys();
-#endif
- mysql_mutex_init(key_LOCK_open, &LOCK_open, MY_MUTEX_INIT_FAST);
- oldest_unused_share= &end_of_unused_share;
- end_of_unused_share.prev= &oldest_unused_share;
-
-
- return my_hash_init(&table_def_cache, &my_charset_bin, table_def_size,
- 0, 0, table_def_key,
- (my_hash_free_key) table_def_free_entry, 0) != 0;
-}
-
-
-/**
- Notify table definition cache that process of shutting down server
- has started so it has to keep number of TABLE and TABLE_SHARE objects
- minimal in order to reduce number of references to pluggable engines.
-*/
-
-void table_def_start_shutdown(void)
-{
- if (table_def_inited)
- {
- mysql_mutex_lock(&LOCK_open);
- /*
- Ensure that TABLE and TABLE_SHARE objects which are created for
- tables that are open during process of plugins' shutdown are
- immediately released. This keeps number of references to engine
- plugins minimal and allows shutdown to proceed smoothly.
- */
- table_def_shutdown_in_progress= TRUE;
- mysql_mutex_unlock(&LOCK_open);
- /* Free all cached but unused TABLEs and TABLE_SHAREs. */
- close_cached_tables(NULL, NULL, FALSE, LONG_TIMEOUT);
- }
-}
-
-
-void table_def_free(void)
-{
- DBUG_ENTER("table_def_free");
- if (table_def_inited)
- {
- table_def_inited= 0;
- /* Free table definitions. */
- my_hash_free(&table_def_cache);
- mysql_mutex_destroy(&LOCK_open);
- }
- DBUG_VOID_RETURN;
-}
-
-
-uint cached_table_definitions(void)
-{
- return table_def_cache.records;
-}
-
-
-/*
- Auxiliary routines for manipulating with per-share used/unused and
- global unused lists of TABLE objects and table_cache_count counter.
- Responsible for preserving invariants between those lists, counter
- and TABLE::in_use member.
- In fact those routines implement sort of implicit table cache as
- part of table definition cache.
-*/
-
-
-/**
- Add newly created TABLE object for table share which is going
- to be used right away.
-*/
-
-static void table_def_add_used_table(THD *thd, TABLE *table)
-{
- DBUG_ASSERT(table->in_use == thd);
- table->s->used_tables.push_front(table);
- table_cache_count++;
-}
-
-
/**
- Prepare used or unused TABLE instance for destruction by removing
- it from share's and global list.
+ Get table cache key for a table list element.
+
+ @param table_list[in] Table list element.
+ @param key[out] On return points to table cache key for the table.
+
+ @note Unlike create_table_def_key() call this function doesn't construct
+ key in a buffer provider by caller. Instead it relies on the fact
+ that table list element for which key is requested has properly
+ initialized MDL_request object and the fact that table definition
+ cache key is suffix of key used in MDL subsystem. So to get table
+ definition key it simply needs to return pointer to appropriate
+ part of MDL_key object nested in this table list element.
+ Indeed, this means that lifetime of key produced by this call is
+ limited by the lifetime of table list element which it got as
+ parameter.
+
+ @return Length of key.
*/
-static void table_def_remove_table(TABLE *table)
+uint get_table_def_key(const TABLE_LIST *table_list, const char **key)
{
- if (table->in_use)
- {
- /* Remove from per-share chain of used TABLE objects. */
- table->s->used_tables.remove(table);
- }
- else
- {
- /* Remove from per-share chain of unused TABLE objects. */
- table->s->free_tables.remove(table);
-
- /* And global unused chain. */
- table->next->prev=table->prev;
- table->prev->next=table->next;
- if (table == unused_tables)
- {
- unused_tables=unused_tables->next;
- if (table == unused_tables)
- unused_tables=0;
- }
- check_unused(current_thd);
- }
- table_cache_count--;
-}
-
-
-/**
- Mark already existing TABLE instance as used.
-*/
-
-static void table_def_use_table(THD *thd, TABLE *table)
-{
- DBUG_ASSERT(!table->in_use);
-
- /* Unlink table from list of unused tables for this share. */
- table->s->free_tables.remove(table);
- /* Unlink able from global unused tables list. */
- if (table == unused_tables)
- { // First unused
- unused_tables=unused_tables->next; // Remove from link
- if (table == unused_tables)
- unused_tables=0;
- }
- table->prev->next=table->next; /* Remove from unused list */
- table->next->prev=table->prev;
- check_unused(thd);
- /* Add table to list of used tables for this share. */
- table->s->used_tables.push_front(table);
- table->in_use= thd;
- /* The ex-unused table must be fully functional. */
- DBUG_ASSERT(table->db_stat && table->file);
- /* The children must be detached from the table. */
- DBUG_ASSERT(! table->file->extra(HA_EXTRA_IS_ATTACHED_CHILDREN));
-}
-
-
-/**
- Mark already existing used TABLE instance as unused.
-*/
-
-static void table_def_unuse_table(TABLE *table)
-{
- THD *thd __attribute__((unused))= table->in_use;
- DBUG_ASSERT(table->in_use);
-
- /* We shouldn't put the table to 'unused' list if the share is old. */
- DBUG_ASSERT(! table->s->has_old_version());
-
- table->in_use= 0;
- /* Remove table from the list of tables used in this share. */
- table->s->used_tables.remove(table);
- /* Add table to the list of unused TABLE objects for this share. */
- table->s->free_tables.push_front(table);
- /* Also link it last in the global list of unused TABLE objects. */
- if (unused_tables)
- {
- table->next=unused_tables;
- table->prev=unused_tables->prev;
- unused_tables->prev=table;
- table->prev->next=table;
- }
- else
- unused_tables=table->next=table->prev=table;
- check_unused(thd);
-}
-
-
-/*
- Get TABLE_SHARE for a table.
-
- get_table_share()
- thd Thread handle
- table_list Table that should be opened
- key Table cache key
- key_length Length of key
- db_flags Flags to open_table_def():
- OPEN_VIEW
- error out: Error code from open_table_def()
-
- IMPLEMENTATION
- Get a table definition from the table definition cache.
- If it doesn't exist, create a new from the table definition file.
-
- NOTES
- We must have wrlock on LOCK_open when we come here
- (To be changed later)
-
- RETURN
- 0 Error
- # Share for table
-*/
-
-TABLE_SHARE *get_table_share(THD *thd, TABLE_LIST *table_list, char *key,
- uint key_length, uint db_flags, int *error,
- my_hash_value_type hash_value)
-{
- TABLE_SHARE *share;
- DBUG_ENTER("get_table_share");
-
- *error= 0;
-
- /*
- To be able perform any operation on table we should own
- some kind of metadata lock on it.
- */
- DBUG_ASSERT(thd->mdl_context.is_lock_owner(MDL_key::TABLE,
- table_list->db,
- table_list->table_name,
- MDL_SHARED));
-
- /* Read table definition from cache */
- if ((share= (TABLE_SHARE*) my_hash_search_using_hash_value(&table_def_cache,
- hash_value, (uchar*) key, key_length)))
- goto found;
-
- if (!(share= alloc_table_share(table_list, key, key_length)))
- {
- DBUG_RETURN(0);
- }
-
- /*
- We assign a new table id under the protection of LOCK_open.
- We do this instead of creating a new mutex
- and using it for the sole purpose of serializing accesses to a
- static variable, we assign the table id here. We assign it to the
- share before inserting it into the table_def_cache to be really
- sure that it cannot be read from the cache without having a table
- id assigned.
-
- CAVEAT. This means that the table cannot be used for
- binlogging/replication purposes, unless get_table_share() has been
- called directly or indirectly.
- */
- assign_new_table_id(share);
-
- if (my_hash_insert(&table_def_cache, (uchar*) share))
- {
- free_table_share(share);
- DBUG_RETURN(0); // return error
- }
- if (open_table_def(thd, share, db_flags))
- {
- *error= share->error;
- (void) my_hash_delete(&table_def_cache, (uchar*) share);
- DBUG_RETURN(0);
- }
- share->ref_count++; // Mark in use
- DBUG_PRINT("exit", ("share: 0x%lx ref_count: %u",
- (ulong) share, share->ref_count));
- DBUG_RETURN(share);
-
-found:
/*
- We found an existing table definition. Return it if we didn't get
- an error when reading the table definition from file.
+ This call relies on the fact that TABLE_LIST::mdl_request::key object
+ is properly initialized, so table definition cache can be produced
+ from key used by MDL subsystem.
*/
- if (share->error)
- {
- /* Table definition contained an error */
- open_table_error(share, share->error, share->open_errno, share->errarg);
- DBUG_RETURN(0);
- }
- if ((share->is_view && !(db_flags & OPEN_VIEW)) ||
- (!share->is_view && (db_flags & OPEN_VIEW_ONLY)))
- {
- open_table_error(share, 1, ENOENT, 0);
- DBUG_RETURN(0);
- }
-
- ++share->ref_count;
-
- if (share->ref_count == 1 && share->prev)
- {
- /*
- Share was not used before and it was in the old_unused_share list
- Unlink share from this list
- */
- DBUG_PRINT("info", ("Unlinking from not used list"));
- *share->prev= share->next;
- share->next->prev= share->prev;
- share->next= 0;
- share->prev= 0;
- }
-
- /* Free cache if too big */
- while (table_def_cache.records > table_def_size &&
- oldest_unused_share->next)
- my_hash_delete(&table_def_cache, (uchar*) oldest_unused_share);
-
- DBUG_PRINT("exit", ("share: 0x%lx ref_count: %u",
- (ulong) share, share->ref_count));
- DBUG_RETURN(share);
-}
-
-
-/**
- Get a table share. If it didn't exist, try creating it from engine
-
- For arguments and return values, see get_table_share()
-*/
-
-static TABLE_SHARE *
-get_table_share_with_discover(THD *thd, TABLE_LIST *table_list,
- char *key, uint key_length,
- uint db_flags, int *error,
- my_hash_value_type hash_value)
-
-{
- TABLE_SHARE *share;
- bool exists;
- DBUG_ENTER("get_table_share_with_discover");
-
- share= get_table_share(thd, table_list, key, key_length, db_flags, error,
- hash_value);
- /*
- If share is not NULL, we found an existing share.
-
- If share is NULL, and there is no error, we're inside
- pre-locking, which silences 'ER_NO_SUCH_TABLE' errors
- with the intention to silently drop non-existing tables
- from the pre-locking list. In this case we still need to try
- auto-discover before returning a NULL share.
-
- Or, we're inside SHOW CREATE VIEW, which
- also installs a silencer for ER_NO_SUCH_TABLE error.
-
- If share is NULL and the error is ER_NO_SUCH_TABLE, this is
- the same as above, only that the error was not silenced by
- pre-locking or SHOW CREATE VIEW.
-
- In both these cases it won't harm to try to discover the
- table.
-
- Finally, if share is still NULL, it's a real error and we need
- to abort.
-
- @todo Rework alternative ways to deal with ER_NO_SUCH TABLE.
- */
- if (share ||
- (thd->is_error() && thd->stmt_da->sql_errno() != ER_NO_SUCH_TABLE &&
- thd->stmt_da->sql_errno() != ER_NO_SUCH_TABLE_IN_ENGINE))
- DBUG_RETURN(share);
-
- *error= 0;
-
- /* Table didn't exist. Check if some engine can provide it */
- if (ha_check_if_table_exists(thd, table_list->db, table_list->table_name,
- &exists))
- {
- thd->clear_error();
- /* Conventionally, the storage engine API does not report errors. */
- my_error(ER_OUT_OF_RESOURCES, MYF(0));
- }
- else if (! exists)
- {
- /*
- No such table in any engine.
- Hide "Table doesn't exist" errors if the table belongs to a view.
- The check for thd->is_error() is necessary to not push an
- unwanted error in case the error was already silenced.
- @todo Rework the alternative ways to deal with ER_NO_SUCH TABLE.
- */
- if (thd->is_error())
- {
- if (table_list->parent_l)
- {
- thd->clear_error();
- my_error(ER_WRONG_MRG_TABLE, MYF(0));
- }
- else if (table_list->belong_to_view)
- {
- TABLE_LIST *view= table_list->belong_to_view;
- thd->clear_error();
- my_error(ER_VIEW_INVALID, MYF(0),
- view->view_db.str, view->view_name.str);
- }
- }
- }
- else
- {
- thd->clear_error();
- *error= 7; /* Run auto-discover. */
- }
- DBUG_RETURN(NULL);
-}
-
-
-/**
- Mark that we are not using table share anymore.
-
- @param share Table share
-
- If the share has no open tables and (we have done a refresh or
- if we have already too many open table shares) then delete the
- definition.
-*/
-
-void release_table_share(TABLE_SHARE *share)
-{
- DBUG_ENTER("release_table_share");
- DBUG_PRINT("enter",
- ("share: 0x%lx table: %s.%s ref_count: %u version: %lu",
- (ulong) share, share->db.str, share->table_name.str,
- share->ref_count, share->version));
+ DBUG_ASSERT(!strcmp(table_list->get_db_name(),
+ table_list->mdl_request.key.db_name()) &&
+ !strcmp(table_list->get_table_name(),
+ table_list->mdl_request.key.name()));
- mysql_mutex_assert_owner(&LOCK_open);
-
- DBUG_ASSERT(share->ref_count);
- if (!--share->ref_count)
- {
- if (share->has_old_version() || table_def_shutdown_in_progress)
- my_hash_delete(&table_def_cache, (uchar*) share);
- else
- {
- /* Link share last in used_table_share list */
- DBUG_PRINT("info",("moving share to unused list"));
-
- DBUG_ASSERT(share->next == 0);
- share->prev= end_of_unused_share.prev;
- *end_of_unused_share.prev= share;
- end_of_unused_share.prev= &share->next;
- share->next= &end_of_unused_share;
-
- if (table_def_cache.records > table_def_size)
- {
- /* Delete the least used share to preserve LRU order. */
- my_hash_delete(&table_def_cache, (uchar*) oldest_unused_share);
- }
- }
- }
-
- DBUG_VOID_RETURN;
+ *key= (const char*)table_list->mdl_request.key.ptr() + 1;
+ return table_list->mdl_request.key.length() - 1;
}
-/*
- Check if table definition exits in cache
-
- SYNOPSIS
- get_cached_table_share()
- db Database name
- table_name Table name
-
- RETURN
- 0 Not cached
- # TABLE_SHARE for table
-*/
-
-TABLE_SHARE *get_cached_table_share(const char *db, const char *table_name)
-{
- char key[SAFE_NAME_LEN*2+2];
- uint key_length;
- mysql_mutex_assert_owner(&LOCK_open);
-
- key_length= create_table_def_key(key, db, table_name);
- return (TABLE_SHARE*) my_hash_search(&table_def_cache,
- (uchar*) key, key_length);
-}
+/*****************************************************************************
+ Functions to handle table definition cache (TABLE_SHARE)
+*****************************************************************************/
/*
Create a list for all open tables matching SQL expression
@@ -850,7 +261,7 @@ TABLE_SHARE *get_cached_table_share(const char *db, const char *table_name)
NOTES
One gets only a list of tables for which one has any kind of privilege.
db and table names are allocated in result struct, so one doesn't need
- a lock on LOCK_open when traversing the return list.
+ a lock when traversing the return list.
RETURN VALUES
NULL Error (Probably OOM)
@@ -859,20 +270,19 @@ TABLE_SHARE *get_cached_table_share(const char *db, const char *table_name)
OPEN_TABLE_LIST *list_open_tables(THD *thd, const char *db, const char *wild)
{
- int result = 0;
OPEN_TABLE_LIST **start_list, *open_list;
TABLE_LIST table_list;
+ TABLE_SHARE *share;
+ TDC_iterator tdc_it;
DBUG_ENTER("list_open_tables");
- mysql_mutex_lock(&LOCK_open);
bzero((char*) &table_list,sizeof(table_list));
start_list= &open_list;
open_list=0;
- for (uint idx=0 ; result == 0 && idx < table_def_cache.records; idx++)
+ tdc_it.init();
+ while ((share= tdc_it.next()))
{
- TABLE_SHARE *share= (TABLE_SHARE *)my_hash_element(&table_def_cache, idx);
-
if (db && my_strcasecmp(system_charset_info, db, share->db.str))
continue;
if (wild && wild_compare(share->table_name.str, wild, 0))
@@ -897,14 +307,18 @@ OPEN_TABLE_LIST *list_open_tables(THD *thd, const char *db, const char *wild)
share->db.str)+1,
share->table_name.str);
(*start_list)->in_use= 0;
- I_P_List_iterator<TABLE, TABLE_share> it(share->used_tables);
- while (it++)
- ++(*start_list)->in_use;
+ mysql_mutex_lock(&share->tdc.LOCK_table_share);
+ TABLE_SHARE::All_share_tables_list::Iterator it(share->tdc.all_tables);
+ TABLE *table;
+ while ((table= it++))
+ if (table->in_use)
+ ++(*start_list)->in_use;
+ mysql_mutex_unlock(&share->tdc.LOCK_table_share);
(*start_list)->locked= 0; /* Obsolete. */
start_list= &(*start_list)->next;
*start_list=0;
}
- mysql_mutex_unlock(&LOCK_open);
+ tdc_it.deinit();
DBUG_RETURN(open_list);
}
@@ -926,33 +340,11 @@ void intern_close_table(TABLE *table)
if (table->file) // Not true if placeholder
(void) closefrm(table, 1); // close file
table->alias.free();
- DBUG_VOID_RETURN;
-}
-
-/*
- Remove table from the open table cache
-
- SYNOPSIS
- free_cache_entry()
- table Table to remove
-
- NOTE
- We need to have a lock on LOCK_open when calling this
-*/
-
-static void free_cache_entry(TABLE *table)
-{
- DBUG_ENTER("free_cache_entry");
-
- /* This should be done before releasing table share. */
- table_def_remove_table(table);
-
- intern_close_table(table);
-
my_free(table);
DBUG_VOID_RETURN;
}
+
/* Free resources allocated by filesort() and read_record() */
void free_io_cache(TABLE *table)
@@ -974,20 +366,24 @@ void free_io_cache(TABLE *table)
@param share Table share.
- @pre Caller should have LOCK_open mutex.
+ @pre Caller should have TABLE_SHARE::tdc.LOCK_table_share mutex.
*/
-static void kill_delayed_threads_for_table(TABLE_SHARE *share)
+void kill_delayed_threads_for_table(TABLE_SHARE *share)
{
- I_P_List_iterator<TABLE, TABLE_share> it(share->used_tables);
+ TABLE_SHARE::All_share_tables_list::Iterator it(share->tdc.all_tables);
TABLE *tab;
- mysql_mutex_assert_owner(&LOCK_open);
+ mysql_mutex_assert_owner(&share->tdc.LOCK_table_share);
+
+ if (!delayed_insert_threads)
+ return;
while ((tab= it++))
{
THD *in_use= tab->in_use;
+ DBUG_ASSERT(in_use && tab->s->tdc.flushed);
if ((in_use->system_thread & SYSTEM_THREAD_DELAYED_INSERT) &&
! in_use->killed)
{
@@ -1028,64 +424,50 @@ bool close_cached_tables(THD *thd, TABLE_LIST *tables,
bool wait_for_refresh, ulong timeout)
{
bool result= FALSE;
- bool found= TRUE;
struct timespec abstime;
+ ulong refresh_version;
DBUG_ENTER("close_cached_tables");
DBUG_ASSERT(thd || (!wait_for_refresh && !tables));
- mysql_mutex_lock(&LOCK_open);
+ refresh_version= tdc_increment_refresh_version();
+
if (!tables)
{
/*
Force close of all open tables.
Note that code in TABLE_SHARE::wait_for_old_version() assumes that
- incrementing of refresh_version and removal of unused tables and
- shares from TDC happens atomically under protection of LOCK_open,
- or putting it another way that TDC does not contain old shares
- which don't have any tables used.
+ incrementing of refresh_version is followed by purge of unused table
+ shares.
*/
- refresh_version++;
- DBUG_PRINT("tcache", ("incremented global refresh_version to: %lu",
- refresh_version));
kill_delayed_threads();
/*
Get rid of all unused TABLE and TABLE_SHARE instances. By doing
this we automatically close all tables which were marked as "old".
*/
- while (unused_tables)
- free_cache_entry(unused_tables);
+ tc_purge(true);
/* Free table shares which were not freed implicitly by loop above. */
- while (oldest_unused_share->next)
- (void) my_hash_delete(&table_def_cache, (uchar*) oldest_unused_share);
+ tdc_purge(true);
}
else
{
bool found=0;
for (TABLE_LIST *table= tables; table; table= table->next_local)
{
- TABLE_SHARE *share= get_cached_table_share(table->db, table->table_name);
-
- if (share)
- {
- kill_delayed_threads_for_table(share);
- /* tdc_remove_table() calls share->remove_from_cache_at_close() */
- tdc_remove_table(thd, TDC_RT_REMOVE_UNUSED, table->db,
- table->table_name, TRUE);
- found=1;
- }
+ /* tdc_remove_table() also sets TABLE_SHARE::version to 0. */
+ found|= tdc_remove_table(thd, TDC_RT_REMOVE_UNUSED, table->db,
+ table->table_name, TRUE);
}
if (!found)
wait_for_refresh=0; // Nothing to wait for
}
- mysql_mutex_unlock(&LOCK_open);
+ DBUG_PRINT("info", ("open table definitions: %d",
+ (int) tdc_records()));
if (!wait_for_refresh)
DBUG_RETURN(result);
- set_timespec(abstime, timeout);
-
if (thd->locked_tables_mode)
{
/*
@@ -1117,67 +499,69 @@ bool close_cached_tables(THD *thd, TABLE_LIST *tables,
result= TRUE;
goto err_with_reopen;
}
- close_all_tables_for_name(thd, table->s, HA_EXTRA_NOT_USED);
+ close_all_tables_for_name(thd, table->s, HA_EXTRA_NOT_USED, NULL);
}
}
/* Wait until all threads have closed all the tables we are flushing. */
DBUG_PRINT("info", ("Waiting for other threads to close their open tables"));
- while (found && ! thd->killed)
- {
- TABLE_SHARE *share;
- found= FALSE;
- /*
- To a self-deadlock or deadlocks with other FLUSH threads
- waiting on our open HANDLERs, we have to flush them.
- */
- mysql_ha_flush(thd);
- DEBUG_SYNC(thd, "after_flush_unlock");
-
- mysql_mutex_lock(&LOCK_open);
+ /*
+ To a self-deadlock or deadlocks with other FLUSH threads
+ waiting on our open HANDLERs, we have to flush them.
+ */
+ mysql_ha_flush(thd);
+ DEBUG_SYNC(thd, "after_flush_unlock");
- if (!tables)
+ if (!tables)
+ {
+ bool found= true;
+ set_timespec(abstime, timeout);
+ while (found && !thd->killed)
{
- for (uint idx=0 ; idx < table_def_cache.records ; idx++)
+ TABLE_SHARE *share;
+ TDC_iterator tdc_it;
+ found= false;
+
+ tdc_it.init();
+ while ((share= tdc_it.next()))
{
- share= (TABLE_SHARE*) my_hash_element(&table_def_cache, idx);
- if (share->has_old_version())
+ mysql_mutex_lock(&share->tdc.LOCK_table_share);
+ if (share->tdc.flushed && share->tdc.version < refresh_version)
{
- found= TRUE;
+ /* wait_for_old_version() will unlock mutex and free share */
+ found= true;
break;
}
+ mysql_mutex_unlock(&share->tdc.LOCK_table_share);
}
- }
- else
- {
- for (TABLE_LIST *table= tables; table; table= table->next_local)
+ tdc_it.deinit();
+
+ if (found)
{
- share= get_cached_table_share(table->db, table->table_name);
- if (share && share->has_old_version())
+ if (share->wait_for_old_version(thd, &abstime,
+ MDL_wait_for_subgraph::DEADLOCK_WEIGHT_DDL))
{
- found= TRUE;
+ result= TRUE;
break;
}
}
}
-
- if (found)
+ }
+ else
+ {
+ for (TABLE_LIST *table= tables; table; table= table->next_local)
{
- /*
- The method below temporarily unlocks LOCK_open and frees
- share's memory.
- */
- if (share->wait_for_old_version(thd, &abstime,
- MDL_wait_for_subgraph::DEADLOCK_WEIGHT_DDL))
+ if (thd->killed)
+ break;
+ if (tdc_wait_for_old_version(thd, table->db, table->table_name, timeout,
+ MDL_wait_for_subgraph::DEADLOCK_WEIGHT_DDL,
+ refresh_version))
{
- mysql_mutex_unlock(&LOCK_open);
result= TRUE;
- goto err_with_reopen;
+ break;
}
}
-
- mysql_mutex_unlock(&LOCK_open);
}
err_with_reopen:
@@ -1190,12 +574,12 @@ err_with_reopen:
*/
thd->locked_tables_list.reopen_tables(thd);
/*
- Since downgrade_exclusive_lock() won't do anything with shared
+ Since downgrade_lock() won't do anything with shared
metadata lock it is much simpler to go through all open tables rather
than picking only those tables that were flushed.
*/
for (TABLE *tab= thd->open_tables; tab; tab= tab->next)
- tab->mdl_ticket->downgrade_exclusive_lock(MDL_SHARED_NO_READ_WRITE);
+ tab->mdl_ticket->downgrade_lock(MDL_SHARED_NO_READ_WRITE);
}
DBUG_RETURN(result);
}
@@ -1208,23 +592,26 @@ err_with_reopen:
bool close_cached_connection_tables(THD *thd, LEX_STRING *connection)
{
- uint idx;
TABLE_LIST tmp, *tables= NULL;
bool result= FALSE;
+ TABLE_SHARE *share;
+ TDC_iterator tdc_it;
DBUG_ENTER("close_cached_connections");
DBUG_ASSERT(thd);
bzero(&tmp, sizeof(TABLE_LIST));
- mysql_mutex_lock(&LOCK_open);
-
- for (idx= 0; idx < table_def_cache.records; idx++)
+ tdc_it.init();
+ while ((share= tdc_it.next()))
{
- TABLE_SHARE *share= (TABLE_SHARE *) my_hash_element(&table_def_cache, idx);
-
+ mysql_mutex_lock(&share->tdc.LOCK_table_share);
/* Ignore if table is not open or does not have a connect_string */
- if (!share->connect_string.length || !share->ref_count)
+ if (!share->connect_string.length || !share->tdc.ref_count)
+ {
+ mysql_mutex_unlock(&share->tdc.LOCK_table_share);
continue;
+ }
+ mysql_mutex_unlock(&share->tdc.LOCK_table_share);
/* Compare the connection string */
if (connection &&
@@ -1244,7 +631,7 @@ bool close_cached_connection_tables(THD *thd, LEX_STRING *connection)
tables= (TABLE_LIST *) memdup_root(thd->mem_root, (char*)&tmp,
sizeof(TABLE_LIST));
}
- mysql_mutex_unlock(&LOCK_open);
+ tdc_it.deinit();
if (tables)
result= close_cached_tables(thd, tables, FALSE, LONG_TIMEOUT);
@@ -1265,11 +652,36 @@ bool close_cached_connection_tables(THD *thd, LEX_STRING *connection)
static void mark_temp_tables_as_free_for_reuse(THD *thd)
{
- for (TABLE *table= thd->temporary_tables ; table ; table= table->next)
+ rpl_group_info *rgi_slave;
+ DBUG_ENTER("mark_temp_tables_as_free_for_reuse");
+
+ if (thd->query_id == 0)
{
- if ((table->query_id == thd->query_id) && ! table->open_by_handler)
- mark_tmp_table_for_reuse(table);
+ /* Thread has not executed any statement and has not used any tmp tables */
+ DBUG_VOID_RETURN;
+ }
+
+ rgi_slave=thd->rgi_slave;
+ if ((!rgi_slave && thd->temporary_tables) ||
+ (rgi_slave && unlikely(rgi_slave->rli->save_temporary_tables)))
+ {
+ thd->lock_temporary_tables();
+ for (TABLE *table= thd->temporary_tables ; table ; table= table->next)
+ {
+ if ((table->query_id == thd->query_id) && ! table->open_by_handler)
+ mark_tmp_table_for_reuse(table);
+ }
+ thd->unlock_temporary_tables();
+ if (rgi_slave)
+ {
+ /*
+ Temporary tables are shared with other by sql execution threads.
+ As a safety messure, clear the pointer to the common area.
+ */
+ thd->temporary_tables= 0;
+ }
}
+ DBUG_VOID_RETURN;
}
@@ -1283,6 +695,7 @@ static void mark_temp_tables_as_free_for_reuse(THD *thd)
void mark_tmp_table_for_reuse(TABLE *table)
{
+ DBUG_ENTER("mark_tmp_table_for_reuse");
DBUG_ASSERT(table->s->tmp_table);
table->query_id= 0;
@@ -1313,6 +726,7 @@ void mark_tmp_table_for_reuse(TABLE *table)
LOCK TABLES is allowed (but ignored) for a temporary table.
*/
table->reginfo.lock_type= TL_WRITE;
+ DBUG_VOID_RETURN;
}
@@ -1364,8 +778,6 @@ static void mark_used_tables_as_free_for_reuse(THD *thd, TABLE *table)
static void close_open_tables(THD *thd)
{
- mysql_mutex_assert_not_owner(&LOCK_open);
-
DBUG_PRINT("info", ("thd->open_tables: 0x%lx", (long) thd->open_tables));
while (thd->open_tables)
@@ -1384,19 +796,26 @@ static void close_open_tables(THD *thd)
access the table cache key
@param[in] extra
- HA_EXTRA_PREPRE_FOR_DROP if the table is being dropped
- HA_EXTRA_PREPARE_FOR_REANME if the table is being renamed
- HA_EXTRA_NOT_USED no drop/rename
- In case of drop/reanme the documented behaviour is to
+ HA_EXTRA_PREPARE_FOR_DROP
+ - The table is dropped
+ HA_EXTRA_PREPARE_FOR_RENAME
+ - The table is renamed
+ HA_EXTRA_NOT_USED
+ - The table is marked as closed in the
+ locked_table_list but kept there so one can call
+ locked_table_list->reopen_tables() to put it back.
+
+ In case of drop/rename the documented behavior is to
implicitly remove the table from LOCK TABLES
- list.
+ list.
@pre Must be called with an X MDL lock on the table.
*/
void
close_all_tables_for_name(THD *thd, TABLE_SHARE *share,
- ha_extra_function extra)
+ ha_extra_function extra,
+ TABLE *skip_table)
{
char key[MAX_DBKEY_LENGTH];
uint key_length= share->table_cache_key.length;
@@ -1405,13 +824,13 @@ close_all_tables_for_name(THD *thd, TABLE_SHARE *share,
memcpy(key, share->table_cache_key.str, key_length);
- mysql_mutex_assert_not_owner(&LOCK_open);
for (TABLE **prev= &thd->open_tables; *prev; )
{
TABLE *table= *prev;
if (table->s->table_cache_key.length == key_length &&
- !memcmp(table->s->table_cache_key.str, key, key_length))
+ !memcmp(table->s->table_cache_key.str, key, key_length) &&
+ table != skip_table)
{
thd->locked_tables_list.unlink_from_list(thd,
table->pos_in_locked_tables,
@@ -1437,9 +856,12 @@ close_all_tables_for_name(THD *thd, TABLE_SHARE *share,
prev= &table->next;
}
}
- /* Remove the table share from the cache. */
- tdc_remove_table(thd, TDC_RT_REMOVE_ALL, db, table_name,
- FALSE);
+ if (skip_table == NULL)
+ {
+ /* Remove the table share from the cache. */
+ tdc_remove_table(thd, TDC_RT_REMOVE_ALL, db, table_name,
+ FALSE);
+ }
}
@@ -1466,7 +888,7 @@ void close_thread_tables(THD *thd)
TABLE *table;
DBUG_ENTER("close_thread_tables");
- thd_proc_info(thd, "closing tables");
+ THD_STAGE_INFO(thd, stage_closing_tables);
#ifdef EXTRA_DEBUG
DBUG_PRINT("tcache", ("open tables:"));
@@ -1591,16 +1013,14 @@ void close_thread_tables(THD *thd)
/* move one table to free list */
-bool close_thread_table(THD *thd, TABLE **table_ptr)
+void close_thread_table(THD *thd, TABLE **table_ptr)
{
- bool found_old_table= 0;
TABLE *table= *table_ptr;
DBUG_ENTER("close_thread_table");
DBUG_PRINT("tcache", ("table: '%s'.'%s' 0x%lx", table->s->db.str,
table->s->table_name.str, (long) table));
DBUG_ASSERT(table->key_read == 0);
DBUG_ASSERT(!table->file || table->file->inited == handler::NONE);
- mysql_mutex_assert_not_owner(&LOCK_open);
/*
The metadata lock must be released after giving back
@@ -1624,34 +1044,22 @@ bool close_thread_table(THD *thd, TABLE **table_ptr)
if (! table->needs_reopen())
{
- /* Avoid having MERGE tables with attached children in unused_tables. */
+ /* Avoid having MERGE tables with attached children in table cache. */
table->file->extra(HA_EXTRA_DETACH_CHILDREN);
/* Free memory and reset for next loop. */
free_field_buffers_larger_than(table, MAX_TDC_BLOB_SIZE);
table->file->ha_reset();
}
- mysql_mutex_lock(&LOCK_open);
+ /*
+ Do this *before* entering the TABLE_SHARE::tdc.LOCK_table_share
+ critical section.
+ */
+ if (table->file != NULL)
+ table->file->unbind_psi();
- if (table->s->has_old_version() || table->needs_reopen() ||
- table_def_shutdown_in_progress)
- {
- free_cache_entry(table);
- found_old_table= 1;
- }
- else
- {
- DBUG_ASSERT(table->file);
- table_def_unuse_table(table);
- /*
- We free the least used table, not the subject table,
- to keep the LRU order.
- */
- if (table_cache_count > table_cache_size)
- free_cache_entry(unused_tables);
- }
- mysql_mutex_unlock(&LOCK_open);
- DBUG_RETURN(found_old_table);
+ tc_release_table(table);
+ DBUG_VOID_RETURN;
}
@@ -1665,6 +1073,10 @@ static inline uint tmpkeyval(THD *thd, TABLE *table)
/*
Close all temporary tables created by 'CREATE TEMPORARY TABLE' for thread
creates one DROP TEMPORARY TABLE binlog event for each pseudo-thread
+
+ Temporary tables created in a sql slave is closed by
+ Relay_log_info::close_temporary_tables()
+
*/
bool close_temporary_tables(THD *thd)
@@ -1679,6 +1091,7 @@ bool close_temporary_tables(THD *thd)
if (!thd->temporary_tables)
DBUG_RETURN(FALSE);
+ DBUG_ASSERT(!thd->rgi_slave);
/*
Ensure we don't have open HANDLERs for tables we are about to close.
@@ -1747,7 +1160,8 @@ bool close_temporary_tables(THD *thd)
/* We always quote db,table names though it is slight overkill */
if (found_user_tables &&
- !(was_quote_show= test(thd->variables.option_bits & OPTION_QUOTE_SHOW_CREATE)))
+ !(was_quote_show= MY_TEST(thd->variables.option_bits &
+ OPTION_QUOTE_SHOW_CREATE)))
{
thd->variables.option_bits |= OPTION_QUOTE_SHOW_CREATE;
}
@@ -1802,7 +1216,7 @@ bool close_temporary_tables(THD *thd)
qinfo.db_len= db.length();
thd->variables.character_set_client= cs_save;
- thd->stmt_da->can_overwrite_status= TRUE;
+ thd->get_stmt_da()->set_overwrite_status(true);
if ((error= (mysql_bin_log.write(&qinfo) || error)))
{
/*
@@ -1820,7 +1234,7 @@ bool close_temporary_tables(THD *thd)
sql_print_error("Failed to write the DROP statement for "
"temporary tables to binary log");
}
- thd->stmt_da->can_overwrite_status= FALSE;
+ thd->get_stmt_da()->set_overwrite_status(false);
thd->variables.pseudo_thread_id= save_pseudo_thread_id;
thd->thread_specific_used= save_thread_specific_used;
@@ -1973,9 +1387,7 @@ retry:
/* Skip if table alias does not match. */
if (check_alias)
{
- if (lower_case_table_names ?
- my_strcasecmp(files_charset_info, t_alias, res->alias) :
- strcmp(t_alias, res->alias))
+ if (my_strcasecmp(table_alias_charset, t_alias, res->alias))
goto next;
}
@@ -2107,7 +1519,7 @@ void update_non_unique_table_error(TABLE_LIST *update,
return;
}
}
- my_error(ER_UPDATE_TABLE_USED, MYF(0), update->alias);
+ my_error(ER_UPDATE_TABLE_USED, MYF(0), update->alias, operation);
}
@@ -2120,12 +1532,9 @@ void update_non_unique_table_error(TABLE_LIST *update,
TABLE *find_temporary_table(THD *thd, const char *db, const char *table_name)
{
- TABLE_LIST tl;
-
- tl.db= (char*) db;
- tl.table_name= (char*) table_name;
-
- return find_temporary_table(thd, &tl);
+ char key[MAX_DBKEY_LENGTH];
+ uint key_length= create_tmp_table_def_key(thd, key, db, table_name);
+ return find_temporary_table(thd, key, key_length);
}
@@ -2138,10 +1547,79 @@ TABLE *find_temporary_table(THD *thd, const char *db, const char *table_name)
TABLE *find_temporary_table(THD *thd, const TABLE_LIST *tl)
{
+ const char *tmp_key;
char key[MAX_DBKEY_LENGTH];
- uint key_length= create_table_def_key(thd, key, tl, 1);
+ uint key_length;
- return find_temporary_table(thd, key, key_length);
+ key_length= get_table_def_key(tl, &tmp_key);
+ memcpy(key, tmp_key, key_length);
+ int4store(key + key_length, thd->variables.server_id);
+ int4store(key + key_length + 4, thd->variables.pseudo_thread_id);
+
+ return find_temporary_table(thd, key, key_length + TMP_TABLE_KEY_EXTRA);
+}
+
+
+static bool
+use_temporary_table(THD *thd, TABLE *table, TABLE **out_table)
+{
+ *out_table= table;
+ if (!table)
+ return false;
+ /*
+ Temporary tables are not safe for parallel replication. They were
+ designed to be visible to one thread only, so have no table locking.
+ Thus there is no protection against two conflicting transactions
+ committing in parallel and things like that.
+
+ So for now, anything that uses temporary tables will be serialised
+ with anything before it, when using parallel replication.
+
+ ToDo: We might be able to introduce a reference count or something
+ on temp tables, and have slave worker threads wait for it to reach
+ zero before being allowed to use the temp table. Might not be worth
+ it though, as statement-based replication using temporary tables is
+ in any case rather fragile.
+ */
+ if (thd->rgi_slave && thd->rgi_slave->is_parallel_exec &&
+ thd->wait_for_prior_commit())
+ return true;
+ /*
+ We need to set the THD as it may be different in case of
+ parallel replication
+ */
+ if (table->in_use != thd)
+ {
+ table->in_use= thd;
+#ifdef REMOVE_AFTER_MERGE_WITH_10
+ if (thd->rgi_slave)
+ {
+ /*
+ We may be stealing an opened temporary tables from one slave
+ thread to another, we need to let the performance schema know that,
+ for aggregates per thread to work properly.
+ */
+ table->file->unbind_psi();
+ table->file->rebind_psi();
+ }
+#endif
+ }
+ return false;
+}
+
+bool
+find_and_use_temporary_table(THD *thd, const char *db, const char *table_name,
+ TABLE **out_table)
+{
+ return use_temporary_table(thd, find_temporary_table(thd, db, table_name),
+ out_table);
+}
+
+
+bool
+find_and_use_temporary_table(THD *thd, const TABLE_LIST *tl, TABLE **out_table)
+{
+ return use_temporary_table(thd, find_temporary_table(thd, tl), out_table);
}
@@ -2155,16 +1633,22 @@ TABLE *find_temporary_table(THD *thd,
const char *table_key,
uint table_key_length)
{
+ TABLE *result= 0;
+ if (!thd->have_temporary_tables())
+ return NULL;
+
+ thd->lock_temporary_tables();
for (TABLE *table= thd->temporary_tables; table; table= table->next)
{
if (table->s->table_cache_key.length == table_key_length &&
!memcmp(table->s->table_cache_key.str, table_key, table_key_length))
{
- return table;
+ result= table;
+ break;
}
}
-
- return NULL;
+ thd->unlock_temporary_tables();
+ return result;
}
@@ -2189,35 +1673,33 @@ TABLE *find_temporary_table(THD *thd,
thd->temporary_tables list, it's impossible to tell here whether
we're dealing with an internal or a user temporary table.
- If is_trans is not null, we return the type of the table:
- either transactional (e.g. innodb) as TRUE or non-transactional
- (e.g. myisam) as FALSE.
+ @param thd Thread handler
+ @param table Temporary table to be deleted
+ @param is_trans Is set to the type of the table:
+ transactional (e.g. innodb) as TRUE or non-transactional
+ (e.g. myisam) as FALSE.
@retval 0 the table was found and dropped successfully.
- @retval 1 the table was not found in the list of temporary tables
- of this thread
@retval -1 the table is in use by a outer query
*/
-int drop_temporary_table(THD *thd, TABLE_LIST *table_list, bool *is_trans)
+int drop_temporary_table(THD *thd, TABLE *table, bool *is_trans)
{
- TABLE *table;
DBUG_ENTER("drop_temporary_table");
DBUG_PRINT("tmptable", ("closing table: '%s'.'%s'",
- table_list->db, table_list->table_name));
-
- if (!(table= find_temporary_table(thd, table_list)))
- DBUG_RETURN(1);
+ table->s->db.str, table->s->table_name.str));
/* Table might be in use by some outer statement. */
if (table->query_id && table->query_id != thd->query_id)
{
+ DBUG_PRINT("info", ("table->query_id: %lu thd->query_id: %lu",
+ (ulong) table->query_id, (ulong) thd->query_id));
+
my_error(ER_CANT_REOPEN_TABLE, MYF(0), table->alias.c_ptr());
DBUG_RETURN(-1);
}
- if (is_trans != NULL)
- *is_trans= table->file->has_transactions();
+ *is_trans= table->file->has_transactions();
/*
If LOCK TABLES list is not empty and contains this table,
@@ -2228,6 +1710,7 @@ int drop_temporary_table(THD *thd, TABLE_LIST *table_list, bool *is_trans)
DBUG_RETURN(0);
}
+
/*
unlink from thd->temporary tables and close temporary table
*/
@@ -2240,6 +1723,7 @@ void close_temporary_table(THD *thd, TABLE *table,
table->s->db.str, table->s->table_name.str,
(long) table, table->alias.c_ptr()));
+ thd->lock_temporary_tables();
if (table->prev)
{
table->prev->next= table->next;
@@ -2259,12 +1743,14 @@ void close_temporary_table(THD *thd, TABLE *table,
if (thd->temporary_tables)
table->next->prev= 0;
}
- if (thd->slave_thread)
+ if (thd->rgi_slave)
{
/* natural invariant of temporary_tables */
DBUG_ASSERT(slave_open_temp_tables || !thd->temporary_tables);
- slave_open_temp_tables--;
+ thread_safe_decrement32(&slave_open_temp_tables, &thread_running_lock);
+ table->in_use= 0; // No statistics
}
+ thd->unlock_temporary_tables();
close_temporary(table, free_share, delete_table);
DBUG_VOID_RETURN;
}
@@ -2285,13 +1771,6 @@ void close_temporary(TABLE *table, bool free_share, bool delete_table)
DBUG_PRINT("tmptable", ("closing table: '%s'.'%s'",
table->s->db.str, table->s->table_name.str));
- /* in_use is not set for replication temporary tables during shutdown */
- if (table->in_use)
- {
- table->file->update_global_table_stats();
- table->file->update_global_index_stats();
- }
-
free_io_cache(table);
closefrm(table, 0);
if (delete_table)
@@ -2319,15 +1798,12 @@ bool rename_temporary_table(THD* thd, TABLE *table, const char *db,
char *key;
uint key_length;
TABLE_SHARE *share= table->s;
- TABLE_LIST table_list;
DBUG_ENTER("rename_temporary_table");
if (!(key=(char*) alloc_root(&share->mem_root, MAX_DBKEY_LENGTH)))
DBUG_RETURN(1); /* purecov: inspected */
- table_list.db= (char*) db;
- table_list.table_name= (char*) table_name;
- key_length= create_table_def_key(thd, key, &table_list, 1);
+ key_length= create_tmp_table_def_key(thd, key, db, table_name);
share->set_table_cache_key(key, key_length);
DBUG_RETURN(0);
}
@@ -2352,19 +1828,19 @@ bool rename_temporary_table(THD* thd, TABLE *table, const char *db,
*/
bool wait_while_table_is_used(THD *thd, TABLE *table,
- enum ha_extra_function function,
- enum_tdc_remove_table_type remove_type)
+ enum ha_extra_function function)
{
DBUG_ENTER("wait_while_table_is_used");
DBUG_PRINT("enter", ("table: '%s' share: 0x%lx db_stat: %u version: %lu",
table->s->table_name.str, (ulong) table->s,
- table->db_stat, table->s->version));
+ table->db_stat, table->s->tdc.version));
- if (thd->mdl_context.upgrade_shared_lock_to_exclusive(
- table->mdl_ticket, thd->variables.lock_wait_timeout))
+ if (thd->mdl_context.upgrade_shared_lock(
+ table->mdl_ticket, MDL_EXCLUSIVE,
+ thd->variables.lock_wait_timeout))
DBUG_RETURN(TRUE);
- tdc_remove_table(thd, remove_type,
+ tdc_remove_table(thd, TDC_RT_REMOVE_NOT_OWN,
table->s->db.str, table->s->table_name.str,
FALSE);
/* extra() call must come only after all instances above are closed */
@@ -2410,78 +1886,13 @@ void drop_open_table(THD *thd, TABLE *table, const char *db_name,
tdc_remove_table(thd, TDC_RT_REMOVE_ALL, db_name, table_name,
FALSE);
/* Remove the table from the storage engine and rm the .frm. */
- quick_rm_table(table_type, db_name, table_name, 0);
- }
+ quick_rm_table(thd, table_type, db_name, table_name, 0);
+ }
DBUG_VOID_RETURN;
}
/**
- Check that table exists in table definition cache, on disk
- or in some storage engine.
-
- @param thd Thread context
- @param table Table list element
- @param fast_check Check only if share or .frm file exists
- @param[out] exists Out parameter which is set to TRUE if table
- exists and to FALSE otherwise.
-
- @note This function acquires LOCK_open internally.
-
- @note If there is no .FRM file for the table but it exists in one
- of engines (e.g. it was created on another node of NDB cluster)
- this function will fetch and create proper .FRM file for it.
-
- @retval TRUE Some error occurred
- @retval FALSE No error. 'exists' out parameter set accordingly.
-*/
-
-bool check_if_table_exists(THD *thd, TABLE_LIST *table, bool fast_check,
- bool *exists)
-{
- char path[FN_REFLEN + 1];
- TABLE_SHARE *share;
- DBUG_ENTER("check_if_table_exists");
-
- *exists= TRUE;
-
- DBUG_ASSERT(fast_check ||
- thd->mdl_context.
- is_lock_owner(MDL_key::TABLE, table->db,
- table->table_name, MDL_SHARED));
-
- mysql_mutex_lock(&LOCK_open);
- share= get_cached_table_share(table->db, table->table_name);
- mysql_mutex_unlock(&LOCK_open);
-
- if (share)
- goto end;
-
- build_table_filename(path, sizeof(path) - 1, table->db, table->table_name,
- reg_ext, 0);
-
- if (!access(path, F_OK))
- goto end;
-
- if (fast_check)
- {
- *exists= FALSE;
- goto end;
- }
-
- /* .FRM file doesn't exist. Check if some engine can provide it. */
- if (ha_check_if_table_exists(thd, table->db, table->table_name, exists))
- {
- my_printf_error(ER_OUT_OF_RESOURCES, "Failed to open '%-.64s', error while "
- "unpacking from engine", MYF(0), table->table_name);
- DBUG_RETURN(TRUE);
- }
-end:
- DBUG_RETURN(FALSE);
-}
-
-
-/**
An error handler which converts, if possible, ER_LOCK_DEADLOCK error
that can occur when we are trying to acquire a metadata lock to
a request for back-off and re-start of open_tables() process.
@@ -2499,9 +1910,9 @@ public:
virtual bool handle_condition(THD *thd,
uint sql_errno,
const char* sqlstate,
- MYSQL_ERROR::enum_warning_level level,
+ Sql_condition::enum_warning_level level,
const char* msg,
- MYSQL_ERROR ** cond_hdl);
+ Sql_condition ** cond_hdl);
private:
/** Open table context to be used for back-off request. */
@@ -2518,9 +1929,9 @@ private:
bool MDL_deadlock_handler::handle_condition(THD *,
uint sql_errno,
const char*,
- MYSQL_ERROR::enum_warning_level,
+ Sql_condition::enum_warning_level,
const char*,
- MYSQL_ERROR ** cond_hdl)
+ Sql_condition ** cond_hdl)
{
*cond_hdl= NULL;
if (! m_is_active && sql_errno == ER_LOCK_DEADLOCK)
@@ -2672,160 +2083,92 @@ open_table_get_mdl_lock(THD *thd, Open_table_context *ot_ctx,
/**
- Check if table's share is being removed from the table definition
- cache and, if yes, wait until the flush is complete.
-
- @param thd Thread context.
- @param table_list Table which share should be checked.
- @param timeout Timeout for waiting.
- @param deadlock_weight Weight of this wait for deadlock detector.
-
- @retval FALSE Success. Share is up to date or has been flushed.
- @retval TRUE Error (OOM, our was killed, the wait resulted
- in a deadlock or timeout). Reported.
+ Open a base table.
+
+ @param thd Thread context.
+ @param table_list Open first table in list.
+ @param mem_root Temporary MEM_ROOT to be used for
+ parsing .FRMs for views.
+ @param ot_ctx Context with flags which modify how open works
+ and which is used to recover from a failed
+ open_table() attempt.
+ Some examples of flags:
+ MYSQL_OPEN_IGNORE_FLUSH - Open table even if
+ someone has done a flush. No version number
+ checking is done.
+ MYSQL_OPEN_HAS_MDL_LOCK - instead of acquiring
+ metadata locks rely on that caller already has
+ appropriate ones.
+
+ Uses a cache of open tables to find a TABLE instance not in use.
+
+ If TABLE_LIST::open_strategy is set to OPEN_IF_EXISTS, the table is
+ opened only if it exists. If the open strategy is OPEN_STUB, the
+ underlying table is never opened. In both cases, metadata locks are
+ always taken according to the lock strategy.
+
+ The function used to open temporary tables, but now it opens base tables
+ only.
+
+ @retval TRUE Open failed. "action" parameter may contain type of action
+ needed to remedy problem before retrying again.
+ @retval FALSE Success. Members of TABLE_LIST structure are filled properly
+ (e.g. TABLE_LIST::table is set for real tables and
+ TABLE_LIST::view is set for views).
*/
-static bool
-tdc_wait_for_old_version(THD *thd, const char *db, const char *table_name,
- ulong wait_timeout, uint deadlock_weight)
-{
- TABLE_SHARE *share;
- bool res= FALSE;
-
- mysql_mutex_lock(&LOCK_open);
- if ((share= get_cached_table_share(db, table_name)) &&
- share->has_old_version())
- {
- struct timespec abstime;
- set_timespec(abstime, wait_timeout);
- res= share->wait_for_old_version(thd, &abstime, deadlock_weight);
- }
- mysql_mutex_unlock(&LOCK_open);
- return res;
-}
-
-
-/*
- Open a table.
-
- SYNOPSIS
- open_table()
- thd Thread context.
- table_list Open first table in list.
- action INOUT Pointer to variable of enum_open_table_action type
- which will be set according to action which is
- required to remedy problem appeared during attempt
- to open table.
- flags Bitmap of flags to modify how open works:
- MYSQL_OPEN_IGNORE_FLUSH - Open table even if
- someone has done a flush or there is a pending
- exclusive metadata lock requests against it
- (i.e. request high priority metadata lock).
- No version number checking is done.
- MYSQL_OPEN_TEMPORARY_ONLY - Open only temporary
- table not the base table or view.
- MYSQL_OPEN_TAKE_UPGRADABLE_MDL - Obtain upgradable
- metadata lock for tables on which we are going to
- take some kind of write table-level lock.
-
- IMPLEMENTATION
- Uses a cache of open tables to find a table not in use.
-
- If TABLE_LIST::open_strategy is set to OPEN_IF_EXISTS, the table is opened
- only if it exists. If the open strategy is OPEN_STUB, the underlying table
- is never opened. In both cases, metadata locks are always taken according
- to the lock strategy.
-
- RETURN
- TRUE Open failed. "action" parameter may contain type of action
- needed to remedy problem before retrying again.
- FALSE Success. Members of TABLE_LIST structure are filled properly (e.g.
- TABLE_LIST::table is set for real tables and TABLE_LIST::view is
- set for views).
-*/
-
-
bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
Open_table_context *ot_ctx)
{
- reg1 TABLE *table;
- char key[MAX_DBKEY_LENGTH];
+ TABLE *table;
+ const char *key;
uint key_length;
char *alias= table_list->alias;
uint flags= ot_ctx->get_flags();
MDL_ticket *mdl_ticket;
- int error;
TABLE_SHARE *share;
- my_hash_value_type hash_value;
+ uint gts_flags;
DBUG_ENTER("open_table");
+ /*
+ The table must not be opened already. The table can be pre-opened for
+ some statements if it is a temporary table.
+
+ open_temporary_table() must be used to open temporary tables.
+ */
+ DBUG_ASSERT(!table_list->table);
+
/* an open table operation needs a lot of the stack space */
if (check_stack_overrun(thd, STACK_MIN_SIZE_FOR_OPEN, (uchar *)&alias))
DBUG_RETURN(TRUE);
- if (thd->killed)
+ if (!(flags & MYSQL_OPEN_IGNORE_KILLED) && thd->killed)
+ {
+ thd->send_kill_message();
DBUG_RETURN(TRUE);
-
- key_length= (create_table_def_key(thd, key, table_list, 1) -
- TMP_TABLE_KEY_EXTRA);
+ }
/*
- Unless requested otherwise, try to resolve this table in the list
- of temporary tables of this thread. In MySQL temporary tables
- are always thread-local and "shadow" possible base tables with the
- same name. This block implements the behaviour.
- TODO: move this block into a separate function.
+ Check if we're trying to take a write lock in a read only transaction.
+
+ Note that we allow write locks on log tables as otherwise logging
+ to general/slow log would be disabled in read only transactions.
*/
- if (table_list->open_type != OT_BASE_ONLY &&
- ! (flags & MYSQL_OPEN_SKIP_TEMPORARY))
+ if (table_list->mdl_request.type >= MDL_SHARED_WRITE &&
+ thd->tx_read_only &&
+ !(flags & (MYSQL_LOCK_LOG_TABLE | MYSQL_OPEN_HAS_MDL_LOCK)))
{
- for (table= thd->temporary_tables; table ; table=table->next)
- {
- if (table->s->table_cache_key.length == key_length +
- TMP_TABLE_KEY_EXTRA &&
- !memcmp(table->s->table_cache_key.str, key,
- key_length + TMP_TABLE_KEY_EXTRA))
- {
- /*
- We're trying to use the same temporary table twice in a query.
- Right now we don't support this because a temporary table
- is always represented by only one TABLE object in THD, and
- it can not be cloned. Emit an error for an unsupported behaviour.
- */
- if (table->query_id)
- {
- DBUG_PRINT("error",
- ("query_id: %lu server_id: %u pseudo_thread_id: %lu",
- (ulong) table->query_id, (uint) thd->server_id,
- (ulong) thd->variables.pseudo_thread_id));
- my_error(ER_CANT_REOPEN_TABLE, MYF(0), table->alias.c_ptr());
- DBUG_RETURN(TRUE);
- }
- table->query_id= thd->query_id;
- thd->thread_specific_used= TRUE;
- DBUG_PRINT("info",("Using temporary table"));
- goto reset;
- }
- }
+ my_error(ER_CANT_EXECUTE_IN_READ_ONLY_TRANSACTION, MYF(0));
+ DBUG_RETURN(true);
}
- if (table_list->open_type == OT_TEMPORARY_ONLY ||
- (flags & MYSQL_OPEN_TEMPORARY_ONLY))
- {
- if (table_list->open_strategy == TABLE_LIST::OPEN_NORMAL)
- {
- my_error(ER_NO_SUCH_TABLE, MYF(0), table_list->db, table_list->table_name);
- DBUG_RETURN(TRUE);
- }
- else
- DBUG_RETURN(FALSE);
- }
+ key_length= get_table_def_key(table_list, &key);
/*
- The table is not temporary - if we're in pre-locked or LOCK TABLES
- mode, let's try to find the requested table in the list of pre-opened
- and locked tables. If the table is not there, return an error - we can't
- open not pre-opened tables in pre-locked/LOCK TABLES mode.
+ If we're in pre-locked or LOCK TABLES mode, let's try to find the
+ requested table in the list of pre-opened and locked tables. If the
+ table is not there, return an error - we can't open not pre-opened
+ tables in pre-locked/LOCK TABLES mode.
TODO: move this block into a separate function.
*/
if (thd->locked_tables_mode &&
@@ -2899,7 +2242,6 @@ bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
MDL_SHARED))
{
char path[FN_REFLEN + 1];
- enum legacy_db_type not_used;
build_table_filename(path, sizeof(path) - 1,
table_list->db, table_list->table_name, reg_ext, 0);
/*
@@ -2909,7 +2251,7 @@ bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
during prelocking process (in this case in theory we still
should hold shared metadata lock on it).
*/
- if (dd_frm_type(thd, path, &not_used) == FRMTYPE_VIEW)
+ if (dd_frm_is_view(thd, path))
{
/*
If parent_l of the table_list is non null then a merge table
@@ -2922,7 +2264,7 @@ bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
}
if (!tdc_open_view(thd, table_list, alias, key, key_length,
- mem_root, 0))
+ mem_root, CHECK_METADATA_VERSION))
{
DBUG_ASSERT(table_list->view != 0);
DBUG_RETURN(FALSE); // VIEW
@@ -2931,7 +2273,7 @@ bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
}
/*
No table in the locked tables list. In case of explicit LOCK TABLES
- this can happen if a user did not include the able into the list.
+ this can happen if a user did not include the table into the list.
In case of pre-locked mode locked tables list is generated automatically,
so we may only end up here if the table did not exist when
locked tables list was created.
@@ -2956,12 +2298,12 @@ bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
global read lock until end of this statement in order to have
this statement blocked by active FLUSH TABLES WITH READ LOCK.
- We don't block acquire this protection under LOCK TABLES as
+ We don't need to acquire this protection under LOCK TABLES as
such protection already acquired at LOCK TABLES time and
not released until UNLOCK TABLES.
We don't block statements which modify only temporary tables
- as these tables are not preserved by backup by any form of
+ as these tables are not preserved by any form of
backup which uses FLUSH TABLES WITH READ LOCK.
TODO: The fact that we sometimes acquire protection against
@@ -3021,52 +2363,61 @@ bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
mdl_ticket= table_list->mdl_request.ticket;
}
- hash_value= my_calc_hash(&table_def_cache, (uchar*) key, key_length);
-
-
if (table_list->open_strategy == TABLE_LIST::OPEN_IF_EXISTS)
{
- bool exists;
-
- if (check_if_table_exists(thd, table_list, 0, &exists))
- DBUG_RETURN(TRUE);
-
- if (!exists)
+ if (!ha_table_exists(thd, table_list->db, table_list->table_name))
DBUG_RETURN(FALSE);
-
- /* Table exists. Let us try to open it. */
}
else if (table_list->open_strategy == TABLE_LIST::OPEN_STUB)
DBUG_RETURN(FALSE);
+ /* Table exists. Let us try to open it. */
+
+ if (table_list->i_s_requested_object & OPEN_TABLE_ONLY)
+ gts_flags= GTS_TABLE;
+ else if (table_list->i_s_requested_object & OPEN_VIEW_ONLY)
+ gts_flags= GTS_VIEW;
+ else
+ gts_flags= GTS_TABLE | GTS_VIEW;
+
retry_share:
- mysql_mutex_lock(&LOCK_open);
+ share= tdc_acquire_share(thd, table_list->db, table_list->table_name,
+ key, key_length,
+ table_list->mdl_request.key.tc_hash_value(),
+ gts_flags, &table);
- if (!(share= get_table_share_with_discover(thd, table_list, key,
- key_length,
- (OPEN_VIEW |
- ((table_list->required_type ==
- FRMTYPE_VIEW) ?
- OPEN_VIEW_ONLY : 0)),
- &error,
- hash_value)))
+ if (!share)
{
- mysql_mutex_unlock(&LOCK_open);
/*
- If thd->is_error() is not set, we either need discover
- (error == 7), or the error was silenced by the prelocking
- handler (error == 0), in which case we should skip this
- table.
+ Hide "Table doesn't exist" errors if the table belongs to a view.
+ The check for thd->is_error() is necessary to not push an
+ unwanted error in case the error was already silenced.
+ @todo Rework the alternative ways to deal with ER_NO_SUCH TABLE.
*/
- if (error == 7 && !thd->is_error())
+ if (thd->is_error())
{
- (void) ot_ctx->request_backoff_action(Open_table_context::OT_DISCOVER,
- table_list);
+ if (table_list->parent_l)
+ {
+ thd->clear_error();
+ my_error(ER_WRONG_MRG_TABLE, MYF(0));
+ }
+ else if (table_list->belong_to_view)
+ {
+ TABLE_LIST *view= table_list->belong_to_view;
+ thd->clear_error();
+ my_error(ER_VIEW_INVALID, MYF(0),
+ view->view_db.str, view->view_name.str);
+ }
}
DBUG_RETURN(TRUE);
}
+ /*
+ Check if this TABLE_SHARE-object corresponds to a view. Note, that there is
+ no need to check TABLE_SHARE::tdc.flushed as we do for regular tables,
+ because view shares are always up to date.
+ */
if (share->is_view)
{
/*
@@ -3076,7 +2427,7 @@ retry_share:
if (table_list->parent_l)
{
my_error(ER_WRONG_MRG_TABLE, MYF(0));
- goto err_unlock;
+ goto err_lock;
}
/*
@@ -3084,13 +2435,7 @@ retry_share:
that it was a view when the statement was prepared.
*/
if (check_and_update_table_version(thd, table_list, share))
- goto err_unlock;
- if (table_list->i_s_requested_object & OPEN_TABLE_ONLY)
- {
- my_error(ER_NO_SUCH_TABLE, MYF(0), table_list->db,
- table_list->table_name);
- goto err_unlock;
- }
+ goto err_lock;
/* Open view */
if (open_new_frm(thd, share, alias,
@@ -3099,37 +2444,22 @@ retry_share:
READ_KEYINFO | COMPUTE_TYPES | EXTRA_RECORD,
thd->open_options,
0, table_list, mem_root))
- goto err_unlock;
+ goto err_lock;
/* TODO: Don't free this */
- release_table_share(share);
+ tdc_release_share(share);
DBUG_ASSERT(table_list->view);
- mysql_mutex_unlock(&LOCK_open);
DBUG_RETURN(FALSE);
}
- /*
- Note that situation when we are trying to open a table for what
- was a view during previous execution of PS will be handled in by
- the caller. Here we should simply open our table even if
- TABLE_LIST::view is true.
- */
-
- if (table_list->i_s_requested_object & OPEN_VIEW_ONLY)
- {
- my_error(ER_NO_SUCH_TABLE, MYF(0), table_list->db,
- table_list->table_name);
- goto err_unlock;
- }
-
- if (!(flags & MYSQL_OPEN_IGNORE_FLUSH) ||
- (share->protected_against_usage() &&
- !(flags & MYSQL_OPEN_FOR_REPAIR)))
+ if (!(flags & MYSQL_OPEN_IGNORE_FLUSH))
{
- if (share->has_old_version())
+ if (share->tdc.flushed)
{
+ DBUG_PRINT("info", ("Found old share version: %lu current: %lu",
+ share->tdc.version, tdc_refresh_version()));
/*
We already have an MDL lock. But we have encountered an old
version of table in the table definition cache which is possible
@@ -3139,13 +2469,14 @@ retry_share:
Release our reference to share, wait until old version of
share goes away and then try to get new version of table share.
*/
+ if (table)
+ tc_release_table(table);
+ else
+ tdc_release_share(share);
+
MDL_deadlock_handler mdl_deadlock_handler(ot_ctx);
bool wait_result;
- DBUG_PRINT("info", ("old version of table share found"));
- release_table_share(share);
- mysql_mutex_unlock(&LOCK_open);
-
thd->push_internal_handler(&mdl_deadlock_handler);
wait_result= tdc_wait_for_old_version(thd, table_list->db,
table_list->table_name,
@@ -3159,7 +2490,7 @@ retry_share:
goto retry_share;
}
- if (thd->open_tables && thd->open_tables->s->version != share->version)
+ if (thd->open_tables && thd->open_tables->s->tdc.flushed)
{
/*
If the version changes while we're opening the tables,
@@ -3167,34 +2498,24 @@ retry_share:
and try to reopen them. Note: refresh_version is currently
changed only during FLUSH TABLES.
*/
- DBUG_PRINT("info", ("share version differs between tables"));
- release_table_share(share);
- mysql_mutex_unlock(&LOCK_open);
+ if (table)
+ tc_release_table(table);
+ else
+ tdc_release_share(share);
(void)ot_ctx->request_backoff_action(Open_table_context::OT_REOPEN_TABLES,
NULL);
DBUG_RETURN(TRUE);
}
}
- if (!share->free_tables.is_empty())
+ if (table)
{
- table= share->free_tables.front();
- table_def_use_table(thd, table);
- /*
- We need to release share as we have EXTRA reference to it in our hands.
- */
- DBUG_PRINT("info", ("release temporarily acquired table share"));
- release_table_share(share);
+ DBUG_ASSERT(table->file != NULL);
+ table->file->rebind_psi();
}
else
{
- /*
- We have too many TABLE instances around let us try to get rid of them.
- */
- while (table_cache_count > table_cache_size && unused_tables)
- free_cache_entry(unused_tables);
-
- mysql_mutex_unlock(&LOCK_open);
+ enum open_frm_error error;
/* make a new table */
if (!(table=(TABLE*) my_malloc(sizeof(*table),MYF(MY_WME))))
@@ -3213,16 +2534,14 @@ retry_share:
{
my_free(table);
- if (error == 7)
+ if (error == OPEN_FRM_DISCOVER)
(void) ot_ctx->request_backoff_action(Open_table_context::OT_DISCOVER,
table_list);
else if (share->crashed)
(void) ot_ctx->request_backoff_action(Open_table_context::OT_REPAIR,
table_list);
-
goto err_lock;
}
-
if (open_table_entry_fini(thd, share, table))
{
closefrm(table, 0);
@@ -3230,13 +2549,10 @@ retry_share:
goto err_lock;
}
- mysql_mutex_lock(&LOCK_open);
/* Add table to the share's used tables list. */
- table_def_add_used_table(thd, table);
+ tc_add_table(thd, table);
}
- mysql_mutex_unlock(&LOCK_open);
-
table->mdl_ticket= mdl_ticket;
table->next= thd->open_tables; /* Link into simple list */
@@ -3253,15 +2569,27 @@ retry_share:
table_list->updatable= 1; // It is not derived table nor non-updatable VIEW
table_list->table= table;
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+ if (table->part_info)
+ {
+ /* Set all [named] partitions as used. */
+ if (table->part_info->set_partition_bitmaps(table_list))
+ DBUG_RETURN(true);
+ }
+ else if (table_list->partition_names)
+ {
+ /* Don't allow PARTITION () clause on a nonpartitioned table */
+ my_error(ER_PARTITION_CLAUSE_ON_NONPARTITIONED, MYF(0));
+ DBUG_RETURN(true);
+ }
+#endif
+
table->init(thd, table_list);
DBUG_RETURN(FALSE);
err_lock:
- mysql_mutex_lock(&LOCK_open);
-err_unlock:
- release_table_share(share);
- mysql_mutex_unlock(&LOCK_open);
+ tdc_release_share(share);
DBUG_PRINT("exit", ("failed"));
DBUG_RETURN(TRUE);
@@ -3281,7 +2609,7 @@ err_unlock:
TABLE *find_locked_table(TABLE *list, const char *db, const char *table_name)
{
char key[MAX_DBKEY_LENGTH];
- uint key_length= create_table_def_key(key, db, table_name);
+ uint key_length= tdc_create_key(key, db, table_name);
for (TABLE *table= list; table ; table=table->next)
{
@@ -3309,9 +2637,9 @@ TABLE *find_locked_table(TABLE *list, const char *db, const char *table_name)
upgrade the lock and ER_TABLE_NOT_LOCKED_FOR_WRITE will be
reported.
- @return Pointer to TABLE instance with MDL_SHARED_NO_WRITE,
- MDL_SHARED_NO_READ_WRITE, or MDL_EXCLUSIVE metadata
- lock, NULL otherwise.
+ @return Pointer to TABLE instance with MDL_SHARED_UPGRADABLE
+ MDL_SHARED_NO_WRITE, MDL_SHARED_NO_READ_WRITE, or
+ MDL_EXCLUSIVE metadata lock, NULL otherwise.
*/
TABLE *find_table_for_mdl_upgrade(THD *thd, const char *db,
@@ -3381,9 +2709,9 @@ Locked_tables_list::init_locked_tables(THD *thd)
{
TABLE_LIST *src_table_list= table->pos_in_table_list;
char *db, *table_name, *alias;
- size_t db_len= src_table_list->db_length;
- size_t table_name_len= src_table_list->table_name_length;
- size_t alias_len= strlen(src_table_list->alias);
+ size_t db_len= table->s->db.length;
+ size_t table_name_len= table->s->table_name.length;
+ size_t alias_len= table->alias.length();
TABLE_LIST *dst_table_list;
if (! multi_alloc_root(&m_locked_tables_root,
@@ -3393,23 +2721,15 @@ Locked_tables_list::init_locked_tables(THD *thd)
&alias, alias_len + 1,
NullS))
{
- unlock_locked_tables(0);
+ reset();
return TRUE;
}
- memcpy(db, src_table_list->db, db_len + 1);
- memcpy(table_name, src_table_list->table_name, table_name_len + 1);
- memcpy(alias, src_table_list->alias, alias_len + 1);
- /**
- Sic: remember the *actual* table level lock type taken, to
- acquire the exact same type in reopen_tables().
- E.g. if the table was locked for write, src_table_list->lock_type is
- TL_WRITE_DEFAULT, whereas reginfo.lock_type has been updated from
- thd->update_lock_default.
- */
+ memcpy(db, table->s->db.str, db_len + 1);
+ memcpy(table_name, table->s->table_name.str, table_name_len + 1);
+ strmake(alias, table->alias.ptr(), alias_len);
dst_table_list->init_one_table(db, db_len, table_name, table_name_len,
- alias,
- src_table_list->table->reginfo.lock_type);
+ alias, table->reginfo.lock_type);
dst_table_list->table= table;
dst_table_list->mdl_request.ticket= src_table_list->mdl_request.ticket;
@@ -3430,7 +2750,7 @@ Locked_tables_list::init_locked_tables(THD *thd)
(m_locked_tables_count+1));
if (m_reopen_array == NULL)
{
- unlock_locked_tables(0);
+ reset();
return TRUE;
}
}
@@ -3451,42 +2771,82 @@ Locked_tables_list::init_locked_tables(THD *thd)
void
Locked_tables_list::unlock_locked_tables(THD *thd)
{
- if (thd)
+ DBUG_ASSERT(!thd->in_sub_stmt &&
+ !(thd->state_flags & Open_tables_state::BACKUPS_AVAIL));
+ /*
+ Sic: we must be careful to not close open tables if
+ we're not in LOCK TABLES mode: unlock_locked_tables() is
+ sometimes called implicitly, expecting no effect on
+ open tables, e.g. from begin_trans().
+ */
+ if (thd->locked_tables_mode != LTM_LOCK_TABLES)
+ return;
+
+ for (TABLE_LIST *table_list= m_locked_tables;
+ table_list; table_list= table_list->next_global)
{
- DBUG_ASSERT(!thd->in_sub_stmt &&
- !(thd->state_flags & Open_tables_state::BACKUPS_AVAIL));
/*
- Sic: we must be careful to not close open tables if
- we're not in LOCK TABLES mode: unlock_locked_tables() is
- sometimes called implicitly, expecting no effect on
- open tables, e.g. from begin_trans().
+ Clear the position in the list, the TABLE object will be
+ returned to the table cache.
*/
- if (thd->locked_tables_mode != LTM_LOCK_TABLES)
- return;
+ if (table_list->table) // If not closed
+ table_list->table->pos_in_locked_tables= NULL;
+ }
+ thd->leave_locked_tables_mode();
- for (TABLE_LIST *table_list= m_locked_tables;
- table_list; table_list= table_list->next_global)
- {
- /*
- Clear the position in the list, the TABLE object will be
- returned to the table cache.
- */
- if (table_list->table) // If not closed
- table_list->table->pos_in_locked_tables= NULL;
- }
- thd->leave_locked_tables_mode();
+ DBUG_ASSERT(thd->transaction.stmt.is_empty());
+ close_thread_tables(thd);
+
+ /*
+ We rely on the caller to implicitly commit the
+ transaction and release transactional locks.
+ */
- DBUG_ASSERT(thd->transaction.stmt.is_empty());
- close_thread_tables(thd);
- /*
- We rely on the caller to implicitly commit the
- transaction and release transactional locks.
- */
- }
/*
After closing tables we can free memory used for storing lock
request for metadata locks and TABLE_LIST elements.
*/
+ reset();
+}
+
+
+/**
+ Remove all meta data locks associated with table and release locked
+ table mode if there is no locked tables anymore
+*/
+
+void
+Locked_tables_list::unlock_locked_table(THD *thd, MDL_ticket *mdl_ticket)
+{
+ /*
+ Ensure we are in locked table mode.
+ As this function is only called on error condition it's better
+ to check this condition here than in the caller.
+ */
+ if (thd->locked_tables_mode != LTM_LOCK_TABLES)
+ return;
+
+ if (mdl_ticket)
+ {
+ /*
+ Under LOCK TABLES we may have several instances of table open
+ and locked and therefore have to remove several metadata lock
+ requests associated with them.
+ */
+ thd->mdl_context.release_all_locks_for_name(mdl_ticket);
+ }
+
+ if (thd->lock->table_count == 0)
+ unlock_locked_tables(thd);
+}
+
+
+/*
+ Free memory allocated for storing locks
+*/
+
+void Locked_tables_list::reset()
+{
free_root(&m_locked_tables_root, MYF(0));
m_locked_tables= NULL;
m_locked_tables_last= &m_locked_tables;
@@ -3551,6 +2911,7 @@ void Locked_tables_list::unlink_from_list(THD *thd,
m_locked_tables_last= table_list->prev_global;
else
table_list->next_global->prev_global= table_list->prev_global;
+ m_locked_tables_count--;
}
}
@@ -3604,8 +2965,13 @@ unlink_all_closed_tables(THD *thd, MYSQL_LOCK *lock, size_t reopen_count)
m_locked_tables_last= table_list->prev_global;
else
table_list->next_global->prev_global= table_list->prev_global;
+ m_locked_tables_count--;
}
}
+
+ /* If no tables left, do an automatic UNLOCK TABLES */
+ if (thd->lock && thd->lock->table_count == 0)
+ unlock_locked_tables(thd);
}
@@ -3628,6 +2994,7 @@ Locked_tables_list::reopen_tables(THD *thd)
size_t reopen_count= 0;
MYSQL_LOCK *lock;
MYSQL_LOCK *merged_lock;
+ DBUG_ENTER("Locked_tables_list::reopen_tables");
for (TABLE_LIST *table_list= m_locked_tables;
table_list; table_list= table_list->next_global)
@@ -3639,7 +3006,7 @@ Locked_tables_list::reopen_tables(THD *thd)
if (open_table(thd, table_list, thd->mem_root, &ot_ctx))
{
unlink_all_closed_tables(thd, 0, reopen_count);
- return TRUE;
+ DBUG_RETURN(TRUE);
}
table_list->table->pos_in_locked_tables= table_list;
/* See also the comment on lock type in init_locked_tables(). */
@@ -3671,70 +3038,70 @@ Locked_tables_list::reopen_tables(THD *thd)
unlink_all_closed_tables(thd, lock, reopen_count);
if (! thd->killed)
my_error(ER_LOCK_DEADLOCK, MYF(0));
- return TRUE;
+ DBUG_RETURN(TRUE);
}
thd->lock= merged_lock;
}
- return FALSE;
+ DBUG_RETURN(FALSE);
}
+/**
+ Add back a locked table to the locked list that we just removed from it.
+ This is needed in CREATE OR REPLACE TABLE where we are dropping, creating
+ and re-opening a locked table.
-/*
- Function to assign a new table map id to a table share.
-
- PARAMETERS
-
- share - Pointer to table share structure
+ @return 0 0k
+ @return 1 error
+*/
- DESCRIPTION
+bool Locked_tables_list::restore_lock(THD *thd, TABLE_LIST *dst_table_list,
+ TABLE *table, MYSQL_LOCK *lock)
+{
+ MYSQL_LOCK *merged_lock;
+ DBUG_ENTER("restore_lock");
+ DBUG_ASSERT(!strcmp(dst_table_list->table_name, table->s->table_name.str));
- We are intentionally not checking that share->mutex is locked
- since this function should only be called when opening a table
- share and before it is entered into the table_def_cache (meaning
- that it cannot be fetched by another thread, even accidentally).
+ /* Ensure we have the memory to add the table back */
+ if (!(merged_lock= mysql_lock_merge(thd->lock, lock)))
+ DBUG_RETURN(1);
+ thd->lock= merged_lock;
- PRE-CONDITION(S)
+ /* Link to the new table */
+ dst_table_list->table= table;
+ /*
+ The lock type may have changed (normally it should not as create
+ table will lock the table in write mode
+ */
+ dst_table_list->lock_type= table->reginfo.lock_type;
+ table->pos_in_locked_tables= dst_table_list;
- share is non-NULL
- The LOCK_open mutex is locked.
+ add_back_last_deleted_lock(dst_table_list);
- POST-CONDITION(S)
+ table->mdl_ticket->downgrade_lock(table->reginfo.lock_type >=
+ TL_WRITE_ALLOW_WRITE ?
+ MDL_SHARED_NO_READ_WRITE :
+ MDL_SHARED_READ);
- share->table_map_id is given a value that with a high certainty is
- not used by any other table (the only case where a table id can be
- reused is on wrap-around, which means more than 4 billion table
- share opens have been executed while one table was open all the
- time).
+ DBUG_RETURN(0);
+}
- share->table_map_id is not ~0UL.
- */
-static ulong last_table_id= ~0UL;
+/*
+ Add back the last deleted lock structure.
+ This should be followed by a call to reopen_tables() to
+ open the table.
+*/
-void assign_new_table_id(TABLE_SHARE *share)
+void Locked_tables_list::add_back_last_deleted_lock(TABLE_LIST *dst_table_list)
{
-
- DBUG_ENTER("assign_new_table_id");
-
- /* Preconditions */
- DBUG_ASSERT(share != NULL);
- mysql_mutex_assert_owner(&LOCK_open);
-
- ulong tid= ++last_table_id; /* get next id */
- /*
- There is one reserved number that cannot be used. Remember to
- change this when 6-byte global table id's are introduced.
- */
- if (unlikely(tid == ~0UL))
- tid= ++last_table_id;
- share->table_map_id= tid;
- DBUG_PRINT("info", ("table_id=%lu", tid));
-
- /* Post conditions */
- DBUG_ASSERT(share->table_map_id != ~0UL);
-
- DBUG_VOID_RETURN;
+ /* Link the lock back in the locked tables list */
+ dst_table_list->prev_global= m_locked_tables_last;
+ *m_locked_tables_last= dst_table_list;
+ m_locked_tables_last= &dst_table_list->next_global;
+ dst_table_list->next_global= 0;
+ m_locked_tables_count++;
}
+
#ifndef DBUG_OFF
/* Cause a spurious statement reprepare for debug purposes. */
static bool inject_reprepare(THD *thd)
@@ -3885,42 +3252,44 @@ check_and_update_routine_version(THD *thd, Sroutine_hash_entry *rt,
*/
bool tdc_open_view(THD *thd, TABLE_LIST *table_list, const char *alias,
- char *cache_key, uint cache_key_length,
+ const char *cache_key, uint cache_key_length,
MEM_ROOT *mem_root, uint flags)
{
TABLE not_used;
- int error;
- my_hash_value_type hash_value;
TABLE_SHARE *share;
+ bool err= TRUE;
- hash_value= my_calc_hash(&table_def_cache, (uchar*) cache_key,
- cache_key_length);
- mysql_mutex_lock(&LOCK_open);
+ if (!(share= tdc_acquire_share(thd, table_list->db, table_list->table_name,
+ cache_key, cache_key_length, GTS_VIEW)))
+ return TRUE;
- if (!(share= get_table_share(thd, table_list, cache_key,
- cache_key_length,
- OPEN_VIEW, &error,
- hash_value)))
- goto err;
+ DBUG_ASSERT(share->is_view);
- if (share->is_view &&
- !open_new_frm(thd, share, alias,
- (uint) (HA_OPEN_KEYFILE | HA_OPEN_RNDFILE |
- HA_GET_INDEX | HA_TRY_READ_ONLY),
- READ_KEYINFO | COMPUTE_TYPES | EXTRA_RECORD |
- flags, thd->open_options, &not_used, table_list,
- mem_root))
+ if (flags & CHECK_METADATA_VERSION)
{
- release_table_share(share);
- mysql_mutex_unlock(&LOCK_open);
- return FALSE;
+ /*
+ Check TABLE_SHARE-version of view only if we have been instructed to do
+ so. We do not need to check the version if we're executing CREATE VIEW or
+ ALTER VIEW statements.
+
+ In the future, this functionality should be moved out from
+ tdc_open_view(), and tdc_open_view() should became a part of a clean
+ table-definition-cache interface.
+ */
+ if (check_and_update_table_version(thd, table_list, share))
+ goto ret;
}
- my_error(ER_WRONG_OBJECT, MYF(0), share->db.str, share->table_name.str, "VIEW");
- release_table_share(share);
-err:
- mysql_mutex_unlock(&LOCK_open);
- return TRUE;
+ err= open_new_frm(thd, share, alias,
+ (HA_OPEN_KEYFILE | HA_OPEN_RNDFILE |
+ HA_GET_INDEX | HA_TRY_READ_ONLY),
+ READ_KEYINFO | COMPUTE_TYPES | EXTRA_RECORD | flags,
+ thd->open_options, &not_used, table_list, mem_root);
+
+ret:
+ tdc_release_share(share);
+
+ return err;
}
@@ -3974,40 +3343,19 @@ static bool open_table_entry_fini(THD *thd, TABLE_SHARE *share, TABLE *entry)
static bool auto_repair_table(THD *thd, TABLE_LIST *table_list)
{
- char cache_key[MAX_DBKEY_LENGTH];
- uint cache_key_length;
TABLE_SHARE *share;
TABLE *entry;
- int not_used;
bool result= TRUE;
- my_hash_value_type hash_value;
-
- cache_key_length= create_table_def_key(thd, cache_key, table_list, 0);
thd->clear_error();
- hash_value= my_calc_hash(&table_def_cache, (uchar*) cache_key,
- cache_key_length);
- mysql_mutex_lock(&LOCK_open);
-
- if (!(share= get_table_share(thd, table_list, cache_key,
- cache_key_length,
- OPEN_VIEW, &not_used,
- hash_value)))
- goto end_unlock;
+ if (!(entry= (TABLE*)my_malloc(sizeof(TABLE), MYF(MY_WME))))
+ return result;
- if (share->is_view)
- {
- release_table_share(share);
- goto end_unlock;
- }
+ if (!(share= tdc_acquire_share_shortlived(thd, table_list, GTS_TABLE)))
+ goto end_free;
- if (!(entry= (TABLE*)my_malloc(sizeof(TABLE), MYF(MY_WME))))
- {
- release_table_share(share);
- goto end_unlock;
- }
- mysql_mutex_unlock(&LOCK_open);
+ DBUG_ASSERT(! share->is_view);
if (open_table_from_share(thd, share, table_list->alias,
(uint) (HA_OPEN_KEYFILE | HA_OPEN_RNDFILE |
@@ -4032,16 +3380,14 @@ static bool auto_repair_table(THD *thd, TABLE_LIST *table_list)
closefrm(entry, 0);
result= FALSE;
}
- my_free(entry);
- mysql_mutex_lock(&LOCK_open);
- release_table_share(share);
+ tdc_release_share(share);
/* Remove the repaired share from the table cache. */
tdc_remove_table(thd, TDC_RT_REMOVE_ALL,
table_list->db, table_list->table_name,
- TRUE);
-end_unlock:
- mysql_mutex_unlock(&LOCK_open);
+ FALSE);
+end_free:
+ my_free(entry);
return result;
}
@@ -4145,6 +3491,7 @@ request_backoff_action(enum_open_table_action action_arg,
table->table_name,
table->table_name_length,
table->alias, TL_WRITE);
+ m_failed_table->open_strategy= table->open_strategy;
m_failed_table->mdl_request.set_type(MDL_EXCLUSIVE);
}
m_action= action_arg;
@@ -4162,9 +3509,9 @@ public:
virtual bool handle_condition(THD *thd,
uint sql_errno,
const char* sqlstate,
- MYSQL_ERROR::enum_warning_level level,
+ Sql_condition::enum_warning_level level,
const char* msg,
- MYSQL_ERROR ** cond_hdl)
+ Sql_condition ** cond_hdl)
{
if (sql_errno == ER_LOCK_DEADLOCK)
{
@@ -4189,8 +3536,7 @@ public:
*/
bool
-Open_table_context::
-recover_from_failed_open()
+Open_table_context::recover_from_failed_open()
{
bool result= FALSE;
MDL_deadlock_discovery_repair_handler handler;
@@ -4209,17 +3555,31 @@ recover_from_failed_open()
case OT_DISCOVER:
{
if ((result= lock_table_names(m_thd, m_failed_table, NULL,
- get_timeout(),
- MYSQL_OPEN_SKIP_TEMPORARY)))
+ get_timeout(), 0)))
break;
tdc_remove_table(m_thd, TDC_RT_REMOVE_ALL, m_failed_table->db,
m_failed_table->table_name, FALSE);
- ha_create_table_from_engine(m_thd, m_failed_table->db,
- m_failed_table->table_name);
- m_thd->warning_info->clear_warning_info(m_thd->query_id);
+ m_thd->get_stmt_da()->clear_warning_info(m_thd->query_id);
m_thd->clear_error(); // Clear error message
+
+ No_such_table_error_handler no_such_table_handler;
+ bool open_if_exists= m_failed_table->open_strategy == TABLE_LIST::OPEN_IF_EXISTS;
+
+ if (open_if_exists)
+ m_thd->push_internal_handler(&no_such_table_handler);
+
+ result= !tdc_acquire_share(m_thd, m_failed_table->db,
+ m_failed_table->table_name,
+ GTS_TABLE | GTS_FORCE_DISCOVERY | GTS_NOLOCK);
+ if (open_if_exists)
+ {
+ m_thd->pop_internal_handler();
+ if (result && no_such_table_handler.safely_trapped_errors())
+ result= FALSE;
+ }
+
/*
Rollback to start of the current statement to release exclusive lock
on table which was discovered but preserve locks from previous statements
@@ -4231,8 +3591,7 @@ recover_from_failed_open()
case OT_REPAIR:
{
if ((result= lock_table_names(m_thd, m_failed_table, NULL,
- get_timeout(),
- MYSQL_OPEN_SKIP_TEMPORARY)))
+ get_timeout(), 0)))
break;
tdc_remove_table(m_thd, TDC_RT_REMOVE_ALL, m_failed_table->db,
@@ -4272,9 +3631,12 @@ recover_from_failed_open()
/*
Return a appropriate read lock type given a table object.
- @param thd Thread context
- @param prelocking_ctx Prelocking context.
- @param table_list Table list element for table to be locked.
+ @param thd Thread context
+ @param prelocking_ctx Prelocking context.
+ @param table_list Table list element for table to be locked.
+ @param routine_modifies_data
+ Some routine that is invoked by statement
+ modifies data.
@remark Due to a statement-based replication limitation, statements such as
INSERT INTO .. SELECT FROM .. and CREATE TABLE .. SELECT FROM need
@@ -4287,9 +3649,13 @@ recover_from_failed_open()
This also applies to SELECT/SET/DO statements which use stored
functions. Calls to such functions are going to be logged as a
whole and thus should be serialized against concurrent changes
- to tables used by those functions. This can be avoided if functions
- only read data but doing so requires more complex analysis than it
- is done now.
+ to tables used by those functions. This is avoided when functions
+ do not modify data but only read it, since in this case nothing is
+ written to the binary log. Argument routine_modifies_data
+ denotes the same. So effectively, if the statement is not a
+ update query and routine_modifies_data is false, then
+ prelocking_placeholder does not take importance.
+
Furthermore, this does not apply to I_S and log tables as it's
always unsafe to replicate such tables under statement-based
replication as the table on the slave might contain other data
@@ -4304,7 +3670,8 @@ recover_from_failed_open()
thr_lock_type read_lock_type_for_table(THD *thd,
Query_tables_list *prelocking_ctx,
- TABLE_LIST *table_list)
+ TABLE_LIST *table_list,
+ bool routine_modifies_data)
{
/*
In cases when this function is called for a sub-statement executed in
@@ -4315,11 +3682,11 @@ thr_lock_type read_lock_type_for_table(THD *thd,
*/
bool log_on= mysql_bin_log.is_open() && thd->variables.sql_log_bin;
ulong binlog_format= thd->variables.binlog_format;
- if ((log_on == FALSE) || (WSREP_BINLOG_FORMAT(binlog_format) == BINLOG_FORMAT_ROW) ||
+ if ((log_on == FALSE) || (WSREP_FORMAT(binlog_format) == BINLOG_FORMAT_ROW) ||
(table_list->table->s->table_category == TABLE_CATEGORY_LOG) ||
(table_list->table->s->table_category == TABLE_CATEGORY_PERFORMANCE) ||
!(is_update_query(prelocking_ctx->sql_command) ||
- table_list->prelocking_placeholder ||
+ (routine_modifies_data && table_list->prelocking_placeholder) ||
(thd->locked_tables_mode > LTM_LOCK_TABLES)))
return TL_READ;
else
@@ -4332,19 +3699,21 @@ thr_lock_type read_lock_type_for_table(THD *thd,
and, if prelocking strategy prescribes so, extend the prelocking set
with tables and routines used by it.
- @param[in] thd Thread context.
- @param[in] prelocking_ctx Prelocking context.
- @param[in] rt Element of prelocking set to be processed.
- @param[in] prelocking_strategy Strategy which specifies how the
- prelocking set should be extended when
- one of its elements is processed.
- @param[in] has_prelocking_list Indicates that prelocking set/list for
- this statement has already been built.
- @param[in] ot_ctx Context of open_table used to recover from
- locking failures.
- @param[out] need_prelocking Set to TRUE if it was detected that this
- statement will require prelocked mode for
- its execution, not touched otherwise.
+ @param[in] thd Thread context.
+ @param[in] prelocking_ctx Prelocking context.
+ @param[in] rt Element of prelocking set to be processed.
+ @param[in] prelocking_strategy Strategy which specifies how the
+ prelocking set should be extended when
+ one of its elements is processed.
+ @param[in] has_prelocking_list Indicates that prelocking set/list for
+ this statement has already been built.
+ @param[in] ot_ctx Context of open_table used to recover from
+ locking failures.
+ @param[out] need_prelocking Set to TRUE if it was detected that this
+ statement will require prelocked mode for
+ its execution, not touched otherwise.
+ @param[out] routine_modifies_data Set to TRUE if it was detected that this
+ routine does modify table data.
@retval FALSE Success.
@retval TRUE Failure (Conflicting metadata lock, OOM, other errors).
@@ -4356,11 +3725,13 @@ open_and_process_routine(THD *thd, Query_tables_list *prelocking_ctx,
Prelocking_strategy *prelocking_strategy,
bool has_prelocking_list,
Open_table_context *ot_ctx,
- bool *need_prelocking)
+ bool *need_prelocking, bool *routine_modifies_data)
{
MDL_key::enum_mdl_namespace mdl_type= rt->mdl_request.key.mdl_namespace();
DBUG_ENTER("open_and_process_routine");
+ *routine_modifies_data= false;
+
switch (mdl_type)
{
case MDL_key::FUNCTION:
@@ -4413,10 +3784,13 @@ open_and_process_routine(THD *thd, Query_tables_list *prelocking_ctx,
DBUG_RETURN(TRUE);
/* 'sp' is NULL when there is no such routine. */
- if (sp && !has_prelocking_list)
+ if (sp)
{
- prelocking_strategy->handle_routine(thd, prelocking_ctx, rt, sp,
- need_prelocking);
+ *routine_modifies_data= sp->modifies_data();
+
+ if (!has_prelocking_list)
+ prelocking_strategy->handle_routine(thd, prelocking_ctx, rt, sp,
+ need_prelocking);
}
}
else
@@ -4570,9 +3944,35 @@ open_and_process_table(THD *thd, LEX *lex, TABLE_LIST *tables,
tables->db, tables->table_name, tables)); //psergey: invalid read of size 1 here
(*counter)++;
- /* Not a placeholder: must be a base table or a view. Let us open it. */
- DBUG_ASSERT(!tables->table);
+ /*
+ Not a placeholder: must be a base/temporary table or a view. Let us open it.
+ */
+ if (tables->table)
+ {
+ /*
+ If this TABLE_LIST object has an associated open TABLE object
+ (TABLE_LIST::table is not NULL), that TABLE object must be a pre-opened
+ temporary table.
+ */
+ DBUG_ASSERT(is_temporary_table(tables));
+ }
+ else if (tables->open_type == OT_TEMPORARY_ONLY)
+ {
+ /*
+ OT_TEMPORARY_ONLY means that we are in CREATE TEMPORARY TABLE statement.
+ Also such table list element can't correspond to prelocking placeholder
+ or to underlying table of merge table.
+ So existing temporary table should have been preopened by this moment
+ and we can simply continue without trying to open temporary or base
+ table.
+ */
+ DBUG_ASSERT(tables->open_strategy);
+ DBUG_ASSERT(!tables->prelocking_placeholder);
+ DBUG_ASSERT(!tables->parent_l);
+ DBUG_RETURN(0);
+ }
+ /* Not a placeholder: must be a base table or a view. Let us open it. */
if (tables->prelocking_placeholder)
{
/*
@@ -4583,7 +3983,35 @@ open_and_process_table(THD *thd, LEX *lex, TABLE_LIST *tables,
*/
No_such_table_error_handler no_such_table_handler;
thd->push_internal_handler(&no_such_table_handler);
- error= open_table(thd, tables, new_frm_mem, ot_ctx);
+
+ /*
+ We're opening a table from the prelocking list.
+
+ Since this table list element might have been added after pre-opening
+ of temporary tables we have to try to open temporary table for it.
+
+ We can't simply skip this table list element and postpone opening of
+ temporary tabletill the execution of substatement for several reasons:
+ - Temporary table can be a MERGE table with base underlying tables,
+ so its underlying tables has to be properly open and locked at
+ prelocking stage.
+ - Temporary table can be a MERGE table and we might be in PREPARE
+ phase for a prepared statement. In this case it is important to call
+ HA_ATTACH_CHILDREN for all merge children.
+ This is necessary because merge children remember "TABLE_SHARE ref type"
+ and "TABLE_SHARE def version" in the HA_ATTACH_CHILDREN operation.
+ If HA_ATTACH_CHILDREN is not called, these attributes are not set.
+ Then, during the first EXECUTE, those attributes need to be updated.
+ That would cause statement re-preparing (because changing those
+ attributes during EXECUTE is caught by THD::m_reprepare_observers).
+ The problem is that since those attributes are not set in merge
+ children, another round of PREPARE will not help.
+ */
+ error= open_temporary_table(thd, tables);
+
+ if (!error && !tables->table)
+ error= open_table(thd, tables, new_frm_mem, ot_ctx);
+
thd->pop_internal_handler();
safe_to_ignore_table= no_such_table_handler.safely_trapped_errors();
}
@@ -4597,12 +4025,29 @@ open_and_process_table(THD *thd, LEX *lex, TABLE_LIST *tables,
*/
Repair_mrg_table_error_handler repair_mrg_table_handler;
thd->push_internal_handler(&repair_mrg_table_handler);
- error= open_table(thd, tables, new_frm_mem, ot_ctx);
+
+ error= open_temporary_table(thd, tables);
+ if (!error && !tables->table)
+ error= open_table(thd, tables, new_frm_mem, ot_ctx);
+
thd->pop_internal_handler();
safe_to_ignore_table= repair_mrg_table_handler.safely_trapped_errors();
}
else
- error= open_table(thd, tables, new_frm_mem, ot_ctx);
+ {
+ if (tables->parent_l)
+ {
+ /*
+ Even if we are opening table not from the prelocking list we
+ still might need to look for a temporary table if this table
+ list element corresponds to underlying table of a merge table.
+ */
+ error= open_temporary_table(thd, tables);
+ }
+
+ if (!error && !tables->table)
+ error= open_table(thd, tables, new_frm_mem, ot_ctx);
+ }
free_root(new_frm_mem, MYF(MY_KEEP_PREALLOC));
@@ -4690,16 +4135,7 @@ open_and_process_table(THD *thd, LEX *lex, TABLE_LIST *tables,
goto end;
}
- if (tables->lock_type != TL_UNLOCK && ! thd->locked_tables_mode)
- {
- if (tables->lock_type == TL_WRITE_DEFAULT)
- tables->table->reginfo.lock_type= thd->update_lock_default;
- else if (tables->lock_type == TL_READ_DEFAULT)
- tables->table->reginfo.lock_type=
- read_lock_type_for_table(thd, lex, tables);
- else
- tables->table->reginfo.lock_type= tables->lock_type;
- }
+ /* Copy grant information from TABLE_LIST instance to TABLE one. */
tables->table->grant= tables->grant;
/* Check and update metadata version of a base table. */
@@ -4721,6 +4157,32 @@ open_and_process_table(THD *thd, LEX *lex, TABLE_LIST *tables,
goto end;
}
+ if (get_use_stat_tables_mode(thd) > NEVER && tables->table)
+ {
+ TABLE_SHARE *table_share= tables->table->s;
+ if (table_share && table_share->table_category == TABLE_CATEGORY_USER &&
+ table_share->tmp_table == NO_TMP_TABLE)
+ {
+ if (table_share->stats_cb.stats_can_be_read ||
+ !alloc_statistics_for_table_share(thd, table_share, FALSE))
+ {
+ if (table_share->stats_cb.stats_can_be_read)
+ {
+ KEY *key_info= table_share->key_info;
+ KEY *key_info_end= key_info + table_share->keys;
+ KEY *table_key_info= tables->table->key_info;
+ for ( ; key_info < key_info_end; key_info++, table_key_info++)
+ table_key_info->read_stats= key_info->read_stats;
+ Field **field_ptr= table_share->field;
+ Field **table_field_ptr= tables->table->field;
+ for ( ; *field_ptr; field_ptr++, table_field_ptr++)
+ (*table_field_ptr)->read_stats= (*field_ptr)->read_stats;
+ tables->table->stats_is_read= table_share->stats_cb.stats_is_read;
+ }
+ }
+ }
+ }
+
process_view_routines:
/*
Again we may need cache all routines used by this view and add
@@ -4747,10 +4209,9 @@ end:
DBUG_RETURN(error);
}
-extern "C" uchar *schema_set_get_key(const uchar *record, size_t *length,
+extern "C" uchar *schema_set_get_key(const TABLE_LIST *table, size_t *length,
my_bool not_used __attribute__((unused)))
{
- TABLE_LIST *table=(TABLE_LIST*) record;
*length= table->db_length;
return (uchar*) table->db;
}
@@ -4791,7 +4252,7 @@ lock_table_names(THD *thd,
MDL_request_list mdl_requests;
TABLE_LIST *table;
MDL_request global_request;
- Hash_set<TABLE_LIST, schema_set_get_key> schema_set;
+ Hash_set<TABLE_LIST> schema_set(schema_set_get_key);
ulong org_lock_wait_timeout= lock_wait_timeout;
/* Check if we are using CREATE TABLE ... IF NOT EXISTS */
bool create_table;
@@ -4803,26 +4264,33 @@ lock_table_names(THD *thd,
for (table= tables_start; table && table != tables_end;
table= table->next_global)
{
- if (table->mdl_request.type >= MDL_SHARED_NO_WRITE &&
- !(table->open_type == OT_TEMPORARY_ONLY ||
- (flags & MYSQL_OPEN_TEMPORARY_ONLY) ||
- (table->open_type != OT_BASE_ONLY &&
- ! (flags & MYSQL_OPEN_SKIP_TEMPORARY) &&
- find_temporary_table(thd, table))))
+ if (table->mdl_request.type < MDL_SHARED_UPGRADABLE ||
+ table->open_type == OT_TEMPORARY_ONLY ||
+ (table->open_type == OT_TEMPORARY_OR_BASE && is_temporary_table(table)))
{
- if (! (flags & MYSQL_OPEN_SKIP_SCOPED_MDL_LOCK) &&
- schema_set.insert(table))
- DBUG_RETURN(TRUE);
- mdl_requests.push_front(&table->mdl_request);
+ continue;
}
+
+ /* Write lock on normal tables is not allowed in a read only transaction. */
+ if (thd->tx_read_only)
+ {
+ my_error(ER_CANT_EXECUTE_IN_READ_ONLY_TRANSACTION, MYF(0));
+ DBUG_RETURN(true);
+ }
+
+ if (! (flags & MYSQL_OPEN_SKIP_SCOPED_MDL_LOCK) &&
+ schema_set.insert(table))
+ DBUG_RETURN(TRUE);
+
+ mdl_requests.push_front(&table->mdl_request);
}
if (mdl_requests.is_empty())
DBUG_RETURN(FALSE);
- /* Check if CREATE TABLE IF NOT EXISTS was used */
- create_table= (tables_start && tables_start->open_strategy ==
- TABLE_LIST::OPEN_IF_EXISTS);
+ /* Check if CREATE TABLE without REPLACE was used */
+ create_table= (thd->lex->sql_command == SQLCOM_CREATE_TABLE &&
+ !(thd->lex->create_info.options & HA_LEX_CREATE_REPLACE));
if (!(flags & MYSQL_OPEN_SKIP_SCOPED_MDL_LOCK))
{
@@ -4830,7 +4298,7 @@ lock_table_names(THD *thd,
Scoped locks: Take intention exclusive locks on all involved
schemas.
*/
- Hash_set<TABLE_LIST, schema_set_get_key>::Iterator it(schema_set);
+ Hash_set<TABLE_LIST>::Iterator it(schema_set);
while ((table= it++))
{
MDL_request *schema_request= new (thd->mem_root) MDL_request;
@@ -4859,12 +4327,9 @@ lock_table_names(THD *thd,
for (;;)
{
- bool exists= TRUE;
- bool res;
-
if (create_table)
thd->push_internal_handler(&error_handler); // Avoid warnings & errors
- res= thd->mdl_context.acquire_locks(&mdl_requests, lock_wait_timeout);
+ bool res= thd->mdl_context.acquire_locks(&mdl_requests, lock_wait_timeout);
if (create_table)
thd->pop_internal_handler();
if (!res)
@@ -4874,17 +4339,14 @@ lock_table_names(THD *thd,
DBUG_RETURN(TRUE); // Return original error
/*
- We come here in the case of lock timeout when executing
- CREATE TABLE IF NOT EXISTS.
- Verify that table really exists (it should as we got a lock conflict)
+ We come here in the case of lock timeout when executing CREATE TABLE.
+ Verify that table does exist (it usually does, as we got a lock conflict)
*/
- if (check_if_table_exists(thd, tables_start, 1, &exists))
- DBUG_RETURN(TRUE); // Should never happen
- if (exists)
+ if (ha_table_exists(thd, tables_start->db, tables_start->table_name))
{
if (thd->lex->create_info.options & HA_LEX_CREATE_IF_NOT_EXISTS)
{
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
ER_TABLE_EXISTS_ERROR, ER(ER_TABLE_EXISTS_ERROR),
tables_start->table_name);
}
@@ -4892,17 +4354,16 @@ lock_table_names(THD *thd,
my_error(ER_TABLE_EXISTS_ERROR, MYF(0), tables_start->table_name);
DBUG_RETURN(TRUE);
}
- /* purecov: begin inspected */
/*
- We got error from acquire_locks but table didn't exists.
- In theory this should never happen, except maybe in
- CREATE or DROP DATABASE scenario.
+ We got error from acquire_locks, but the table didn't exists.
+ This could happen if another connection runs a statement
+ involving this non-existent table, and this statement took the mdl,
+ but didn't error out with ER_NO_SUCH_TABLE yet (yes, a race condition).
We play safe and restart the original acquire_locks with the
- original timeout
+ original timeout.
*/
create_table= 0;
lock_wait_timeout= org_lock_wait_timeout;
- /* purecov: end */
}
}
@@ -4934,34 +4395,33 @@ open_tables_check_upgradable_mdl(THD *thd, TABLE_LIST *tables_start,
for (table= tables_start; table && table != tables_end;
table= table->next_global)
{
- if (table->mdl_request.type >= MDL_SHARED_NO_WRITE &&
- !(table->open_type == OT_TEMPORARY_ONLY ||
- (flags & MYSQL_OPEN_TEMPORARY_ONLY) ||
- (table->open_type != OT_BASE_ONLY &&
- ! (flags & MYSQL_OPEN_SKIP_TEMPORARY) &&
- find_temporary_table(thd, table))))
+ if (table->mdl_request.type < MDL_SHARED_UPGRADABLE ||
+ table->open_type == OT_TEMPORARY_ONLY ||
+ (table->open_type == OT_TEMPORARY_OR_BASE && is_temporary_table(table)))
{
- /*
- We don't need to do anything about the found TABLE instance as it
- will be handled later in open_tables(), we only need to check that
- an upgradable lock is already acquired. When we enter LOCK TABLES
- mode, SNRW locks are acquired before all other locks. So if under
- LOCK TABLES we find that there is TABLE instance with upgradeable
- lock, all other instances of TABLE for the same table will have the
- same ticket.
-
- Note that this works OK even for CREATE TABLE statements which
- request X type of metadata lock. This is because under LOCK TABLES
- such statements don't create the table but only check if it exists
- or, in most complex case, only insert into it.
- Thus SNRW lock should be enough.
-
- Note that find_table_for_mdl_upgrade() will report an error if
- no suitable ticket is found.
- */
- if (!find_table_for_mdl_upgrade(thd, table->db, table->table_name, false))
- return TRUE;
+ continue;
}
+
+ /*
+ We don't need to do anything about the found TABLE instance as it
+ will be handled later in open_tables(), we only need to check that
+ an upgradable lock is already acquired. When we enter LOCK TABLES
+ mode, SNRW locks are acquired before all other locks. So if under
+ LOCK TABLES we find that there is TABLE instance with upgradeable
+ lock, all other instances of TABLE for the same table will have the
+ same ticket.
+
+ Note that this works OK even for CREATE TABLE statements which
+ request X type of metadata lock. This is because under LOCK TABLES
+ such statements don't create the table but only check if it exists
+ or, in most complex case, only insert into it.
+ Thus SNRW lock should be enough.
+
+ Note that find_table_for_mdl_upgrade() will report an error if
+ no suitable ticket is found.
+ */
+ if (!find_table_for_mdl_upgrade(thd, table->db, table->table_name, false))
+ return TRUE;
}
return FALSE;
@@ -5001,11 +4461,12 @@ bool open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags,
Prelocking_strategy *prelocking_strategy)
{
/*
- We use pointers to "next_global" member in the last processed TABLE_LIST
- element and to the "next" member in the last processed Sroutine_hash_entry
- element as iterators over, correspondingly, the table list and stored routines
- list which stay valid and allow to continue iteration when new elements are
- added to the tail of the lists.
+ We use pointers to "next_global" member in the last processed
+ TABLE_LIST element and to the "next" member in the last processed
+ Sroutine_hash_entry element as iterators over, correspondingly,
+ the table list and stored routines list which stay valid and allow
+ to continue iteration when new elements are added to the tail of
+ the lists.
*/
TABLE_LIST **table_to_open;
Sroutine_hash_entry **sroutine_to_open;
@@ -5013,6 +4474,7 @@ bool open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags,
Open_table_context ot_ctx(thd, flags);
bool error= FALSE;
MEM_ROOT new_frm_mem;
+ bool some_routine_modifies_data= FALSE;
bool has_prelocking_list;
DBUG_ENTER("open_tables");
@@ -5025,11 +4487,11 @@ bool open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags,
}
/*
- Initialize temporary MEM_ROOT for new .FRM parsing. Do not allocate
+ Initialize temporary MEM_ROOT for new .FRM parsing. Do not alloctaate
anything yet, to avoid penalty for statements which don't use views
and thus new .FRM format.
*/
- init_sql_alloc(&new_frm_mem, 8024, 0);
+ init_sql_alloc(&new_frm_mem, 8024, 0, MYF(0));
thd->current_tablenr= 0;
restart:
@@ -5048,7 +4510,7 @@ restart:
table_to_open= start;
sroutine_to_open= (Sroutine_hash_entry**) &thd->lex->sroutines_list.first;
*counter= 0;
- thd_proc_info(thd, "Opening tables");
+ THD_STAGE_INFO(thd, stage_opening_tables);
/*
If we are executing LOCK TABLES statement or a DDL statement
@@ -5094,7 +4556,7 @@ restart:
for (table= *start; table && table != thd->lex->first_not_own_table();
table= table->next_global)
{
- if (table->mdl_request.type >= MDL_SHARED_NO_WRITE)
+ if (table->mdl_request.type >= MDL_SHARED_UPGRADABLE)
table->mdl_request.ticket= NULL;
}
}
@@ -5149,6 +4611,10 @@ restart:
if (ot_ctx.recover_from_failed_open())
goto err;
+ /* Re-open temporary tables after close_tables_for_reopen(). */
+ if (open_temporary_tables(thd, *start))
+ goto err;
+
error= FALSE;
goto restart;
}
@@ -5181,11 +4647,16 @@ restart:
sroutine_to_open= &rt->next, rt= rt->next)
{
bool need_prelocking= false;
+ bool routine_modifies_data;
TABLE_LIST **save_query_tables_last= thd->lex->query_tables_last;
error= open_and_process_routine(thd, thd->lex, rt, prelocking_strategy,
has_prelocking_list, &ot_ctx,
- &need_prelocking);
+ &need_prelocking,
+ &routine_modifies_data);
+
+ // Remember if any of SF modifies data.
+ some_routine_modifies_data|= routine_modifies_data;
if (need_prelocking && ! thd->lex->requires_prelocking())
thd->lex->mark_as_requiring_prelocking(save_query_tables_last);
@@ -5202,6 +4673,10 @@ restart:
if (ot_ctx.recover_from_failed_open())
goto err;
+ /* Re-open temporary tables after close_tables_for_reopen(). */
+ if (open_temporary_tables(thd, *start))
+ goto err;
+
error= FALSE;
goto restart;
}
@@ -5221,6 +4696,10 @@ restart:
children, attach the children to their parents. At end of statement,
the children are detached. Attaching and detaching are always done,
even under LOCK TABLES.
+
+ We also convert all TL_WRITE_DEFAULT and TL_READ_DEFAULT locks to
+ appropriate "real" lock types to be used for locking and to be passed
+ to storage engine.
*/
for (tables= *start; tables; tables= tables->next_global)
{
@@ -5237,6 +4716,19 @@ restart:
goto err;
}
}
+
+ /* Set appropriate TABLE::lock_type. */
+ if (tbl && tables->lock_type != TL_UNLOCK && !thd->locked_tables_mode)
+ {
+ if (tables->lock_type == TL_WRITE_DEFAULT)
+ tbl->reginfo.lock_type= thd->update_lock_default;
+ else if (tables->lock_type == TL_READ_DEFAULT)
+ tbl->reginfo.lock_type=
+ read_lock_type_for_table(thd, thd->lex, tables,
+ some_routine_modifies_data);
+ else
+ tbl->reginfo.lock_type= tables->lock_type;
+ }
}
#ifdef WITH_WSREP
if (wsrep_replicate_myisam &&
@@ -5259,7 +4751,17 @@ restart:
#endif
err:
- thd_proc_info(thd, "After opening tables");
+ THD_STAGE_INFO(thd, stage_after_opening_tables);
+
+#ifdef WITH_WSREP
+ if (WSREP(thd))
+ thd_proc_info(thd, "exit open_tables()");
+ else
+ thd_proc_info(thd, 0);
+#else /* WITH_WSREP */
+ thd_proc_info(thd, 0);
+#endif /* WITH_WSREP */
+
free_root(&new_frm_mem, MYF(0)); // Free pre-alloced block
if (error && *table_to_open)
@@ -5517,11 +5019,15 @@ static bool check_lock_and_start_stmt(THD *thd,
engine is important as, for example, InnoDB uses it to determine
what kind of row locks should be acquired when executing statement
in prelocked mode or under LOCK TABLES with @@innodb_table_locks = 0.
+
+ Last argument routine_modifies_data for read_lock_type_for_table()
+ is ignored, as prelocking placeholder will never be set here.
*/
+ DBUG_ASSERT(table_list->prelocking_placeholder == false);
if (table_list->lock_type == TL_WRITE_DEFAULT)
lock_type= thd->update_lock_default;
else if (table_list->lock_type == TL_READ_DEFAULT)
- lock_type= read_lock_type_for_table(thd, prelocking_ctx, table_list);
+ lock_type= read_lock_type_for_table(thd, prelocking_ctx, table_list, true);
else
lock_type= table_list->lock_type;
@@ -5636,16 +5142,20 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type lock_type,
bool error;
DBUG_ENTER("open_ltable");
+ /* Ignore temporary tables as they have already ben opened*/
+ if (table_list->table)
+ DBUG_RETURN(table_list->table);
+
/* should not be used in a prelocked_mode context, see NOTE above */
DBUG_ASSERT(thd->locked_tables_mode < LTM_PRELOCKED);
- thd_proc_info(thd, "Opening table");
+ THD_STAGE_INFO(thd, stage_opening_tables);
thd->current_tablenr= 0;
/* open_ltable can be used only for BASIC TABLEs */
table_list->required_type= FRMTYPE_TABLE;
/* This function can't properly handle requests for such metadata locks. */
- DBUG_ASSERT(table_list->mdl_request.type < MDL_SHARED_NO_WRITE);
+ DBUG_ASSERT(table_list->mdl_request.type < MDL_SHARED_UPGRADABLE);
while ((error= open_table(thd, table_list, thd->mem_root, &ot_ctx)) &&
ot_ctx.can_recover_from_failed_open())
@@ -5708,7 +5218,18 @@ end:
trans_rollback_stmt(thd);
close_thread_tables(thd);
}
- thd_proc_info(thd, "After opening table");
+
+ THD_STAGE_INFO(thd, stage_after_opening_tables);
+
+#ifdef WITH_WSREP
+ if (WSREP(thd))
+ thd_proc_info(thd, "End opening table");
+ else
+ thd_proc_info(thd, 0);
+#else /* WITH_WSREP */
+ thd_proc_info(thd, 0);
+#endif /* WITH_WSREP */
+
DBUG_RETURN(table);
}
@@ -5754,6 +5275,8 @@ bool open_and_lock_tables(THD *thd, TABLE_LIST *tables,
if (lock_tables(thd, tables, counter, flags))
goto err;
+ (void) read_statistics_for_tables_if_needed(thd, tables);
+
if (derived)
{
if (mysql_handle_derived(thd->lex, DT_INIT))
@@ -5884,7 +5407,6 @@ bool lock_tables(THD *thd, TABLE_LIST *tables, uint count,
uint flags)
{
TABLE_LIST *table;
-
DBUG_ENTER("lock_tables");
/*
We can't meet statement requiring prelocking if we already
@@ -6026,6 +5548,36 @@ bool lock_tables(THD *thd, TABLE_LIST *tables, uint count,
}
+/*
+ Restart transaction for tables
+
+ This is used when we had to do an implicit commit after tables are opened
+ and want to restart transactions on tables.
+
+ This is used in case of:
+ LOCK TABLES xx
+ CREATE OR REPLACE TABLE xx;
+*/
+
+bool restart_trans_for_tables(THD *thd, TABLE_LIST *table)
+{
+ DBUG_ENTER("restart_trans_for_tables");
+
+ for (; table; table= table->next_global)
+ {
+ if (table->placeholder())
+ continue;
+
+ if (check_lock_and_start_stmt(thd, thd->lex, table))
+ {
+ DBUG_ASSERT(0); // Should never happen
+ DBUG_RETURN(TRUE);
+ }
+ }
+ DBUG_RETURN(FALSE);
+}
+
+
/**
Prepare statement for reopening of tables and recalculation of set of
prelocked tables.
@@ -6087,12 +5639,17 @@ void close_tables_for_reopen(THD *thd, TABLE_LIST **tables,
the opened TABLE instance will be addded to THD::temporary_tables list.
@param thd Thread context.
+ @param hton Storage engine of the table, if known,
+ or NULL otherwise.
@param path Path (without .frm)
@param db Database name.
@param table_name Table name.
@param add_to_temporary_tables_list Specifies if the opened TABLE
instance should be linked into
THD::temporary_tables list.
+ @param open_in_engine Indicates that we need to open table
+ in storage engine in addition to
+ constructing TABLE object for it.
@note This function is used:
- by alter_table() to open a temporary table;
@@ -6102,26 +5659,34 @@ void close_tables_for_reopen(THD *thd, TABLE_LIST **tables,
@retval NULL on error.
*/
-TABLE *open_table_uncached(THD *thd, const char *path, const char *db,
+TABLE *open_table_uncached(THD *thd, handlerton *hton,
+ const char *path, const char *db,
const char *table_name,
- bool add_to_temporary_tables_list)
+ bool add_to_temporary_tables_list,
+ bool open_in_engine)
{
TABLE *tmp_table;
TABLE_SHARE *share;
char cache_key[MAX_DBKEY_LENGTH], *saved_cache_key, *tmp_path;
uint key_length;
- TABLE_LIST table_list;
DBUG_ENTER("open_table_uncached");
DBUG_PRINT("enter",
("table: '%s'.'%s' path: '%s' server_id: %u "
"pseudo_thread_id: %lu",
db, table_name, path,
- (uint) thd->server_id, (ulong) thd->variables.pseudo_thread_id));
+ (uint) thd->variables.server_id,
+ (ulong) thd->variables.pseudo_thread_id));
+
+ if (add_to_temporary_tables_list)
+ {
+ /* Temporary tables are not safe for parallel replication. */
+ if (thd->rgi_slave && thd->rgi_slave->is_parallel_exec &&
+ thd->wait_for_prior_commit())
+ DBUG_RETURN(NULL);
+ }
- table_list.db= (char*) db;
- table_list.table_name= (char*) table_name;
/* Create the cache_key for temporary tables */
- key_length= create_table_def_key(thd, cache_key, &table_list, 1);
+ key_length= create_tmp_table_def_key(thd, cache_key, db, table_name);
if (!(tmp_table= (TABLE*) my_malloc(sizeof(*tmp_table) + sizeof(*share) +
strlen(path)+1 + key_length,
@@ -6135,14 +5700,30 @@ TABLE *open_table_uncached(THD *thd, const char *path, const char *db,
init_tmp_table_share(thd, share, saved_cache_key, key_length,
strend(saved_cache_key)+1, tmp_path);
+ share->db_plugin= ha_lock_engine(thd, hton);
- if (open_table_def(thd, share, 0) ||
- open_table_from_share(thd, share, table_name,
+ if (open_table_def(thd, share, GTS_TABLE | GTS_USE_DISCOVERY))
+ {
+ /* No need to lock share->mutex as this is not needed for tmp tables */
+ free_table_share(share);
+ my_free(tmp_table);
+ DBUG_RETURN(0);
+ }
+
+ share->m_psi= PSI_CALL_get_table_share(true, share);
+
+ if (open_table_from_share(thd, share, table_name,
+ open_in_engine ?
(uint) (HA_OPEN_KEYFILE | HA_OPEN_RNDFILE |
- HA_GET_INDEX),
+ HA_GET_INDEX) : 0,
READ_KEYINFO | COMPUTE_TYPES | EXTRA_RECORD,
ha_open_options,
- tmp_table, FALSE))
+ tmp_table,
+ /*
+ Set "is_create_table" if the table does not
+ exist in SE
+ */
+ open_in_engine ? false : true))
{
/* No need to lock share->mutex as this is not needed for tmp tables */
free_table_share(share);
@@ -6151,19 +5732,24 @@ TABLE *open_table_uncached(THD *thd, const char *path, const char *db,
}
tmp_table->reginfo.lock_type= TL_WRITE; // Simulate locked
+ tmp_table->grant.privilege= TMP_TABLE_ACLS;
share->tmp_table= (tmp_table->file->has_transactions() ?
TRANSACTIONAL_TMP_TABLE : NON_TRANSACTIONAL_TMP_TABLE);
if (add_to_temporary_tables_list)
{
+ thd->lock_temporary_tables();
/* growing temp list at the head */
tmp_table->next= thd->temporary_tables;
if (tmp_table->next)
tmp_table->next->prev= tmp_table;
thd->temporary_tables= tmp_table;
thd->temporary_tables->prev= 0;
- if (thd->slave_thread)
- slave_open_temp_tables++;
+ if (thd->rgi_slave)
+ {
+ thread_safe_increment32(&slave_open_temp_tables, &thread_running_lock);
+ }
+ thd->unlock_temporary_tables();
}
tmp_table->pos_in_table_list= 0;
DBUG_PRINT("tmptable", ("opened table: '%s'.'%s' 0x%lx", tmp_table->s->db.str,
@@ -6273,6 +5859,164 @@ static void update_field_dependencies(THD *thd, Field *field, TABLE *table)
}
+/**
+ Find a temporary table specified by TABLE_LIST instance in the cache and
+ prepare its TABLE instance for use.
+
+ This function tries to resolve this table in the list of temporary tables
+ of this thread. Temporary tables are thread-local and "shadow" base
+ tables with the same name.
+
+ @note In most cases one should use open_temporary_tables() instead
+ of this call.
+
+ @note One should finalize process of opening temporary table for table
+ list element by calling open_and_process_table(). This function
+ is responsible for table version checking and handling of merge
+ tables.
+
+ @note We used to check global_read_lock before opening temporary tables.
+ However, that limitation was artificial and is removed now.
+
+ @return Error status.
+ @retval FALSE On success. If a temporary table exists for the given
+ key, tl->table is set.
+ @retval TRUE On error. my_error() has been called.
+*/
+
+bool open_temporary_table(THD *thd, TABLE_LIST *tl)
+{
+ TABLE *table;
+ DBUG_ENTER("open_temporary_table");
+ DBUG_PRINT("enter", ("table: '%s'.'%s'", tl->db, tl->table_name));
+
+ /*
+ Code in open_table() assumes that TABLE_LIST::table can
+ be non-zero only for pre-opened temporary tables.
+ */
+ DBUG_ASSERT(tl->table == NULL);
+
+ /*
+ This function should not be called for cases when derived or I_S
+ tables can be met since table list elements for such tables can
+ have invalid db or table name.
+ Instead open_temporary_tables() should be used.
+ */
+ DBUG_ASSERT(!tl->derived && !tl->schema_table);
+
+ if (tl->open_type == OT_BASE_ONLY || !thd->have_temporary_tables())
+ {
+ DBUG_PRINT("info", ("skip_temporary is set or no temporary tables"));
+ DBUG_RETURN(FALSE);
+ }
+
+ if (find_and_use_temporary_table(thd, tl, &table))
+ DBUG_RETURN(TRUE);
+ if (!table)
+ {
+ if (tl->open_type == OT_TEMPORARY_ONLY &&
+ tl->open_strategy == TABLE_LIST::OPEN_NORMAL)
+ {
+ my_error(ER_NO_SUCH_TABLE, MYF(0), tl->db, tl->table_name);
+ DBUG_RETURN(TRUE);
+ }
+ DBUG_RETURN(FALSE);
+ }
+
+ /*
+ Temporary tables are not safe for parallel replication. They were
+ designed to be visible to one thread only, so have no table locking.
+ Thus there is no protection against two conflicting transactions
+ committing in parallel and things like that.
+
+ So for now, anything that uses temporary tables will be serialised
+ with anything before it, when using parallel replication.
+
+ ToDo: We might be able to introduce a reference count or something
+ on temp tables, and have slave worker threads wait for it to reach
+ zero before being allowed to use the temp table. Might not be worth
+ it though, as statement-based replication using temporary tables is
+ in any case rather fragile.
+ */
+ if (thd->rgi_slave && thd->rgi_slave->is_parallel_exec &&
+ thd->wait_for_prior_commit())
+ DBUG_RETURN(true);
+
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+ if (tl->partition_names)
+ {
+ /* Partitioned temporary tables is not supported. */
+ DBUG_ASSERT(!table->part_info);
+ my_error(ER_PARTITION_CLAUSE_ON_NONPARTITIONED, MYF(0));
+ DBUG_RETURN(true);
+ }
+#endif
+
+ if (table->query_id)
+ {
+ /*
+ We're trying to use the same temporary table twice in a query.
+ Right now we don't support this because a temporary table is always
+ represented by only one TABLE object in THD, and it can not be
+ cloned. Emit an error for an unsupported behaviour.
+ */
+
+ DBUG_PRINT("error",
+ ("query_id: %lu server_id: %u pseudo_thread_id: %lu",
+ (ulong) table->query_id, (uint) thd->variables.server_id,
+ (ulong) thd->variables.pseudo_thread_id));
+ my_error(ER_CANT_REOPEN_TABLE, MYF(0), table->alias.c_ptr());
+ DBUG_RETURN(TRUE);
+ }
+
+ table->query_id= thd->query_id;
+ thd->thread_specific_used= TRUE;
+
+ tl->updatable= 1; // It is not derived table nor non-updatable VIEW.
+ tl->table= table;
+
+ table->init(thd, tl);
+
+ DBUG_PRINT("info", ("Using temporary table"));
+ DBUG_RETURN(FALSE);
+}
+
+
+/**
+ Pre-open temporary tables corresponding to table list elements.
+
+ @note One should finalize process of opening temporary tables
+ by calling open_tables(). This function is responsible
+ for table version checking and handling of merge tables.
+
+ @return Error status.
+ @retval FALSE On success. If a temporary tables exists for the
+ given element, tl->table is set.
+ @retval TRUE On error. my_error() has been called.
+*/
+
+bool open_temporary_tables(THD *thd, TABLE_LIST *tl_list)
+{
+ TABLE_LIST *first_not_own= thd->lex->first_not_own_table();
+ DBUG_ENTER("open_temporary_tables");
+
+ for (TABLE_LIST *tl= tl_list; tl && tl != first_not_own; tl= tl->next_global)
+ {
+ if (tl->derived || tl->schema_table)
+ {
+ /*
+ Derived and I_S tables will be handled by a later call to open_tables().
+ */
+ continue;
+ }
+
+ if (open_temporary_table(thd, tl))
+ DBUG_RETURN(TRUE);
+ }
+
+ DBUG_RETURN(FALSE);
+}
+
/*
Find a field by name in a view that uses merge algorithm.
@@ -6669,6 +6413,7 @@ find_field_in_table_ref(THD *thd, TABLE_LIST *table_list,
*/
table_name && table_name[0] &&
(my_strcasecmp(table_alias_charset, table_list->alias, table_name) ||
+ (db_name && db_name[0] && (!table_list->db || !table_list->db[0])) ||
(db_name && db_name[0] && table_list->db && table_list->db[0] &&
(table_list->schema_table ?
my_strcasecmp(system_charset_info, db_name, table_list->db) :
@@ -7142,7 +6887,10 @@ find_item_in_list(Item *find, List<Item> &items, uint *counter,
for (uint i= 0; (item=li++); i++)
{
- if (field_name && item->real_item()->type() == Item::FIELD_ITEM)
+ if (field_name &&
+ (item->real_item()->type() == Item::FIELD_ITEM ||
+ ((item->type() == Item::REF_ITEM) &&
+ (((Item_ref *)item)->ref_type() == Item_ref::VIEW_REF))))
{
Item_ident *item_field= (Item_ident*) item;
@@ -7268,35 +7016,6 @@ find_item_in_list(Item *find, List<Item> &items, uint *counter,
break;
}
}
- else if (table_name && item->type() == Item::REF_ITEM &&
- ((Item_ref *)item)->ref_type() == Item_ref::VIEW_REF)
- {
- /*
- TODO:Here we process prefixed view references only. What we should
- really do is process all types of Item_refs. But this will currently
- lead to a clash with the way references to outer SELECTs (from the
- HAVING clause) are handled in e.g. :
- SELECT 1 FROM t1 AS t1_o GROUP BY a
- HAVING (SELECT t1_o.a FROM t1 AS t1_i GROUP BY t1_i.a LIMIT 1).
- Processing all Item_refs here will cause t1_o.a to resolve to itself.
- We still need to process the special case of Item_direct_view_ref
- because in the context of views they have the same meaning as
- Item_field for tables.
- */
- Item_ident *item_ref= (Item_ident *) item;
- if (field_name && item_ref->name && item_ref->table_name &&
- !my_strcasecmp(system_charset_info, item_ref->name, field_name) &&
- !my_strcasecmp(table_alias_charset, item_ref->table_name,
- table_name) &&
- (!db_name || (item_ref->db_name &&
- !strcmp (item_ref->db_name, db_name))))
- {
- found= li.ref();
- *counter= i;
- *resolution= RESOLVED_IGNORING_ALIAS;
- break;
- }
- }
}
if (!found)
{
@@ -7634,14 +7353,6 @@ mark_common_columns(THD *thd, TABLE_LIST *table_ref_1, TABLE_LIST *table_ref_2,
*/
result= FALSE;
- /*
- Save the lists made during natural join matching (because
- the matching done only once but we need the list in case
- of prepared statements).
- */
- table_ref_1->persistent_used_items= table_ref_1->used_items;
- table_ref_2->persistent_used_items= table_ref_2->used_items;
-
err:
if (arena)
thd->restore_active_arena(arena, &backup);
@@ -8217,7 +7928,7 @@ bool setup_fields(THD *thd, Item **ref_pointer_array,
thd->lex->allow_sum_func= save_allow_sum_func;
thd->mark_used_columns= save_mark_used_columns;
DBUG_PRINT("info", ("thd->mark_used_columns: %d", thd->mark_used_columns));
- DBUG_RETURN(test(thd->is_error()));
+ DBUG_RETURN(MY_TEST(thd->is_error()));
}
@@ -8688,11 +8399,6 @@ insert_fields(THD *thd, Name_resolution_context *context, const char *db_name,
}
}
#endif
- /*
- field_iterator.create_item() builds used_items which we
- have to save because changes made once and they are persistent
- */
- tables->persistent_used_items= tables->used_items;
if ((field= field_iterator.field()))
{
@@ -8752,9 +8458,16 @@ insert_fields(THD *thd, Name_resolution_context *context, const char *db_name,
meaningful message than ER_BAD_TABLE_ERROR.
*/
if (!table_name)
- my_message(ER_NO_TABLES_USED, ER(ER_NO_TABLES_USED), MYF(0));
+ my_error(ER_NO_TABLES_USED, MYF(0));
+ else if (!db_name && !thd->db)
+ my_error(ER_NO_DB_ERROR, MYF(0));
else
- my_error(ER_BAD_TABLE_ERROR, MYF(0), table_name);
+ {
+ char name[FN_REFLEN];
+ my_snprintf(name, sizeof(name), "%s.%s",
+ db_name ? db_name : thd->db, table_name);
+ my_error(ER_BAD_TABLE_ERROR, MYF(0), name);
+ }
DBUG_RETURN(TRUE);
}
@@ -8943,7 +8656,7 @@ int setup_conds(THD *thd, TABLE_LIST *tables, List<TABLE_LIST> &leaves,
select_lex->where= *conds;
}
thd->lex->current_select->is_item_list_lookup= save_is_item_list_lookup;
- DBUG_RETURN(test(thd->is_error()));
+ DBUG_RETURN(MY_TEST(thd->is_error()));
err_no_arena:
select_lex->is_item_list_lookup= save_is_item_list_lookup;
@@ -8957,34 +8670,33 @@ err_no_arena:
******************************************************************************/
-/*
- Fill fields with given items.
+/**
+ Fill the fields of a table with the values of an Item list
- SYNOPSIS
- fill_record()
- thd thread handler
- fields Item_fields list to be filled
- values values to fill with
- ignore_errors TRUE if we should ignore errors
+ @param thd thread handler
+ @param table_arg the table that is being modified
+ @param fields Item_fields list to be filled
+ @param values values to fill with
+ @param ignore_errors TRUE if we should ignore errors
- NOTE
+ @details
fill_record() may set table->auto_increment_field_not_null and a
caller should make sure that it is reset after their last call to this
function.
- RETURN
- FALSE OK
- TRUE error occured
+ @return Status
+ @retval true An error occured.
+ @retval false OK.
*/
-static bool
-fill_record(THD * thd, List<Item> &fields, List<Item> &values,
+bool
+fill_record(THD * thd, TABLE *table_arg, List<Item> &fields, List<Item> &values,
bool ignore_errors)
{
List_iterator_fast<Item> f(fields),v(values);
Item *value, *fld;
Item_field *field;
- TABLE *table= 0, *vcol_table= 0;
+ TABLE *vcol_table= 0;
bool save_abort_on_warning= thd->abort_on_warning;
bool save_no_errors= thd->no_errors;
DBUG_ENTER("fill_record");
@@ -9001,27 +8713,28 @@ fill_record(THD * thd, List<Item> &fields, List<Item> &values,
thus we safely can take table from the first field.
*/
fld= (Item_field*)f++;
- if (!(field= fld->filed_for_view_update()))
+ if (!(field= fld->field_for_view_update()))
{
my_error(ER_NONUPDATEABLE_COLUMN, MYF(0), fld->name);
goto err;
}
- table= field->field->table;
- table->auto_increment_field_not_null= FALSE;
+ DBUG_ASSERT(field->field->table == table_arg);
+ table_arg->auto_increment_field_not_null= FALSE;
f.rewind();
}
else if (thd->lex->unit.insert_table_with_stored_vcol)
vcol_table= thd->lex->unit.insert_table_with_stored_vcol;
+
while ((fld= f++))
{
- if (!(field= fld->filed_for_view_update()))
+ if (!(field= fld->field_for_view_update()))
{
my_error(ER_NONUPDATEABLE_COLUMN, MYF(0), fld->name);
goto err;
}
value=v++;
Field *rfield= field->field;
- table= rfield->table;
+ TABLE* table= rfield->table;
if (rfield == table->next_number_field)
table->auto_increment_field_not_null= TRUE;
if (rfield->vcol_info &&
@@ -9029,7 +8742,7 @@ fill_record(THD * thd, List<Item> &fields, List<Item> &values,
value->type() != Item::NULL_ITEM &&
table->s->table_category != TABLE_CATEGORY_TEMPORARY)
{
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_WARNING_NON_DEFAULT_VALUE_FOR_VIRTUAL_COLUMN,
ER(ER_WARNING_NON_DEFAULT_VALUE_FOR_VIRTUAL_COLUMN),
rfield->field_name, table->s->table_name.str);
@@ -9040,6 +8753,7 @@ fill_record(THD * thd, List<Item> &fields, List<Item> &values,
my_message(ER_UNKNOWN_ERROR, ER(ER_UNKNOWN_ERROR), MYF(0));
goto err;
}
+ rfield->set_explicit_default(value);
DBUG_ASSERT(vcol_table == 0 || vcol_table == table);
vcol_table= table;
}
@@ -9056,8 +8770,8 @@ fill_record(THD * thd, List<Item> &fields, List<Item> &values,
err:
thd->abort_on_warning= save_abort_on_warning;
thd->no_errors= save_no_errors;
- if (table)
- table->auto_increment_field_not_null= FALSE;
+ if (fields.elements)
+ table_arg->auto_increment_field_not_null= FALSE;
DBUG_RETURN(TRUE);
}
@@ -9066,90 +8780,86 @@ err:
Fill fields in list with values from the list of items and invoke
before triggers.
- SYNOPSIS
- fill_record_n_invoke_before_triggers()
- thd thread context
- fields Item_fields list to be filled
- values values to fill with
- ignore_errors TRUE if we should ignore errors
- triggers object holding list of triggers to be invoked
- event event type for triggers to be invoked
+ @param thd thread context
+ @param table the table that is being modified
+ @param fields Item_fields list to be filled
+ @param values values to fill with
+ @param ignore_errors TRUE if we should ignore errors
+ @param event event type for triggers to be invoked
- NOTE
+ @detail
This function assumes that fields which values will be set and triggers
to be invoked belong to the same table, and that TABLE::record[0] and
record[1] buffers correspond to new and old versions of row respectively.
- RETURN
- FALSE OK
- TRUE error occured
+ @return Status
+ @retval true An error occured.
+ @retval false OK.
*/
bool
-fill_record_n_invoke_before_triggers(THD *thd, List<Item> &fields,
+fill_record_n_invoke_before_triggers(THD *thd, TABLE *table, List<Item> &fields,
List<Item> &values, bool ignore_errors,
- Table_triggers_list *triggers,
enum trg_event_type event)
{
bool result;
- result= (fill_record(thd, fields, values, ignore_errors) ||
+ Table_triggers_list *triggers= table->triggers;
+ result= (fill_record(thd, table, fields, values, ignore_errors) ||
(triggers && triggers->process_triggers(thd, event,
TRG_ACTION_BEFORE, TRUE)));
/*
Re-calculate virtual fields to cater for cases when base columns are
updated by the triggers.
*/
- if (!result && triggers)
+ if (!result && triggers && table)
{
- TABLE *table= 0;
List_iterator_fast<Item> f(fields);
Item *fld;
Item_field *item_field;
if (fields.elements)
{
fld= (Item_field*)f++;
- item_field= fld->filed_for_view_update();
- if (item_field && item_field->field &&
- (table= item_field->field->table) &&
- table->vfield)
+ item_field= fld->field_for_view_update();
+ if (item_field && item_field->field && table && table->vfield)
+ {
+ DBUG_ASSERT(table == item_field->field->table);
result= update_virtual_fields(thd, table,
table->triggers ? VCOL_UPDATE_ALL :
VCOL_UPDATE_FOR_WRITE);
+ }
}
}
return result;
}
-/*
- Fill field buffer with values from Field list
+/**
+ Fill the field buffer of a table with the values of an Item list
- SYNOPSIS
- fill_record()
- thd thread handler
- ptr pointer on pointer to record
- values list of fields
- ignore_errors TRUE if we should ignore errors
- use_value forces usage of value of the items instead of result
+ @param thd thread handler
+ @param table_arg the table that is being modified
+ @param ptr pointer on pointer to record of fields
+ @param values values to fill with
+ @param ignore_errors TRUE if we should ignore errors
+ @param use_value forces usage of value of the items instead of result
- NOTE
+ @details
fill_record() may set table->auto_increment_field_not_null and a
caller should make sure that it is reset after their last call to this
function.
- RETURN
- FALSE OK
- TRUE error occured
+ @return Status
+ @retval true An error occured.
+ @retval false OK.
*/
bool
-fill_record(THD *thd, Field **ptr, List<Item> &values, bool ignore_errors,
- bool use_value)
+fill_record(THD *thd, TABLE *table, Field **ptr, List<Item> &values,
+ bool ignore_errors, bool use_value)
{
List_iterator_fast<Item> v(values);
List<TABLE> tbl_list;
Item *value;
- TABLE *table= 0;
Field *field;
bool abort_on_warning_saved= thd->abort_on_warning;
DBUG_ENTER("fill_record");
@@ -9164,7 +8874,7 @@ fill_record(THD *thd, Field **ptr, List<Item> &values, bool ignore_errors,
On INSERT or UPDATE fields are checked to be from the same table,
thus we safely can take table from the first field.
*/
- table= (*ptr)->table;
+ DBUG_ASSERT((*ptr)->table == table);
/*
Reset the table->auto_increment_field_not_null as it is valid for
@@ -9184,7 +8894,7 @@ fill_record(THD *thd, Field **ptr, List<Item> &values, bool ignore_errors,
value->type() != Item::NULL_ITEM &&
table->s->table_category != TABLE_CATEGORY_TEMPORARY)
{
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_WARNING_NON_DEFAULT_VALUE_FOR_VIRTUAL_COLUMN,
ER(ER_WARNING_NON_DEFAULT_VALUE_FOR_VIRTUAL_COLUMN),
field->field_name, table->s->table_name.str);
@@ -9195,6 +8905,7 @@ fill_record(THD *thd, Field **ptr, List<Item> &values, bool ignore_errors,
else
if (value->save_in_field(field, 0) < 0)
goto err;
+ field->set_explicit_default(value);
}
/* Update virtual fields*/
thd->abort_on_warning= FALSE;
@@ -9214,36 +8925,34 @@ err:
/*
- Fill fields in array with values from the list of items and invoke
+ Fill fields in an array with values from the list of items and invoke
before triggers.
- SYNOPSIS
- fill_record_n_invoke_before_triggers()
- thd thread context
- ptr NULL-ended array of fields to be filled
- values values to fill with
- ignore_errors TRUE if we should ignore errors
- triggers object holding list of triggers to be invoked
- event event type for triggers to be invoked
+ @param thd thread context
+ @param table the table that is being modified
+ @param ptr the fields to be filled
+ @param values values to fill with
+ @param ignore_errors TRUE if we should ignore errors
+ @param event event type for triggers to be invoked
- NOTE
+ @detail
This function assumes that fields which values will be set and triggers
to be invoked belong to the same table, and that TABLE::record[0] and
record[1] buffers correspond to new and old versions of row respectively.
- RETURN
- FALSE OK
- TRUE error occured
+ @return Status
+ @retval true An error occured.
+ @retval false OK.
*/
bool
-fill_record_n_invoke_before_triggers(THD *thd, Field **ptr,
+fill_record_n_invoke_before_triggers(THD *thd, TABLE *table, Field **ptr,
List<Item> &values, bool ignore_errors,
- Table_triggers_list *triggers,
enum trg_event_type event)
{
bool result;
- result= (fill_record(thd, ptr, values, ignore_errors, FALSE) ||
+ Table_triggers_list *triggers= table->triggers;
+ result= (fill_record(thd, table, ptr, values, ignore_errors, FALSE) ||
(triggers && triggers->process_triggers(thd, event,
TRG_ACTION_BEFORE, TRUE)));
/*
@@ -9252,7 +8961,7 @@ fill_record_n_invoke_before_triggers(THD *thd, Field **ptr,
*/
if (!result && triggers && *ptr)
{
- TABLE *table= (*ptr)->table;
+ DBUG_ASSERT(table == (*ptr)->table);
if (table->vfield)
result= update_virtual_fields(thd, table,
table->triggers ? VCOL_UPDATE_ALL :
@@ -9287,15 +8996,10 @@ my_bool mysql_rm_tmp_tables(void)
/* Remove all SQLxxx tables from directory */
- for (idx=0 ; idx < (uint) dirp->number_off_files ; idx++)
+ for (idx=0 ; idx < (uint) dirp->number_of_files ; idx++)
{
file=dirp->dir_entry+idx;
- /* skiping . and .. */
- if (file->name[0] == '.' && (!file->name[1] ||
- (file->name[1] == '.' && !file->name[2])))
- continue;
-
if (!memcmp(file->name, tmp_file_prefix,
tmp_file_prefix_length))
{
@@ -9311,7 +9015,7 @@ my_bool mysql_rm_tmp_tables(void)
memcpy(filePathCopy, filePath, filePath_len - ext_len);
filePathCopy[filePath_len - ext_len]= 0;
init_tmp_table_share(thd, &share, "", 0, "", filePathCopy);
- if (!open_table_def(thd, &share, 0) &&
+ if (!open_table_def(thd, &share) &&
((handler_file= get_new_handler(&share, thd->mem_root,
share.db_type()))))
{
@@ -9331,7 +9035,6 @@ my_bool mysql_rm_tmp_tables(void)
my_dirend(dirp);
}
delete thd;
- my_pthread_setspecific_ptr(THR_THD, 0);
DBUG_RETURN(0);
}
@@ -9340,23 +9043,6 @@ my_bool mysql_rm_tmp_tables(void)
unireg support functions
*****************************************************************************/
-/*
- free all unused tables
-
- NOTE
- This is called by 'handle_manager' when one wants to periodicly flush
- all not used tables.
-*/
-
-void tdc_flush_unused_tables()
-{
- mysql_mutex_lock(&LOCK_open);
- while (unused_tables)
- free_cache_entry(unused_tables);
- mysql_mutex_unlock(&LOCK_open);
-}
-
-
/**
A callback to the server internals that is used to address
special cases of the locking protocol.
@@ -9439,129 +9125,6 @@ bool mysql_notify_thread_having_shared_lock(THD *thd, THD *in_use,
}
-/**
- Remove all or some (depending on parameter) instances of TABLE and
- TABLE_SHARE from the table definition cache.
-
- @param thd Thread context
- @param remove_type Type of removal:
- TDC_RT_REMOVE_ALL - remove all TABLE instances and
- TABLE_SHARE instance. There
- should be no used TABLE objects
- and caller should have exclusive
- metadata lock on the table.
- TDC_RT_REMOVE_NOT_OWN - remove all TABLE instances
- except those that belong to
- this thread. There should be
- no TABLE objects used by other
- threads and caller should have
- exclusive metadata lock on the
- table.
- TDC_RT_REMOVE_UNUSED - remove all unused TABLE
- instances (if there are no
- used instances will also
- remove TABLE_SHARE).
- @param db Name of database
- @param table_name Name of table
- @param has_lock If TRUE, LOCK_open is already acquired
-
- @note It assumes that table instances are already not used by any
- (other) thread (this should be achieved by using meta-data locks).
-*/
-
-void tdc_remove_table(THD *thd, enum_tdc_remove_table_type remove_type,
- const char *db, const char *table_name,
- bool has_lock)
-{
- char key[MAX_DBKEY_LENGTH];
- uint key_length;
- TABLE *table;
- TABLE_SHARE *share;
- DBUG_ENTER("tdc_remove_table");
- DBUG_PRINT("enter",("name: %s remove_type: %d", table_name, remove_type));
-
- if (! has_lock)
- mysql_mutex_lock(&LOCK_open);
- else
- {
- mysql_mutex_assert_owner(&LOCK_open);
- }
-
-#ifdef WITH_WSREP
- /* if thd was BF aborted, exclusive locks were canceled */
-#else
- DBUG_ASSERT(remove_type == TDC_RT_REMOVE_UNUSED ||
- thd->mdl_context.is_lock_owner(MDL_key::TABLE, db, table_name,
- MDL_EXCLUSIVE));
-#endif /* WITH_WSREP */
-
- key_length= create_table_def_key(key, db, table_name);
-
- if ((share= (TABLE_SHARE*) my_hash_search(&table_def_cache,(uchar*) key,
- key_length)))
- {
- if (share->ref_count)
- {
- I_P_List_iterator<TABLE, TABLE_share> it(share->free_tables);
-#ifndef DBUG_OFF
- if (remove_type == TDC_RT_REMOVE_ALL)
- {
- DBUG_ASSERT(share->used_tables.is_empty());
- }
- else if (remove_type == TDC_RT_REMOVE_NOT_OWN ||
- remove_type == TDC_RT_REMOVE_NOT_OWN_AND_MARK_NOT_USABLE)
- {
- I_P_List_iterator<TABLE, TABLE_share> it2(share->used_tables);
- while ((table= it2++))
-#ifdef WITH_WSREP
- /* if thd was BF aborted, exclusive locks were canceled,
- thus others can use table */
-
- if (table->in_use != thd &&
- table->in_use->wsrep_conflict_state != MUST_ABORT)
- {
-#endif
- if (table->in_use != thd)
- {
- DBUG_ASSERT(0);
- }
-#ifdef WITH_WSREP
- }
-#endif
- }
-#endif
- /*
- Mark share to ensure that it gets automatically deleted once
- it is no longer referenced.
-
- Note that code in TABLE_SHARE::wait_for_old_version() assumes
- that marking share as old and removal of its unused tables
- and of the share itself from TDC happens atomically under
- protection of LOCK_open, or, putting it another way, that
- TDC does not contain old shares which don't have any tables
- used.
- */
- if (remove_type == TDC_RT_REMOVE_NOT_OWN)
- share->remove_from_cache_at_close();
- else
- {
- /* Ensure that no can open the table while it's used */
- share->protect_against_usage();
- }
-
- while ((table= it++))
- free_cache_entry(table);
- }
- else
- (void) my_hash_delete(&table_def_cache, (uchar*) share);
- }
-
- if (! has_lock)
- mysql_mutex_unlock(&LOCK_open);
- DBUG_VOID_RETURN;
-}
-
-
int setup_ftfuncs(SELECT_LEX *select_lex)
{
List_iterator<Item_func_match> li(*(select_lex->ftfunc_list)),
@@ -9689,6 +9252,12 @@ bool is_equal(const LEX_STRING *a, const LEX_STRING *b)
must call close_system_tables() to close systems tables opened
with this call.
+ NOTES
+ In some situations we use this function to open system tables for
+ writing. It happens, for examples, with statistical tables when
+ they are updated by an ANALYZE command. In these cases we should
+ guarantee that system tables will not be deadlocked.
+
RETURN
FALSE Success
TRUE Error
@@ -9841,12 +9410,7 @@ open_log_table(THD *thd, TABLE_LIST *one_table, Open_tables_backup *backup)
DBUG_ASSERT(table->s->table_category == TABLE_CATEGORY_LOG);
/* Make sure all columns get assigned to a default value */
table->use_all_columns();
- table->no_replicate= 1;
- /*
- Don't set automatic timestamps as we may want to use time of logging,
- not from query start
- */
- table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
+ DBUG_ASSERT(table->no_replicate);
}
else
thd->restore_backup_open_tables_state(backup);
@@ -9902,6 +9466,7 @@ int dynamic_column_error_message(enum_dyncol_func_result rc)
switch (rc) {
case ER_DYNCOL_YES:
case ER_DYNCOL_OK:
+ case ER_DYNCOL_TRUNCATED:
break; // it is not an error
case ER_DYNCOL_FORMAT:
my_error(ER_DYN_COL_WRONG_FORMAT, MYF(0));
diff --git a/sql/sql_base.h b/sql/sql_base.h
index d49554d5473..aa4a041fc10 100644
--- a/sql/sql_base.h
+++ b/sql/sql_base.h
@@ -16,10 +16,10 @@
#ifndef SQL_BASE_INCLUDED
#define SQL_BASE_INCLUDED
-#include "unireg.h" // REQUIRED: for other includes
#include "sql_trigger.h" /* trg_event_type */
#include "sql_class.h" /* enum_mark_columns */
#include "mysqld.h" /* key_map */
+#include "table_cache.h"
class Item_ident;
struct Name_resolution_context;
@@ -59,67 +59,16 @@ enum find_item_error_report_type {REPORT_ALL_ERRORS, REPORT_EXCEPT_NOT_FOUND,
IGNORE_ERRORS, REPORT_EXCEPT_NON_UNIQUE,
IGNORE_EXCEPT_NON_UNIQUE};
-enum enum_tdc_remove_table_type {TDC_RT_REMOVE_ALL, TDC_RT_REMOVE_NOT_OWN,
- TDC_RT_REMOVE_UNUSED,
- TDC_RT_REMOVE_NOT_OWN_AND_MARK_NOT_USABLE};
-
-/* bits for last argument to remove_table_from_cache() */
-#define RTFC_NO_FLAG 0x0000
-#define RTFC_OWNED_BY_THD_FLAG 0x0001
-#define RTFC_WAIT_OTHER_THREAD_FLAG 0x0002
-#define RTFC_CHECK_KILLED_FLAG 0x0004
-
-bool check_dup(const char *db, const char *name, TABLE_LIST *tables);
-extern mysql_mutex_t LOCK_open;
-bool table_cache_init(void);
-void table_cache_free(void);
-bool table_def_init(void);
-void table_def_free(void);
-void table_def_start_shutdown(void);
-void assign_new_table_id(TABLE_SHARE *share);
-uint cached_open_tables(void);
-uint cached_table_definitions(void);
-uint create_table_def_key(THD *thd, char *key,
- const TABLE_LIST *table_list,
- bool tmp_table);
-
-/**
- Create a table cache key for non-temporary table.
-
- @param key Buffer for key (must be at least NAME_LEN*2+2 bytes).
- @param db Database name.
- @param table_name Table name.
-
- @return Length of key.
-
- @sa create_table_def_key(thd, char *, table_list, bool)
-*/
-
-inline uint
-create_table_def_key(char *key, const char *db, const char *table_name)
-{
- /*
- In theory caller should ensure that both db and table_name are
- not longer than NAME_LEN bytes. In practice we play safe to avoid
- buffer overruns.
- */
- return (uint)(strmake(strmake(key, db, NAME_LEN) + 1, table_name,
- NAME_LEN) - key + 1);
-}
-
-TABLE_SHARE *get_table_share(THD *thd, TABLE_LIST *table_list, char *key,
- uint key_length, uint db_flags, int *error,
- my_hash_value_type hash_value);
-void release_table_share(TABLE_SHARE *share);
-TABLE_SHARE *get_cached_table_share(const char *db, const char *table_name);
-
+uint create_tmp_table_def_key(THD *thd, char *key, const char *db,
+ const char *table_name);
+uint get_table_def_key(const TABLE_LIST *table_list, const char **key);
TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type update,
uint lock_flags);
/* mysql_lock_tables() and open_table() flags bits */
#define MYSQL_OPEN_IGNORE_GLOBAL_READ_LOCK 0x0001
#define MYSQL_OPEN_IGNORE_FLUSH 0x0002
-#define MYSQL_OPEN_TEMPORARY_ONLY 0x0004
+/* MYSQL_OPEN_TEMPORARY_ONLY (0x0004) is not used anymore. */
#define MYSQL_LOCK_IGNORE_GLOBAL_READ_ONLY 0x0008
#define MYSQL_LOCK_LOG_TABLE 0x0010
/**
@@ -132,8 +81,7 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type update,
a new instance of the table.
*/
#define MYSQL_OPEN_GET_NEW_TABLE 0x0040
-/** Don't look up the table in the list of temporary tables. */
-#define MYSQL_OPEN_SKIP_TEMPORARY 0x0080
+/* 0x0080 used to be MYSQL_OPEN_SKIP_TEMPORARY */
/** Fail instead of waiting when conficting metadata lock is discovered. */
#define MYSQL_OPEN_FAIL_ON_MDL_CONFLICT 0x0100
/** Open tables using MDL_SHARED lock instead of one specified in parser. */
@@ -154,7 +102,11 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type update,
*/
#define MYSQL_OPEN_SKIP_SCOPED_MDL_LOCK 0x1000
#define MYSQL_LOCK_NOT_TEMPORARY 0x2000
-#define MYSQL_OPEN_FOR_REPAIR 0x4000
+/**
+ Only check THD::killed if waits happen (e.g. wait on MDL, wait on
+ table flush, wait on thr_lock.c locks) while opening and locking table.
+*/
+#define MYSQL_OPEN_IGNORE_KILLED 0x8000
/** Please refer to the internals manual. */
#define MYSQL_OPEN_REOPEN (MYSQL_OPEN_IGNORE_FLUSH |\
@@ -162,11 +114,11 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type update,
MYSQL_LOCK_IGNORE_GLOBAL_READ_ONLY |\
MYSQL_LOCK_IGNORE_TIMEOUT |\
MYSQL_OPEN_GET_NEW_TABLE |\
- MYSQL_OPEN_SKIP_TEMPORARY |\
MYSQL_OPEN_HAS_MDL_LOCK)
bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
Open_table_context *ot_ctx);
+
bool open_new_frm(THD *thd, TABLE_SHARE *share, const char *alias,
uint db_stat, uint prgflag,
uint ha_open_flags, TABLE *outparam, TABLE_LIST *table_desc,
@@ -174,15 +126,17 @@ bool open_new_frm(THD *thd, TABLE_SHARE *share, const char *alias,
bool get_key_map_from_key_list(key_map *map, TABLE *table,
List<String> *index_list);
-TABLE *open_table_uncached(THD *thd, const char *path, const char *db,
- const char *table_name,
- bool add_to_temporary_tables_list);
+TABLE *open_table_uncached(THD *thd, handlerton *hton, const char *path,
+ const char *db, const char *table_name,
+ bool add_to_temporary_tables_list,
+ bool open_in_engine);
TABLE *find_locked_table(TABLE *list, const char *db, const char *table_name);
TABLE *find_write_locked_table(TABLE *list, const char *db,
const char *table_name);
thr_lock_type read_lock_type_for_table(THD *thd,
Query_tables_list *prelocking_ctx,
- TABLE_LIST *table_list);
+ TABLE_LIST *table_list,
+ bool routine_modifies_data);
my_bool mysql_rm_tmp_tables(void);
bool rm_temporary_table(handlerton *base, const char *path);
@@ -193,19 +147,23 @@ TABLE_LIST *find_table_in_list(TABLE_LIST *table,
const char *db_name,
const char *table_name);
TABLE *find_temporary_table(THD *thd, const char *db, const char *table_name);
+bool find_and_use_temporary_table(THD *thd, const char *db,
+ const char *table_name, TABLE **out_table);
TABLE *find_temporary_table(THD *thd, const TABLE_LIST *tl);
+bool find_and_use_temporary_table(THD *thd, const TABLE_LIST *tl,
+ TABLE **out_table);
TABLE *find_temporary_table(THD *thd, const char *table_key,
uint table_key_length);
void close_thread_tables(THD *thd);
-bool fill_record_n_invoke_before_triggers(THD *thd, List<Item> &fields,
+bool fill_record_n_invoke_before_triggers(THD *thd, TABLE *table,
+ List<Item> &fields,
List<Item> &values,
bool ignore_errors,
- Table_triggers_list *triggers,
enum trg_event_type event);
-bool fill_record_n_invoke_before_triggers(THD *thd, Field **field,
+bool fill_record_n_invoke_before_triggers(THD *thd, TABLE *table,
+ Field **field,
List<Item> &values,
bool ignore_errors,
- Table_triggers_list *triggers,
enum trg_event_type event);
bool insert_fields(THD *thd, Name_resolution_context *context,
const char *db_name, const char *table_name,
@@ -218,7 +176,9 @@ bool setup_fields(THD *thd, Item** ref_pointer_array,
List<Item> &item, enum_mark_columns mark_used_columns,
List<Item> *sum_func_list, bool allow_sum_func);
void unfix_fields(List<Item> &items);
-bool fill_record(THD *thd, Field **field, List<Item> &values,
+bool fill_record(THD * thd, TABLE *table_arg, List<Item> &fields,
+ List<Item> &values, bool ignore_errors);
+bool fill_record(THD *thd, TABLE *table, Field **field, List<Item> &values,
bool ignore_errors, bool use_value);
Field *
@@ -256,9 +216,7 @@ bool setup_tables_and_check_access(THD *thd,
ulong want_access,
bool full_table_list);
bool wait_while_table_is_used(THD *thd, TABLE *table,
- enum ha_extra_function function,
- enum_tdc_remove_table_type remove_type=
- TDC_RT_REMOVE_NOT_OWN);
+ enum ha_extra_function function);
void drop_open_table(THD *thd, TABLE *table, const char *db_name,
const char *table_name);
@@ -289,18 +247,22 @@ bool lock_tables(THD *thd, TABLE_LIST *tables, uint counter, uint flags);
int decide_logging_format(THD *thd, TABLE_LIST *tables);
void free_io_cache(TABLE *entry);
void intern_close_table(TABLE *entry);
-bool close_thread_table(THD *thd, TABLE **table_ptr);
+void kill_delayed_threads_for_table(TABLE_SHARE *share);
+void close_thread_table(THD *thd, TABLE **table_ptr);
bool close_temporary_tables(THD *thd);
TABLE_LIST *unique_table(THD *thd, TABLE_LIST *table, TABLE_LIST *table_list,
bool check_alias);
-int drop_temporary_table(THD *thd, TABLE_LIST *table_list, bool *is_trans);
+int drop_temporary_table(THD *thd, TABLE *table, bool *is_trans);
void close_temporary_table(THD *thd, TABLE *table, bool free_share,
bool delete_table);
void close_temporary(TABLE *table, bool free_share, bool delete_table);
bool rename_temporary_table(THD* thd, TABLE *table, const char *new_db,
const char *table_name);
+bool open_temporary_tables(THD *thd, TABLE_LIST *tl_list);
+bool open_temporary_table(THD *thd, TABLE_LIST *tl);
bool is_equal(const LEX_STRING *a, const LEX_STRING *b);
+class Open_tables_backup;
/* Functions to work with system tables. */
bool open_system_tables_for_read(THD *thd, TABLE_LIST *table_list,
Open_tables_backup *backup);
@@ -318,30 +280,42 @@ bool close_cached_tables(THD *thd, TABLE_LIST *tables,
bool wait_for_refresh, ulong timeout);
bool close_cached_connection_tables(THD *thd, LEX_STRING *connect_string);
void close_all_tables_for_name(THD *thd, TABLE_SHARE *share,
- ha_extra_function extra);
+ ha_extra_function extra,
+ TABLE *skip_table);
OPEN_TABLE_LIST *list_open_tables(THD *thd, const char *db, const char *wild);
-void tdc_remove_table(THD *thd, enum_tdc_remove_table_type remove_type,
- const char *db, const char *table_name,
- bool has_lock);
bool tdc_open_view(THD *thd, TABLE_LIST *table_list, const char *alias,
- char *cache_key, uint cache_key_length,
+ const char *cache_key, uint cache_key_length,
MEM_ROOT *mem_root, uint flags);
-void tdc_flush_unused_tables();
+
+static inline bool tdc_open_view(THD *thd, TABLE_LIST *table_list,
+ const char *alias, MEM_ROOT *mem_root,
+ uint flags)
+{
+ const char *key;
+ uint key_length= get_table_def_key(table_list, &key);
+ return tdc_open_view(thd, table_list, alias, key, key_length, mem_root, flags);
+}
+
TABLE *find_table_for_mdl_upgrade(THD *thd, const char *db,
const char *table_name,
bool no_error);
void mark_tmp_table_for_reuse(TABLE *table);
-bool check_if_table_exists(THD *thd, TABLE_LIST *table, bool fast_check,
- bool *exists);
+
int update_virtual_fields(THD *thd, TABLE *table,
enum enum_vcol_update_mode vcol_update_mode= VCOL_UPDATE_FOR_READ);
int dynamic_column_error_message(enum_dyncol_func_result rc);
-extern TABLE *unused_tables;
+/* open_and_lock_tables with optional derived handling */
+int open_and_lock_tables_derived(THD *thd, TABLE_LIST *tables, bool derived);
+
+extern "C" int simple_raw_key_cmp(void* arg, const void* key1,
+ const void* key2);
+extern "C" int count_distinct_walk(void *elem, element_count count, void *arg);
+int simple_str_key_cmp(void* arg, uchar* key1, uchar* key2);
+
extern Item **not_found_item;
extern Field *not_found_field;
extern Field *view_ref_found;
-extern HASH table_def_cache;
/**
clean/setup table fields and map.
@@ -477,11 +451,6 @@ class Lock_tables_prelocking_strategy : public DML_prelocking_strategy
class Alter_table_prelocking_strategy : public Prelocking_strategy
{
public:
-
- Alter_table_prelocking_strategy(Alter_info *alter_info)
- : m_alter_info(alter_info)
- {}
-
virtual bool handle_routine(THD *thd, Query_tables_list *prelocking_ctx,
Sroutine_hash_entry *rt, sp_head *sp,
bool *need_prelocking);
@@ -489,9 +458,6 @@ public:
TABLE_LIST *table_list, bool *need_prelocking);
virtual bool handle_view(THD *thd, Query_tables_list *prelocking_ctx,
TABLE_LIST *table_list, bool *need_prelocking);
-
-private:
- Alter_info *m_alter_info;
};
@@ -503,7 +469,6 @@ open_tables(THD *thd, TABLE_LIST **tables, uint *counter, uint flags)
return open_tables(thd, tables, counter, flags, &prelocking_strategy);
}
-
inline TABLE *open_n_lock_single_table(THD *thd, TABLE_LIST *table_l,
thr_lock_type lock_type, uint flags)
{
@@ -525,6 +490,8 @@ inline bool open_and_lock_tables(THD *thd, TABLE_LIST *tables,
}
+bool restart_trans_for_tables(THD *thd, TABLE_LIST *table);
+
/**
A context of open_tables() function, used to recover
from a failed open_table() or open_routine() attempt.
@@ -616,6 +583,30 @@ private:
/**
+ Check if a TABLE_LIST instance represents a pre-opened temporary table.
+*/
+
+inline bool is_temporary_table(TABLE_LIST *tl)
+{
+ if (tl->view || tl->schema_table)
+ return FALSE;
+
+ if (!tl->table)
+ return FALSE;
+
+ /*
+ NOTE: 'table->s' might be NULL for specially constructed TABLE
+ instances. See SHOW TRIGGERS for example.
+ */
+
+ if (!tl->table->s)
+ return FALSE;
+
+ return tl->table->s->tmp_table != NO_TMP_TABLE;
+}
+
+
+/**
This internal handler is used to trap ER_NO_SUCH_TABLE.
*/
@@ -629,9 +620,9 @@ public:
bool handle_condition(THD *thd,
uint sql_errno,
const char* sqlstate,
- MYSQL_ERROR::enum_warning_level level,
+ Sql_condition::enum_warning_level level,
const char* msg,
- MYSQL_ERROR ** cond_hdl);
+ Sql_condition ** cond_hdl);
/**
Returns TRUE if one or more ER_NO_SUCH_TABLE errors have been
diff --git a/sql/sql_binlog.cc b/sql/sql_binlog.cc
index 420bd0eb2f0..f0465cdf5bf 100644
--- a/sql/sql_binlog.cc
+++ b/sql/sql_binlog.cc
@@ -14,6 +14,7 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+#include <my_global.h>
#include "sql_priv.h"
#include "sql_binlog.h"
#include "sql_parse.h" // check_global_access
@@ -80,6 +81,8 @@ void mysql_client_binlog_statement(THD* thd)
my_bool have_fd_event= TRUE;
int err;
Relay_log_info *rli;
+ rpl_group_info *rgi;
+
rli= thd->rli_fake;
if (!rli)
{
@@ -95,6 +98,9 @@ void mysql_client_binlog_statement(THD* thd)
new Format_description_log_event(4);
have_fd_event= FALSE;
}
+ if (!(rgi= thd->rgi_fake))
+ rgi= thd->rgi_fake= new rpl_group_info(rli);
+ rgi->thd= thd;
const char *error= 0;
char *buf= (char *) my_malloc(decoded_len, MYF(MY_WME));
@@ -111,14 +117,15 @@ void mysql_client_binlog_statement(THD* thd)
goto end;
}
- rli->sql_thd= thd;
+ rli->sql_driver_thd= thd;
rli->no_storage= TRUE;
for (char const *strptr= thd->lex->comment.str ;
strptr < thd->lex->comment.str + thd->lex->comment.length ; )
{
char const *endptr= 0;
- int bytes_decoded= base64_decode(strptr, coded_len, buf, &endptr);
+ int bytes_decoded= base64_decode(strptr, coded_len, buf, &endptr,
+ MY_BASE64_DECODE_ALLOW_MULTIPLE_CHUNKS);
#ifndef HAVE_valgrind
/*
@@ -232,7 +239,7 @@ void mysql_client_binlog_statement(THD* thd)
(ev->flags & LOG_EVENT_SKIP_REPLICATION_F ?
OPTION_SKIP_REPLICATION : 0);
- err= ev->apply_event(rli);
+ err= ev->apply_event(rgi);
thd->variables.option_bits=
(thd->variables.option_bits & ~OPTION_SKIP_REPLICATION) |
@@ -267,7 +274,7 @@ void mysql_client_binlog_statement(THD* thd)
end:
thd->variables.option_bits= thd_options;
- rli->slave_close_thread_tables(thd);
+ rgi->slave_close_thread_tables(thd);
my_free(buf);
DBUG_VOID_RETURN;
}
diff --git a/sql/sql_bitmap.h b/sql/sql_bitmap.h
index d286d8e1ef0..55b2d7eefd9 100644
--- a/sql/sql_bitmap.h
+++ b/sql/sql_bitmap.h
@@ -1,4 +1,5 @@
-/* Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
+/* Copyright (c) 2003, 2013, Oracle and/or its affiliates
+ Copyright (c) 2009, 2013, Monty Program Ab.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -33,7 +34,7 @@ public:
Bitmap() { init(); }
Bitmap(const Bitmap& from) { *this=from; }
explicit Bitmap(uint prefix_to_set) { init(prefix_to_set); }
- void init() { bitmap_init(&map, buffer, default_width, 0); }
+ void init() { my_bitmap_init(&map, buffer, default_width, 0); }
void init(uint prefix_to_set) { init(); set_prefix(prefix_to_set); }
uint length() const { return default_width; }
Bitmap& operator=(const Bitmap& map2)
@@ -51,7 +52,7 @@ public:
void intersect(ulonglong map2buff)
{
MY_BITMAP map2;
- bitmap_init(&map2, (uint32 *)&map2buff, sizeof(ulonglong)*8, 0);
+ my_bitmap_init(&map2, (uint32 *)&map2buff, sizeof(ulonglong)*8, 0);
bitmap_intersect(&map, &map2);
}
/* Use highest bit for all bits above sizeof(ulonglong)*8. */
@@ -60,7 +61,7 @@ public:
intersect(map2buff);
if (map.n_bits > sizeof(ulonglong) * 8)
bitmap_set_above(&map, sizeof(ulonglong),
- test(map2buff & (LL(1) << (sizeof(ulonglong) * 8 - 1))));
+ MY_TEST(map2buff & (1LL << (sizeof(ulonglong) * 8 - 1))));
}
void subtract(Bitmap& map2) { bitmap_subtract(&map, &map2.map); }
void merge(Bitmap& map2) { bitmap_union(&map, &map2.map); }
@@ -155,7 +156,7 @@ public:
void intersect_extended(ulonglong map2) { map&= map2; }
void subtract(Bitmap<64>& map2) { map&= ~map2.map; }
void merge(Bitmap<64>& map2) { map|= map2.map; }
- bool is_set(uint n) const { return test(map & (((ulonglong)1) << n)); }
+ bool is_set(uint n) const { return MY_TEST(map & (((ulonglong) 1) << n)); }
bool is_prefix(uint n) const { return map == (((ulonglong)1) << n)-1; }
bool is_clear_all() const { return map == (ulonglong)0; }
bool is_set_all() const { return map == ~(ulonglong)0; }
diff --git a/sql/sql_bootstrap.cc b/sql/sql_bootstrap.cc
new file mode 100644
index 00000000000..30d03029ce6
--- /dev/null
+++ b/sql/sql_bootstrap.cc
@@ -0,0 +1,119 @@
+/* Copyright (c) 2010, Oracle and/or its affiliates.
+
+ 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,
+ 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */
+
+
+#include <my_global.h>
+#include <ctype.h>
+#include <string.h>
+#include "sql_bootstrap.h"
+
+int read_bootstrap_query(char *query, int *query_length,
+ fgets_input_t input, fgets_fn_t fgets_fn, int *error)
+{
+ char line_buffer[MAX_BOOTSTRAP_LINE_SIZE];
+ const char *line;
+ int len;
+ int query_len= 0;
+ int fgets_error= 0;
+ *error= 0;
+
+ for ( ; ; )
+ {
+ line= (*fgets_fn)(line_buffer, sizeof(line_buffer), input, &fgets_error);
+
+ if (error)
+ *error= fgets_error;
+
+ if (fgets_error != 0)
+ return READ_BOOTSTRAP_ERROR;
+
+ if (line == NULL)
+ return (query_len == 0) ? READ_BOOTSTRAP_EOF : READ_BOOTSTRAP_ERROR;
+
+ len= strlen(line);
+
+ /*
+ Remove trailing whitespace characters.
+ This assumes:
+ - no multibyte encoded character can be found at the very end of a line,
+ - whitespace characters from the "C" locale only.
+ which is sufficient for the kind of queries found
+ in the bootstrap scripts.
+ */
+ while (len && (isspace(line[len - 1])))
+ len--;
+ /*
+ Cleanly end the string, so we don't have to test len > x
+ all the time before reading line[x], in the code below.
+ */
+ line_buffer[len]= '\0';
+
+ /* Skip blank lines */
+ if (len == 0)
+ continue;
+
+ /* Skip # comments */
+ if (line[0] == '#')
+ continue;
+
+ /* Skip -- comments */
+ if ((line[0] == '-') && (line[1] == '-'))
+ continue;
+
+ /* Skip delimiter, ignored. */
+ if (strncmp(line, "delimiter", 9) == 0)
+ continue;
+
+ /* Append the current line to a multi line query. If the new line will make
+ the query too long, preserve the partial line to provide context for the
+ error message.
+ */
+ if (query_len + len + 1 >= MAX_BOOTSTRAP_QUERY_SIZE)
+ {
+ int new_len= MAX_BOOTSTRAP_QUERY_SIZE - query_len - 1;
+ if ((new_len > 0) && (query_len < MAX_BOOTSTRAP_QUERY_SIZE))
+ {
+ memcpy(query + query_len, line, new_len);
+ query_len+= new_len;
+ }
+ query[query_len]= '\0';
+ *query_length= query_len;
+ return READ_BOOTSTRAP_QUERY_SIZE;
+ }
+
+ if (query_len != 0)
+ {
+ /*
+ Append a \n to the current line, if any,
+ to preserve the intended presentation.
+ */
+ query[query_len++]= '\n';
+ }
+ memcpy(query + query_len, line, len);
+ query_len+= len;
+
+ if (line[len - 1] == ';')
+ {
+ /*
+ The last line is terminated by ';'.
+ Return the query found.
+ */
+ query[query_len]= '\0';
+ *query_length= query_len;
+ return READ_BOOTSTRAP_SUCCESS;
+ }
+ }
+}
+
diff --git a/sql/sql_bootstrap.h b/sql/sql_bootstrap.h
new file mode 100644
index 00000000000..b8a302a8646
--- /dev/null
+++ b/sql/sql_bootstrap.h
@@ -0,0 +1,47 @@
+/* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+
+ 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,
+ 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */
+
+
+#ifndef SQL_BOOTSTRAP_H
+#define SQL_BOOTSTRAP_H
+
+/**
+ The maximum size of a bootstrap query.
+ Increase this size if parsing a longer query during bootstrap is necessary.
+ The longest query in use depends on the documentation content,
+ see the file fill_help_tables.sql
+*/
+#define MAX_BOOTSTRAP_QUERY_SIZE 20000
+/**
+ The maximum size of a bootstrap query, expressed in a single line.
+ Do not increase this size, use the multiline syntax instead.
+*/
+#define MAX_BOOTSTRAP_LINE_SIZE 20000
+#define MAX_BOOTSTRAP_ERROR_LEN 256
+
+#define READ_BOOTSTRAP_SUCCESS 0
+#define READ_BOOTSTRAP_EOF 1
+#define READ_BOOTSTRAP_ERROR 2
+#define READ_BOOTSTRAP_QUERY_SIZE 3
+
+typedef void *fgets_input_t;
+typedef char * (*fgets_fn_t)(char *, size_t, fgets_input_t, int *error);
+
+int read_bootstrap_query(char *query, int *query_length,
+ fgets_input_t input, fgets_fn_t fgets_fn, int *error);
+
+#endif
+
+
diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc
index 8794cdb2a45..2e9e90346fb 100644
--- a/sql/sql_cache.cc
+++ b/sql/sql_cache.cc
@@ -11,8 +11,8 @@
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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+ along with this program; if not, write to the Free Software Foundation,
+ 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */
/*
Description of the query cache:
@@ -328,7 +328,7 @@ TODO list:
(This could be done with almost no speed penalty)
*/
-#include "my_global.h" /* NO_EMBEDDED_ACCESS_CHECKS */
+#include <my_global.h> /* NO_EMBEDDED_ACCESS_CHECKS */
#include "sql_priv.h"
#include "sql_cache.h"
#include "sql_parse.h" // check_table_access
@@ -336,6 +336,7 @@ TODO list:
#include "sql_acl.h" // SELECT_ACL
#include "sql_base.h" // TMP_TABLE_KEY_EXTRA
#include "debug_sync.h" // DEBUG_SYNC
+#include "sql_table.h"
#ifdef HAVE_QUERY_CACHE
#include <m_ctype.h>
#include <my_dir.h>
@@ -345,6 +346,7 @@ TODO list:
#include "probes_mysql.h"
#include "log_slow.h"
#include "transaction.h"
+#include "strfunc.h"
const uchar *query_state_map;
@@ -412,23 +414,28 @@ const uchar *query_state_map;
struct Query_cache_wait_state
{
THD *m_thd;
- const char *m_proc_info;
+ PSI_stage_info m_old_stage;
+ const char *m_func;
+ const char *m_file;
+ int m_line;
Query_cache_wait_state(THD *thd, const char *func,
const char *file, unsigned int line)
: m_thd(thd),
- m_proc_info(NULL)
+ m_old_stage(),
+ m_func(func), m_file(file), m_line(line)
{
if (m_thd)
- m_proc_info= set_thd_proc_info(m_thd,
- "Waiting for query cache lock",
- func, file, line);
+ set_thd_stage_info(m_thd,
+ &stage_waiting_for_query_cache_lock,
+ &m_old_stage,
+ m_func, m_file, m_line);
}
~Query_cache_wait_state()
{
if (m_thd)
- set_thd_proc_info(m_thd, m_proc_info, NULL, NULL, 0);
+ set_thd_stage_info(m_thd, &m_old_stage, NULL, m_func, m_file, m_line);
}
};
@@ -1038,8 +1045,8 @@ void query_cache_insert(const char *packet, ulong length,
/*
Current_thd can be NULL when a new connection is immediately ended
due to "Too many connections". thd->store_globals() has not been
- called at this time and hence my_pthread_setspecific_ptr(THR_THD,
- this) has not been called for this thread.
+ called at this time and hence set_current_thd(this) has not been
+ called for this thread.
*/
if (!thd)
@@ -1143,7 +1150,7 @@ Query_cache::abort(Query_cache_tls *query_cache_tls)
if (query_block)
{
thd= current_thd;
- thd_proc_info(thd, "storing result in query cache");
+ THD_STAGE_INFO(thd, stage_storing_result_in_query_cache);
DUMP(this);
BLOCK_LOCK_WR(query_block);
// The following call will remove the lock on query_block
@@ -1170,7 +1177,7 @@ void Query_cache::end_of_result(THD *thd)
DBUG_VOID_RETURN;
/* Ensure that only complete results are cached. */
- DBUG_ASSERT(thd->stmt_da->is_eof());
+ DBUG_ASSERT(thd->get_stmt_da()->is_eof());
if (thd->killed)
{
@@ -1194,7 +1201,7 @@ void Query_cache::end_of_result(THD *thd)
suitable size if needed and setting block type. Since this is the last
block, the writer should be dropped.
*/
- thd_proc_info(thd, "storing result in query cache");
+ THD_STAGE_INFO(thd, stage_storing_result_in_query_cache);
DUMP(this);
BLOCK_LOCK_WR(query_block);
Query_cache_query *header= query_block->query();
@@ -1218,7 +1225,7 @@ void Query_cache::end_of_result(THD *thd)
}
last_result_block= header->result()->prev;
allign_size= ALIGN_SIZE(last_result_block->used);
- len= max(query_cache.min_allocation_unit, allign_size);
+ len= MY_MAX(query_cache.min_allocation_unit, allign_size);
if (last_result_block->length >= query_cache.min_allocation_unit + len)
query_cache.split_block(last_result_block,len);
@@ -1398,9 +1405,9 @@ void Query_cache::store_query(THD *thd, TABLE_LIST *tables_used)
Query_cache_query_flags flags;
// fill all gaps between fields with 0 to get repeatable key
bzero(&flags, QUERY_CACHE_FLAGS_SIZE);
- flags.client_long_flag= test(thd->client_capabilities & CLIENT_LONG_FLAG);
- flags.client_protocol_41= test(thd->client_capabilities &
- CLIENT_PROTOCOL_41);
+ flags.client_long_flag= MY_TEST(thd->client_capabilities & CLIENT_LONG_FLAG);
+ flags.client_protocol_41= MY_TEST(thd->client_capabilities &
+ CLIENT_PROTOCOL_41);
/*
Protocol influences result format, so statement results in the binary
protocol (COM_EXECUTE) cannot be served to statements asking for results
@@ -1409,10 +1416,10 @@ void Query_cache::store_query(THD *thd, TABLE_LIST *tables_used)
flags.protocol_type= (unsigned int) thd->protocol->type();
/* PROTOCOL_LOCAL results are not cached. */
DBUG_ASSERT(flags.protocol_type != (unsigned int) Protocol::PROTOCOL_LOCAL);
- flags.more_results_exists= test(thd->server_status &
- SERVER_MORE_RESULTS_EXISTS);
+ flags.more_results_exists= MY_TEST(thd->server_status &
+ SERVER_MORE_RESULTS_EXISTS);
flags.in_trans= thd->in_active_multi_stmt_transaction();
- flags.autocommit= test(thd->server_status & SERVER_STATUS_AUTOCOMMIT);
+ flags.autocommit= MY_TEST(thd->server_status & SERVER_STATUS_AUTOCOMMIT);
flags.pkt_nr= net->pkt_nr;
flags.character_set_client_num=
thd->variables.character_set_client->number;
@@ -1634,6 +1641,41 @@ send_data_in_chunks(NET *net, const uchar *packet, ulong len)
#endif
+/**
+ Build a normalized table name suitable for query cache engine callback
+
+ This consist of normalized directory '/' normalized_file_name
+ followed by suffix.
+ Suffix is needed for partitioned tables.
+*/
+
+size_t build_normalized_name(char *buff, size_t bufflen,
+ const char *db, size_t db_len,
+ const char *table_name, size_t table_len,
+ size_t suffix_len)
+{
+ uint errors;
+ size_t length;
+ char *pos= buff, *end= buff+bufflen;
+ DBUG_ENTER("build_normalized_name");
+
+ (*pos++)= FN_LIBCHAR;
+ length= strconvert(system_charset_info, db, db_len,
+ &my_charset_filename, pos, bufflen - 3,
+ &errors);
+ pos+= length;
+ (*pos++)= FN_LIBCHAR;
+ length= strconvert(system_charset_info, table_name, table_len,
+ &my_charset_filename, pos, (uint) (end - pos),
+ &errors);
+ pos+= length;
+ if (pos + suffix_len < end)
+ pos= strmake(pos, table_name + table_len, suffix_len);
+
+ DBUG_RETURN((size_t) (pos - buff));
+}
+
+
/*
Check if the query is in the cache. If it was cached, send it
to the user.
@@ -1852,18 +1894,18 @@ Query_cache::send_result_to_client(THD *thd, char *org_sql, uint query_length)
DBUG_PRINT("qcache", ("No active database"));
}
- thd_proc_info(thd, "checking query cache for query");
+ THD_STAGE_INFO(thd, stage_checking_query_cache_for_query);
// fill all gaps between fields with 0 to get repeatable key
bzero(&flags, QUERY_CACHE_FLAGS_SIZE);
- flags.client_long_flag= test(thd->client_capabilities & CLIENT_LONG_FLAG);
- flags.client_protocol_41= test(thd->client_capabilities &
- CLIENT_PROTOCOL_41);
+ flags.client_long_flag= MY_TEST(thd->client_capabilities & CLIENT_LONG_FLAG);
+ flags.client_protocol_41= MY_TEST(thd->client_capabilities &
+ CLIENT_PROTOCOL_41);
flags.protocol_type= (unsigned int) thd->protocol->type();
- flags.more_results_exists= test(thd->server_status &
- SERVER_MORE_RESULTS_EXISTS);
+ flags.more_results_exists= MY_TEST(thd->server_status &
+ SERVER_MORE_RESULTS_EXISTS);
flags.in_trans= thd->in_active_multi_stmt_transaction();
- flags.autocommit= test(thd->server_status & SERVER_STATUS_AUTOCOMMIT);
+ flags.autocommit= MY_TEST(thd->server_status & SERVER_STATUS_AUTOCOMMIT);
flags.pkt_nr= thd->net.pkt_nr;
flags.character_set_client_num= thd->variables.character_set_client->number;
flags.character_set_results_num=
@@ -1963,7 +2005,7 @@ lookup:
}
// Check access;
- thd_proc_info(thd, "checking privileges on cached query");
+ THD_STAGE_INFO(thd, stage_checking_privileges_on_cached_query);
block_table= query_block->table(0);
block_table_end= block_table+query_block->n_tables;
for (; block_table != block_table_end; block_table++)
@@ -2027,40 +2069,55 @@ lookup:
}
#endif /*!NO_EMBEDDED_ACCESS_CHECKS*/
engine_data= table->engine_data();
- if (table->callback() &&
- !(*table->callback())(thd, table->db(),
- table->key_length(),
- &engine_data))
+ if (table->callback())
{
- DBUG_PRINT("qcache", ("Handler does not allow caching for %s.%s",
- table_list.db, table_list.alias));
- BLOCK_UNLOCK_RD(query_block);
- if (engine_data != table->engine_data())
- {
- DBUG_PRINT("qcache",
- ("Handler require invalidation queries of %s.%s %lu-%lu",
- table_list.db, table_list.alias,
- (ulong) engine_data, (ulong) table->engine_data()));
- invalidate_table_internal(thd,
- (uchar *) table->db(),
- table->key_length());
- }
- else
+ char qcache_se_key_name[FN_REFLEN + 10];
+ uint qcache_se_key_len, db_length= strlen(table->db());
+ engine_data= table->engine_data();
+
+ qcache_se_key_len= build_normalized_name(qcache_se_key_name,
+ sizeof(qcache_se_key_name),
+ table->db(),
+ db_length,
+ table->table(),
+ table->key_length() -
+ db_length - 2 -
+ table->suffix_length(),
+ table->suffix_length());
+
+ if (!(*table->callback())(thd, qcache_se_key_name,
+ qcache_se_key_len, &engine_data))
{
+ DBUG_PRINT("qcache", ("Handler does not allow caching for %.*s",
+ qcache_se_key_len, qcache_se_key_name));
+ BLOCK_UNLOCK_RD(query_block);
+ if (engine_data != table->engine_data())
+ {
+ DBUG_PRINT("qcache",
+ ("Handler require invalidation queries of %.*s %lu-%lu",
+ qcache_se_key_len, qcache_se_key_name,
+ (ulong) engine_data, (ulong) table->engine_data()));
+ invalidate_table_internal(thd,
+ (uchar *) table->db(),
+ table->key_length());
+ }
+ else
+ {
+ /*
+ As this can change from call to call, don't reset set
+ thd->lex->safe_to_cache_query
+ */
+ thd->query_cache_is_applicable= 0; // Query can't be cached
+ }
/*
- As this can change from call to call, don't reset set
- thd->lex->safe_to_cache_query
+ End the statement transaction potentially started by engine.
+ Currently our engines do not request rollback from callbacks.
+ If this is going to change code needs to be reworked.
*/
- thd->query_cache_is_applicable= 0; // Query can't be cached
+ DBUG_ASSERT(! thd->transaction_rollback_request);
+ trans_rollback_stmt(thd);
+ goto err_unlock; // Parse query
}
- /*
- End the statement transaction potentially started by engine.
- Currently our engines do not request rollback from callbacks.
- If this is going to change code needs to be reworked.
- */
- DBUG_ASSERT(! thd->transaction_rollback_request);
- trans_rollback_stmt(thd);
- goto err_unlock; // Parse query
}
else
DBUG_PRINT("qcache", ("handler allow caching %s,%s",
@@ -2074,7 +2131,7 @@ lookup:
Send cached result to client
*/
#ifndef EMBEDDED_LIBRARY
- thd_proc_info(thd, "sending cached result to client");
+ THD_STAGE_INFO(thd, stage_sending_cached_result_to_client);
do
{
DBUG_PRINT("qcache", ("Results (len: %lu used: %lu headers: %lu)",
@@ -2099,13 +2156,13 @@ lookup:
}
#endif /*!EMBEDDED_LIBRARY*/
- thd->sent_row_count= thd->limit_found_rows = query->found_rows();
+ thd->set_sent_row_count(thd->limit_found_rows = query->found_rows());
thd->status_var.last_query_cost= 0.0;
thd->query_plan_flags= (thd->query_plan_flags & ~QPLAN_QC_NO) | QPLAN_QC;
- if (!thd->sent_row_count)
+ if (!thd->get_sent_row_count())
status_var_increment(thd->status_var.empty_queries);
else
- status_var_add(thd->status_var.rows_sent, thd->sent_row_count);
+ status_var_add(thd->status_var.rows_sent, thd->get_sent_row_count());
/*
End the statement transaction potentially started by an
@@ -2114,8 +2171,8 @@ lookup:
response, we can't handle it anyway.
*/
(void) trans_commit_stmt(thd);
- if (!thd->stmt_da->is_set())
- thd->stmt_da->disable_status();
+ if (!thd->get_stmt_da()->is_set())
+ thd->get_stmt_da()->disable_status();
BLOCK_UNLOCK_RD(query_block);
MYSQL_QUERY_CACHE_HIT(thd->query(), (ulong) thd->limit_found_rows);
@@ -2179,7 +2236,7 @@ void Query_cache::invalidate(THD *thd, CHANGED_TABLE_LIST *tables_used)
for (; tables_used; tables_used= tables_used->next)
{
- thd_proc_info(thd, "invalidating query cache entries (table list)");
+ THD_STAGE_INFO(thd, stage_invalidating_query_cache_entries_table_list);
invalidate_table(thd, (uchar*) tables_used->key, tables_used->key_length);
DBUG_PRINT("qcache", ("db: %s table: %s", tables_used->key,
tables_used->key+
@@ -2208,7 +2265,7 @@ void Query_cache::invalidate_locked_for_write(THD *thd,
for (; tables_used; tables_used= tables_used->next_local)
{
- thd_proc_info(thd, "invalidating query cache entries (table)");
+ THD_STAGE_INFO(thd, stage_invalidating_query_cache_entries_table);
if (tables_used->lock_type >= TL_WRITE_ALLOW_WRITE &&
tables_used->table)
{
@@ -2267,6 +2324,8 @@ void Query_cache::invalidate(THD *thd, char *db)
if (is_disabled())
DBUG_VOID_RETURN;
+ DBUG_ASSERT(ok_for_lower_case_names(db));
+
bool restart= FALSE;
/*
Lock the query cache and queue all invalidation attempts to avoid
@@ -2336,6 +2395,9 @@ void Query_cache::invalidate_by_MyISAM_filename(const char *filename)
{
DBUG_ENTER("Query_cache::invalidate_by_MyISAM_filename");
+ if (is_disabled())
+ DBUG_VOID_RETURN;
+
/* Calculate the key outside the lock to make the lock shorter */
char key[MAX_DBKEY_LENGTH];
uint32 db_length;
@@ -2918,7 +2980,7 @@ Query_cache::write_block_data(ulong data_len, uchar* data,
DBUG_ENTER("Query_cache::write_block_data");
DBUG_PRINT("qcache", ("data: %ld, header: %ld, all header: %ld",
data_len, header_len, all_headers_len));
- Query_cache_block *block= allocate_block(max(align_len,
+ Query_cache_block *block= allocate_block(MY_MAX(align_len,
min_allocation_unit),1, 0);
if (block != 0)
{
@@ -2973,7 +3035,7 @@ Query_cache::append_result_data(Query_cache_block **current_block,
ulong append_min = get_min_append_result_data_size();
if (last_block_free_space < data_len &&
append_next_free_block(last_block,
- max(tail, append_min)))
+ MY_MAX(tail, append_min)))
last_block_free_space = last_block->length - last_block->used;
// If no space in last block (even after join) allocate new block
if (last_block_free_space < data_len)
@@ -3001,7 +3063,7 @@ Query_cache::append_result_data(Query_cache_block **current_block,
// Now finally write data to the last block
if (success && last_block_free_space > 0)
{
- ulong to_copy = min(data_len,last_block_free_space);
+ ulong to_copy = MY_MIN(data_len,last_block_free_space);
DBUG_PRINT("qcache", ("use free space %lub at block 0x%lx to copy %lub",
last_block_free_space, (ulong)last_block, to_copy));
memcpy((uchar*) last_block + last_block->used, data, to_copy);
@@ -3089,8 +3151,8 @@ inline ulong Query_cache::get_min_first_result_data_size()
if (queries_in_cache < QUERY_CACHE_MIN_ESTIMATED_QUERIES_NUMBER)
return min_result_data_size;
ulong avg_result = (query_cache_size - free_memory) / queries_in_cache;
- avg_result = min(avg_result, query_cache_limit);
- return max(min_result_data_size, avg_result);
+ avg_result = MY_MIN(avg_result, query_cache_limit);
+ return MY_MAX(min_result_data_size, avg_result);
}
inline ulong Query_cache::get_min_append_result_data_size()
@@ -3122,7 +3184,7 @@ my_bool Query_cache::allocate_data_chain(Query_cache_block **result_block,
ulong len= data_len + all_headers_len;
ulong align_len= ALIGN_SIZE(len);
- if (!(new_block= allocate_block(max(min_size, align_len),
+ if (!(new_block= allocate_block(MY_MAX(min_size, align_len),
min_result_data_size == 0,
all_headers_len + min_result_data_size)))
{
@@ -3131,7 +3193,7 @@ my_bool Query_cache::allocate_data_chain(Query_cache_block **result_block,
}
new_block->n_tables = 0;
- new_block->used = min(len, new_block->length);
+ new_block->used = MY_MIN(len, new_block->length);
new_block->type = Query_cache_block::RES_INCOMPLETE;
new_block->next = new_block->prev = new_block;
Query_cache_result *header = new_block->result();
@@ -3172,11 +3234,9 @@ void Query_cache::invalidate_table(THD *thd, TABLE_LIST *table_list)
invalidate_table(thd, table_list->table); // Table is open
else
{
- char key[MAX_DBKEY_LENGTH];
+ const char *key;
uint key_length;
-
- key_length= create_table_def_key(key, table_list->db,
- table_list->table_name);
+ key_length= get_table_def_key(table_list, &key);
// We don't store temporary tables => no key_length+=4 ...
invalidate_table(thd, (uchar *)key, key_length);
@@ -3289,18 +3349,17 @@ Query_cache::register_tables_from_list(THD *thd, TABLE_LIST *tables_used,
(*block_table)->n= n;
if (tables_used->view)
{
- char key[MAX_DBKEY_LENGTH];
+ const char *key;
uint key_length;
DBUG_PRINT("qcache", ("view: %s db: %s",
tables_used->view_name.str,
tables_used->view_db.str));
- key_length= create_table_def_key(key, tables_used->view_db.str,
- tables_used->view_name.str);
+ key_length= get_table_def_key(tables_used, &key);
/*
There are not callback function for for VIEWs
*/
if (!insert_table(key_length, key, (*block_table),
- tables_used->view_db.length,
+ tables_used->view_db.length, 0,
HA_CACHE_TBL_NONTRANSACT, 0, 0, TRUE))
DBUG_RETURN(0);
/*
@@ -3321,7 +3380,7 @@ Query_cache::register_tables_from_list(THD *thd, TABLE_LIST *tables_used,
if (!insert_table(tables_used->table->s->table_cache_key.length,
tables_used->table->s->table_cache_key.str,
(*block_table),
- tables_used->db_length,
+ tables_used->db_length, 0,
tables_used->table->file->table_cache_type(),
tables_used->callback_func,
tables_used->engine_data,
@@ -3371,7 +3430,7 @@ my_bool Query_cache::register_all_tables(THD *thd,
if (block_table->parent)
unlink_table(block_table);
}
- return test(n);
+ return MY_TEST(n);
}
@@ -3384,9 +3443,10 @@ my_bool Query_cache::register_all_tables(THD *thd,
*/
my_bool
-Query_cache::insert_table(uint key_len, char *key,
+Query_cache::insert_table(uint key_len, const char *key,
Query_cache_block_table *node,
- uint32 db_length, uint8 cache_type,
+ uint32 db_length, uint8 suffix_length_arg,
+ uint8 cache_type,
qc_engine_callback callback,
ulonglong engine_data,
my_bool hash)
@@ -3461,6 +3521,7 @@ Query_cache::insert_table(uint key_len, char *key,
char *db= header->db();
header->table(db + db_length + 1);
header->key_length(key_len);
+ header->suffix_length(suffix_length_arg);
header->type(cache_type);
header->callback(callback);
header->engine_data(engine_data);
@@ -3537,7 +3598,7 @@ Query_cache::allocate_block(ulong len, my_bool not_less, ulong min)
DBUG_PRINT("qcache", ("len %lu, not less %d, min %lu",
len, not_less,min));
- if (len >= min(query_cache_size, query_cache_limit))
+ if (len >= MY_MIN(query_cache_size, query_cache_limit))
{
DBUG_PRINT("qcache", ("Query cache hase only %lu memory and limit %lu",
query_cache_size, query_cache_limit));
@@ -4069,7 +4130,7 @@ Query_cache::is_cacheable(THD *thd, LEX *lex,
(long) OPTION_TO_QUERY_CACHE,
(long) lex->select_lex.options,
(int) thd->variables.query_cache_type,
- (uint) test(qc_is_able_to_intercept_result(thd))));
+ (uint) MY_TEST(qc_is_able_to_intercept_result(thd))));
DBUG_RETURN(0);
}
@@ -4098,13 +4159,13 @@ my_bool Query_cache::ask_handler_allowance(THD *thd,
continue;
handler= table->file;
if (!handler->register_query_cache_table(thd,
- table->s->table_cache_key.str,
- table->s->table_cache_key.length,
+ table->s->normalized_path.str,
+ table->s->normalized_path.length,
&tables_used->callback_func,
&tables_used->engine_data))
{
- DBUG_PRINT("qcache", ("Handler does not allow caching for %s.%s",
- tables_used->db, tables_used->alias));
+ DBUG_PRINT("qcache", ("Handler does not allow caching for %s",
+ table->s->normalized_path.str));
/*
As this can change from call to call, don't reset set
thd->lex->safe_to_cache_query
@@ -4523,7 +4584,7 @@ uint Query_cache::filename_2_table_key (char *key, const char *path,
DBUG_PRINT("qcache", ("table '%-.*s.%s'", *db_length, dbname, filename));
DBUG_RETURN((uint) (strmake(strmake(key, dbname,
- min(*db_length, NAME_LEN)) + 1,
+ MY_MIN(*db_length, NAME_LEN)) + 1,
filename, NAME_LEN) - key) + 1);
}
diff --git a/sql/sql_cache.h b/sql/sql_cache.h
index f35ac889b23..69520d668ac 100644
--- a/sql/sql_cache.h
+++ b/sql/sql_cache.h
@@ -190,6 +190,7 @@ struct Query_cache_table
Query_cache_table() {} /* Remove gcc warning */
char *tbl;
uint32 key_len;
+ uint8 suffix_len; /* For partitioned tables */
uint8 table_type;
/* unique for every engine reference */
qc_engine_callback callback_func;
@@ -210,6 +211,8 @@ struct Query_cache_table
inline void table(char *table_arg) { tbl= table_arg; }
inline uint32 key_length() { return key_len; }
inline void key_length(uint32 len) { key_len= len; }
+ inline uint8 suffix_length() { return suffix_len; }
+ inline void suffix_length(uint8 len) { suffix_len= len; }
inline uint8 type() { return table_type; }
inline void type(uint8 t) { table_type= t; }
inline qc_engine_callback callback() { return callback_func; }
@@ -488,9 +491,10 @@ protected:
const char *packet,
ulong length,
unsigned pkt_nr);
- my_bool insert_table(uint key_len, char *key,
+ my_bool insert_table(uint key_len, const char *key,
Query_cache_block_table *node,
- uint32 db_length, uint8 cache_type,
+ uint32 db_length, uint8 suffix_length_arg,
+ uint8 cache_type,
qc_engine_callback callback,
ulonglong engine_data,
my_bool hash);
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index 5322f49768e..205bce08a12 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -28,13 +28,13 @@
#pragma implementation // gcc: Class implementation
#endif
-#include "my_global.h" /* NO_EMBEDDED_ACCESS_CHECKS */
+#include <my_global.h> /* NO_EMBEDDED_ACCESS_CHECKS */
#include "sql_priv.h"
-#include "unireg.h" // REQUIRED: for other includes
#include "sql_class.h"
#include "sql_cache.h" // query_cache_abort
#include "sql_base.h" // close_thread_tables
#include "sql_time.h" // date_time_format_copy
+#include "tztime.h" // MYSQL_TIME <-> my_time_t
#include "sql_acl.h" // NO_ACCESS,
// acl_getroot_no_password
#include "sql_base.h" // close_temporary_tables
@@ -62,8 +62,10 @@
#include "debug_sync.h"
#include "sql_parse.h" // is_update_query
#include "sql_callback.h"
+#include "lock.h"
#ifdef WITH_WSREP
#include "wsrep_mysqld.h"
+#include "wsrep_thd.h"
#endif
#include "sql_connect.h"
@@ -76,23 +78,6 @@ char empty_c_string[1]= {0}; /* used for not defined db */
const char * const THD::DEFAULT_WHERE= "field list";
-
-/*****************************************************************************
-** Instansiate templates
-*****************************************************************************/
-
-#ifdef HAVE_EXPLICIT_TEMPLATE_INSTANTIATION
-/* Used templates */
-template class List<Key>;
-template class List_iterator<Key>;
-template class List<Key_part_spec>;
-template class List_iterator<Key_part_spec>;
-template class List<Alter_drop>;
-template class List_iterator<Alter_drop>;
-template class List<Alter_column>;
-template class List_iterator<Alter_column>;
-#endif
-
/****************************************************************************
** User variables
****************************************************************************/
@@ -132,7 +117,8 @@ Key::Key(const Key &rhs, MEM_ROOT *mem_root)
columns(rhs.columns, mem_root),
name(rhs.name),
option_list(rhs.option_list),
- generated(rhs.generated)
+ generated(rhs.generated),
+ create_if_not_exists(rhs.create_if_not_exists)
{
list_copy_and_replace_each_value(columns, mem_root);
}
@@ -146,6 +132,7 @@ Key::Key(const Key &rhs, MEM_ROOT *mem_root)
Foreign_key::Foreign_key(const Foreign_key &rhs, MEM_ROOT *mem_root)
:Key(rhs,mem_root),
+ ref_db(rhs.ref_db),
ref_table(rhs.ref_table),
ref_columns(rhs.ref_columns,mem_root),
delete_opt(rhs.delete_opt),
@@ -370,29 +357,6 @@ void thd_set_thread_stack(THD *thd, char *stack_start)
}
/**
- Lock connection data for the set of connections this connection
- belongs to
-
- @param thd THD object
-*/
-void thd_lock_thread_count(THD *)
-{
- mysql_mutex_lock(&LOCK_thread_count);
-}
-
-/**
- Lock connection data for the set of connections this connection
- belongs to
-
- @param thd THD object
-*/
-void thd_unlock_thread_count(THD *)
-{
- mysql_cond_broadcast(&COND_thread_count);
- mysql_mutex_unlock(&LOCK_thread_count);
-}
-
-/**
Close the socket used by this connection
@param thd THD object
@@ -498,7 +462,7 @@ void thd_set_mysys_var(THD *thd, st_my_thread_var *mysys_var)
*/
my_socket thd_get_fd(THD *thd)
{
- return thd->net.vio->sd;
+ return mysql_socket_getfd(thd->net.vio->mysql_socket);
}
#endif
@@ -554,54 +518,103 @@ extern "C" int mysql_tmpfile(const char *prefix)
extern "C"
int thd_in_lock_tables(const THD *thd)
{
- return test(thd->in_lock_tables);
+ return MY_TEST(thd->in_lock_tables);
}
extern "C"
int thd_tablespace_op(const THD *thd)
{
- return test(thd->tablespace_op);
+ return MY_TEST(thd->tablespace_op);
}
-
extern "C"
-const char *set_thd_proc_info(THD *thd, const char *info,
+const char *set_thd_proc_info(THD *thd_arg, const char *info,
const char *calling_function,
const char *calling_file,
const unsigned int calling_line)
{
- if (!thd)
+ PSI_stage_info old_stage;
+ PSI_stage_info new_stage;
+
+ old_stage.m_key= 0;
+ old_stage.m_name= info;
+
+ set_thd_stage_info(thd_arg, & old_stage, & new_stage,
+ calling_function, calling_file, calling_line);
+
+ return new_stage.m_name;
+}
+
+extern "C"
+void set_thd_stage_info(void *thd_arg,
+ const PSI_stage_info *new_stage,
+ PSI_stage_info *old_stage,
+ const char *calling_func,
+ const char *calling_file,
+ const unsigned int calling_line)
+{
+ THD *thd= (THD*) thd_arg;
+ if (thd == NULL)
thd= current_thd;
- const char *old_info= thd->proc_info;
- DBUG_PRINT("proc_info", ("%s:%d %s", calling_file, calling_line, info));
+ thd->enter_stage(new_stage, old_stage, calling_func, calling_file,
+ calling_line);
+}
+
+void THD::enter_stage(const PSI_stage_info *new_stage,
+ PSI_stage_info *old_stage,
+ const char *calling_func,
+ const char *calling_file,
+ const unsigned int calling_line)
+{
+ DBUG_PRINT("THD::enter_stage", ("%s:%d", calling_file, calling_line));
+
+ if (old_stage != NULL)
+ {
+ old_stage->m_key= m_current_stage_key;
+ old_stage->m_name= proc_info;
+ }
+
+ if (new_stage != NULL)
+ {
+ const char *msg= new_stage->m_name;
#if defined(ENABLED_PROFILING)
- thd->profiling.status_change(info,
- calling_function, calling_file, calling_line);
+ profiling.status_change(msg, calling_func, calling_file, calling_line);
#endif
- thd->proc_info= info;
- return old_info;
+
+ m_current_stage_key= new_stage->m_key;
+ proc_info= msg;
+
+#ifdef HAVE_PSI_THREAD_INTERFACE
+ PSI_THREAD_CALL(set_thread_state)(msg);
+ MYSQL_SET_STAGE(m_current_stage_key, calling_file, calling_line);
+#endif
+ }
+ return;
}
-extern "C"
-const char* thd_enter_cond(MYSQL_THD thd, mysql_cond_t *cond,
- mysql_mutex_t *mutex, const char *msg)
+void thd_enter_cond(MYSQL_THD thd, mysql_cond_t *cond, mysql_mutex_t *mutex,
+ const PSI_stage_info *stage, PSI_stage_info *old_stage,
+ const char *src_function, const char *src_file,
+ int src_line)
{
if (!thd)
thd= current_thd;
- return thd->enter_cond(cond, mutex, msg);
+ return thd->enter_cond(cond, mutex, stage, old_stage, src_function, src_file,
+ src_line);
}
-extern "C"
-void thd_exit_cond(MYSQL_THD thd, const char *old_msg)
+void thd_exit_cond(MYSQL_THD thd, const PSI_stage_info *stage,
+ const char *src_function, const char *src_file,
+ int src_line)
{
if (!thd)
thd= current_thd;
- thd->exit_cond(old_msg);
+ thd->exit_cond(stage, src_function, src_file, src_line);
return;
}
@@ -647,6 +660,17 @@ void thd_set_ha_data(THD *thd, const struct handlerton *hton,
}
+/**
+ Allow storage engine to wakeup commits waiting in THD::wait_for_prior_commit.
+ @see thd_wakeup_subsequent_commits() definition in plugin.h
+*/
+extern "C"
+void thd_wakeup_subsequent_commits(THD *thd, int wakeup_error)
+{
+ thd->wakeup_subsequent_commits(wakeup_error);
+}
+
+
extern "C"
long long thd_test_options(const THD *thd, long long test_options)
{
@@ -666,9 +690,34 @@ int thd_tx_isolation(const THD *thd)
}
extern "C"
-void thd_inc_row_count(THD *thd)
+int thd_tx_is_read_only(const THD *thd)
{
- thd->warning_info->inc_current_row_for_warning();
+ return (int) thd->tx_read_only;
+}
+
+
+extern "C"
+{ /* Functions for thd_error_context_service */
+
+ const char *thd_get_error_message(const THD *thd)
+ {
+ return thd->get_stmt_da()->message();
+ }
+
+ uint thd_get_error_number(const THD *thd)
+ {
+ return thd->get_stmt_da()->sql_errno();
+ }
+
+ ulong thd_get_error_row(const THD *thd)
+ {
+ return thd->get_stmt_da()->current_row_for_warning();
+ }
+
+ void thd_inc_error_row(THD *thd)
+ {
+ thd->get_stmt_da()->inc_current_row_for_warning();
+ }
}
@@ -691,8 +740,9 @@ void thd_inc_row_count(THD *thd)
*/
extern "C"
-char *thd_security_context(THD *thd, char *buffer, unsigned int length,
- unsigned int max_query_len)
+char *thd_get_error_context_description(THD *thd, char *buffer,
+ unsigned int length,
+ unsigned int max_query_len)
{
String str(buffer, length, &my_charset_latin1);
const Security_context *sctx= &thd->main_security_ctx;
@@ -747,7 +797,7 @@ char *thd_security_context(THD *thd, char *buffer, unsigned int length,
if (max_query_len < 1)
len= thd->query_length();
else
- len= min(thd->query_length(), max_query_len);
+ len= MY_MIN(thd->query_length(), max_query_len);
str.append('\n');
str.append(thd->query(), len);
}
@@ -762,7 +812,7 @@ char *thd_security_context(THD *thd, char *buffer, unsigned int length,
was reallocated to a larger buffer to be able to fit.
*/
DBUG_ASSERT(buffer != NULL);
- length= min(str.length(), length-1);
+ length= MY_MIN(str.length(), length-1);
memcpy(buffer, str.c_ptr_quick(), length);
/* Make sure that the new string is null terminated */
buffer[length]= '\0';
@@ -770,7 +820,7 @@ char *thd_security_context(THD *thd, char *buffer, unsigned int length,
}
#ifdef WITH_WSREP
-extern "C" int wsrep_on(void *thd)
+extern int wsrep_on(void *thd)
{
return (int)(WSREP(((THD*)thd)));
}
@@ -853,11 +903,11 @@ extern "C" wsrep_ws_handle_t* wsrep_thd_ws_handle(THD *thd)
return &thd->wsrep_ws_handle;
}
-extern "C"void wsrep_thd_LOCK(THD *thd)
+extern "C" void wsrep_thd_LOCK(THD *thd)
{
mysql_mutex_lock(&thd->LOCK_wsrep_thd);
}
-extern "C"void wsrep_thd_UNLOCK(THD *thd)
+extern "C" void wsrep_thd_UNLOCK(THD *thd)
{
mysql_mutex_unlock(&thd->LOCK_wsrep_thd);
}
@@ -893,11 +943,10 @@ extern "C" void wsrep_thd_set_wsrep_last_query_id(THD *thd, query_id_t id)
{
thd->wsrep_last_query_id= id;
}
-extern "C" void wsrep_thd_awake(THD* bf_thd, THD *thd, my_bool signal)
+extern "C" void wsrep_thd_awake(THD *thd, my_bool signal)
{
if (signal)
{
- thd->wsrep_bf_thd = bf_thd;
mysql_mutex_lock(&thd->LOCK_thd_data);
thd->awake(KILL_QUERY);
mysql_mutex_unlock(&thd->LOCK_thd_data);
@@ -909,8 +958,16 @@ extern "C" void wsrep_thd_awake(THD* bf_thd, THD *thd, my_bool signal)
mysql_mutex_unlock(&LOCK_wsrep_replaying);
}
}
+extern "C" int wsrep_thd_retry_counter(THD *thd)
+{
+ return(thd->wsrep_retry_counter);
+}
+extern "C" bool wsrep_thd_skip_append_keys(THD *thd)
+{
+ return thd->wsrep_skip_append_keys;
+}
-extern "C" int
+extern int
wsrep_trx_order_before(void *thd1, void *thd2)
{
if (wsrep_thd_trx_seqno((THD*)thd1) < wsrep_thd_trx_seqno((THD*)thd2)) {
@@ -937,6 +994,21 @@ wsrep_trx_is_aborting(void *thd_ptr)
}
#endif
+#if MARIA_PLUGIN_INTERFACE_VERSION < 0x0200
+/**
+ TODO: This function is for API compatibility, remove it eventually.
+ All engines should switch to use thd_get_error_context_description()
+ plugin service function.
+*/
+extern "C"
+char *thd_security_context(THD *thd,
+ char *buffer, unsigned int length,
+ unsigned int max_query_len)
+{
+ return thd_get_error_context_description(thd, buffer, length, max_query_len);
+}
+#endif
+
/**
Implementation of Drop_table_error_handler::handle_condition().
The reason in having this implementation is to silence technical low-level
@@ -954,9 +1026,9 @@ wsrep_trx_is_aborting(void *thd_ptr)
bool Drop_table_error_handler::handle_condition(THD *thd,
uint sql_errno,
const char* sqlstate,
- MYSQL_ERROR::enum_warning_level level,
+ Sql_condition::enum_warning_level level,
const char* msg,
- MYSQL_ERROR ** cond_hdl)
+ Sql_condition ** cond_hdl)
{
*cond_hdl= NULL;
return ((sql_errno == EE_DELETE && my_errno == ENOENT) ||
@@ -971,7 +1043,7 @@ THD::THD()
#endif
:Statement(&main_lex, &main_mem_root, STMT_CONVENTIONAL_EXECUTION,
/* statement id */ 0),
- rli_fake(0), rli_slave(NULL),
+ rli_fake(0), rgi_fake(0), rgi_slave(NULL),
in_sub_stmt(0), log_all_errors(0),
binlog_unsafe_warning_flags(0),
binlog_table_maps(0),
@@ -981,10 +1053,12 @@ THD::THD()
first_successful_insert_id_in_prev_stmt_for_binlog(0),
first_successful_insert_id_in_cur_stmt(0),
stmt_depends_on_first_successful_insert_id_in_prev_stmt(FALSE),
- examined_row_count(0),
+ m_examined_row_count(0),
accessed_rows_and_keys(0),
- warning_info(&main_warning_info),
- stmt_da(&main_da),
+ m_digest(NULL),
+ m_statement_psi(NULL),
+ m_idle_psi(NULL),
+ thread_id(0),
global_disable_checkpoint(0),
failed_com_change_user(0),
is_fatal_error(0),
@@ -995,29 +1069,48 @@ THD::THD()
in_lock_tables(0),
bootstrap(0),
derived_tables_processing(FALSE),
+ waiting_on_group_commit(FALSE), has_waiter(FALSE),
spcont(NULL),
+ m_parser_state(NULL),
+#if defined(ENABLED_DEBUG_SYNC)
+ debug_sync_control(0),
+#endif /* defined(ENABLED_DEBUG_SYNC) */
+ wait_for_commit_ptr(0),
+ main_da(0, false, false),
+ m_stmt_da(&main_da)
#ifdef WITH_WSREP
+ ,
wsrep_applier(is_applier),
wsrep_applier_closing(FALSE),
wsrep_client_thread(0),
+ wsrep_po_handle(WSREP_PO_INITIALIZER),
+ wsrep_po_cnt(0),
+ wsrep_po_in_trans(FALSE),
wsrep_apply_format(0),
wsrep_apply_toi(false),
+ wsrep_skip_append_keys(false)
#endif
- m_parser_state(NULL),
-#if defined(ENABLED_DEBUG_SYNC)
- debug_sync_control(0),
-#endif /* defined(ENABLED_DEBUG_SYNC) */
- main_warning_info(0, false)
{
ulong tmp;
mdl_context.init(this);
/*
+ We set THR_THD to temporally point to this THD to register all the
+ variables that allocates memory for this THD
+ */
+ THD *old_THR_THD= current_thd;
+ set_current_thd(this);
+ status_var.memory_used= 0;
+ main_da.init();
+
+ /*
Pass nominal parameters to init_alloc_root only to ensure that
the destructor works OK in case of an error. The main_mem_root
will be re-initialized in init_for_queries().
*/
- init_sql_alloc(&main_mem_root, ALLOC_ROOT_MIN_BLOCK_SIZE, 0);
+ init_sql_alloc(&main_mem_root, ALLOC_ROOT_MIN_BLOCK_SIZE, 0,
+ MYF(MY_THREAD_SPECIFIC));
+
stmt_arena= this;
thread_stack= 0;
scheduler= thread_scheduler; // Will be fixed later
@@ -1036,9 +1129,10 @@ THD::THD()
col_access=0;
is_slave_error= thread_specific_used= FALSE;
my_hash_clear(&handler_tables_hash);
+ my_hash_clear(&ull_hash);
tmp_table=0;
cuted_fields= 0L;
- sent_row_count= 0L;
+ m_sent_row_count= 0L;
limit_found_rows= 0;
m_row_count_func= -1;
statement_id_counter= 0UL;
@@ -1052,9 +1146,10 @@ THD::THD()
progress.max_counter= 0;
current_linfo = 0;
slave_thread = 0;
+ connection_name.str= 0;
+ connection_name.length= 0;
+
bzero(&variables, sizeof(variables));
- thread_id= 0;
- one_shot_set= 0;
file_id = 0;
query_id= 0;
query_name_consts= 0;
@@ -1064,6 +1159,7 @@ THD::THD()
mysys_var=0;
binlog_evt_union.do_union= FALSE;
enable_slow_log= 0;
+ durability_property= HA_REGULAR_DURABILITY;
#ifndef DBUG_OFF
dbug_sentry=THD_SENTRY_MAGIC;
@@ -1072,8 +1168,8 @@ THD::THD()
mysql_audit_init_thd(this);
#endif
net.vio=0;
+ net.buff= 0;
client_capabilities= 0; // minimalistic client
- ull=0;
system_thread= NON_SYSTEM_THREAD;
cleanup_done= abort_on_warning= 0;
peer_port= 0; // For SHOW PROCESSLIST
@@ -1098,9 +1194,9 @@ THD::THD()
/* Variables with default values */
proc_info="login";
where= THD::DEFAULT_WHERE;
- server_id = ::server_id;
+ variables.server_id = global_system_variables.server_id;
slave_net = 0;
- command=COM_CONNECT;
+ m_command=COM_CONNECT;
*scramble= '\0';
#ifdef WITH_WSREP
@@ -1115,7 +1211,6 @@ THD::THD()
wsrep_consistency_check = NO_CONSISTENCY_CHECK;
wsrep_status_vars = 0;
wsrep_mysql_replicated = 0;
- wsrep_bf_thd = NULL;
wsrep_TOI_pre_query = NULL;
wsrep_TOI_pre_query_len = 0;
wsrep_sync_wait_gtid= WSREP_GTID_UNDEFINED;
@@ -1130,7 +1225,7 @@ THD::THD()
user_connect=(USER_CONN *)0;
my_hash_init(&user_vars, system_charset_info, USER_VARS_HASH_SIZE, 0, 0,
(my_hash_get_key) get_var_key,
- (my_hash_free_key) free_user_var, 0);
+ (my_hash_free_key) free_user_var, HASH_THREAD_SPECIFIC);
sp_proc_cache= NULL;
sp_func_cache= NULL;
@@ -1138,7 +1233,7 @@ THD::THD()
/* For user vars replication*/
if (opt_bin_log)
my_init_dynamic_array(&user_var_events,
- sizeof(BINLOG_USER_VAR_EVENT *), 16, 16);
+ sizeof(BINLOG_USER_VAR_EVENT *), 16, 16, MYF(0));
else
bzero((char*) &user_var_events, sizeof(user_var_events));
@@ -1148,7 +1243,14 @@ THD::THD()
protocol_binary.init(this);
tablespace_op=FALSE;
- tmp= sql_rnd_with_mutex();
+
+ /*
+ Initialize the random generator. We call my_rnd() without a lock as
+ it's not really critical if two threads modifies the structure at the
+ same time. We ensure that we have an unique number foreach thread
+ by adding the address of the stack.
+ */
+ tmp= (ulong) (my_rnd(&sql_rand) * 0xffffffff);
my_rnd_init(&rand, tmp + (ulong) &rand, tmp + (ulong) ::global_query_id);
substitute_null_with_insert_id = FALSE;
thr_lock_info_init(&lock_info); /* safety: will be reset after start */
@@ -1160,14 +1262,23 @@ THD::THD()
#endif /* WSREP_PROC_INFO */
#endif /* WITH_WSREP */
+ m_token_array= NULL;
+ if (max_digest_length > 0)
+ {
+ m_token_array= (unsigned char*) my_malloc(max_digest_length,
+ MYF(MY_WME|MY_THREAD_SPECIFIC));
+ }
+
m_internal_handler= NULL;
- m_binlog_invoker= FALSE;
+ m_binlog_invoker= INVOKER_NONE;
arena_for_cached_items= 0;
memset(&invoker_user, 0, sizeof(invoker_user));
memset(&invoker_host, 0, sizeof(invoker_host));
prepare_derived_at_open= FALSE;
create_tmp_table_for_derived= FALSE;
save_prep_leaf_list= FALSE;
+ /* Restore THR_THD */
+ set_current_thd(old_THR_THD);
}
@@ -1188,9 +1299,9 @@ void THD::push_internal_handler(Internal_error_handler *handler)
bool THD::handle_condition(uint sql_errno,
const char* sqlstate,
- MYSQL_ERROR::enum_warning_level level,
+ Sql_condition::enum_warning_level level,
const char* msg,
- MYSQL_ERROR ** cond_hdl)
+ Sql_condition ** cond_hdl)
{
if (!m_internal_handler)
{
@@ -1227,7 +1338,7 @@ void THD::raise_error(uint sql_errno)
const char* msg= ER(sql_errno);
(void) raise_condition(sql_errno,
NULL,
- MYSQL_ERROR::WARN_LEVEL_ERROR,
+ Sql_condition::WARN_LEVEL_ERROR,
msg);
}
@@ -1243,7 +1354,7 @@ void THD::raise_error_printf(uint sql_errno, ...)
va_end(args);
(void) raise_condition(sql_errno,
NULL,
- MYSQL_ERROR::WARN_LEVEL_ERROR,
+ Sql_condition::WARN_LEVEL_ERROR,
ebuff);
DBUG_VOID_RETURN;
}
@@ -1253,7 +1364,7 @@ void THD::raise_warning(uint sql_errno)
const char* msg= ER(sql_errno);
(void) raise_condition(sql_errno,
NULL,
- MYSQL_ERROR::WARN_LEVEL_WARN,
+ Sql_condition::WARN_LEVEL_WARN,
msg);
}
@@ -1269,7 +1380,7 @@ void THD::raise_warning_printf(uint sql_errno, ...)
va_end(args);
(void) raise_condition(sql_errno,
NULL,
- MYSQL_ERROR::WARN_LEVEL_WARN,
+ Sql_condition::WARN_LEVEL_WARN,
ebuff);
DBUG_VOID_RETURN;
}
@@ -1283,7 +1394,7 @@ void THD::raise_note(uint sql_errno)
const char* msg= ER(sql_errno);
(void) raise_condition(sql_errno,
NULL,
- MYSQL_ERROR::WARN_LEVEL_NOTE,
+ Sql_condition::WARN_LEVEL_NOTE,
msg);
DBUG_VOID_RETURN;
}
@@ -1302,24 +1413,25 @@ void THD::raise_note_printf(uint sql_errno, ...)
va_end(args);
(void) raise_condition(sql_errno,
NULL,
- MYSQL_ERROR::WARN_LEVEL_NOTE,
+ Sql_condition::WARN_LEVEL_NOTE,
ebuff);
DBUG_VOID_RETURN;
}
-MYSQL_ERROR* THD::raise_condition(uint sql_errno,
+Sql_condition* THD::raise_condition(uint sql_errno,
const char* sqlstate,
- MYSQL_ERROR::enum_warning_level level,
+ Sql_condition::enum_warning_level level,
const char* msg)
{
- MYSQL_ERROR *cond= NULL;
+ Diagnostics_area *da= get_stmt_da();
+ Sql_condition *cond= NULL;
DBUG_ENTER("THD::raise_condition");
if (!(variables.option_bits & OPTION_SQL_NOTES) &&
- (level == MYSQL_ERROR::WARN_LEVEL_NOTE))
+ (level == Sql_condition::WARN_LEVEL_NOTE))
DBUG_RETURN(NULL);
- warning_info->opt_clear_warning_info(query_id);
+ da->opt_clear_warning_info(query_id);
/*
TODO: replace by DBUG_ASSERT(sql_errno != 0) once all bugs similar to
@@ -1333,24 +1445,24 @@ MYSQL_ERROR* THD::raise_condition(uint sql_errno,
if (sqlstate == NULL)
sqlstate= mysql_errno_to_sqlstate(sql_errno);
- if ((level == MYSQL_ERROR::WARN_LEVEL_WARN) &&
+ if ((level == Sql_condition::WARN_LEVEL_WARN) &&
really_abort_on_warning())
{
/*
FIXME:
push_warning and strict SQL_MODE case.
*/
- level= MYSQL_ERROR::WARN_LEVEL_ERROR;
+ level= Sql_condition::WARN_LEVEL_ERROR;
killed= KILL_BAD_DATA;
}
switch (level)
{
- case MYSQL_ERROR::WARN_LEVEL_NOTE:
- case MYSQL_ERROR::WARN_LEVEL_WARN:
+ case Sql_condition::WARN_LEVEL_NOTE:
+ case Sql_condition::WARN_LEVEL_WARN:
got_warning= 1;
break;
- case MYSQL_ERROR::WARN_LEVEL_ERROR:
+ case Sql_condition::WARN_LEVEL_ERROR:
break;
default:
DBUG_ASSERT(FALSE);
@@ -1359,16 +1471,16 @@ MYSQL_ERROR* THD::raise_condition(uint sql_errno,
if (handle_condition(sql_errno, sqlstate, level, msg, &cond))
DBUG_RETURN(cond);
- if (level == MYSQL_ERROR::WARN_LEVEL_ERROR)
+ if (level == Sql_condition::WARN_LEVEL_ERROR)
{
mysql_audit_general(this, MYSQL_AUDIT_GENERAL_ERROR, sql_errno, msg);
is_slave_error= 1; // needed to catch query errors during replication
- if (! stmt_da->is_error())
+ if (!da->is_error())
{
set_row_count_func(-1);
- stmt_da->set_error_status(this, sql_errno, msg, sqlstate);
+ da->set_error_status(sql_errno, msg, sqlstate, cond);
}
}
@@ -1382,7 +1494,7 @@ MYSQL_ERROR* THD::raise_condition(uint sql_errno,
if (!(is_fatal_error && (sql_errno == EE_OUTOFMEMORY ||
sql_errno == ER_OUTOFMEMORY)))
{
- cond= warning_info->push_warning(this, sql_errno, sqlstate, level, msg);
+ cond= da->push_warning(this, sql_errno, sqlstate, level, msg);
}
DBUG_RETURN(cond);
}
@@ -1416,8 +1528,8 @@ LEX_STRING *thd_make_lex_string(THD *thd, LEX_STRING *lex_str,
const char *str, unsigned int size,
int allocate_lex_string)
{
- return thd->make_lex_string(lex_str, str, size,
- (bool) allocate_lex_string);
+ return allocate_lex_string ? thd->make_lex_string(str, size)
+ : thd->make_lex_string(lex_str, str, size);
}
extern "C"
@@ -1432,6 +1544,26 @@ void thd_get_xid(const MYSQL_THD thd, MYSQL_XID *xid)
*xid = *(MYSQL_XID *) &thd->transaction.xid_state.xid;
}
+
+extern "C"
+my_time_t thd_TIME_to_gmt_sec(MYSQL_THD thd, const MYSQL_TIME *ltime,
+ unsigned int *errcode)
+{
+ Time_zone *tz= thd ? thd->variables.time_zone :
+ global_system_variables.time_zone;
+ return tz->TIME_to_gmt_sec(ltime, errcode);
+}
+
+
+extern "C"
+void thd_gmt_sec_to_TIME(MYSQL_THD thd, MYSQL_TIME *ltime, my_time_t t)
+{
+ Time_zone *tz= thd ? thd->variables.time_zone :
+ global_system_variables.time_zone;
+ tz->gmt_sec_to_TIME(ltime, t);
+}
+
+
#ifdef _WIN32
extern "C" THD *_current_thd_noinline(void)
{
@@ -1444,6 +1576,7 @@ extern "C" THD *_current_thd_noinline(void)
void THD::init(void)
{
+ DBUG_ENTER("thd::init");
mysql_mutex_lock(&LOCK_global_system_variables);
plugin_thdvar_init(this);
/*
@@ -1452,7 +1585,14 @@ void THD::init(void)
avoid temporary tables replication failure.
*/
variables.pseudo_thread_id= thread_id;
+
+ variables.default_master_connection.str= default_master_connection_buff;
+ ::strmake(variables.default_master_connection.str,
+ global_system_variables.default_master_connection.str,
+ variables.default_master_connection.length);
+
mysql_mutex_unlock(&LOCK_global_system_variables);
+
server_status= SERVER_STATUS_AUTOCOMMIT;
if (variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES)
server_status|= SERVER_STATUS_NO_BACKSLASH_ESCAPES;
@@ -1464,11 +1604,14 @@ void THD::init(void)
TL_WRITE_LOW_PRIORITY :
TL_WRITE);
tx_isolation= (enum_tx_isolation) variables.tx_isolation;
+ tx_read_only= variables.tx_read_only;
update_charset();
reset_current_stmt_binlog_format_row();
- bzero((char *) &status_var, sizeof(status_var));
+ reset_binlog_local_stmt_filter();
+ set_status_var_init();
bzero((char *) &org_status_var, sizeof(org_status_var));
start_bytes_received= 0;
+ last_commit_gtid.seq_no= 0;
status_in_global= 0;
#ifdef WITH_WSREP
@@ -1479,13 +1622,11 @@ void THD::init(void)
wsrep_trx_meta.gtid= WSREP_GTID_UNDEFINED;
wsrep_trx_meta.depends_on= WSREP_SEQNO_UNDEFINED;
wsrep_converted_lock_session= false;
- //wsrep_retry_autocommit= ::wsrep_retry_autocommit;
wsrep_retry_counter= 0;
- wsrep_rli= NULL;
+ wsrep_rgi= NULL;
wsrep_PA_safe= true;
wsrep_consistency_check = NO_CONSISTENCY_CHECK;
wsrep_mysql_replicated = 0;
- wsrep_bf_thd = NULL;
wsrep_TOI_pre_query = NULL;
wsrep_TOI_pre_query_len = 0;
wsrep_sync_wait_gtid= WSREP_GTID_UNDEFINED;
@@ -1511,6 +1652,8 @@ void THD::init(void)
/* Initialize the Debug Sync Facility. See debug_sync.cc. */
debug_sync_init_thread(this);
#endif /* defined(ENABLED_DEBUG_SYNC) */
+ apc_target.init(&LOCK_thd_data);
+ DBUG_VOID_RETURN;
}
@@ -1649,8 +1792,6 @@ void THD::cleanup(void)
if (global_read_lock.is_acquired())
global_read_lock.unlock_global_read_lock(this);
- /* All metadata locks must have been released by now. */
- DBUG_ASSERT(!mdl_context.has_locks());
if (user_connect)
{
decrease_user_connections(user_connect);
@@ -1667,14 +1808,11 @@ void THD::cleanup(void)
sp_cache_clear(&sp_proc_cache);
sp_cache_clear(&sp_func_cache);
- if (ull)
- {
- mysql_mutex_lock(&LOCK_user_locks);
- item_user_lock_release(ull);
- mysql_mutex_unlock(&LOCK_user_locks);
- ull= NULL;
- }
+ mysql_ull_cleanup(this);
+ /* All metadata locks must have been released by now. */
+ DBUG_ASSERT(!mdl_context.has_locks());
+ apc_target.destroy();
cleanup_done=1;
DBUG_VOID_RETURN;
}
@@ -1682,8 +1820,16 @@ void THD::cleanup(void)
THD::~THD()
{
+ THD *orig_thd= current_thd;
THD_CHECK_SENTRY(this);
DBUG_ENTER("~THD()");
+
+ /*
+ In error cases, thd may not be current thd. We have to fix this so
+ that memory allocation counting is done correctly
+ */
+ set_current_thd(this);
+
/* Ensure that no one is using THD */
mysql_mutex_lock(&LOCK_thd_data);
mysql_mutex_unlock(&LOCK_thd_data);
@@ -1692,16 +1838,14 @@ THD::~THD()
mysql_mutex_lock(&LOCK_wsrep_thd);
mysql_mutex_unlock(&LOCK_wsrep_thd);
mysql_mutex_destroy(&LOCK_wsrep_thd);
- if (wsrep_rli) delete wsrep_rli;
- if (wsrep_status_vars) wsrep->stats_free(wsrep, wsrep_status_vars);
+ if (wsrep_rgi) delete wsrep_rgi;
+ wsrep_free_status(this);
#endif
/* Close connection */
#ifndef EMBEDDED_LIBRARY
if (net.vio)
- {
vio_delete(net.vio);
- net_end(&net);
- }
+ net_end(&net);
#endif
stmt_map.reset(); /* close all prepared statements */
if (!cleanup_done)
@@ -1712,7 +1856,6 @@ THD::~THD()
mysql_audit_release(this);
plugin_thdvar_cleanup(this);
- DBUG_PRINT("info", ("freeing security context"));
main_security_ctx.destroy();
my_free(db);
db= NULL;
@@ -1724,6 +1867,11 @@ THD::~THD()
dbug_sentry= THD_SENTRY_GONE;
#endif
#ifndef EMBEDDED_LIBRARY
+ if (rgi_fake)
+ {
+ delete rgi_fake;
+ rgi_fake= NULL;
+ }
if (rli_fake)
{
delete rli_fake;
@@ -1731,12 +1879,22 @@ THD::~THD()
}
mysql_audit_free_thd(this);
- if (rli_slave)
- rli_slave->cleanup_after_session();
+ if (rgi_slave)
+ rgi_slave->cleanup_after_session();
my_free(semisync_info);
#endif
free_root(&main_mem_root, MYF(0));
+ my_free(m_token_array);
+ main_da.free_memory();
+ if (status_var.memory_used != 0)
+ {
+ DBUG_PRINT("error", ("memory_used: %lld", status_var.memory_used));
+ SAFEMALLOC_REPORT_MEMORY(my_thread_dbug_id());
+ DBUG_ASSERT(status_var.memory_used == 0); // Ensure everything is freed
+ }
+
+ set_current_thd(orig_thd == this ? 0 : orig_thd);
DBUG_VOID_RETURN;
}
@@ -1834,7 +1992,8 @@ void add_diff_to_status(STATUS_VAR *to_var, STATUS_VAR *from_var,
void THD::awake(killed_state state_to_set)
{
DBUG_ENTER("THD::awake");
- DBUG_PRINT("enter", ("this: %p current_thd: %p", this, current_thd));
+ DBUG_PRINT("enter", ("this: %p current_thd: %p state: %d",
+ this, current_thd, (int) state_to_set));
THD_CHECK_SENTRY(this);
mysql_mutex_assert_owner(&LOCK_thd_data);
@@ -1864,7 +2023,7 @@ void THD::awake(killed_state state_to_set)
/* Interrupt target waiting inside a storage engine. */
if (state_to_set != NOT_KILLED)
#ifdef WITH_WSREP
- if (!wsrep_bf_thd || wsrep_bf_thd->wsrep_exec_mode == LOCAL_STATE)
+ /* TODO: prevent applier close here */
#endif /* WITH_WSREP */
ha_kill_query(this, thd_kill_level(this));
@@ -1966,6 +2125,58 @@ void THD::disconnect()
}
+bool THD::notify_shared_lock(MDL_context_owner *ctx_in_use,
+ bool needs_thr_lock_abort)
+{
+ THD *in_use= ctx_in_use->get_thd();
+ bool signalled= FALSE;
+
+ if ((in_use->system_thread & SYSTEM_THREAD_DELAYED_INSERT) &&
+ !in_use->killed)
+ {
+ in_use->killed= KILL_CONNECTION;
+ mysql_mutex_lock(&in_use->mysys_var->mutex);
+ if (in_use->mysys_var->current_cond)
+ mysql_cond_broadcast(in_use->mysys_var->current_cond);
+ mysql_mutex_unlock(&in_use->mysys_var->mutex);
+ signalled= TRUE;
+ }
+
+ if (needs_thr_lock_abort)
+ {
+ mysql_mutex_lock(&in_use->LOCK_thd_data);
+ for (TABLE *thd_table= in_use->open_tables;
+ thd_table ;
+ thd_table= thd_table->next)
+ {
+ /*
+ Check for TABLE::needs_reopen() is needed since in some places we call
+ handler::close() for table instance (and set TABLE::db_stat to 0)
+ and do not remove such instances from the THD::open_tables
+ for some time, during which other thread can see those instances
+ (e.g. see partitioning code).
+ */
+ if (!thd_table->needs_reopen())
+#ifdef WITH_WSREP
+ {
+ signalled|= mysql_lock_abort_for_thread(this, thd_table);
+ if (this && WSREP(this) && wsrep_thd_is_BF((void *)this, FALSE))
+ {
+ WSREP_DEBUG("remove_table_from_cache: %llu",
+ (unsigned long long) this->real_id);
+ wsrep_abort_thd((void *)this, (void *)in_use, FALSE);
+ }
+ }
+#else
+ signalled|= mysql_lock_abort_for_thread(this, thd_table);
+#endif
+ }
+ mysql_mutex_unlock(&in_use->LOCK_thd_data);
+ }
+ return signalled;
+}
+
+
/*
Get error number for killed state
Note that the error message can't have any parameters.
@@ -2015,7 +2226,7 @@ bool THD::store_globals()
*/
DBUG_ASSERT(thread_stack);
- if (my_pthread_setspecific_ptr(THR_THD, this) ||
+ if (set_current_thd(this) ||
my_pthread_setspecific_ptr(THR_MALLOC, &mem_root))
return 1;
/*
@@ -2059,7 +2270,7 @@ void THD::reset_globals()
mysql_mutex_unlock(&LOCK_thd_data);
/* Undocking the thread specific data. */
- my_pthread_setspecific_ptr(THR_THD, NULL);
+ set_current_thd(0);
my_pthread_setspecific_ptr(THR_MALLOC, NULL);
}
@@ -2112,10 +2323,18 @@ void THD::cleanup_after_query()
which is intended to consume its event (there can be other
SET statements between them).
*/
- if ((rli_slave || rli_fake) && is_update_query(lex->sql_command))
+ if ((rgi_slave || rli_fake) && is_update_query(lex->sql_command))
auto_inc_intervals_forced.empty();
#endif
}
+ /*
+ Forget the binlog stmt filter for the next query.
+ There are some code paths that:
+ - do not call THD::decide_logging_format()
+ - do call THD::binlog_query(),
+ making this reset necessary.
+ */
+ reset_binlog_local_stmt_filter();
if (first_successful_insert_id_in_cur_stmt > 0)
{
/* set what LAST_INSERT_ID() will return */
@@ -2131,12 +2350,18 @@ void THD::cleanup_after_query()
where= THD::DEFAULT_WHERE;
/* reset table map for multi-table update */
table_map_for_update= 0;
- m_binlog_invoker= FALSE;
- /* reset replication info structure */
+ m_binlog_invoker= INVOKER_NONE;
+#ifdef WITH_WSREP
+ if (TOTAL_ORDER == wsrep_exec_mode)
+ {
+ wsrep_exec_mode = LOCAL_STATE;
+ }
+ //wsrep_trx_seqno = 0;
+#endif /* WITH_WSREP */
#ifndef EMBEDDED_LIBRARY
- if (rli_slave)
- rli_slave->cleanup_after_query();
+ if (rgi_slave)
+ rgi_slave->cleanup_after_query();
#endif
#ifdef WITH_WSREP
@@ -2147,30 +2372,6 @@ void THD::cleanup_after_query()
}
-/**
- Create a LEX_STRING in this connection.
-
- @param lex_str pointer to LEX_STRING object to be initialized
- @param str initializer to be copied into lex_str
- @param length length of str, in bytes
- @param allocate_lex_string if TRUE, allocate new LEX_STRING object,
- instead of using lex_str value
- @return NULL on failure, or pointer to the LEX_STRING object
-*/
-LEX_STRING *THD::make_lex_string(LEX_STRING *lex_str,
- const char* str, uint length,
- bool allocate_lex_string)
-{
- if (allocate_lex_string)
- if (!(lex_str= (LEX_STRING *)alloc_root(mem_root, sizeof(LEX_STRING))))
- return 0;
- if (!(lex_str->str= strmake_root(mem_root, str, length)))
- return 0;
- lex_str->length= length;
- return lex_str;
-}
-
-
/*
Convert a string to another character set
@@ -2358,6 +2559,21 @@ CHANGED_TABLE_LIST* THD::changed_table_dup(const char *key, long key_length)
int THD::send_explain_fields(select_result *result)
{
List<Item> field_list;
+ make_explain_field_list(field_list);
+ result->prepare(field_list, NULL);
+ return (result->send_result_set_metadata(field_list,
+ Protocol::SEND_NUM_ROWS |
+ Protocol::SEND_EOF));
+}
+
+
+/*
+ Populate the provided field_list with EXPLAIN output columns.
+ this->lex->describe has the EXPLAIN flags
+*/
+
+void THD::make_explain_field_list(List<Item> &field_list)
+{
Item *item;
CHARSET_INFO *cs= system_charset_info;
field_list.push_back(item= new Item_return_int("id",3, MYSQL_TYPE_LONGLONG));
@@ -2396,10 +2612,9 @@ int THD::send_explain_fields(select_result *result)
}
item->maybe_null= 1;
field_list.push_back(new Item_empty_string("Extra", 255, cs));
- return (result->send_result_set_metadata(field_list,
- Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF));
}
+
#ifdef SIGNAL_WITH_VIO_CLOSE
void THD::close_active_vio()
{
@@ -2512,12 +2727,6 @@ select_result::select_result()
thd=current_thd;
}
-void select_result::send_error(uint errcode,const char *err)
-{
- my_message(errcode, err, MYF(0));
-}
-
-
void select_result::cleanup()
{
/* do nothing */
@@ -2609,7 +2818,8 @@ int select_send::send_data(List<Item> &items)
Protocol *protocol= thd->protocol;
DBUG_ENTER("select_send::send_data");
- if (unit->offset_limit_cnt)
+ /* unit is not set when using 'delete ... returning' */
+ if (unit && unit->offset_limit_cnt)
{ // using limit offset,count
unit->offset_limit_cnt--;
DBUG_RETURN(FALSE);
@@ -2631,7 +2841,7 @@ int select_send::send_data(List<Item> &items)
DBUG_RETURN(TRUE);
}
- thd->sent_row_count++;
+ thd->inc_sent_row_count(1);
if (thd->vio_ok())
DBUG_RETURN(protocol->write());
@@ -2639,6 +2849,7 @@ int select_send::send_data(List<Item> &items)
DBUG_RETURN(0);
}
+
bool select_send::send_eof()
{
/*
@@ -2664,23 +2875,9 @@ bool select_send::send_eof()
Handling writing to file
************************************************************************/
-void select_to_file::send_error(uint errcode,const char *err)
-{
- my_message(errcode, err, MYF(0));
- if (file > 0)
- {
- (void) end_io_cache(&cache);
- mysql_file_close(file, MYF(0));
- /* Delete file on error */
- mysql_file_delete(key_select_to_file, path, MYF(0));
- file= -1;
- }
-}
-
-
bool select_to_file::send_eof()
{
- int error= test(end_io_cache(&cache));
+ int error= MY_TEST(end_io_cache(&cache));
if (mysql_file_close(file, MYF(MY_WME)) || thd->is_error())
error= true;
@@ -2723,7 +2920,7 @@ select_to_file::~select_to_file()
select_export::~select_export()
{
- thd->sent_row_count=row_count;
+ thd->set_sent_row_count(row_count);
}
@@ -2845,7 +3042,7 @@ select_export::prepare(List<Item> &list, SELECT_LEX_UNIT *u)
Non-ASCII separator arguments are not fully supported
*/
- push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning(thd, Sql_condition::WARN_LEVEL_WARN,
WARN_NON_ASCII_SEPARATOR_NOT_IMPLEMENTED,
ER(WARN_NON_ASCII_SEPARATOR_NOT_IMPLEMENTED));
}
@@ -2861,8 +3058,8 @@ select_export::prepare(List<Item> &list, SELECT_LEX_UNIT *u)
escape_char= (int) (uchar) (*exchange->escaped)[0];
else
escape_char= -1;
- is_ambiguous_field_sep= test(strchr(ESCAPE_CHARS, field_sep_char));
- is_unsafe_field_sep= test(strchr(NUMERIC_CHARS, field_sep_char));
+ is_ambiguous_field_sep= MY_TEST(strchr(ESCAPE_CHARS, field_sep_char));
+ is_unsafe_field_sep= MY_TEST(strchr(NUMERIC_CHARS, field_sep_char));
line_sep_char= (exchange->line_term->length() ?
(int) (uchar) (*exchange->line_term)[0] : INT_MAX);
if (!field_term_length)
@@ -2876,7 +3073,7 @@ select_export::prepare(List<Item> &list, SELECT_LEX_UNIT *u)
(exchange->opt_enclosed && non_string_results &&
field_term_length && strchr(NUMERIC_CHARS, field_term_char)))
{
- push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning(thd, Sql_condition::WARN_LEVEL_WARN,
ER_AMBIGUOUS_FIELD_TERM, ER(ER_AMBIGUOUS_FIELD_TERM));
is_ambiguous_field_term= TRUE;
}
@@ -2959,7 +3156,7 @@ int select_export::send_data(List<Item> &items)
convert_to_printable(printable_buff, sizeof(printable_buff),
error_pos, res->ptr() + res->length() - error_pos,
res->charset(), 6);
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_TRUNCATED_WRONG_VALUE_FOR_FIELD,
ER(ER_TRUNCATED_WRONG_VALUE_FOR_FIELD),
"string", printable_buff,
@@ -2970,7 +3167,7 @@ int select_export::send_data(List<Item> &items)
/*
result is longer than UINT_MAX32 and doesn't fit into String
*/
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
WARN_DATA_TRUNCATED, ER(WARN_DATA_TRUNCATED),
item->full_name(), static_cast<long>(row_count));
}
@@ -3005,7 +3202,7 @@ int select_export::send_data(List<Item> &items)
else
{
if (fixed_row_size)
- used_length=min(res->length(),item->max_length);
+ used_length=MY_MIN(res->length(),item->max_length);
else
used_length=res->length();
if ((result_type == STRING_RESULT || is_unsafe_field_sep) &&
@@ -3503,6 +3700,10 @@ void THD::end_statement()
}
+/*
+ Start using arena specified by @set. Current arena data will be saved to
+ *backup.
+*/
void THD::set_n_backup_active_arena(Query_arena *set, Query_arena *backup)
{
DBUG_ENTER("THD::set_n_backup_active_arena");
@@ -3517,6 +3718,12 @@ void THD::set_n_backup_active_arena(Query_arena *set, Query_arena *backup)
}
+/*
+ Stop using the temporary arena, and start again using the arena that is
+ specified in *backup.
+ The temporary arena is returned back into *set.
+*/
+
void THD::restore_active_arena(Query_arena *set, Query_arena *backup)
{
DBUG_ENTER("THD::restore_active_arena");
@@ -3736,7 +3943,7 @@ int select_dumpvar::send_data(List<Item> &items)
bool select_dumpvar::send_eof()
{
if (! row_count)
- push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning(thd, Sql_condition::WARN_LEVEL_WARN,
ER_SP_FETCH_NO_DATA, ER(ER_SP_FETCH_NO_DATA));
/*
Don't send EOF if we're in error condition (which implies we've already
@@ -3875,6 +4082,12 @@ void thd_increment_bytes_sent(ulong length)
}
}
+my_bool thd_net_is_killed()
+{
+ THD *thd= current_thd;
+ return thd && thd->killed ? 1 : 0;
+}
+
void thd_increment_bytes_received(ulong length)
{
@@ -3890,7 +4103,8 @@ void thd_increment_net_big_packet_count(ulong length)
void THD::set_status_var_init()
{
- bzero((char*) &status_var, sizeof(status_var));
+ bzero((char*) &status_var, offsetof(STATUS_VAR,
+ last_cleared_system_status_var));
}
@@ -3898,7 +4112,7 @@ void Security_context::init()
{
host= user= ip= external_user= 0;
host_or_ip= "connecting host";
- priv_user[0]= priv_host[0]= proxy_user[0]= '\0';
+ priv_user[0]= priv_host[0]= proxy_user[0]= priv_role[0]= '\0';
master_access= 0;
#ifndef NO_EMBEDDED_ACCESS_CHECKS
db_access= NO_ACCESS;
@@ -3908,6 +4122,7 @@ void Security_context::init()
void Security_context::destroy()
{
+ DBUG_PRINT("info", ("freeing security context"));
// If not pointer to constant
if (host != my_localhost)
{
@@ -4095,12 +4310,7 @@ void THD::restore_backup_open_tables_state(Open_tables_backup *backup)
#undef thd_killed
extern "C" int thd_killed(const MYSQL_THD thd)
{
- if (!thd)
- thd= current_thd;
-
- if (!(thd->killed & KILL_HARD_BIT))
- return 0;
- return thd->killed != 0;
+ return thd_kill_level(thd) > THD_ABORT_SOFTLY;
}
#else
#error now thd_killed() function can go away
@@ -4112,8 +4322,17 @@ extern "C" int thd_killed(const MYSQL_THD thd)
*/
extern "C" enum thd_kill_levels thd_kill_level(const MYSQL_THD thd)
{
+ THD* current= current_thd;
+
if (!thd)
- thd= current_thd;
+ thd= current;
+
+ if (thd == current)
+ {
+ Apc_target *apc_target= (Apc_target*)&thd->apc_target;
+ if (apc_target->have_apc_requests())
+ apc_target->process_apc_requests();
+ }
if (likely(thd->killed == NOT_KILLED))
return THD_IS_NOT_KILLED;
@@ -4121,6 +4340,7 @@ extern "C" enum thd_kill_levels thd_kill_level(const MYSQL_THD thd)
return thd->killed & KILL_HARD_BIT ? THD_ABORT_ASAP : THD_ABORT_SOFTLY;
}
+
/**
Send an out-of-band progress report to the client
@@ -4136,7 +4356,7 @@ static void thd_send_progress(THD *thd)
ulonglong report_time= my_interval_timer();
if (report_time > thd->progress.next_report_time)
{
- uint seconds_to_next= max(thd->variables.progress_report_time,
+ uint seconds_to_next= MY_MAX(thd->variables.progress_report_time,
global_system_variables.progress_report_time);
if (seconds_to_next == 0) // Turned off
seconds_to_next= 1; // Check again after 1 second
@@ -4252,11 +4472,15 @@ extern "C" unsigned long thd_get_thread_id(const MYSQL_THD thd)
return((unsigned long)thd->thread_id);
}
-extern "C" enum_tx_isolation thd_get_trx_isolation(const MYSQL_THD thd)
+/**
+ Check if THD socket is still connected.
+ */
+extern "C" int thd_is_connected(MYSQL_THD thd)
{
- return thd->tx_isolation;
+ return thd->is_connected();
}
+
#ifdef INNODB_COMPATIBILITY_HOOKS
extern "C" const struct charset_info_st *thd_charset(MYSQL_THD thd)
{
@@ -4288,6 +4512,230 @@ extern "C" int thd_slave_thread(const MYSQL_THD thd)
return(thd->slave_thread);
}
+/* Returns true for a worker thread in parallel replication. */
+extern "C" int thd_rpl_is_parallel(const MYSQL_THD thd)
+{
+ return thd->rgi_slave && thd->rgi_slave->is_parallel_exec;
+}
+
+/*
+ This function can optionally be called to check if thd_report_wait_for()
+ needs to be called for waits done by a given transaction.
+
+ If this function returns false for a given thd, there is no need to do any
+ calls to thd_report_wait_for() on that thd.
+
+ This call is optional; it is safe to call thd_report_wait_for() in any case.
+ This call can be used to save some redundant calls to thd_report_wait_for()
+ if desired. (This is unlikely to matter much unless there are _lots_ of
+ waits to report, as the overhead of thd_report_wait_for() is small).
+*/
+extern "C" int
+thd_need_wait_for(const MYSQL_THD thd)
+{
+ rpl_group_info *rgi;
+
+ if (mysql_bin_log.is_open() && opt_binlog_commit_wait_count > 0)
+ return true;
+ if (!thd)
+ return false;
+ rgi= thd->rgi_slave;
+ if (!rgi)
+ return false;
+ return rgi->is_parallel_exec;
+}
+
+/*
+ Used by InnoDB/XtraDB to report that one transaction THD is about to go to
+ wait for a transactional lock held by another transactions OTHER_THD.
+
+ This is used for parallel replication, where transactions are required to
+ commit in the same order on the slave as they did on the master. If the
+ transactions on the slave encounters lock conflicts on the slave that did
+ not exist on the master, this can cause deadlocks.
+
+ Normally, such conflicts will not occur, because the same conflict would
+ have prevented the two transactions from committing in parallel on the
+ master, thus preventing them from running in parallel on the slave in the
+ first place. However, it is possible in case when the optimizer chooses a
+ different plan on the slave than on the master (eg. table scan instead of
+ index scan).
+
+ InnoDB/XtraDB reports lock waits using this call. If a lock wait causes a
+ deadlock with the pre-determined commit order, we kill the later transaction,
+ and later re-try it, to resolve the deadlock.
+
+ This call need only receive reports about waits for locks that will remain
+ until the holding transaction commits. InnoDB/XtraDB auto-increment locks
+ are released earlier, and so need not be reported. (Such false positives are
+ not harmful, but could lead to unnecessary kill and retry, so best avoided).
+*/
+extern "C" void
+thd_report_wait_for(MYSQL_THD thd, MYSQL_THD other_thd)
+{
+ rpl_group_info *rgi;
+ rpl_group_info *other_rgi;
+
+ if (!thd || !other_thd)
+ return;
+ binlog_report_wait_for(thd, other_thd);
+ rgi= thd->rgi_slave;
+ other_rgi= other_thd->rgi_slave;
+ if (!rgi || !other_rgi)
+ return;
+ if (!rgi->is_parallel_exec)
+ return;
+ if (rgi->rli != other_rgi->rli)
+ return;
+ if (!rgi->gtid_sub_id || !other_rgi->gtid_sub_id)
+ return;
+ if (rgi->current_gtid.domain_id != other_rgi->current_gtid.domain_id)
+ return;
+ if (rgi->gtid_sub_id > other_rgi->gtid_sub_id)
+ return;
+ /*
+ This transaction is about to wait for another transaction that is required
+ by replication binlog order to commit after. This would cause a deadlock.
+
+ So send a kill to the other transaction, with a temporary error; this will
+ cause replication to rollback (and later re-try) the other transaction,
+ releasing the lock for this transaction so replication can proceed.
+ */
+ other_rgi->killed_for_retry= true;
+ mysql_mutex_lock(&other_thd->LOCK_thd_data);
+ other_thd->awake(KILL_CONNECTION);
+ mysql_mutex_unlock(&other_thd->LOCK_thd_data);
+}
+
+/*
+ This function is called from InnoDB/XtraDB to check if the commit order of
+ two transactions has already been decided by the upper layer. This happens
+ in parallel replication, where the commit order is forced to be the same on
+ the slave as it was originally on the master.
+
+ If this function returns false, it means that such commit order will be
+ enforced. This allows the storage engine to optionally omit gap lock waits
+ or similar measures that would otherwise be needed to ensure that
+ transactions would be serialised in a way that would cause a commit order
+ that is correct for binlogging for statement-based replication.
+
+ Since transactions are only run in parallel on the slave if they ran without
+ lock conflicts on the master, normally no lock conflicts on the slave happen
+ during parallel replication. However, there are a couple of corner cases
+ where it can happen, like these secondary-index operations:
+
+ T1: INSERT INTO t1 VALUES (7, NULL);
+ T2: DELETE FROM t1 WHERE b <= 3;
+
+ T1: UPDATE t1 SET secondary=NULL WHERE primary=1
+ T2: DELETE t1 WHERE secondary <= 3
+
+ The DELETE takes a gap lock that can block the INSERT/UPDATE, but the row
+ locks set by INSERT/UPDATE do not block the DELETE. Thus, the execution
+ order of the transactions determine whether a lock conflict occurs or
+ not. Thus a lock conflict can occur on the slave where it did not on the
+ master.
+
+ If this function returns true, normal locking should be done as required by
+ the binlogging and transaction isolation level in effect. But if it returns
+ false, the correct order will be enforced anyway, and InnoDB/XtraDB can
+ avoid taking the gap lock, preventing the lock conflict.
+
+ Calling this function is just an optimisation to avoid unnecessary
+ deadlocks. If it was not used, a gap lock would be set that could eventually
+ cause a deadlock; the deadlock would be caught by thd_report_wait_for() and
+ the transaction T2 killed and rolled back (and later re-tried).
+*/
+extern "C" int
+thd_need_ordering_with(const MYSQL_THD thd, const MYSQL_THD other_thd)
+{
+ rpl_group_info *rgi, *other_rgi;
+
+ DBUG_EXECUTE_IF("disable_thd_need_ordering_with", return 1;);
+ if (!thd || !other_thd)
+ return 1;
+ rgi= thd->rgi_slave;
+ other_rgi= other_thd->rgi_slave;
+ if (!rgi || !other_rgi)
+ return 1;
+ if (!rgi->is_parallel_exec)
+ return 1;
+ if (rgi->rli != other_rgi->rli)
+ return 1;
+ if (rgi->current_gtid.domain_id != other_rgi->current_gtid.domain_id)
+ return 1;
+ if (!rgi->commit_id || rgi->commit_id != other_rgi->commit_id)
+ return 1;
+ DBUG_EXECUTE_IF("thd_need_ordering_with_force", return 1;);
+ /*
+ Otherwise, these two threads are doing parallel replication within the same
+ replication domain. Their commit order is already fixed, so we do not need
+ gap locks or similar to otherwise enforce ordering (and in fact such locks
+ could lead to unnecessary deadlocks and transaction retry).
+ */
+ return 0;
+}
+
+
+/*
+ If the storage engine detects a deadlock, and needs to choose a victim
+ transaction to roll back, it can call this function to ask the upper
+ server layer for which of two possible transactions is prefered to be
+ aborted and rolled back.
+
+ In parallel replication, if two transactions are running in parallel and
+ one is fixed to commit before the other, then the one that commits later
+ will be prefered as the victim - chosing the early transaction as a victim
+ will not resolve the deadlock anyway, as the later transaction still needs
+ to wait for the earlier to commit.
+
+ Otherwise, a transaction that uses only transactional tables, and can thus
+ be safely rolled back, will be prefered as a deadlock victim over a
+ transaction that also modified non-transactional (eg. MyISAM) tables.
+
+ The return value is -1 if the first transaction is prefered as a deadlock
+ victim, 1 if the second transaction is prefered, or 0 for no preference (in
+ which case the storage engine can make the choice as it prefers).
+*/
+extern "C" int
+thd_deadlock_victim_preference(const MYSQL_THD thd1, const MYSQL_THD thd2)
+{
+ rpl_group_info *rgi1, *rgi2;
+ bool nontrans1, nontrans2;
+
+ if (!thd1 || !thd2)
+ return 0;
+
+ /*
+ If the transactions are participating in the same replication domain in
+ parallel replication, then request to select the one that will commit
+ later (in the fixed commit order from the master) as the deadlock victim.
+ */
+ rgi1= thd1->rgi_slave;
+ rgi2= thd2->rgi_slave;
+ if (rgi1 && rgi2 &&
+ rgi1->is_parallel_exec &&
+ rgi1->rli == rgi2->rli &&
+ rgi1->current_gtid.domain_id == rgi2->current_gtid.domain_id)
+ return rgi1->gtid_sub_id < rgi2->gtid_sub_id ? 1 : -1;
+
+ /*
+ If one transaction has modified non-transactional tables (so that it
+ cannot be safely rolled back), and the other has not, then prefer to
+ select the purely transactional one as the victim.
+ */
+ nontrans1= thd1->transaction.all.modified_non_trans_table;
+ nontrans2= thd2->transaction.all.modified_non_trans_table;
+ if (nontrans1 && !nontrans2)
+ return 1;
+ else if (!nontrans1 && nontrans2)
+ return -1;
+
+ /* No preferences, let the storage engine decide. */
+ return 0;
+}
+
+
extern "C" int thd_non_transactional_update(const MYSQL_THD thd)
{
return(thd->transaction.all.modified_non_trans_table);
@@ -4301,7 +4749,7 @@ extern "C" int thd_binlog_format(const MYSQL_THD thd)
#else
if (mysql_bin_log.is_open() && (thd->variables.option_bits & OPTION_BIN_LOG))
#endif
- return (int) WSREP_BINLOG_FORMAT(thd->variables.binlog_format);
+ return (int) WSREP_FORMAT(thd->variables.binlog_format);
else
return BINLOG_FORMAT_UNSPEC;
}
@@ -4317,12 +4765,57 @@ extern "C" bool thd_binlog_filter_ok(const MYSQL_THD thd)
return binlog_filter->db_ok(thd->db);
}
+/*
+ This is similar to sqlcom_can_generate_row_events, with the expection
+ that we only return 1 if we are going to generate row events in a
+ transaction.
+ CREATE OR REPLACE is always safe to do as this will run in it's own
+ transaction.
+*/
+
extern "C" bool thd_sqlcom_can_generate_row_events(const MYSQL_THD thd)
{
- return sqlcom_can_generate_row_events(thd);
+ return (sqlcom_can_generate_row_events(thd) && thd->lex->sql_command !=
+ SQLCOM_CREATE_TABLE);
+}
+
+
+extern "C" enum durability_properties thd_get_durability_property(const MYSQL_THD thd)
+{
+ enum durability_properties ret= HA_REGULAR_DURABILITY;
+
+ if (thd != NULL)
+ ret= thd->durability_property;
+
+ return ret;
+}
+
+/** Get the auto_increment_offset auto_increment_increment.
+Exposed by thd_autoinc_service.
+Needed by InnoDB.
+@param thd Thread object
+@param off auto_increment_offset
+@param inc auto_increment_increment */
+extern "C" void thd_get_autoinc(const MYSQL_THD thd, ulong* off, ulong* inc)
+{
+ *off = thd->variables.auto_increment_offset;
+ *inc = thd->variables.auto_increment_increment;
}
+/**
+ Is strict sql_mode set.
+ Needed by InnoDB.
+ @param thd Thread object
+ @return True if sql_mode has strict mode (all or trans).
+ @retval true sql_mode has strict mode (all or trans).
+ @retval false sql_mode has not strict mode (all or trans).
+*/
+extern "C" bool thd_is_strict_mode(const MYSQL_THD thd)
+{
+ return thd->is_strict_mode();
+}
+
/*
Interface for MySQL Server, plugins and storage engines to report
@@ -4427,8 +4920,8 @@ void THD::reset_sub_statement_state(Sub_statement_state *backup,
backup->enable_slow_log= enable_slow_log;
backup->query_plan_flags= query_plan_flags;
backup->limit_found_rows= limit_found_rows;
- backup->examined_row_count= examined_row_count;
- backup->sent_row_count= sent_row_count;
+ backup->examined_row_count= m_examined_row_count;
+ backup->sent_row_count= m_sent_row_count;
backup->cuted_fields= cuted_fields;
backup->client_capabilities= client_capabilities;
backup->savepoints= transaction.savepoints;
@@ -4451,8 +4944,8 @@ void THD::reset_sub_statement_state(Sub_statement_state *backup,
/* Disable result sets */
client_capabilities &= ~CLIENT_MULTI_RESULTS;
in_sub_stmt|= new_state;
- examined_row_count= 0;
- sent_row_count= 0;
+ m_examined_row_count= 0;
+ m_sent_row_count= 0;
cuted_fields= 0;
transaction.savepoints= 0;
first_successful_insert_id_in_cur_stmt= 0;
@@ -4499,7 +4992,7 @@ void THD::restore_sub_statement_state(Sub_statement_state *backup)
first_successful_insert_id_in_cur_stmt=
backup->first_successful_insert_id_in_cur_stmt;
limit_found_rows= backup->limit_found_rows;
- sent_row_count= backup->sent_row_count;
+ set_sent_row_count(backup->sent_row_count);
client_capabilities= backup->client_capabilities;
/*
If we've left sub-statement mode, reset the fatal error flag.
@@ -4520,7 +5013,7 @@ void THD::restore_sub_statement_state(Sub_statement_state *backup)
The following is added to the old values as we are interested in the
total complexity of the query
*/
- examined_row_count+= backup->examined_row_count;
+ inc_examined_row_count(backup->examined_row_count);
cuted_fields+= backup->cuted_fields;
DBUG_VOID_RETURN;
}
@@ -4533,6 +5026,141 @@ void THD::set_statement(Statement *stmt)
mysql_mutex_unlock(&LOCK_thd_data);
}
+void THD::set_sent_row_count(ha_rows count)
+{
+ m_sent_row_count= count;
+ MYSQL_SET_STATEMENT_ROWS_SENT(m_statement_psi, m_sent_row_count);
+}
+
+void THD::set_examined_row_count(ha_rows count)
+{
+ m_examined_row_count= count;
+ MYSQL_SET_STATEMENT_ROWS_EXAMINED(m_statement_psi, m_examined_row_count);
+}
+
+void THD::inc_sent_row_count(ha_rows count)
+{
+ m_sent_row_count+= count;
+ MYSQL_SET_STATEMENT_ROWS_SENT(m_statement_psi, m_sent_row_count);
+}
+
+void THD::inc_examined_row_count(ha_rows count)
+{
+ m_examined_row_count+= count;
+ MYSQL_SET_STATEMENT_ROWS_EXAMINED(m_statement_psi, m_examined_row_count);
+}
+
+void THD::inc_status_created_tmp_disk_tables()
+{
+ status_var_increment(status_var.created_tmp_disk_tables_);
+#ifdef HAVE_PSI_STATEMENT_INTERFACE
+ PSI_STATEMENT_CALL(inc_statement_created_tmp_disk_tables)(m_statement_psi, 1);
+#endif
+}
+
+void THD::inc_status_created_tmp_tables()
+{
+ status_var_increment(status_var.created_tmp_tables_);
+#ifdef HAVE_PSI_STATEMENT_INTERFACE
+ PSI_STATEMENT_CALL(inc_statement_created_tmp_tables)(m_statement_psi, 1);
+#endif
+}
+
+void THD::inc_status_select_full_join()
+{
+ status_var_increment(status_var.select_full_join_count_);
+#ifdef HAVE_PSI_STATEMENT_INTERFACE
+ PSI_STATEMENT_CALL(inc_statement_select_full_join)(m_statement_psi, 1);
+#endif
+}
+
+void THD::inc_status_select_full_range_join()
+{
+ status_var_increment(status_var.select_full_range_join_count_);
+#ifdef HAVE_PSI_STATEMENT_INTERFACE
+ PSI_STATEMENT_CALL(inc_statement_select_full_range_join)(m_statement_psi, 1);
+#endif
+}
+
+void THD::inc_status_select_range()
+{
+ status_var_increment(status_var.select_range_count_);
+#ifdef HAVE_PSI_STATEMENT_INTERFACE
+ PSI_STATEMENT_CALL(inc_statement_select_range)(m_statement_psi, 1);
+#endif
+}
+
+void THD::inc_status_select_range_check()
+{
+ status_var_increment(status_var.select_range_check_count_);
+#ifdef HAVE_PSI_STATEMENT_INTERFACE
+ PSI_STATEMENT_CALL(inc_statement_select_range_check)(m_statement_psi, 1);
+#endif
+}
+
+void THD::inc_status_select_scan()
+{
+ status_var_increment(status_var.select_scan_count_);
+#ifdef HAVE_PSI_STATEMENT_INTERFACE
+ PSI_STATEMENT_CALL(inc_statement_select_scan)(m_statement_psi, 1);
+#endif
+}
+
+void THD::inc_status_sort_merge_passes()
+{
+ status_var_increment(status_var.filesort_merge_passes_);
+#ifdef HAVE_PSI_STATEMENT_INTERFACE
+ PSI_STATEMENT_CALL(inc_statement_sort_merge_passes)(m_statement_psi, 1);
+#endif
+}
+
+void THD::inc_status_sort_range()
+{
+ status_var_increment(status_var.filesort_range_count_);
+#ifdef HAVE_PSI_STATEMENT_INTERFACE
+ PSI_STATEMENT_CALL(inc_statement_sort_range)(m_statement_psi, 1);
+#endif
+}
+
+void THD::inc_status_sort_rows(ha_rows count)
+{
+ statistic_add(status_var.filesort_rows_, count, &LOCK_status);
+#ifdef HAVE_PSI_STATEMENT_INTERFACE
+ PSI_STATEMENT_CALL(inc_statement_sort_rows)(m_statement_psi, count);
+#endif
+}
+
+void THD::inc_status_sort_scan()
+{
+ status_var_increment(status_var.filesort_scan_count_);
+#ifdef HAVE_PSI_STATEMENT_INTERFACE
+ PSI_STATEMENT_CALL(inc_statement_sort_scan)(m_statement_psi, 1);
+#endif
+}
+
+void THD::set_status_no_index_used()
+{
+ server_status|= SERVER_QUERY_NO_INDEX_USED;
+#ifdef HAVE_PSI_STATEMENT_INTERFACE
+ PSI_STATEMENT_CALL(set_statement_no_index_used)(m_statement_psi);
+#endif
+}
+
+void THD::set_status_no_good_index_used()
+{
+ server_status|= SERVER_QUERY_NO_GOOD_INDEX_USED;
+#ifdef HAVE_PSI_STATEMENT_INTERFACE
+ PSI_STATEMENT_CALL(set_statement_no_good_index_used)(m_statement_psi);
+#endif
+}
+
+void THD::set_command(enum enum_server_command command)
+{
+ m_command= command;
+#ifdef HAVE_PSI_THREAD_INTERFACE
+ PSI_STATEMENT_CALL(set_thread_command)(m_command);
+#endif
+}
/** Assign a new value to thd->query. */
@@ -4541,6 +5169,10 @@ void THD::set_query(const CSET_STRING &string_arg)
mysql_mutex_lock(&LOCK_thd_data);
set_query_inner(string_arg);
mysql_mutex_unlock(&LOCK_thd_data);
+
+#ifdef HAVE_PSI_THREAD_INTERFACE
+ PSI_THREAD_CALL(set_thread_info)(query(), query_length());
+#endif
}
/** Assign a new value to thd->query and thd->query_id. */
@@ -4551,17 +5183,8 @@ void THD::set_query_and_id(char *query_arg, uint32 query_length_arg,
{
mysql_mutex_lock(&LOCK_thd_data);
set_query_inner(query_arg, query_length_arg, cs);
- query_id= new_query_id;
mysql_mutex_unlock(&LOCK_thd_data);
-}
-
-/** Assign a new value to thd->query_id. */
-
-void THD::set_query_id(query_id_t new_query_id)
-{
- mysql_mutex_lock(&LOCK_thd_data);
query_id= new_query_id;
- mysql_mutex_unlock(&LOCK_thd_data);
}
/** Assign a new value to thd->mysys_var. */
@@ -4595,13 +5218,15 @@ void THD::leave_locked_tables_mode()
/* Also ensure that we don't release metadata locks for open HANDLERs. */
if (handler_tables_hash.records)
mysql_ha_set_explicit_lock_duration(this);
+ if (ull_hash.records)
+ mysql_ull_set_explicit_lock_duration(this);
}
locked_tables_mode= LTM_NONE;
}
-void THD::get_definer(LEX_USER *definer)
+void THD::get_definer(LEX_USER *definer, bool role)
{
- binlog_invoker();
+ binlog_invoker(role);
#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
if (slave_thread && has_invoker())
{
@@ -4613,7 +5238,7 @@ void THD::get_definer(LEX_USER *definer)
}
else
#endif
- get_default_definer(this, definer);
+ get_default_definer(this, definer, role);
}
@@ -4912,16 +5537,13 @@ has_write_table_auto_increment_not_first_in_pk(TABLE_LIST *tables)
BINLOG_FORMAT = STATEMENT and at least one table uses a storage
engine limited to row-logging.
- 6. Error: Cannot execute row injection: binlogging impossible since
- BINLOG_FORMAT = STATEMENT.
-
- 7. Warning: Unsafe statement binlogged in statement format since
+ 6. Warning: Unsafe statement binlogged in statement format since
BINLOG_FORMAT = STATEMENT.
In addition, we can produce the following error (not depending on
the variables of the decision diagram):
- 8. Error: Cannot execute statement: binlogging impossible since more
+ 7. Error: Cannot execute statement: binlogging impossible since more
than one engine is involved and at least one engine is
self-logging.
@@ -4948,13 +5570,15 @@ int THD::decide_logging_format(TABLE_LIST *tables)
DBUG_PRINT("info", ("lex->get_stmt_unsafe_flags(): 0x%x",
lex->get_stmt_unsafe_flags()));
+ reset_binlog_local_stmt_filter();
+
/*
We should not decide logging format if the binlog is closed or
binlogging is off, or if the statement is filtered out from the
binlog by filtering rules.
*/
if (mysql_bin_log.is_open() && (variables.option_bits & OPTION_BIN_LOG) &&
- !(WSREP_BINLOG_FORMAT(variables.binlog_format) == BINLOG_FORMAT_STMT &&
+ !(WSREP_FORMAT(variables.binlog_format) == BINLOG_FORMAT_STMT &&
!binlog_filter->db_ok(db)))
{
/*
@@ -4990,6 +5614,28 @@ int THD::decide_logging_format(TABLE_LIST *tables)
A pointer to a previous table that was accessed.
*/
TABLE* prev_access_table= NULL;
+ /**
+ The number of tables used in the current statement,
+ that should be replicated.
+ */
+ uint replicated_tables_count= 0;
+ /**
+ The number of tables written to in the current statement,
+ that should not be replicated.
+ A table should not be replicated when it is considered
+ 'local' to a MySQL instance.
+ Currently, these tables are:
+ - mysql.slow_log
+ - mysql.general_log
+ - mysql.slave_relay_log_info
+ - mysql.slave_master_info
+ - mysql.slave_worker_info
+ - performance_schema.*
+ - TODO: information_schema.*
+ In practice, from this list, only performance_schema.* tables
+ are written to by user queries.
+ */
+ uint non_replicated_tables_count= 0;
#ifndef DBUG_OFF
{
@@ -5037,14 +5683,38 @@ int THD::decide_logging_format(TABLE_LIST *tables)
if (table->placeholder())
continue;
- if (table->table->s->table_category == TABLE_CATEGORY_PERFORMANCE ||
- table->table->s->table_category == TABLE_CATEGORY_LOG)
- lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_SYSTEM_TABLE);
-
handler::Table_flags const flags= table->table->file->ha_table_flags();
DBUG_PRINT("info", ("table: %s; ha_table_flags: 0x%llx",
table->table_name, flags));
+
+ if (table->table->no_replicate)
+ {
+ /*
+ The statement uses a table that is not replicated.
+ The following properties about the table:
+ - persistent / transient
+ - transactional / non transactional
+ - temporary / permanent
+ - read or write
+ - multiple engines involved because of this table
+ are not relevant, as this table is completely ignored.
+ Because the statement uses a non replicated table,
+ using STATEMENT format in the binlog is impossible.
+ Either this statement will be discarded entirely,
+ or it will be logged (possibly partially) in ROW format.
+ */
+ lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_SYSTEM_TABLE);
+
+ if (table->lock_type >= TL_WRITE_ALLOW_WRITE)
+ {
+ non_replicated_tables_count++;
+ continue;
+ }
+ }
+
+ replicated_tables_count++;
+
if (table->lock_type >= TL_WRITE_ALLOW_WRITE)
{
if (prev_write_table && prev_write_table->file->ht !=
@@ -5066,32 +5736,12 @@ int THD::decide_logging_format(TABLE_LIST *tables)
prev_write_table= table->table;
- /*
- INSERT...ON DUPLICATE KEY UPDATE on a table with more than one unique keys
- can be unsafe. Check for it if the flag is already not marked for the
- given statement.
- */
- if (!lex->is_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_INSERT_TWO_KEYS) &&
- lex->sql_command == SQLCOM_INSERT &&
- /* Duplicate key update is not supported by INSERT DELAYED */
- command != COM_DELAYED_INSERT && lex->duplicates == DUP_UPDATE)
- {
- uint keys= table->table->s->keys, i= 0, unique_keys= 0;
- for (KEY* keyinfo= table->table->s->key_info;
- i < keys && unique_keys <= 1; i++, keyinfo++)
- {
- if (keyinfo->flags & HA_NOSAME)
- unique_keys++;
- }
- if (unique_keys > 1 )
- lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_INSERT_TWO_KEYS);
- }
}
flags_access_some_set |= flags;
if (lex->sql_command != SQLCOM_CREATE_TABLE ||
(lex->sql_command == SQLCOM_CREATE_TABLE &&
- (lex->create_info.options & HA_LEX_CREATE_TMP_TABLE)))
+ lex->create_info.tmp_table()))
{
my_bool trans= table->table->file->has_transactions();
@@ -5163,7 +5813,7 @@ int THD::decide_logging_format(TABLE_LIST *tables)
*/
my_error((error= ER_BINLOG_ROW_INJECTION_AND_STMT_ENGINE), MYF(0));
}
- else if (WSREP_BINLOG_FORMAT(variables.binlog_format) == BINLOG_FORMAT_ROW &&
+ else if (WSREP_FORMAT(variables.binlog_format) == BINLOG_FORMAT_ROW &&
sqlcom_can_generate_row_events(this))
{
/*
@@ -5192,15 +5842,15 @@ int THD::decide_logging_format(TABLE_LIST *tables)
else
{
/* binlog_format = STATEMENT */
- if (WSREP_BINLOG_FORMAT(variables.binlog_format) == BINLOG_FORMAT_STMT)
+ if (WSREP_FORMAT(variables.binlog_format) == BINLOG_FORMAT_STMT)
{
if (lex->is_stmt_row_injection())
{
/*
- 6. Error: Cannot execute row injection since
- BINLOG_FORMAT = STATEMENT
+ We have to log the statement as row or give an error.
+ Better to accept what master gives us than stopping replication.
*/
- my_error((error= ER_BINLOG_ROW_INJECTION_AND_STMT_MODE), MYF(0));
+ set_current_stmt_binlog_format_row();
}
else if ((flags_write_all_set & HA_BINLOG_STMT_CAPABLE) == 0 &&
sqlcom_can_generate_row_events(this))
@@ -5232,7 +5882,7 @@ int THD::decide_logging_format(TABLE_LIST *tables)
DBUG_PRINT("info", ("binlog_unsafe_warning_flags: 0x%x",
binlog_unsafe_warning_flags));
}
- /* log in statement format! */
+ /* log in statement format (or row if row event)! */
}
/* No statement-only engines and binlog_format != STATEMENT.
I.e., nothing prevents us from row logging if needed. */
@@ -5247,6 +5897,30 @@ int THD::decide_logging_format(TABLE_LIST *tables)
}
}
+ if (non_replicated_tables_count > 0)
+ {
+ if ((replicated_tables_count == 0) || ! is_write)
+ {
+ DBUG_PRINT("info", ("decision: no logging, no replicated table affected"));
+ set_binlog_local_stmt_filter();
+ }
+ else
+ {
+ if (! is_current_stmt_binlog_format_row())
+ {
+ my_error((error= ER_BINLOG_STMT_MODE_AND_NO_REPL_TABLES), MYF(0));
+ }
+ else
+ {
+ clear_binlog_local_stmt_filter();
+ }
+ }
+ }
+ else
+ {
+ clear_binlog_local_stmt_filter();
+ }
+
if (error) {
DBUG_PRINT("info", ("decision: no logging since an error was generated"));
DBUG_RETURN(-1);
@@ -5285,7 +5959,7 @@ int THD::decide_logging_format(TABLE_LIST *tables)
Replace the last ',' with '.' for table_names
*/
table_names.replace(table_names.length()-1, 1, ".", 1);
- push_warning_printf(this, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(this, Sql_condition::WARN_LEVEL_WARN,
ER_UNKNOWN_ERROR,
"Row events are not logged for %s statements "
"that modify BLACKHOLE tables in row format. "
@@ -5304,7 +5978,7 @@ int THD::decide_logging_format(TABLE_LIST *tables)
"and binlog_filter->db_ok(db) = %d",
mysql_bin_log.is_open(),
(variables.option_bits & OPTION_BIN_LOG),
- WSREP_BINLOG_FORMAT(variables.binlog_format),
+ WSREP_FORMAT(variables.binlog_format),
binlog_filter->db_ok(db)));
#endif
@@ -5354,7 +6028,11 @@ THD::binlog_prepare_pending_rows_event(TABLE* table, uint32 serv_id,
DBUG_ASSERT(table->s->table_map_id != ~0UL);
/* Fetch the type code for the RowsEventT template parameter */
- int const type_code= RowsEventT::TYPE_CODE;
+ int const general_type_code= RowsEventT::TYPE_CODE;
+
+ /* Ensure that all events in a GTID group are in the same cache */
+ if (variables.option_bits & OPTION_GTID_BEGIN)
+ is_transactional= 1;
/*
There is no good place to set up the transactional data, so we
@@ -5381,7 +6059,7 @@ THD::binlog_prepare_pending_rows_event(TABLE* table, uint32 serv_id,
if (!pending ||
pending->server_id != serv_id ||
pending->get_table_id() != table->s->table_map_id ||
- pending->get_type_code() != type_code ||
+ pending->get_general_type_code() != general_type_code ||
pending->get_data_size() + needed > opt_binlog_rows_event_max_size ||
pending->get_width() != colcnt ||
!bitmap_cmp(pending->get_cols(), cols))
@@ -5410,27 +6088,6 @@ THD::binlog_prepare_pending_rows_event(TABLE* table, uint32 serv_id,
DBUG_RETURN(pending); /* This is the current pending event */
}
-#ifdef HAVE_EXPLICIT_TEMPLATE_INSTANTIATION
-/*
- Instantiate the versions we need, we have -fno-implicit-template as
- compiling option.
-*/
-template Rows_log_event*
-THD::binlog_prepare_pending_rows_event(TABLE*, uint32, MY_BITMAP const*,
- size_t, size_t, bool,
- Write_rows_log_event*);
-
-template Rows_log_event*
-THD::binlog_prepare_pending_rows_event(TABLE*, uint32, MY_BITMAP const*,
- size_t colcnt, size_t, bool,
- Delete_rows_log_event *);
-
-template Rows_log_event*
-THD::binlog_prepare_pending_rows_event(TABLE*, uint32, MY_BITMAP const*,
- size_t colcnt, size_t, bool,
- Update_rows_log_event *);
-#endif
-
/* Declare in unnamed namespace. */
CPP_UNNAMED_NS_START
/**
@@ -5578,8 +6235,12 @@ int THD::binlog_write_row(TABLE* table, bool is_trans,
size_t const len= pack_row(table, cols, row_data, record);
+ /* Ensure that all events in a GTID group are in the same cache */
+ if (variables.option_bits & OPTION_GTID_BEGIN)
+ is_trans= 1;
+
Rows_log_event* const ev=
- binlog_prepare_pending_rows_event(table, server_id, cols, colcnt,
+ binlog_prepare_pending_rows_event(table, variables.server_id, cols, colcnt,
len, is_trans,
static_cast<Write_rows_log_event*>(0));
@@ -5617,6 +6278,10 @@ int THD::binlog_update_row(TABLE* table, bool is_trans,
size_t const after_size= pack_row(table, cols, after_row,
after_record);
+ /* Ensure that all events in a GTID group are in the same cache */
+ if (variables.option_bits & OPTION_GTID_BEGIN)
+ is_trans= 1;
+
/*
Don't print debug messages when running valgrind since they can
trigger false warnings.
@@ -5629,7 +6294,7 @@ int THD::binlog_update_row(TABLE* table, bool is_trans,
#endif
Rows_log_event* const ev=
- binlog_prepare_pending_rows_event(table, server_id, cols, colcnt,
+ binlog_prepare_pending_rows_event(table, variables.server_id, cols, colcnt,
before_size + after_size, is_trans,
static_cast<Update_rows_log_event*>(0));
@@ -5665,8 +6330,12 @@ int THD::binlog_delete_row(TABLE* table, bool is_trans,
size_t const len= pack_row(table, cols, row_data, record);
+ /* Ensure that all events in a GTID group are in the same cache */
+ if (variables.option_bits & OPTION_GTID_BEGIN)
+ is_trans= 1;
+
Rows_log_event* const ev=
- binlog_prepare_pending_rows_event(table, server_id, cols, colcnt,
+ binlog_prepare_pending_rows_event(table, variables.server_id, cols, colcnt,
len, is_trans,
static_cast<Delete_rows_log_event*>(0));
@@ -5689,6 +6358,10 @@ int THD::binlog_remove_pending_rows_event(bool clear_maps,
#endif
DBUG_RETURN(0);
+ /* Ensure that all events in a GTID group are in the same cache */
+ if (variables.option_bits & OPTION_GTID_BEGIN)
+ is_transactional= 1;
+
mysql_bin_log.remove_pending_rows_event(this, is_transactional);
if (clear_maps)
@@ -5712,6 +6385,10 @@ int THD::binlog_flush_pending_rows_event(bool stmt_end, bool is_transactional)
#endif
DBUG_RETURN(0);
+ /* Ensure that all events in a GTID group are in the same cache */
+ if (variables.option_bits & OPTION_GTID_BEGIN)
+ is_transactional= 1;
+
/*
Mark the event as the last event of a statement if the stmt_end
flag is set.
@@ -5756,23 +6433,35 @@ show_query_type(THD::enum_binlog_query_type qtype)
Constants required for the limit unsafe warnings suppression
*/
//seconds after which the limit unsafe warnings suppression will be activated
-#define LIMIT_UNSAFE_WARNING_ACTIVATION_TIMEOUT 50
+#define LIMIT_UNSAFE_WARNING_ACTIVATION_TIMEOUT 5*60
//number of limit unsafe warnings after which the suppression will be activated
-#define LIMIT_UNSAFE_WARNING_ACTIVATION_THRESHOLD_COUNT 50
+#define LIMIT_UNSAFE_WARNING_ACTIVATION_THRESHOLD_COUNT 10
-static ulonglong limit_unsafe_suppression_start_time= 0;
-static bool unsafe_warning_suppression_is_activated= false;
-static int limit_unsafe_warning_count= 0;
+static ulonglong unsafe_suppression_start_time= 0;
+static bool unsafe_warning_suppression_active[LEX::BINLOG_STMT_UNSAFE_COUNT];
+static ulong unsafe_warnings_count[LEX::BINLOG_STMT_UNSAFE_COUNT];
+static ulong total_unsafe_warnings_count;
/**
Auxiliary function to reset the limit unsafety warning suppression.
+ This is done without mutex protection, but this should be good
+ enough as it doesn't matter if we loose a couple of suppressed
+ messages or if this is called multiple times.
*/
-static void reset_binlog_unsafe_suppression()
+
+static void reset_binlog_unsafe_suppression(ulonglong now)
{
+ uint i;
DBUG_ENTER("reset_binlog_unsafe_suppression");
- unsafe_warning_suppression_is_activated= false;
- limit_unsafe_warning_count= 0;
- limit_unsafe_suppression_start_time= my_interval_timer()/10000000;
+
+ unsafe_suppression_start_time= now;
+ total_unsafe_warnings_count= 0;
+
+ for (i= 0 ; i < LEX::BINLOG_STMT_UNSAFE_COUNT ; i++)
+ {
+ unsafe_warnings_count[i]= 0;
+ unsafe_warning_suppression_active[i]= 0;
+ }
DBUG_VOID_RETURN;
}
@@ -5790,95 +6479,94 @@ static void print_unsafe_warning_to_log(int unsafe_type, char* buf,
}
/**
- Auxiliary function to check if the warning for limit unsafety should be
- thrown or suppressed. Details of the implementation can be found in the
- comments inline.
+ Auxiliary function to check if the warning for unsafe repliction statements
+ should be thrown or suppressed.
+
+ Logic is:
+ - If we get more than LIMIT_UNSAFE_WARNING_ACTIVATION_THRESHOLD_COUNT errors
+ of one type, that type of errors will be suppressed for
+ LIMIT_UNSAFE_WARNING_ACTIVATION_TIMEOUT.
+ - When the time limit has been reached, all suppression is reset.
+
+ This means that if one gets many different types of errors, some of them
+ may be reset less than LIMIT_UNSAFE_WARNING_ACTIVATION_TIMEOUT. However at
+ least one error is disable for this time.
+
SYNOPSIS:
@params
- buf - buffer to hold the warning message text
unsafe_type - The type of unsafety.
- query - The actual query statement.
- TODO: Remove this function and implement a general service for all warnings
- that would prevent flooding the error log.
+ RETURN:
+ 0 0k to log
+ 1 Message suppressed
*/
-static void do_unsafe_limit_checkout(char* buf, int unsafe_type, char* query)
+
+static bool protect_against_unsafe_warning_flood(int unsafe_type)
{
- ulonglong now= 0;
- DBUG_ENTER("do_unsafe_limit_checkout");
- DBUG_ASSERT(unsafe_type == LEX::BINLOG_STMT_UNSAFE_LIMIT);
- limit_unsafe_warning_count++;
+ ulong count;
+ ulonglong now= my_interval_timer()/1000000000ULL;
+ DBUG_ENTER("protect_against_unsafe_warning_flood");
+
+ count= ++unsafe_warnings_count[unsafe_type];
+ total_unsafe_warnings_count++;
+
/*
INITIALIZING:
If this is the first time this function is called with log warning
enabled, the monitoring the unsafe warnings should start.
*/
- if (limit_unsafe_suppression_start_time == 0)
+ if (unsafe_suppression_start_time == 0)
{
- limit_unsafe_suppression_start_time= my_interval_timer()/10000000;
- print_unsafe_warning_to_log(unsafe_type, buf, query);
+ reset_binlog_unsafe_suppression(now);
+ DBUG_RETURN(0);
}
- else
+
+ /*
+ The following is true if we got too many errors or if the error was
+ already suppressed
+ */
+ if (count >= LIMIT_UNSAFE_WARNING_ACTIVATION_THRESHOLD_COUNT)
{
- if (!unsafe_warning_suppression_is_activated)
- print_unsafe_warning_to_log(unsafe_type, buf, query);
+ ulonglong diff_time= (now - unsafe_suppression_start_time);
- if (limit_unsafe_warning_count >=
- LIMIT_UNSAFE_WARNING_ACTIVATION_THRESHOLD_COUNT)
+ if (!unsafe_warning_suppression_active[unsafe_type])
{
- now= my_interval_timer()/10000000;
- if (!unsafe_warning_suppression_is_activated)
+ /*
+ ACTIVATION:
+ We got LIMIT_UNSAFE_WARNING_ACTIVATION_THRESHOLD_COUNT warnings in
+ less than LIMIT_UNSAFE_WARNING_ACTIVATION_TIMEOUT we activate the
+ suppression.
+ */
+ if (diff_time <= LIMIT_UNSAFE_WARNING_ACTIVATION_TIMEOUT)
{
- /*
- ACTIVATION:
- We got LIMIT_UNSAFE_WARNING_ACTIVATION_THRESHOLD_COUNT warnings in
- less than LIMIT_UNSAFE_WARNING_ACTIVATION_TIMEOUT we activate the
- suppression.
- */
- if ((now-limit_unsafe_suppression_start_time) <=
- LIMIT_UNSAFE_WARNING_ACTIVATION_TIMEOUT)
- {
- unsafe_warning_suppression_is_activated= true;
- DBUG_PRINT("info",("A warning flood has been detected and the limit \
-unsafety warning suppression has been activated."));
- }
- else
- {
- /*
- there is no flooding till now, therefore we restart the monitoring
- */
- limit_unsafe_suppression_start_time= my_interval_timer()/10000000;
- limit_unsafe_warning_count= 0;
- }
+ unsafe_warning_suppression_active[unsafe_type]= 1;
+ sql_print_information("Suppressing warnings of type '%s' for up to %d seconds because of flooding",
+ ER(LEX::binlog_stmt_unsafe_errcode[unsafe_type]),
+ LIMIT_UNSAFE_WARNING_ACTIVATION_TIMEOUT);
}
else
{
/*
- Print the suppression note and the unsafe warning.
- */
- sql_print_information("The following warning was suppressed %d times \
-during the last %d seconds in the error log",
- limit_unsafe_warning_count,
- (int)
- (now-limit_unsafe_suppression_start_time));
- print_unsafe_warning_to_log(unsafe_type, buf, query);
- /*
- DEACTIVATION: We got LIMIT_UNSAFE_WARNING_ACTIVATION_THRESHOLD_COUNT
- warnings in more than LIMIT_UNSAFE_WARNING_ACTIVATION_TIMEOUT, the
- suppression should be deactivated.
+ There is no flooding till now, therefore we restart the monitoring
*/
- if ((now - limit_unsafe_suppression_start_time) >
- LIMIT_UNSAFE_WARNING_ACTIVATION_TIMEOUT)
- {
- reset_binlog_unsafe_suppression();
- DBUG_PRINT("info",("The limit unsafety warning supression has been \
-deactivated"));
- }
+ reset_binlog_unsafe_suppression(now);
+ }
+ }
+ else
+ {
+ /* This type of warnings was suppressed */
+ if (diff_time > LIMIT_UNSAFE_WARNING_ACTIVATION_TIMEOUT)
+ {
+ ulong save_count= total_unsafe_warnings_count;
+ /* Print a suppression note and remove the suppression */
+ reset_binlog_unsafe_suppression(now);
+ sql_print_information("Suppressed %lu unsafe warnings during "
+ "the last %d seconds",
+ save_count, (int) diff_time);
}
- limit_unsafe_warning_count= 0;
}
}
- DBUG_VOID_RETURN;
+ DBUG_RETURN(unsafe_warning_suppression_active[unsafe_type]);
}
/**
@@ -5890,6 +6578,7 @@ deactivated"));
void THD::issue_unsafe_warnings()
{
char buf[MYSQL_ERRMSG_SIZE * 2];
+ uint32 unsafe_type_flags;
DBUG_ENTER("issue_unsafe_warnings");
/*
Ensure that binlog_unsafe_warning_flags is big enough to hold all
@@ -5897,8 +6586,10 @@ void THD::issue_unsafe_warnings()
*/
DBUG_ASSERT(LEX::BINLOG_STMT_UNSAFE_COUNT <=
sizeof(binlog_unsafe_warning_flags) * CHAR_BIT);
-
- uint32 unsafe_type_flags= binlog_unsafe_warning_flags;
+
+ if (!(unsafe_type_flags= binlog_unsafe_warning_flags))
+ DBUG_VOID_RETURN; // Nothing to do
+
/*
For each unsafe_type, check if the statement is unsafe in this way
and issue a warning.
@@ -5909,17 +6600,13 @@ void THD::issue_unsafe_warnings()
{
if ((unsafe_type_flags & (1 << unsafe_type)) != 0)
{
- push_warning_printf(this, MYSQL_ERROR::WARN_LEVEL_NOTE,
+ push_warning_printf(this, Sql_condition::WARN_LEVEL_NOTE,
ER_BINLOG_UNSAFE_STATEMENT,
ER(ER_BINLOG_UNSAFE_STATEMENT),
ER(LEX::binlog_stmt_unsafe_errcode[unsafe_type]));
- if (global_system_variables.log_warnings)
- {
- if (unsafe_type == LEX::BINLOG_STMT_UNSAFE_LIMIT)
- do_unsafe_limit_checkout( buf, unsafe_type, query());
- else //cases other than LIMIT unsafety
- print_unsafe_warning_to_log(unsafe_type, buf, query());
- }
+ if (global_system_variables.log_warnings > 0 &&
+ !protect_against_unsafe_warning_flood(unsafe_type))
+ print_unsafe_warning_to_log(unsafe_type, buf, query());
}
}
DBUG_VOID_RETURN;
@@ -5963,6 +6650,23 @@ int THD::binlog_query(THD::enum_binlog_query_type qtype, char const *query_arg,
#else
DBUG_ASSERT(query_arg && mysql_bin_log.is_open());
#endif
+
+ /* If this is withing a BEGIN ... COMMIT group, don't log it */
+ if (variables.option_bits & OPTION_GTID_BEGIN)
+ {
+ direct= 0;
+ is_trans= 1;
+ }
+ DBUG_PRINT("info", ("is_trans: %d direct: %d", is_trans, direct));
+
+ if (get_binlog_local_stmt_filter() == BINLOG_FILTER_SET)
+ {
+ /*
+ The current statement is to be ignored, and not written to
+ the binlog. Do not call issue_unsafe_warnings().
+ */
+ DBUG_RETURN(0);
+ }
/*
If we are not in prelocked mode, mysql_unlock_tables() will be
called after this binlog_query(), so we have to flush the pending
@@ -6074,6 +6778,335 @@ THD::signal_wakeup_ready()
}
+void THD::rgi_lock_temporary_tables()
+{
+ mysql_mutex_lock(&rgi_slave->rli->data_lock);
+ temporary_tables= rgi_slave->rli->save_temporary_tables;
+}
+
+void THD::rgi_unlock_temporary_tables()
+{
+ rgi_slave->rli->save_temporary_tables= temporary_tables;
+ mysql_mutex_unlock(&rgi_slave->rli->data_lock);
+}
+
+bool THD::rgi_have_temporary_tables()
+{
+ return rgi_slave->rli->save_temporary_tables != 0;
+}
+
+
+void
+wait_for_commit::reinit()
+{
+ subsequent_commits_list= NULL;
+ next_subsequent_commit= NULL;
+ waitee= NULL;
+ opaque_pointer= NULL;
+ wakeup_error= 0;
+ wakeup_subsequent_commits_running= false;
+ commit_started= false;
+#ifdef SAFE_MUTEX
+ /*
+ When using SAFE_MUTEX, the ordering between taking the LOCK_wait_commit
+ mutexes is checked. This causes a problem when we re-use a mutex, as then
+ the expected locking order may change.
+
+ So in this case, do a re-init of the mutex. In release builds, we want to
+ avoid the overhead of a re-init though.
+ */
+ mysql_mutex_destroy(&LOCK_wait_commit);
+ mysql_mutex_init(key_LOCK_wait_commit, &LOCK_wait_commit, MY_MUTEX_INIT_FAST);
+#endif
+}
+
+
+wait_for_commit::wait_for_commit()
+{
+ mysql_mutex_init(key_LOCK_wait_commit, &LOCK_wait_commit, MY_MUTEX_INIT_FAST);
+ mysql_cond_init(key_COND_wait_commit, &COND_wait_commit, 0);
+ reinit();
+}
+
+
+wait_for_commit::~wait_for_commit()
+{
+ /*
+ Since we do a dirty read of the waiting_for_commit flag in
+ wait_for_prior_commit() and in unregister_wait_for_prior_commit(), we need
+ to take extra care before freeing the wait_for_commit object.
+
+ It is possible for the waitee to be pre-empted inside wakeup(), just after
+ it has cleared the waiting_for_commit flag and before it has released the
+ LOCK_wait_commit mutex. And then it is possible for the waiter to find the
+ flag cleared in wait_for_prior_commit() and go finish up things and
+ de-allocate the LOCK_wait_commit and COND_wait_commit objects before the
+ waitee has time to be re-scheduled and finish unlocking the mutex and
+ signalling the condition. This would lead to the waitee accessing no
+ longer valid memory.
+
+ To prevent this, we do an extra lock/unlock of the mutex here before
+ deallocation; this makes certain that any waitee has completed wakeup()
+ first.
+ */
+ mysql_mutex_lock(&LOCK_wait_commit);
+ mysql_mutex_unlock(&LOCK_wait_commit);
+
+ mysql_mutex_destroy(&LOCK_wait_commit);
+ mysql_cond_destroy(&COND_wait_commit);
+}
+
+
+void
+wait_for_commit::wakeup(int wakeup_error)
+{
+ /*
+ We signal each waiter on their own condition and mutex (rather than using
+ pthread_cond_broadcast() or something like that).
+
+ Otherwise we would need to somehow ensure that they were done
+ waking up before we could allow this THD to be destroyed, which would
+ be annoying and unnecessary.
+
+ Note that wakeup_subsequent_commits2() depends on this function being a
+ full memory barrier (it is, because it takes a mutex lock).
+
+ */
+ mysql_mutex_lock(&LOCK_wait_commit);
+ waitee= NULL;
+ this->wakeup_error= wakeup_error;
+ /*
+ Note that it is critical that the mysql_cond_signal() here is done while
+ still holding the mutex. As soon as we release the mutex, the waiter might
+ deallocate the condition object.
+ */
+ mysql_cond_signal(&COND_wait_commit);
+ mysql_mutex_unlock(&LOCK_wait_commit);
+}
+
+
+/*
+ Register that the next commit of this THD should wait to complete until
+ commit in another THD (the waitee) has completed.
+
+ The wait may occur explicitly, with the waiter sitting in
+ wait_for_prior_commit() until the waitee calls wakeup_subsequent_commits().
+
+ Alternatively, the TC (eg. binlog) may do the commits of both waitee and
+ waiter at once during group commit, resolving both of them in the right
+ order.
+
+ Only one waitee can be registered for a waiter; it must be removed by
+ wait_for_prior_commit() or unregister_wait_for_prior_commit() before a new
+ one is registered. But it is ok for several waiters to register a wait for
+ the same waitee. It is also permissible for one THD to be both a waiter and
+ a waitee at the same time.
+*/
+void
+wait_for_commit::register_wait_for_prior_commit(wait_for_commit *waitee)
+{
+ DBUG_ASSERT(!this->waitee /* No prior registration allowed */);
+ wakeup_error= 0;
+ this->waitee= waitee;
+
+ mysql_mutex_lock(&waitee->LOCK_wait_commit);
+ /*
+ If waitee is in the middle of wakeup, then there is nothing to wait for,
+ so we need not register. This is necessary to avoid a race in unregister,
+ see comments on wakeup_subsequent_commits2() for details.
+ */
+ if (waitee->wakeup_subsequent_commits_running)
+ this->waitee= NULL;
+ else
+ {
+ /*
+ Put ourself at the head of the waitee's list of transactions that must
+ wait for it to commit first.
+ */
+ this->next_subsequent_commit= waitee->subsequent_commits_list;
+ waitee->subsequent_commits_list= this;
+ }
+ mysql_mutex_unlock(&waitee->LOCK_wait_commit);
+}
+
+
+/*
+ Wait for commit of another transaction to complete, as already registered
+ with register_wait_for_prior_commit(). If the commit already completed,
+ returns immediately.
+*/
+int
+wait_for_commit::wait_for_prior_commit2(THD *thd)
+{
+ PSI_stage_info old_stage;
+ wait_for_commit *loc_waitee;
+
+ mysql_mutex_lock(&LOCK_wait_commit);
+ DEBUG_SYNC(thd, "wait_for_prior_commit_waiting");
+ thd->ENTER_COND(&COND_wait_commit, &LOCK_wait_commit,
+ &stage_waiting_for_prior_transaction_to_commit,
+ &old_stage);
+ while ((loc_waitee= this->waitee) && !thd->check_killed())
+ mysql_cond_wait(&COND_wait_commit, &LOCK_wait_commit);
+ if (!loc_waitee)
+ {
+ if (wakeup_error)
+ my_error(ER_PRIOR_COMMIT_FAILED, MYF(0));
+ goto end;
+ }
+ /*
+ Wait was interrupted by kill. We need to unregister our wait and give the
+ error. But if a wakeup is already in progress, then we must ignore the
+ kill and not give error, otherwise we get inconsistency between waitee and
+ waiter as to whether we succeed or fail (eg. we may roll back but waitee
+ might attempt to commit both us and any subsequent commits waiting for us).
+ */
+ mysql_mutex_lock(&loc_waitee->LOCK_wait_commit);
+ if (loc_waitee->wakeup_subsequent_commits_running)
+ {
+ /* We are being woken up; ignore the kill and just wait. */
+ mysql_mutex_unlock(&loc_waitee->LOCK_wait_commit);
+ do
+ {
+ mysql_cond_wait(&COND_wait_commit, &LOCK_wait_commit);
+ } while (this->waitee);
+ if (wakeup_error)
+ my_error(ER_PRIOR_COMMIT_FAILED, MYF(0));
+ goto end;
+ }
+ remove_from_list(&loc_waitee->subsequent_commits_list);
+ mysql_mutex_unlock(&loc_waitee->LOCK_wait_commit);
+ this->waitee= NULL;
+
+ wakeup_error= thd->killed_errno();
+ if (!wakeup_error)
+ wakeup_error= ER_QUERY_INTERRUPTED;
+ my_message(wakeup_error, ER(wakeup_error), MYF(0));
+ thd->EXIT_COND(&old_stage);
+ /*
+ Must do the DEBUG_SYNC() _after_ exit_cond(), as DEBUG_SYNC is not safe to
+ use within enter_cond/exit_cond.
+ */
+ DEBUG_SYNC(thd, "wait_for_prior_commit_killed");
+ return wakeup_error;
+
+end:
+ thd->EXIT_COND(&old_stage);
+ return wakeup_error;
+}
+
+
+/*
+ Wakeup anyone waiting for us to have committed.
+
+ Note about locking:
+
+ We have a potential race or deadlock between wakeup_subsequent_commits() in
+ the waitee and unregister_wait_for_prior_commit() in the waiter.
+
+ Both waiter and waitee needs to take their own lock before it is safe to take
+ a lock on the other party - else the other party might disappear and invalid
+ memory data could be accessed. But if we take the two locks in different
+ order, we may end up in a deadlock.
+
+ The waiter needs to lock the waitee to delete itself from the list in
+ unregister_wait_for_prior_commit(). Thus wakeup_subsequent_commits() can not
+ hold its own lock while locking waiters, as this could lead to deadlock.
+
+ So we need to prevent unregister_wait_for_prior_commit() running while wakeup
+ is in progress - otherwise the unregister could complete before the wakeup,
+ leading to incorrect spurious wakeup or accessing invalid memory.
+
+ However, if we are in the middle of running wakeup_subsequent_commits(), then
+ there is no need for unregister_wait_for_prior_commit() in the first place -
+ the waiter can just do a normal wait_for_prior_commit(), as it will be
+ immediately woken up.
+
+ So the solution to the potential race/deadlock is to set a flag in the waitee
+ that wakeup_subsequent_commits() is in progress. When this flag is set,
+ unregister_wait_for_prior_commit() becomes just wait_for_prior_commit().
+
+ Then also register_wait_for_prior_commit() needs to check if
+ wakeup_subsequent_commits() is running, and skip the registration if
+ so. This is needed in case a new waiter manages to register itself and
+ immediately try to unregister while wakeup_subsequent_commits() is
+ running. Else the new waiter would also wait rather than unregister, but it
+ would not be woken up until next wakeup, which could be potentially much
+ later than necessary.
+*/
+
+void
+wait_for_commit::wakeup_subsequent_commits2(int wakeup_error)
+{
+ wait_for_commit *waiter;
+
+ mysql_mutex_lock(&LOCK_wait_commit);
+ wakeup_subsequent_commits_running= true;
+ waiter= subsequent_commits_list;
+ subsequent_commits_list= NULL;
+ mysql_mutex_unlock(&LOCK_wait_commit);
+
+ while (waiter)
+ {
+ /*
+ Important: we must grab the next pointer before waking up the waiter;
+ once the wakeup is done, the field could be invalidated at any time.
+ */
+ wait_for_commit *next= waiter->next_subsequent_commit;
+ waiter->wakeup(wakeup_error);
+ waiter= next;
+ }
+
+ /*
+ We need a full memory barrier between walking the list above, and clearing
+ the flag wakeup_subsequent_commits_running below. This barrier is needed
+ to ensure that no other thread will start to modify the list pointers
+ before we are done traversing the list.
+
+ But wait_for_commit::wakeup() does a full memory barrier already (it locks
+ a mutex), so no extra explicit barrier is needed here.
+ */
+ wakeup_subsequent_commits_running= false;
+ DBUG_EXECUTE_IF("inject_wakeup_subsequent_commits_sleep", my_sleep(21000););
+}
+
+
+/* Cancel a previously registered wait for another THD to commit before us. */
+void
+wait_for_commit::unregister_wait_for_prior_commit2()
+{
+ wait_for_commit *loc_waitee;
+
+ mysql_mutex_lock(&LOCK_wait_commit);
+ if ((loc_waitee= this->waitee))
+ {
+ mysql_mutex_lock(&loc_waitee->LOCK_wait_commit);
+ if (loc_waitee->wakeup_subsequent_commits_running)
+ {
+ /*
+ When a wakeup is running, we cannot safely remove ourselves from the
+ list without corrupting it. Instead we can just wait, as wakeup is
+ already in progress and will thus be immediate.
+
+ See comments on wakeup_subsequent_commits2() for more details.
+ */
+ mysql_mutex_unlock(&loc_waitee->LOCK_wait_commit);
+ while (this->waitee)
+ mysql_cond_wait(&COND_wait_commit, &LOCK_wait_commit);
+ }
+ else
+ {
+ /* Remove ourselves from the list in the waitee. */
+ remove_from_list(&loc_waitee->subsequent_commits_list);
+ mysql_mutex_unlock(&loc_waitee->LOCK_wait_commit);
+ this->waitee= NULL;
+ }
+ }
+ wakeup_error= 0;
+ mysql_mutex_unlock(&LOCK_wait_commit);
+}
+
+
bool Discrete_intervals_list::append(ulonglong start, ulonglong val,
ulonglong incr)
{
diff --git a/sql/sql_class.h b/sql/sql_class.h
index 6c072fe513e..a2bbe0861d1 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -15,26 +15,19 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */
-
#ifndef SQL_CLASS_INCLUDED
#define SQL_CLASS_INCLUDED
/* Classes in mysql */
-#ifdef USE_PRAGMA_INTERFACE
-#pragma interface /* gcc class implementation */
-#endif
-
#include "my_global.h" /* NO_EMBEDDED_ACCESS_CHECKS */
-#ifdef MYSQL_SERVER
-#include "unireg.h" // REQUIRED: for other includes
-#endif
#include <waiting_threads.h>
#include "sql_const.h"
#include <mysql/plugin_audit.h>
#include "log.h"
#include "rpl_tblmap.h"
#include "mdl.h"
+#include "field.h" // Create_field
#include "probes_mysql.h"
#include "sql_locale.h" /* my_locale_st */
#include "sql_profile.h" /* PROFILING */
@@ -43,7 +36,27 @@
#include "violite.h" /* vio_is_connected */
#include "thr_lock.h" /* thr_lock_type, THR_LOCK_DATA,
THR_LOCK_INFO */
-
+#include "sql_digest_stream.h" // sql_digest_state
+
+#include <mysql/psi/mysql_stage.h>
+#include <mysql/psi/mysql_statement.h>
+#include <mysql/psi/mysql_idle.h>
+#include <mysql/psi/mysql_table.h>
+#include <mysql_com_server.h>
+
+extern "C"
+void set_thd_stage_info(void *thd,
+ const PSI_stage_info *new_stage,
+ PSI_stage_info *old_stage,
+ const char *calling_func,
+ const char *calling_file,
+ const unsigned int calling_line);
+
+#define THD_STAGE_INFO(thd, stage) \
+ (thd)->enter_stage(& stage, NULL, __func__, __FILE__, __LINE__)
+
+#include "my_apc.h"
+#include "rpl_gtid.h"
#ifdef WITH_WSREP
#include "wsrep_mysqld.h"
@@ -59,6 +72,8 @@ struct wsrep_thd_shadow {
#endif
class Reprepare_observer;
class Relay_log_info;
+struct rpl_group_info;
+class Rpl_filter;
class Query_log_event;
class Load_log_event;
class Slave_log_event;
@@ -68,18 +83,21 @@ class Lex_input_stream;
class Parser_state;
class Rows_log_event;
class Sroutine_hash_entry;
-class User_level_lock;
class user_var_entry;
struct Trans_binlog_info;
+class rpl_io_thread_info;
+class rpl_sql_thread_info;
-enum enum_enable_or_disable { LEAVE_AS_IS, ENABLE, DISABLE };
enum enum_ha_read_modes { RFIRST, RNEXT, RPREV, RLAST, RKEY, RNEXT_SAME };
enum enum_duplicates { DUP_ERROR, DUP_REPLACE, DUP_UPDATE };
enum enum_delay_key_write { DELAY_KEY_WRITE_NONE, DELAY_KEY_WRITE_ON,
DELAY_KEY_WRITE_ALL };
enum enum_slave_exec_mode { SLAVE_EXEC_MODE_STRICT,
SLAVE_EXEC_MODE_IDEMPOTENT,
- SLAVE_EXEC_MODE_LAST_BIT};
+ SLAVE_EXEC_MODE_LAST_BIT };
+enum enum_slave_run_triggers_for_rbr { SLAVE_RUN_TRIGGERS_FOR_RBR_NO,
+ SLAVE_RUN_TRIGGERS_FOR_RBR_YES,
+ SLAVE_RUN_TRIGGERS_FOR_RBR_LOGGING};
enum enum_slave_type_conversions { SLAVE_TYPE_CONVERSIONS_ALL_LOSSY,
SLAVE_TYPE_CONVERSIONS_ALL_NON_LOSSY};
enum enum_mark_columns
@@ -87,45 +105,47 @@ enum enum_mark_columns
enum enum_filetype { FILETYPE_CSV, FILETYPE_XML };
/* Bits for different SQL modes modes (including ANSI mode) */
-#define MODE_REAL_AS_FLOAT 1
-#define MODE_PIPES_AS_CONCAT 2
-#define MODE_ANSI_QUOTES 4
-#define MODE_IGNORE_SPACE 8
-#define MODE_IGNORE_BAD_TABLE_OPTIONS 16
-#define MODE_ONLY_FULL_GROUP_BY 32
-#define MODE_NO_UNSIGNED_SUBTRACTION 64
-#define MODE_NO_DIR_IN_CREATE 128
-#define MODE_POSTGRESQL 256
-#define MODE_ORACLE 512
-#define MODE_MSSQL 1024
-#define MODE_DB2 2048
-#define MODE_MAXDB 4096
-#define MODE_NO_KEY_OPTIONS 8192
-#define MODE_NO_TABLE_OPTIONS 16384
-#define MODE_NO_FIELD_OPTIONS 32768
-#define MODE_MYSQL323 65536L
-#define MODE_MYSQL40 (MODE_MYSQL323*2)
-#define MODE_ANSI (MODE_MYSQL40*2)
-#define MODE_NO_AUTO_VALUE_ON_ZERO (MODE_ANSI*2)
-#define MODE_NO_BACKSLASH_ESCAPES (MODE_NO_AUTO_VALUE_ON_ZERO*2)
-#define MODE_STRICT_TRANS_TABLES (MODE_NO_BACKSLASH_ESCAPES*2)
-#define MODE_STRICT_ALL_TABLES (MODE_STRICT_TRANS_TABLES*2)
-#define MODE_NO_ZERO_IN_DATE (MODE_STRICT_ALL_TABLES*2)
-#define MODE_NO_ZERO_DATE (MODE_NO_ZERO_IN_DATE*2)
-#define MODE_INVALID_DATES (MODE_NO_ZERO_DATE*2)
-#define MODE_ERROR_FOR_DIVISION_BY_ZERO (MODE_INVALID_DATES*2)
-#define MODE_TRADITIONAL (MODE_ERROR_FOR_DIVISION_BY_ZERO*2)
-#define MODE_NO_AUTO_CREATE_USER (MODE_TRADITIONAL*2)
-#define MODE_HIGH_NOT_PRECEDENCE (MODE_NO_AUTO_CREATE_USER*2)
-#define MODE_NO_ENGINE_SUBSTITUTION (MODE_HIGH_NOT_PRECEDENCE*2)
-#define MODE_PAD_CHAR_TO_FULL_LENGTH (ULL(1) << 31)
+#define MODE_REAL_AS_FLOAT (1ULL << 0)
+#define MODE_PIPES_AS_CONCAT (1ULL << 1)
+#define MODE_ANSI_QUOTES (1ULL << 2)
+#define MODE_IGNORE_SPACE (1ULL << 3)
+#define MODE_IGNORE_BAD_TABLE_OPTIONS (1ULL << 4)
+#define MODE_ONLY_FULL_GROUP_BY (1ULL << 5)
+#define MODE_NO_UNSIGNED_SUBTRACTION (1ULL << 6)
+#define MODE_NO_DIR_IN_CREATE (1ULL << 7)
+#define MODE_POSTGRESQL (1ULL << 8)
+#define MODE_ORACLE (1ULL << 9)
+#define MODE_MSSQL (1ULL << 10)
+#define MODE_DB2 (1ULL << 11)
+#define MODE_MAXDB (1ULL << 12)
+#define MODE_NO_KEY_OPTIONS (1ULL << 13)
+#define MODE_NO_TABLE_OPTIONS (1ULL << 14)
+#define MODE_NO_FIELD_OPTIONS (1ULL << 15)
+#define MODE_MYSQL323 (1ULL << 16)
+#define MODE_MYSQL40 (1ULL << 17)
+#define MODE_ANSI (1ULL << 18)
+#define MODE_NO_AUTO_VALUE_ON_ZERO (1ULL << 19)
+#define MODE_NO_BACKSLASH_ESCAPES (1ULL << 20)
+#define MODE_STRICT_TRANS_TABLES (1ULL << 21)
+#define MODE_STRICT_ALL_TABLES (1ULL << 22)
+#define MODE_NO_ZERO_IN_DATE (1ULL << 23)
+#define MODE_NO_ZERO_DATE (1ULL << 24)
+#define MODE_INVALID_DATES (1ULL << 25)
+#define MODE_ERROR_FOR_DIVISION_BY_ZERO (1ULL << 26)
+#define MODE_TRADITIONAL (1ULL << 27)
+#define MODE_NO_AUTO_CREATE_USER (1ULL << 28)
+#define MODE_HIGH_NOT_PRECEDENCE (1ULL << 29)
+#define MODE_NO_ENGINE_SUBSTITUTION (1ULL << 30)
+#define MODE_PAD_CHAR_TO_FULL_LENGTH (1ULL << 31)
/* Bits for different old style modes */
-#define OLD_MODE_NO_DUP_KEY_WARNINGS_WITH_IGNORE 1
-#define OLD_MODE_NO_PROGRESS_INFO 2
+#define OLD_MODE_NO_DUP_KEY_WARNINGS_WITH_IGNORE (1 << 0)
+#define OLD_MODE_NO_PROGRESS_INFO (1 << 1)
+#define OLD_MODE_ZERO_DATE_TIME_CAST (1 << 2)
extern char internal_table_name[2];
extern char empty_c_string[1];
+extern LEX_STRING EMPTY_STR;
extern MYSQL_PLUGIN_IMPORT const char **errmesg;
extern bool volatile shutdown_in_progress;
@@ -165,9 +185,6 @@ public:
};
-#define TC_LOG_PAGE_SIZE 8192
-#define TC_LOG_MIN_SIZE (3*TC_LOG_PAGE_SIZE)
-
#define TC_HEURISTIC_RECOVER_COMMIT 1
#define TC_HEURISTIC_RECOVER_ROLLBACK 2
extern ulong tc_heuristic_recover;
@@ -239,11 +256,15 @@ public:
class Alter_drop :public Sql_alloc {
public:
- enum drop_type {KEY, COLUMN };
+ enum drop_type {KEY, COLUMN, FOREIGN_KEY };
const char *name;
enum drop_type type;
- Alter_drop(enum drop_type par_type,const char *par_name)
- :name(par_name), type(par_type) {}
+ bool drop_if_exists;
+ Alter_drop(enum drop_type par_type,const char *par_name, bool par_exists)
+ :name(par_name), type(par_type), drop_if_exists(par_exists)
+ {
+ DBUG_ASSERT(par_name != NULL);
+ }
/**
Used to make a clone of this object for ALTER/CREATE TABLE
@sa comment for Key_part_spec::clone
@@ -277,20 +298,23 @@ public:
LEX_STRING name;
engine_option_value *option_list;
bool generated;
+ bool create_if_not_exists;
Key(enum Keytype type_par, const LEX_STRING &name_arg,
KEY_CREATE_INFO *key_info_arg,
bool generated_arg, List<Key_part_spec> &cols,
- engine_option_value *create_opt)
+ engine_option_value *create_opt, bool if_not_exists_opt)
:type(type_par), key_create_info(*key_info_arg), columns(cols),
- name(name_arg), option_list(create_opt), generated(generated_arg)
+ name(name_arg), option_list(create_opt), generated(generated_arg),
+ create_if_not_exists(if_not_exists_opt)
{}
Key(enum Keytype type_par, const char *name_arg, size_t name_len_arg,
KEY_CREATE_INFO *key_info_arg, bool generated_arg,
List<Key_part_spec> &cols,
- engine_option_value *create_opt)
+ engine_option_value *create_opt, bool if_not_exists_opt)
:type(type_par), key_create_info(*key_info_arg), columns(cols),
- option_list(create_opt), generated(generated_arg)
+ option_list(create_opt), generated(generated_arg),
+ create_if_not_exists(if_not_exists_opt)
{
name.str= (char *)name_arg;
name.length= name_len_arg;
@@ -307,7 +331,6 @@ public:
{ return new (mem_root) Key(*this, mem_root); }
};
-class Table_ident;
class Foreign_key: public Key {
public:
@@ -316,18 +339,25 @@ public:
enum fk_option { FK_OPTION_UNDEF, FK_OPTION_RESTRICT, FK_OPTION_CASCADE,
FK_OPTION_SET_NULL, FK_OPTION_NO_ACTION, FK_OPTION_DEFAULT};
- Table_ident *ref_table;
+ LEX_STRING ref_db;
+ LEX_STRING ref_table;
List<Key_part_spec> ref_columns;
uint delete_opt, update_opt, match_opt;
Foreign_key(const LEX_STRING &name_arg, List<Key_part_spec> &cols,
- Table_ident *table, List<Key_part_spec> &ref_cols,
- uint delete_opt_arg, uint update_opt_arg, uint match_opt_arg)
- :Key(FOREIGN_KEY, name_arg, &default_key_create_info, 0, cols, NULL),
- ref_table(table), ref_columns(ref_cols),
+ const LEX_STRING &ref_db_arg, const LEX_STRING &ref_table_arg,
+ List<Key_part_spec> &ref_cols,
+ uint delete_opt_arg, uint update_opt_arg, uint match_opt_arg,
+ bool if_not_exists_opt)
+ :Key(FOREIGN_KEY, name_arg, &default_key_create_info, 0, cols, NULL,
+ if_not_exists_opt),
+ ref_db(ref_db_arg), ref_table(ref_table_arg), ref_columns(ref_cols),
delete_opt(delete_opt_arg), update_opt(update_opt_arg),
match_opt(match_opt_arg)
- {}
- Foreign_key(const Foreign_key &rhs, MEM_ROOT *mem_root);
+ {
+ // We don't check for duplicate FKs.
+ key_create_info.check_for_duplicate_indexes= false;
+ }
+ Foreign_key(const Foreign_key &rhs, MEM_ROOT *mem_root);
/**
Used to make a clone of this object for ALTER/CREATE TABLE
@sa comment for Key_part_spec::clone
@@ -450,7 +480,8 @@ extern int killed_errno(killed_state killed);
enum killed_type
{
KILL_TYPE_ID,
- KILL_TYPE_USER
+ KILL_TYPE_USER,
+ KILL_TYPE_QUERY
};
#include "sql_lex.h" /* Must be here */
@@ -465,6 +496,8 @@ class Time_zone;
#define THD_CHECK_SENTRY(thd) DBUG_ASSERT(thd->dbug_sentry == THD_SENTRY_MAGIC)
+typedef ulonglong sql_mode_t;
+
typedef struct system_variables
{
/*
@@ -488,8 +521,8 @@ typedef struct system_variables
ulonglong tmp_table_size;
ulonglong long_query_time;
ulonglong optimizer_switch;
- ulonglong sql_mode; ///< which non-standard SQL behaviour should be enabled
- ulonglong old_behavior; ///< which old SQL behaviour should be enabled
+ sql_mode_t sql_mode; ///< which non-standard SQL behaviour should be enabled
+ sql_mode_t old_behavior; ///< which old SQL behaviour should be enabled
ulonglong option_bits; ///< OPTION_xxx constants, e.g. OPTION_PROFILING
ulonglong join_buff_space_limit;
ulonglong log_slow_filter;
@@ -498,6 +531,15 @@ typedef struct system_variables
ulonglong join_buff_size;
ulonglong sortbuff_size;
ulonglong group_concat_max_len;
+ ulonglong default_regex_flags;
+
+ /**
+ Place holders to store Multi-source variables in sys_var.cc during
+ update and show of variables.
+ */
+ ulonglong slave_skip_counter;
+ ulonglong max_relay_log_size;
+
ha_rows select_limit;
ha_rows max_join_size;
ha_rows expensive_subquery_limit;
@@ -520,6 +562,11 @@ typedef struct system_variables
ulong net_write_timeout;
ulong optimizer_prune_level;
ulong optimizer_search_depth;
+ ulong optimizer_selectivity_sampling_limit;
+ ulong optimizer_use_condition_selectivity;
+ ulong use_stat_tables;
+ ulong histogram_size;
+ ulong histogram_type;
ulong preload_buff_size;
ulong profiling_history_size;
ulong read_buff_size;
@@ -549,12 +596,23 @@ typedef struct system_variables
ulong tx_isolation;
ulong updatable_views_with_limit;
int max_user_connections;
+ ulong server_id;
/**
In slave thread we need to know in behalf of which
thread the query is being run to replicate temp tables properly
*/
my_thread_id pseudo_thread_id;
+ /**
+ When replicating an event group with GTID, keep these values around so
+ slave binlog can receive the same GTID as the original.
+ */
+ uint32 gtid_domain_id;
+ uint64 gtid_seq_no;
+ /**
+ Default transaction access mode. READ ONLY (true) or READ WRITE (false).
+ */
+ my_bool tx_read_only;
my_bool low_priority_updates;
my_bool query_cache_wlock_invalidate;
my_bool engine_condition_pushdown;
@@ -578,6 +636,9 @@ typedef struct system_variables
CHARSET_INFO *collation_database;
CHARSET_INFO *collation_connection;
+ /* Names. These will be allocated in buffers in thd */
+ LEX_STRING default_master_connection;
+
/* Error messages */
MY_LOCALE *lc_messages;
/* Locale Support */
@@ -597,6 +658,7 @@ typedef struct system_variables
my_bool wsrep_dirty_reads;
uint wsrep_sync_wait;
ulong wsrep_retry_autocommit;
+ ulong wsrep_OSU_method;
#endif
double long_query_time_double;
@@ -614,8 +676,9 @@ typedef struct system_status_var
{
ulong com_other;
ulong com_stat[(uint) SQLCOM_END];
- ulong created_tmp_disk_tables;
- ulong created_tmp_tables;
+ ulong com_register_slave;
+ ulong created_tmp_disk_tables_;
+ ulong created_tmp_tables_;
ulong ha_commit_count;
ulong ha_delete_count;
ulong ha_read_first_count;
@@ -647,23 +710,25 @@ typedef struct system_status_var
ulong ha_discover_count;
ulong ha_savepoint_count;
ulong ha_savepoint_rollback_count;
+ ulong ha_external_lock_count;
ulong net_big_packet_count;
ulong opened_tables;
ulong opened_shares;
ulong opened_views; /* +1 opening a view */
- ulong select_full_join_count;
- ulong select_full_range_join_count;
- ulong select_range_count;
- ulong select_range_check_count;
- ulong select_scan_count;
+ ulong select_full_join_count_;
+ ulong select_full_range_join_count_;
+ ulong select_range_count_;
+ ulong select_range_check_count_;
+ ulong select_scan_count_;
ulong executed_triggers;
ulong long_query_count;
- ulong filesort_merge_passes;
- ulong filesort_range_count;
- ulong filesort_rows;
- ulong filesort_scan_count;
+ ulong filesort_merge_passes_;
+ ulong filesort_range_count_;
+ ulong filesort_rows_;
+ ulong filesort_scan_count_;
+ ulong filesort_pq_sorts_;
/* Prepared statements and binary protocol */
ulong com_stmt_prepare;
ulong com_stmt_reprepare;
@@ -704,6 +769,8 @@ typedef struct system_status_var
ulonglong binlog_bytes_written;
double last_query_cost;
double cpu_time, busy_time;
+ /* Don't initialize */
+ volatile int64 memory_used; /* This shouldn't be accumulated */
} STATUS_VAR;
/*
@@ -713,12 +780,47 @@ typedef struct system_status_var
*/
#define last_system_status_var questions
+#define last_cleared_system_status_var memory_used
+
+/*
+ Global status variables
+*/
+
+extern ulong feature_files_opened_with_delayed_keys;
+
void add_to_status(STATUS_VAR *to_var, STATUS_VAR *from_var);
void add_diff_to_status(STATUS_VAR *to_var, STATUS_VAR *from_var,
STATUS_VAR *dec_var);
+/**
+ Get collation by name, send error to client on failure.
+ @param name Collation name
+ @param name_cs Character set of the name string
+ @return
+ @retval NULL on error
+ @retval Pointter to CHARSET_INFO with the given name on success
+*/
+inline CHARSET_INFO *
+mysqld_collation_get_by_name(const char *name,
+ CHARSET_INFO *name_cs= system_charset_info)
+{
+ CHARSET_INFO *cs;
+ MY_CHARSET_LOADER loader;
+ my_charset_loader_init_mysys(&loader);
+ if (!(cs= my_collation_get_by_name(&loader, name, MYF(0))))
+ {
+ ErrConvString err(name, name_cs);
+ my_error(ER_UNKNOWN_COLLATION, MYF(0), err.ptr());
+ if (loader.error[0])
+ push_warning_printf(current_thd,
+ Sql_condition::WARN_LEVEL_WARN,
+ ER_UNKNOWN_COLLATION, "%s", loader.error);
+ }
+ return cs;
+}
+
#ifdef MYSQL_SERVER
void free_tmp_table(THD *thd, TABLE *entry);
@@ -1041,6 +1143,8 @@ public:
char proxy_user[USERNAME_LENGTH + MAX_HOSTNAME + 5];
/* The host privilege we are using */
char priv_host[MAX_HOSTNAME];
+ /* The role privilege we are using */
+ char priv_role[USERNAME_LENGTH];
/* The external user (if available) */
char *external_user;
/* points to host if host is available, otherwise points to ip */
@@ -1280,7 +1384,9 @@ enum enum_thread_type
SYSTEM_THREAD_SLAVE_SQL= 4,
SYSTEM_THREAD_NDBCLUSTER_BINLOG= 8,
SYSTEM_THREAD_EVENT_SCHEDULER= 16,
- SYSTEM_THREAD_EVENT_WORKER= 32
+ SYSTEM_THREAD_EVENT_WORKER= 32,
+ SYSTEM_THREAD_BINLOG_BACKGROUND= 64,
+ SYSTEM_THREAD_SLAVE_INIT= 128,
};
inline char const *
@@ -1345,9 +1451,9 @@ public:
virtual bool handle_condition(THD *thd,
uint sql_errno,
const char* sqlstate,
- MYSQL_ERROR::enum_warning_level level,
+ Sql_condition::enum_warning_level level,
const char* msg,
- MYSQL_ERROR ** cond_hdl) = 0;
+ Sql_condition ** cond_hdl) = 0;
private:
Internal_error_handler *m_prev_internal_handler;
@@ -1366,9 +1472,9 @@ public:
bool handle_condition(THD *thd,
uint sql_errno,
const char* sqlstate,
- MYSQL_ERROR::enum_warning_level level,
+ Sql_condition::enum_warning_level level,
const char* msg,
- MYSQL_ERROR ** cond_hdl)
+ Sql_condition ** cond_hdl)
{
/* Ignore error */
return TRUE;
@@ -1393,9 +1499,9 @@ public:
bool handle_condition(THD *thd,
uint sql_errno,
const char* sqlstate,
- MYSQL_ERROR::enum_warning_level level,
+ Sql_condition::enum_warning_level level,
const char* msg,
- MYSQL_ERROR ** cond_hdl);
+ Sql_condition ** cond_hdl);
private:
};
@@ -1448,13 +1554,16 @@ public:
m_reopen_array(NULL),
m_locked_tables_count(0)
{
- init_sql_alloc(&m_locked_tables_root, MEM_ROOT_BLOCK_SIZE, 0);
+ init_sql_alloc(&m_locked_tables_root, MEM_ROOT_BLOCK_SIZE, 0,
+ MYF(MY_THREAD_SPECIFIC));
}
void unlock_locked_tables(THD *thd);
+ void unlock_locked_table(THD *thd, MDL_ticket *mdl_ticket);
~Locked_tables_list()
{
- unlock_locked_tables(0);
+ reset();
}
+ void reset();
bool init_locked_tables(THD *thd);
TABLE_LIST *locked_tables() { return m_locked_tables; }
void unlink_from_list(THD *thd, TABLE_LIST *table_list,
@@ -1463,6 +1572,9 @@ public:
MYSQL_LOCK *lock,
size_t reopen_count);
bool reopen_tables(THD *thd);
+ bool restore_lock(THD *thd, TABLE_LIST *dst_table_list, TABLE *table,
+ MYSQL_LOCK *lock);
+ void add_back_last_deleted_lock(TABLE_LIST *dst_table_list);
};
@@ -1552,8 +1664,163 @@ private:
};
+/*
+ Class to facilitate the commit of one transactions waiting for the commit of
+ another transaction to complete first.
+
+ This is used during (parallel) replication, to allow different transactions
+ to be applied in parallel, but still commit in order.
+
+ The transaction that wants to wait for a prior commit must first register
+ to wait with register_wait_for_prior_commit(waitee). Such registration
+ must be done holding the waitee->LOCK_wait_commit, to prevent the other
+ THD from disappearing during the registration.
+
+ Then during commit, if a THD is registered to wait, it will call
+ wait_for_prior_commit() as part of ha_commit_trans(). If no wait is
+ registered, or if the waitee for has already completed commit, then
+ wait_for_prior_commit() returns immediately.
+
+ And when a THD that may be waited for has completed commit (more precisely
+ commit_ordered()), then it must call wakeup_subsequent_commits() to wake
+ up any waiters. Note that this must be done at a point that is guaranteed
+ to be later than any waiters registering themselves. It is safe to call
+ wakeup_subsequent_commits() multiple times, as waiters are removed from
+ registration as part of the wakeup.
+
+ The reason for separate register and wait calls is that this allows to
+ register the wait early, at a point where the waited-for THD is known to
+ exist. And then the actual wait can be done much later, where the
+ waited-for THD may have been long gone. By registering early, the waitee
+ can signal before disappearing.
+*/
+struct wait_for_commit
+{
+ /*
+ The LOCK_wait_commit protects the fields subsequent_commits_list and
+ wakeup_subsequent_commits_running (for a waitee), and the pointer
+ waiterr and associated COND_wait_commit (for a waiter).
+ */
+ mysql_mutex_t LOCK_wait_commit;
+ mysql_cond_t COND_wait_commit;
+ /* List of threads that did register_wait_for_prior_commit() on us. */
+ wait_for_commit *subsequent_commits_list;
+ /* Link field for entries in subsequent_commits_list. */
+ wait_for_commit *next_subsequent_commit;
+ /*
+ Our waitee, if we did register_wait_for_prior_commit(), and were not
+ yet woken up. Else NULL.
+
+ When this is cleared for wakeup, the COND_wait_commit condition is
+ signalled.
+ */
+ wait_for_commit *waitee;
+ /*
+ Generic pointer for use by the transaction coordinator to optimise the
+ waiting for improved group commit.
+
+ Currently used by binlog TC to signal that a waiter is ready to commit, so
+ that the waitee can grab it and group commit it directly. It is free to be
+ used by another transaction coordinator for similar purposes.
+ */
+ void *opaque_pointer;
+ /* The wakeup error code from the waitee. 0 means no error. */
+ int wakeup_error;
+ /*
+ Flag set when wakeup_subsequent_commits_running() is active, see comments
+ on that function for details.
+ */
+ bool wakeup_subsequent_commits_running;
+ /*
+ This flag can be set when a commit starts, but has not completed yet.
+ It is used by binlog group commit to allow a waiting transaction T2 to
+ join the group commit of an earlier transaction T1. When T1 has queued
+ itself for group commit, it will set the commit_started flag. Then when
+ T2 becomes ready to commit and needs to wait for T1 to commit first, T2
+ can queue itself before waiting, and thereby participate in the same
+ group commit as T1.
+ */
+ bool commit_started;
+
+ void register_wait_for_prior_commit(wait_for_commit *waitee);
+ int wait_for_prior_commit(THD *thd)
+ {
+ /*
+ Quick inline check, to avoid function call and locking in the common case
+ where no wakeup is registered, or a registered wait was already signalled.
+ */
+ if (waitee)
+ return wait_for_prior_commit2(thd);
+ else
+ {
+ if (wakeup_error)
+ my_error(ER_PRIOR_COMMIT_FAILED, MYF(0));
+ return wakeup_error;
+ }
+ }
+ void wakeup_subsequent_commits(int wakeup_error)
+ {
+ /*
+ Do the check inline, so only the wakeup case takes the cost of a function
+ call for every commmit.
+
+ Note that the check is done without locking. It is the responsibility of
+ the user of the wakeup facility to ensure that no waiters can register
+ themselves after the last call to wakeup_subsequent_commits().
+
+ This avoids having to take another lock for every commit, which would be
+ pointless anyway - even if we check under lock, there is nothing to
+ prevent a waiter from arriving just after releasing the lock.
+ */
+ if (subsequent_commits_list)
+ wakeup_subsequent_commits2(wakeup_error);
+ }
+ void unregister_wait_for_prior_commit()
+ {
+ if (waitee)
+ unregister_wait_for_prior_commit2();
+ else
+ wakeup_error= 0;
+ }
+ /*
+ Remove a waiter from the list in the waitee. Used to unregister a wait.
+ The caller must be holding the locks of both waiter and waitee.
+ */
+ void remove_from_list(wait_for_commit **next_ptr_ptr)
+ {
+ wait_for_commit *cur;
+
+ while ((cur= *next_ptr_ptr) != NULL)
+ {
+ if (cur == this)
+ {
+ *next_ptr_ptr= this->next_subsequent_commit;
+ break;
+ }
+ next_ptr_ptr= &cur->next_subsequent_commit;
+ }
+ waitee= NULL;
+ }
+
+ void wakeup(int wakeup_error);
+
+ int wait_for_prior_commit2(THD *thd);
+ void wakeup_subsequent_commits2(int wakeup_error);
+ void unregister_wait_for_prior_commit2();
+
+ wait_for_commit();
+ ~wait_for_commit();
+ void reinit();
+};
+
+
extern "C" void my_message_sql(uint error, const char *str, myf MyFlags);
+class THD;
+#ifndef DBUG_OFF
+void dbug_serve_apcs(THD *thd, int n_calls);
+#endif
+
/**
@class THD
For each client connection we create a separate thread with THD serving as
@@ -1561,6 +1828,7 @@ extern "C" void my_message_sql(uint error, const char *str, myf MyFlags);
*/
class THD :public Statement,
+ public MDL_context_owner,
public Open_tables_state
{
private:
@@ -1581,8 +1849,14 @@ public:
/* Used to execute base64 coded binlog events in MySQL server */
Relay_log_info* rli_fake;
+ rpl_group_info* rgi_fake;
/* Slave applier execution context */
- Relay_log_info* rli_slave;
+ rpl_group_info* rgi_slave;
+
+ union {
+ rpl_io_thread_info *rpl_io_info;
+ rpl_sql_thread_info *rpl_sql_info;
+ } system_thread_info;
void reset_for_next_command();
/*
@@ -1616,6 +1890,8 @@ public:
Query_cache_tls query_cache_tls;
#endif
NET net; // client connection descriptor
+ /** Aditional network instrumentation for the server only. */
+ NET_SERVER m_net_server_extension;
scheduler_functions *scheduler; // Scheduler for this connection
Protocol *protocol; // Current protocol
Protocol_text protocol_text; // Normal protocol
@@ -1682,6 +1958,19 @@ public:
*/
const char *proc_info;
+private:
+ unsigned int m_current_stage_key;
+
+public:
+ void enter_stage(const PSI_stage_info *stage,
+ PSI_stage_info *old_stage,
+ const char *calling_func,
+ const char *calling_file,
+ const unsigned int calling_line);
+
+ const char *get_proc_info() const
+ { return proc_info; }
+
/*
Used in error messages to tell user in what part of MySQL we found an
error. E. g. when where= "having clause", if fix_fields() fails, user
@@ -1697,21 +1986,23 @@ public:
HASH handler_tables_hash;
/*
- One thread can hold up to one named user-level lock. This variable
- points to a lock object if the lock is present. See item_func.cc and
+ A thread can hold named user-level locks. This variable
+ contains granted tickets if a lock is present. See item_func.cc and
chapter 'Miscellaneous functions', for functions GET_LOCK, RELEASE_LOCK.
*/
- User_level_lock *ull;
+ HASH ull_hash;
#ifndef DBUG_OFF
uint dbug_sentry; // watch out for memory corruption
#endif
struct st_my_thread_var *mysys_var;
+private:
/*
Type of current query: COM_STMT_PREPARE, COM_QUERY, etc. Set from
first byte of the packet in do_command()
*/
- enum enum_server_command command;
- uint32 server_id;
+ enum enum_server_command m_command;
+
+public:
uint32 file_id; // for LOAD DATA INFILE
/* remote (peer) port */
uint16 peer_port;
@@ -1749,7 +2040,10 @@ public:
uint in_sub_stmt;
/* True when opt_userstat_running is set at start of query */
bool userstat_running;
- /* True if we want to log all errors */
+ /*
+ True if we have to log all errors. Are set by some engines to temporary
+ force errors to the error log.
+ */
bool log_all_errors;
/* Do not set socket timeouts for wait_timeout (used with threadpool) */
@@ -1791,7 +2085,7 @@ public:
MY_BITMAP const* cols, size_t colcnt,
const uchar *old_data, const uchar *new_data);
- void set_server_id(uint32 sid) { server_id = sid; }
+ void set_server_id(uint32 sid) { variables.server_id = sid; }
/*
Member functions to handle pending event for row-level logging.
@@ -1824,12 +2118,49 @@ public:
int is_current_stmt_binlog_format_row() const {
DBUG_ASSERT(current_stmt_binlog_format == BINLOG_FORMAT_STMT ||
current_stmt_binlog_format == BINLOG_FORMAT_ROW);
- return (WSREP_BINLOG_FORMAT((ulong)current_stmt_binlog_format) ==
- BINLOG_FORMAT_ROW);
+ return (WSREP_FORMAT((ulong)current_stmt_binlog_format) == BINLOG_FORMAT_ROW);
+ }
+
+ enum binlog_filter_state
+ {
+ BINLOG_FILTER_UNKNOWN,
+ BINLOG_FILTER_CLEAR,
+ BINLOG_FILTER_SET
+ };
+
+ inline void reset_binlog_local_stmt_filter()
+ {
+ m_binlog_filter_state= BINLOG_FILTER_UNKNOWN;
+ }
+
+ inline void clear_binlog_local_stmt_filter()
+ {
+ DBUG_ASSERT(m_binlog_filter_state == BINLOG_FILTER_UNKNOWN);
+ m_binlog_filter_state= BINLOG_FILTER_CLEAR;
+ }
+
+ inline void set_binlog_local_stmt_filter()
+ {
+ DBUG_ASSERT(m_binlog_filter_state == BINLOG_FILTER_UNKNOWN);
+ m_binlog_filter_state= BINLOG_FILTER_SET;
+ }
+
+ inline binlog_filter_state get_binlog_local_stmt_filter()
+ {
+ return m_binlog_filter_state;
}
private:
/**
+ Indicate if the current statement should be discarded
+ instead of written to the binlog.
+ This is used to discard special statements, such as
+ DML or DDL that affects only 'local' (non replicated)
+ tables, such as performance_schema.*
+ */
+ binlog_filter_state m_binlog_filter_state;
+
+ /**
Indicates the format in which the current statement will be
logged. This can only be set from @c decide_logging_format().
*/
@@ -1907,7 +2238,8 @@ public:
{
bzero((char*)this, sizeof(*this));
xid_state.xid.null();
- init_sql_alloc(&mem_root, ALLOC_ROOT_MIN_BLOCK_SIZE, 0);
+ init_sql_alloc(&mem_root, ALLOC_ROOT_MIN_BLOCK_SIZE, 0,
+ MYF(MY_THREAD_SPECIFIC));
}
} transaction;
Global_read_lock global_read_lock;
@@ -2131,11 +2463,12 @@ public:
ha_rows cuted_fields;
+private:
/*
number of rows we actually sent to the client, including "synthetic"
rows in ROLLUP etc.
*/
- ha_rows sent_row_count;
+ ha_rows m_sent_row_count;
/**
Number of rows read and/or evaluated for a statement. Used for
@@ -2147,12 +2480,42 @@ public:
statement including ORDER BY could possibly evaluate the row in
filesort() before reading it for e.g. update.
*/
- ha_rows examined_row_count;
+ ha_rows m_examined_row_count;
+
+public:
+ ha_rows get_sent_row_count() const
+ { return m_sent_row_count; }
+
+ ha_rows get_examined_row_count() const
+ { return m_examined_row_count; }
+
+ void set_sent_row_count(ha_rows count);
+ void set_examined_row_count(ha_rows count);
+
+ void inc_sent_row_count(ha_rows count);
+ void inc_examined_row_count(ha_rows count);
+
+ void inc_status_created_tmp_disk_tables();
+ void inc_status_created_tmp_files();
+ void inc_status_created_tmp_tables();
+ void inc_status_select_full_join();
+ void inc_status_select_full_range_join();
+ void inc_status_select_range();
+ void inc_status_select_range_check();
+ void inc_status_select_scan();
+ void inc_status_sort_merge_passes();
+ void inc_status_sort_range();
+ void inc_status_sort_rows(ha_rows count);
+ void inc_status_sort_scan();
+ void set_status_no_index_used();
+ void set_status_no_good_index_used();
+
/**
The number of rows and/or keys examined by the query, both read,
changed or written.
*/
ulonglong accessed_rows_and_keys;
+
/**
Check if the number of rows accessed by a statement exceeded
LIMIT ROWS EXAMINED. If so, signal the query engine to stop execution.
@@ -2165,12 +2528,30 @@ public:
USER_CONN *user_connect;
CHARSET_INFO *db_charset;
- Warning_info *warning_info;
- Diagnostics_area *stmt_da;
#if defined(ENABLED_PROFILING)
PROFILING profiling;
#endif
+ /** Current statement digest. */
+ sql_digest_state *m_digest;
+ /** Current statement digest token array. */
+ unsigned char *m_token_array;
+ /** Top level statement digest. */
+ sql_digest_state m_digest_state;
+
+ /** Current statement instrumentation. */
+ PSI_statement_locker *m_statement_psi;
+#ifdef HAVE_PSI_STATEMENT_INTERFACE
+ /** Current statement instrumentation state. */
+ PSI_statement_locker_state m_statement_state;
+#endif /* HAVE_PSI_STATEMENT_INTERFACE */
+ /** Idle instrumentation. */
+ PSI_idle_locker *m_idle_psi;
+#ifdef HAVE_PSI_IDLE_INTERFACE
+ /** Idle instrumentation state. */
+ PSI_idle_locker_state m_idle_state;
+#endif /* HAVE_PSI_IDLE_INTERFACE */
+
/*
Id of current query. Statement can be reused to execute several queries
query_id is global in context of the whole MySQL server.
@@ -2218,12 +2599,23 @@ public:
above.
*/
enum_tx_isolation tx_isolation;
+ /*
+ Current or next transaction access mode.
+ See comment above regarding tx_isolation.
+ */
+ bool tx_read_only;
enum_check_fields count_cuted_fields;
DYNAMIC_ARRAY user_var_events; /* For user variables replication */
MEM_ROOT *user_var_events_alloc; /* Allocate above array elements here */
/*
+ Define durability properties that engines may check to
+ improve performance. Not yet used in MariaDB
+ */
+ enum durability_properties durability_property;
+
+ /*
If checking this in conjunction with a wait condition, please
include a check after enter_cond() if you want to avoid a race
condition. For details see the implementation of awake(),
@@ -2231,15 +2623,31 @@ public:
*/
killed_state volatile killed;
+ /* See also thd_killed() */
+ inline bool check_killed()
+ {
+ if (killed)
+ return TRUE;
+ if (apc_target.have_apc_requests())
+ apc_target.process_apc_requests();
+ return FALSE;
+ }
+
/* scramble - random string sent to client on handshake */
char scramble[SCRAMBLE_LENGTH+1];
- bool slave_thread, one_shot_set;
+ /*
+ If this is a slave, the name of the connection stored here.
+ This is used for taging error messages in the log files.
+ */
+ LEX_STRING connection_name;
+ char default_master_connection_buff[MAX_CONNECTION_NAME+1];
+ uint8 password; /* 0, 1 or 2 */
+ uint8 failed_com_change_user;
+ bool slave_thread;
bool extra_port; /* If extra connection */
bool no_errors;
- uint8 password;
- uint8 failed_com_change_user;
/**
Set to TRUE if execution of the current compound statement
@@ -2272,13 +2680,6 @@ public:
/* for IS NULL => = last_insert_id() fix in remove_eq_conds() */
bool substitute_null_with_insert_id;
bool in_lock_tables;
- /**
- True if a slave error. Causes the slave to stop. Not the same
- as the statement execution error (is_error()), since
- a statement may be expected to return an error, e.g. because
- it returned an error on master, and this is OK on the slave.
- */
- bool is_slave_error;
bool bootstrap, cleanup_done;
/** is set if some thread specific value(s) used in a statement. */
@@ -2295,6 +2696,32 @@ public:
/* set during loop of derived table processing */
bool derived_tables_processing;
bool tablespace_op; /* This is TRUE in DISCARD/IMPORT TABLESPACE */
+ /* True if we have to log the current statement */
+ bool log_current_statement;
+ /**
+ True if a slave error. Causes the slave to stop. Not the same
+ as the statement execution error (is_error()), since
+ a statement may be expected to return an error, e.g. because
+ it returned an error on master, and this is OK on the slave.
+ */
+ bool is_slave_error;
+ /*
+ True when a transaction is queued up for binlog group commit.
+ Used so that if another transaction needs to wait for a row lock held by
+ this transaction, it can signal to trigger the group commit immediately,
+ skipping the normal --binlog-commit-wait-count wait.
+ */
+ bool waiting_on_group_commit;
+ /*
+ Set true when another transaction goes to wait on a row lock held by this
+ transaction. Used together with waiting_on_group_commit.
+ */
+ bool has_waiter;
+ /*
+ In case of a slave, set to the error code the master got when executing
+ the query. 0 if no error on the master.
+ */
+ int slave_expected_error;
sp_rcontext *spcont; // SP runtime context
sp_cache *sp_proc_cache;
@@ -2361,42 +2788,6 @@ public:
query_id_t first_query_id;
} binlog_evt_union;
-#ifdef WITH_WSREP
- const bool wsrep_applier; /* dedicated slave applier thread */
- bool wsrep_applier_closing; /* applier marked to close */
- bool wsrep_client_thread; /* to identify client threads*/
- enum wsrep_exec_mode wsrep_exec_mode;
- query_id_t wsrep_last_query_id;
- enum wsrep_query_state wsrep_query_state;
- enum wsrep_conflict_state wsrep_conflict_state;
- mysql_mutex_t LOCK_wsrep_thd;
- // changed from wsrep_seqno_t to wsrep_trx_meta_t in wsrep API rev 75
- // wsrep_seqno_t wsrep_trx_seqno;
- wsrep_trx_meta_t wsrep_trx_meta;
- uint32 wsrep_rand;
- Relay_log_info* wsrep_rli;
- bool wsrep_converted_lock_session;
- wsrep_ws_handle_t wsrep_ws_handle;
-#ifdef WSREP_PROC_INFO
- char wsrep_info[128]; /* string for dynamic proc info */
-#endif /* WSREP_PROC_INFO */
- ulong wsrep_retry_counter; // of autocommit
- bool wsrep_PA_safe;
- char* wsrep_retry_query;
- size_t wsrep_retry_query_len;
- enum enum_server_command wsrep_retry_command;
- enum wsrep_consistency_check_mode
- wsrep_consistency_check;
- wsrep_stats_var* wsrep_status_vars;
- int wsrep_mysql_replicated;
- THD* wsrep_bf_thd;
- const char* wsrep_TOI_pre_query; /* a query to apply before
- the actual TOI query */
- size_t wsrep_TOI_pre_query_len;
- void* wsrep_apply_format;
- bool wsrep_apply_toi; /* applier processing in TOI */
- wsrep_gtid_t wsrep_sync_wait_gtid;
-#endif /* WITH_WSREP */
/**
Internal parser state.
Note that since the parser is not re-entrant, we keep only one parser
@@ -2470,10 +2861,21 @@ public:
void close_active_vio();
#endif
void awake(killed_state state_to_set);
-
+
/** Disconnect the associated communication endpoint. */
void disconnect();
+
+ /*
+ Allows this thread to serve as a target for others to schedule Async
+ Procedure Calls on.
+
+ It's possible to schedule any code to be executed this way, by
+ inheriting from the Apc_call object. Currently, only
+ Show_explain_request uses this.
+ */
+ Apc_target apc_target;
+
#ifndef MYSQL_CLIENT
enum enum_binlog_query_type {
/* The query can be logged in row format or in statement format. */
@@ -2491,22 +2893,20 @@ public:
int errcode);
#endif
- /*
- For enter_cond() / exit_cond() to work the mutex must be got before
- enter_cond(); this mutex is then released by exit_cond().
- Usage must be: lock mutex; enter_cond(); your code; exit_cond().
- */
- inline const char* enter_cond(mysql_cond_t *cond, mysql_mutex_t* mutex,
- const char* msg)
+ inline void
+ enter_cond(mysql_cond_t *cond, mysql_mutex_t* mutex,
+ const PSI_stage_info *stage, PSI_stage_info *old_stage,
+ const char *src_function, const char *src_file,
+ int src_line)
{
- const char* old_msg = proc_info;
mysql_mutex_assert_owner(mutex);
mysys_var->current_mutex = mutex;
mysys_var->current_cond = cond;
- proc_info = msg;
- return old_msg;
+ enter_stage(stage, old_stage, src_function, src_file, src_line);
}
- inline void exit_cond(const char* old_msg)
+ inline void exit_cond(const PSI_stage_info *stage,
+ const char *src_function, const char *src_file,
+ int src_line)
{
/*
Putting the mutex unlock in thd->exit_cond() ensures that
@@ -2518,10 +2918,51 @@ public:
mysql_mutex_lock(&mysys_var->mutex);
mysys_var->current_mutex = 0;
mysys_var->current_cond = 0;
- proc_info = old_msg;
+ enter_stage(stage, NULL, src_function, src_file, src_line);
mysql_mutex_unlock(&mysys_var->mutex);
return;
}
+ virtual int is_killed() { return killed; }
+ virtual THD* get_thd() { return this; }
+
+ /**
+ A callback to the server internals that is used to address
+ special cases of the locking protocol.
+ Invoked when acquiring an exclusive lock, for each thread that
+ has a conflicting shared metadata lock.
+
+ This function:
+ - aborts waiting of the thread on a data lock, to make it notice
+ the pending exclusive lock and back off.
+ - if the thread is an INSERT DELAYED thread, sends it a KILL
+ signal to terminate it.
+
+ @note This function does not wait for the thread to give away its
+ locks. Waiting is done outside for all threads at once.
+
+ @param ctx_in_use The MDL context owner (thread) to wake up.
+ @param needs_thr_lock_abort Indicates that to wake up thread
+ this call needs to abort its waiting
+ on table-level lock.
+
+ @retval TRUE if the thread was woken up
+ @retval FALSE otherwise.
+ */
+ virtual bool notify_shared_lock(MDL_context_owner *ctx_in_use,
+ bool needs_thr_lock_abort);
+
+ // End implementation of MDL_context_owner interface.
+
+ inline bool use_cond_push(handler *file)
+ {
+ return (variables.optimizer_switch & OPTIMIZER_SWITCH_ENGINE_CONDITION_PUSHDOWN)
+ || (file->ha_table_flags() & HA_MUST_USE_TABLE_CONDITION_PUSHDOWN);
+ }
+ inline bool is_strict_mode() const
+ {
+ return (bool) (variables.sql_mode & (MODE_STRICT_TRANS_TABLES |
+ MODE_STRICT_ALL_TABLES));
+ }
inline my_time_t query_start() { query_start_used=1; return start_time; }
inline ulong query_start_sec_part()
{ query_start_sec_part_used=1; return start_time_sec_part; }
@@ -2530,6 +2971,9 @@ public:
my_hrtime_t hrtime= my_hrtime();
start_time= hrtime_to_my_time(hrtime);
start_time_sec_part= hrtime_sec_part(hrtime);
+#ifdef HAVE_PSI_THREAD_INTERFACE
+ PSI_THREAD_CALL(set_thread_start_time)(start_time);
+#endif
}
inline void set_start_time()
{
@@ -2537,6 +2981,9 @@ public:
{
start_time= hrtime_to_my_time(user_time);
start_time_sec_part= hrtime_sec_part(user_time);
+#ifdef HAVE_PSI_THREAD_INTERFACE
+ PSI_THREAD_CALL(set_thread_start_time)(start_time);
+#endif
}
else
set_current_time();
@@ -2556,7 +3003,12 @@ public:
my_hrtime_t hrtime= { hrtime_from_time(t) + sec_part };
set_time(hrtime);
}
- void set_time_after_lock() { utime_after_lock= microsecond_interval_timer(); }
+ void set_time_after_lock()
+ {
+ utime_after_lock= microsecond_interval_timer();
+ MYSQL_SET_STATEMENT_LOCK_TIME(m_statement_psi,
+ (utime_after_lock - start_utime));
+ }
ulonglong current_utime() { return microsecond_interval_timer(); }
/**
@@ -2653,9 +3105,21 @@ public:
return alloc_root(&transaction.mem_root,size);
}
- LEX_STRING *make_lex_string(LEX_STRING *lex_str,
- const char* str, uint length,
- bool allocate_lex_string);
+ LEX_STRING *make_lex_string(LEX_STRING *lex_str, const char* str, uint length)
+ {
+ if (!(lex_str->str= strmake_root(mem_root, str, length)))
+ return 0;
+ lex_str->length= length;
+ return lex_str;
+ }
+
+ LEX_STRING *make_lex_string(const char* str, uint length)
+ {
+ LEX_STRING *lex_str;
+ if (!(lex_str= (LEX_STRING *)alloc_root(mem_root, sizeof(LEX_STRING))))
+ return 0;
+ return make_lex_string(lex_str, str, length);
+ }
bool convert_string(LEX_STRING *to, CHARSET_INFO *to_cs,
const char *from, uint from_length,
@@ -2667,7 +3131,7 @@ public:
void add_changed_table(const char *key, long key_length);
CHANGED_TABLE_LIST * changed_table_dup(const char *key, long key_length);
int send_explain_fields(select_result *result);
-
+ void make_explain_field_list(List<Item> &field_list);
/**
Clear the current error, if any.
We do not clear is_fatal_error or is_fatal_sub_stmt_error since we
@@ -2681,8 +3145,8 @@ public:
inline void clear_error()
{
DBUG_ENTER("clear_error");
- if (stmt_da->is_error())
- stmt_da->reset_diagnostics_area();
+ if (get_stmt_da()->is_error())
+ get_stmt_da()->reset_diagnostics_area();
is_slave_error= 0;
if (killed == KILL_BAD_DATA)
killed= NOT_KILLED; // KILL_BAD_DATA can be reset w/o a mutex
@@ -2711,7 +3175,7 @@ public:
*/
inline void fatal_error()
{
- DBUG_ASSERT(stmt_da->is_error() || killed);
+ DBUG_ASSERT(get_stmt_da()->is_error() || killed);
is_fatal_error= 1;
DBUG_PRINT("error",("Fatal error set"));
}
@@ -2728,7 +3192,20 @@ public:
To raise this flag, use my_error().
*/
- inline bool is_error() const { return stmt_da->is_error(); }
+ inline bool is_error() const { return m_stmt_da->is_error(); }
+
+ /// Returns Diagnostics-area for the current statement.
+ Diagnostics_area *get_stmt_da()
+ { return m_stmt_da; }
+
+ /// Returns Diagnostics-area for the current statement.
+ const Diagnostics_area *get_stmt_da() const
+ { return m_stmt_da; }
+
+ /// Sets Diagnostics-area for the current statement.
+ void set_stmt_da(Diagnostics_area *da)
+ { m_stmt_da= da; }
+
inline CHARSET_INFO *charset() { return variables.character_set_client; }
void update_charset();
@@ -2801,6 +3278,14 @@ public:
mysql_mutex_unlock(&LOCK_thd_data);
}
}
+ inline void reset_kill_query()
+ {
+ if (killed < KILL_CONNECTION)
+ {
+ reset_killed();
+ mysys_var->abort= 0;
+ }
+ }
inline void send_kill_message() const
{
int err= killed_errno();
@@ -2822,6 +3307,27 @@ public:
void set_n_backup_active_arena(Query_arena *set, Query_arena *backup);
void restore_active_arena(Query_arena *set, Query_arena *backup);
+ inline void get_binlog_format(enum_binlog_format *format,
+ enum_binlog_format *current_format)
+ {
+ *format= (enum_binlog_format) variables.binlog_format;
+ *current_format= current_stmt_binlog_format;
+ }
+ inline void set_binlog_format(enum_binlog_format format,
+ enum_binlog_format current_format)
+ {
+ DBUG_ENTER("set_binlog_format");
+ variables.binlog_format= format;
+ current_stmt_binlog_format= current_format;
+ DBUG_VOID_RETURN;
+ }
+ inline void set_binlog_format_stmt()
+ {
+ DBUG_ENTER("set_binlog_format_stmt");
+ variables.binlog_format= BINLOG_FORMAT_STMT;
+ current_stmt_binlog_format= BINLOG_FORMAT_STMT;
+ DBUG_VOID_RETURN;
+ }
/*
@todo Make these methods private or remove them completely. Only
decide_logging_format should call them. /Sven
@@ -2846,22 +3352,32 @@ public:
tests fail and so force them to propagate the
lex->binlog_row_based_if_mixed upwards to the caller.
*/
- if ((WSREP_BINLOG_FORMAT(variables.binlog_format) == BINLOG_FORMAT_MIXED)&&
+ if ((WSREP_FORMAT(variables.binlog_format) == BINLOG_FORMAT_MIXED) &&
(in_sub_stmt == 0))
set_current_stmt_binlog_format_row();
DBUG_VOID_RETURN;
}
+
inline void set_current_stmt_binlog_format_row()
{
DBUG_ENTER("set_current_stmt_binlog_format_row");
current_stmt_binlog_format= BINLOG_FORMAT_ROW;
DBUG_VOID_RETURN;
}
- inline void clear_current_stmt_binlog_format_row()
+ /* Set binlog format temporarily to statement. Returns old format */
+ inline enum_binlog_format set_current_stmt_binlog_format_stmt()
{
- DBUG_ENTER("clear_current_stmt_binlog_format_row");
+ enum_binlog_format orig_format= current_stmt_binlog_format;
+ DBUG_ENTER("set_current_stmt_binlog_format_stmt");
current_stmt_binlog_format= BINLOG_FORMAT_STMT;
+ DBUG_RETURN(orig_format);
+ }
+ inline void restore_stmt_binlog_format(enum_binlog_format format)
+ {
+ DBUG_ENTER("restore_stmt_binlog_format");
+ DBUG_ASSERT(!is_current_stmt_binlog_format_row());
+ current_stmt_binlog_format= format;
DBUG_VOID_RETURN;
}
inline void reset_current_stmt_binlog_format_row()
@@ -2888,10 +3404,10 @@ public:
show_system_thread(system_thread)));
if (in_sub_stmt == 0)
{
- if (WSREP_BINLOG_FORMAT(variables.binlog_format) == BINLOG_FORMAT_ROW)
+ if (WSREP_FORMAT(variables.binlog_format) == BINLOG_FORMAT_ROW)
set_current_stmt_binlog_format_row();
else if (temporary_tables == NULL)
- clear_current_stmt_binlog_format_row();
+ set_current_stmt_binlog_format_stmt();
}
DBUG_VOID_RETURN;
}
@@ -2937,8 +3453,13 @@ public:
db= NULL;
}
db_length= db ? new_db_len : 0;
+ bool result= new_db && !db;
mysql_mutex_unlock(&LOCK_thd_data);
- return new_db && !db;
+#ifdef HAVE_PSI_THREAD_INTERFACE
+ if (result)
+ PSI_THREAD_CALL(set_thread_db)(new_db, new_db_len);
+#endif
+ return result;
}
/**
@@ -2960,6 +3481,9 @@ public:
db= new_db;
db_length= new_db_len;
mysql_mutex_unlock(&LOCK_thd_data);
+#ifdef HAVE_PSI_THREAD_INTERFACE
+ PSI_THREAD_CALL(set_thread_db)(new_db, new_db_len);
+#endif
}
}
/*
@@ -2990,6 +3514,7 @@ public:
*/
void push_internal_handler(Internal_error_handler *handler);
+private:
/**
Handle a sql condition.
@param sql_errno the condition error number
@@ -2999,12 +3524,13 @@ public:
@param[out] cond_hdl the sql condition raised, if any
@return true if the condition is handled
*/
- virtual bool handle_condition(uint sql_errno,
- const char* sqlstate,
- MYSQL_ERROR::enum_warning_level level,
- const char* msg,
- MYSQL_ERROR ** cond_hdl);
+ bool handle_condition(uint sql_errno,
+ const char* sqlstate,
+ Sql_condition::enum_warning_level level,
+ const char* msg,
+ Sql_condition ** cond_hdl);
+public:
/**
Remove the error handler last pushed.
*/
@@ -3054,10 +3580,10 @@ private:
To raise a SQL condition, the code should use the public
raise_error() or raise_warning() methods provided by class THD.
*/
- friend class Signal_common;
- friend class Signal_statement;
- friend class Resignal_statement;
- friend void push_warning(THD*, MYSQL_ERROR::enum_warning_level, uint, const char*);
+ friend class Sql_cmd_common_signal;
+ friend class Sql_cmd_signal;
+ friend class Sql_cmd_resignal;
+ friend void push_warning(THD*, Sql_condition::enum_warning_level, uint, const char*);
friend void my_message_sql(uint, const char *, myf);
/**
@@ -3068,17 +3594,18 @@ private:
@param msg the condition message text
@return The condition raised, or NULL
*/
- MYSQL_ERROR*
+ Sql_condition*
raise_condition(uint sql_errno,
const char* sqlstate,
- MYSQL_ERROR::enum_warning_level level,
+ Sql_condition::enum_warning_level level,
const char* msg);
public:
/** Overloaded to guard query/query_length fields */
virtual void set_statement(Statement *stmt);
+ void set_command(enum enum_server_command command);
inline enum enum_server_command get_command() const
- { return command; }
+ { return m_command; }
/**
Assign a new value to thd->query and thd->query_id and mysys_var.
@@ -3098,7 +3625,10 @@ public:
{ set_query(CSET_STRING()); }
void set_query_and_id(char *query_arg, uint32 query_length_arg,
CHARSET_INFO *cs, query_id_t new_query_id);
- void set_query_id(query_id_t new_query_id);
+ void set_query_id(query_id_t new_query_id)
+ {
+ query_id= new_query_id;
+ }
void set_open_tables(TABLE *open_tables_arg)
{
mysql_mutex_lock(&LOCK_thd_data);
@@ -3127,9 +3657,11 @@ public:
}
void leave_locked_tables_mode();
int decide_logging_format(TABLE_LIST *tables);
- void binlog_invoker() { m_binlog_invoker= TRUE; }
- bool need_binlog_invoker() { return m_binlog_invoker; }
- void get_definer(LEX_USER *definer);
+
+ enum need_invoker { INVOKER_NONE=0, INVOKER_USER, INVOKER_ROLE};
+ void binlog_invoker(bool role) { m_binlog_invoker= role ? INVOKER_ROLE : INVOKER_USER; }
+ enum need_invoker need_binlog_invoker() { return m_binlog_invoker; }
+ void get_definer(LEX_USER *definer, bool role);
void set_invoker(const LEX_STRING *user, const LEX_STRING *host)
{
invoker_user= *user;
@@ -3189,6 +3721,28 @@ public:
mysql_mutex_unlock(&LOCK_status);
}
+ wait_for_commit *wait_for_commit_ptr;
+ int wait_for_prior_commit()
+ {
+ if (wait_for_commit_ptr)
+ return wait_for_commit_ptr->wait_for_prior_commit(this);
+ return 0;
+ }
+ void wakeup_subsequent_commits(int wakeup_error)
+ {
+ if (wait_for_commit_ptr)
+ wait_for_commit_ptr->wakeup_subsequent_commits(wakeup_error);
+ }
+ wait_for_commit *suspend_subsequent_commits() {
+ wait_for_commit *suspended= wait_for_commit_ptr;
+ wait_for_commit_ptr= NULL;
+ return suspended;
+ }
+ void resume_subsequent_commits(wait_for_commit *suspended) {
+ DBUG_ASSERT(!wait_for_commit_ptr);
+ wait_for_commit_ptr= suspended;
+ }
+
void mark_transaction_to_rollback(bool all);
private:
@@ -3211,18 +3765,19 @@ private:
tree itself is reused between executions and thus is stored elsewhere.
*/
MEM_ROOT main_mem_root;
- Warning_info main_warning_info;
Diagnostics_area main_da;
+ Diagnostics_area *m_stmt_da;
/**
- It will be set TURE if CURRENT_USER() is called in account management
- statements or default definer is set in CREATE/ALTER SP, SF, Event,
- TRIGGER or VIEW statements.
+ It will be set if CURRENT_USER() or CURRENT_ROLE() is called in account
+ management statements or default definer is set in CREATE/ALTER SP, SF,
+ Event, TRIGGER or VIEW statements.
- Current user will be binlogged into Query_log_event if m_binlog_invoker
- is TRUE; It will be stored into invoker_host and invoker_user by SQL thread.
+ Current user or role will be binlogged into Query_log_event if
+ m_binlog_invoker is not NONE; It will be stored into invoker_host and
+ invoker_user by SQL thread.
*/
- bool m_binlog_invoker;
+ enum need_invoker m_binlog_invoker;
/**
It points to the invoker in the Query_log_event.
@@ -3232,6 +3787,12 @@ private:
*/
LEX_STRING invoker_user;
LEX_STRING invoker_host;
+
+ /* Protect against add/delete of temporary tables in parallel replication */
+ void rgi_lock_temporary_tables();
+ void rgi_unlock_temporary_tables();
+ bool rgi_have_temporary_tables();
+public:
/*
Flag, mutex and condition for a thread to wait for a signal from another
thread.
@@ -3242,27 +3803,91 @@ private:
bool wakeup_ready;
mysql_mutex_t LOCK_wakeup_ready;
mysql_cond_t COND_wakeup_ready;
+ /*
+ The GTID assigned to the last commit. If no GTID was assigned to any commit
+ so far, this is indicated by last_commit_gtid.seq_no == 0.
+ */
+ rpl_gtid last_commit_gtid;
+
+ inline void lock_temporary_tables()
+ {
+ if (rgi_slave)
+ rgi_lock_temporary_tables();
+ }
+ inline void unlock_temporary_tables()
+ {
+ if (rgi_slave)
+ rgi_unlock_temporary_tables();
+ }
+ inline bool have_temporary_tables()
+ {
+ return (temporary_tables ||
+ (rgi_slave && rgi_have_temporary_tables()));
+ }
+
+#ifdef WITH_WSREP
+ const bool wsrep_applier; /* dedicated slave applier thread */
+ bool wsrep_applier_closing; /* applier marked to close */
+ bool wsrep_client_thread; /* to identify client threads*/
+ enum wsrep_exec_mode wsrep_exec_mode;
+ query_id_t wsrep_last_query_id;
+ enum wsrep_query_state wsrep_query_state;
+ enum wsrep_conflict_state wsrep_conflict_state;
+ mysql_mutex_t LOCK_wsrep_thd;
+ // changed from wsrep_seqno_t to wsrep_trx_meta_t in wsrep API rev 75
+ // wsrep_seqno_t wsrep_trx_seqno;
+ wsrep_trx_meta_t wsrep_trx_meta;
+ uint32 wsrep_rand;
+ rpl_group_info* wsrep_rgi;
+ bool wsrep_converted_lock_session;
+ wsrep_ws_handle_t wsrep_ws_handle;
+#ifdef WSREP_PROC_INFO
+ char wsrep_info[128]; /* string for dynamic proc info */
+#endif /* WSREP_PROC_INFO */
+ ulong wsrep_retry_counter; // of autocommit
+ bool wsrep_PA_safe;
+ char* wsrep_retry_query;
+ size_t wsrep_retry_query_len;
+ enum enum_server_command wsrep_retry_command;
+ enum wsrep_consistency_check_mode
+ wsrep_consistency_check;
+ wsrep_stats_var* wsrep_status_vars;
+ int wsrep_mysql_replicated;
+ const char* wsrep_TOI_pre_query; /* a query to apply before
+ the actual TOI query */
+ size_t wsrep_TOI_pre_query_len;
+ wsrep_po_handle_t wsrep_po_handle;
+ size_t wsrep_po_cnt;
+ my_bool wsrep_po_in_trans;
+#ifdef GTID_SUPPORT
+ rpl_sid wsrep_po_sid;
+#endif /* GTID_SUPPORT */
+ void* wsrep_apply_format;
+ bool wsrep_apply_toi; /* applier processing in TOI */
+ bool wsrep_skip_append_keys;
+ wsrep_gtid_t wsrep_sync_wait_gtid;
+#endif /* WITH_WSREP */
};
-/** A short cut for thd->stmt_da->set_ok_status(). */
+/** A short cut for thd->get_stmt_da()->set_ok_status(). */
inline void
my_ok(THD *thd, ulonglong affected_rows= 0, ulonglong id= 0,
const char *message= NULL)
{
thd->set_row_count_func(affected_rows);
- thd->stmt_da->set_ok_status(thd, affected_rows, id, message);
+ thd->get_stmt_da()->set_ok_status(affected_rows, id, message);
}
-/** A short cut for thd->stmt_da->set_eof_status(). */
+/** A short cut for thd->get_stmt_da()->set_eof_status(). */
inline void
my_eof(THD *thd)
{
thd->set_row_count_func(-1);
- thd->stmt_da->set_eof_status(thd);
+ thd->get_stmt_da()->set_eof_status(thd);
}
#define tmp_disable_binlog(A) \
@@ -3272,25 +3897,10 @@ my_eof(THD *thd)
#define reenable_binlog(A) (A)->variables.option_bits= tmp_disable_binlog__save_options;}
-/*
- These functions are for making it later easy to add strict
- checking for all date handling.
-*/
-
-const my_bool strict_date_checking= 0;
-
-inline ulong sql_mode_for_dates(THD *thd)
-{
- if (strict_date_checking)
- return (thd->variables.sql_mode &
- (MODE_NO_ZERO_DATE | MODE_NO_ZERO_IN_DATE |
- MODE_INVALID_DATES));
- return (thd->variables.sql_mode & MODE_INVALID_DATES);
-}
-
-inline ulong sql_mode_for_dates()
+inline sql_mode_t sql_mode_for_dates(THD *thd)
{
- return sql_mode_for_dates(current_thd);
+ return thd->variables.sql_mode &
+ (MODE_NO_ZERO_DATE | MODE_NO_ZERO_IN_DATE | MODE_INVALID_DATES);
}
/*
@@ -3320,10 +3930,42 @@ public:
class JOIN;
-class select_result :public Sql_alloc {
+/* Pure interface for sending tabular data */
+class select_result_sink: public Sql_alloc
+{
+public:
+ /*
+ send_data returns 0 on ok, 1 on error and -1 if data was ignored, for
+ example for a duplicate row entry written to a temp table.
+ */
+ virtual int send_data(List<Item> &items)=0;
+ virtual ~select_result_sink() {};
+};
+
+
+/*
+ Interface for sending tabular data, together with some other stuff:
+
+ - Primary purpose seems to be seding typed tabular data:
+ = the DDL is sent with send_fields()
+ = the rows are sent with send_data()
+ Besides that,
+ - there seems to be an assumption that the sent data is a result of
+ SELECT_LEX_UNIT *unit,
+ - nest_level is used by SQL parser
+*/
+
+class select_result :public select_result_sink
+{
protected:
THD *thd;
+ /*
+ All descendant classes have their send_data() skip the first
+ unit->offset_limit_cnt rows sent. Select_materialize
+ also uses unit->get_unit_column_types().
+ */
SELECT_LEX_UNIT *unit;
+ /* Something used only by the parser: */
public:
select_result();
virtual ~select_result() {};
@@ -3341,13 +3983,7 @@ public:
virtual uint field_count(List<Item> &fields) const
{ return fields.elements; }
virtual bool send_result_set_metadata(List<Item> &list, uint flags)=0;
- /*
- send_data returns 0 on ok, 1 on error and -1 if data was ignored, for
- example for a duplicate row entry written to a temp table.
- */
- virtual int send_data(List<Item> &items)=0;
virtual bool initialize_tables (JOIN *join=0) { return 0; }
- virtual void send_error(uint errcode,const char *err);
virtual bool send_eof()=0;
/**
Check if this query returns a result set and therefore is allowed in
@@ -3370,6 +4006,57 @@ public:
void begin_dataset() {}
#endif
virtual void update_used_tables() {}
+
+ void reset_offset_limit()
+ {
+ unit->offset_limit_cnt= 0;
+ }
+};
+
+
+/*
+ This is a select_result_sink which simply writes all data into a (temporary)
+ table. Creation/deletion of the table is outside of the scope of the class
+
+ It is aimed at capturing SHOW EXPLAIN output, so:
+ - Unlike select_result class, we don't assume that the sent data is an
+ output of a SELECT_LEX_UNIT (and so we dont apply "LIMIT x,y" from the
+ unit)
+ - We don't try to convert the target table to MyISAM
+*/
+
+class select_result_explain_buffer : public select_result_sink
+{
+public:
+ select_result_explain_buffer(THD *thd_arg, TABLE *table_arg) :
+ thd(thd_arg), dst_table(table_arg) {};
+
+ THD *thd;
+ TABLE *dst_table; /* table to write into */
+
+ /* The following is called in the child thread: */
+ int send_data(List<Item> &items);
+};
+
+
+/*
+ This is a select_result_sink which stores the data in text form.
+*/
+
+class select_result_text_buffer : public select_result_sink
+{
+public:
+ select_result_text_buffer(THD *thd_arg) : thd(thd_arg) {}
+ int send_data(List<Item> &items);
+ bool send_result_set_metadata(List<Item> &fields, uint flag);
+
+ void save_to(String *res);
+private:
+ int append_row(List<Item> &items, bool send_names);
+
+ THD *thd;
+ List<char*> rows;
+ int n_columns;
};
@@ -3423,7 +4110,6 @@ public:
select_to_file(sql_exchange *ex) :exchange(ex), file(-1),row_count(0L)
{ path[0]=0; }
~select_to_file();
- void send_error(uint errcode,const char *err);
bool send_eof();
void cleanup();
};
@@ -3496,7 +4182,6 @@ class select_insert :public select_result_interceptor {
virtual int send_data(List<Item> &items);
virtual void store_values(List<Item> &values);
virtual bool can_rollback_data() { return 0; }
- void send_error(uint errcode,const char *err);
bool prepare_eof();
bool send_ok_packet();
bool send_eof();
@@ -3517,6 +4202,8 @@ class select_create: public select_insert {
MYSQL_LOCK *m_lock;
/* m_lock or thd->extra_lock */
MYSQL_LOCK **m_plock;
+ bool exit_done;
+
public:
select_create (TABLE_LIST *table_arg,
HA_CREATE_INFO *create_info_par,
@@ -3528,13 +4215,12 @@ public:
create_info(create_info_par),
select_tables(select_tables_arg),
alter_info(alter_info_arg),
- m_plock(NULL)
+ m_plock(NULL), exit_done(0)
{}
int prepare(List<Item> &list, SELECT_LEX_UNIT *u);
int binlog_show_create_table(TABLE **tables, uint count);
void store_values(List<Item> &values);
- void send_error(uint errcode,const char *err);
bool send_eof();
virtual void abort_result_set();
virtual bool can_rollback_data() { return 1; }
@@ -3549,12 +4235,22 @@ public:
#ifdef WITH_ARIA_STORAGE_ENGINE
#include <maria.h>
+#else
+#undef USE_ARIA_FOR_TMP_TABLES
#endif
#ifdef USE_ARIA_FOR_TMP_TABLES
-#define ENGINE_COLUMNDEF MARIA_COLUMNDEF
+#define TMP_ENGINE_COLUMNDEF MARIA_COLUMNDEF
+#define TMP_ENGINE_HTON maria_hton
+#define TMP_ENGINE_NAME "Aria"
+inline uint tmp_table_max_key_length() { return maria_max_key_length(); }
+inline uint tmp_table_max_key_parts() { return maria_max_key_segments(); }
#else
-#define ENGINE_COLUMNDEF MI_COLUMNDEF
+#define TMP_ENGINE_COLUMNDEF MI_COLUMNDEF
+#define TMP_ENGINE_HTON myisam_hton
+#define TMP_ENGINE_NAME "MyISAM"
+inline uint tmp_table_max_key_length() { return MI_MAX_KEY_LENGTH; }
+inline uint tmp_table_max_key_parts() { return MI_MAX_KEY_SEG; }
#endif
/*
@@ -3577,7 +4273,7 @@ public:
Copy_field *save_copy_field, *save_copy_field_end;
uchar *group_buff;
Item **items_to_copy; /* Fields in tmp table */
- ENGINE_COLUMNDEF *recinfo, *start_recinfo;
+ TMP_ENGINE_COLUMNDEF *recinfo, *start_recinfo;
KEY *keyinfo;
ha_rows end_write_records;
/**
@@ -3827,13 +4523,13 @@ public:
/*
Cost to materialize - execute the sub-join and write rows into temp.table
*/
- COST_VECT materialization_cost;
+ Cost_estimate materialization_cost;
/* Cost to make one lookup in the temptable */
- COST_VECT lookup_cost;
+ Cost_estimate lookup_cost;
/* Cost of scanning the materialized table */
- COST_VECT scan_cost;
+ Cost_estimate scan_cost;
/* --- Execution structures ---------- */
@@ -3922,7 +4618,7 @@ public:
table.str= internal_table_name;
table.length=1;
}
- bool is_derived_table() const { return test(sel); }
+ bool is_derived_table() const { return MY_TEST(sel); }
inline void change_db(char *db_name)
{
db.str= db_name; db.length= (uint) strlen(db_name);
@@ -3948,6 +4644,8 @@ class user_var_entry
DTCollation collation;
};
+user_var_entry *get_variable(HASH *hash, LEX_STRING &name,
+ bool create_if_not_exists);
/*
Unique -- class for unique (removing of duplicates).
@@ -3970,6 +4668,7 @@ class Unique :public Sql_alloc
uint size;
uint full_size;
uint min_dupl_count; /* always 0 for unions, > 0 for intersections */
+ bool with_counters;
bool merge(TABLE *table, uchar *buff, bool without_last_merge);
@@ -4043,7 +4742,7 @@ class multi_delete :public select_result_interceptor
bool delete_while_scanning;
/*
error handling (rollback and binlogging) can happen in send_eof()
- so that afterward send_error() needs to find out that.
+ so that afterward abort_result_set() needs to find out that.
*/
bool error_handled;
@@ -4053,7 +4752,6 @@ public:
int prepare(List<Item> &list, SELECT_LEX_UNIT *u);
int send_data(List<Item> &items);
bool initialize_tables (JOIN *join);
- void send_error(uint errcode,const char *err);
int do_deletes();
int do_table_deletes(TABLE *table, bool ignore);
bool send_eof();
@@ -4089,10 +4787,12 @@ class multi_update :public select_result_interceptor
bool ignore;
/*
error handling (rollback and binlogging) can happen in send_eof()
- so that afterward send_error() needs to find out that.
+ so that afterward abort_result_set() needs to find out that.
*/
bool error_handled;
-
+
+ /* Need this to protect against multiple prepare() calls */
+ bool prepared;
public:
multi_update(TABLE_LIST *ut, List<TABLE_LIST> *leaves_list,
List<Item> *fields, List<Item> *values,
@@ -4101,7 +4801,6 @@ public:
int prepare(List<Item> &list, SELECT_LEX_UNIT *u);
int send_data(List<Item> &items);
bool initialize_tables (JOIN *join);
- void send_error(uint errcode,const char *err);
int do_updates();
bool send_eof();
inline ha_rows num_found()
@@ -4217,6 +4916,47 @@ public:
*/
#define CF_CAN_GENERATE_ROW_EVENTS (1U << 9)
+/**
+ Identifies statements which may deal with temporary tables and for which
+ temporary tables should be pre-opened to simplify privilege checks.
+*/
+#define CF_PREOPEN_TMP_TABLES (1U << 10)
+
+/**
+ Identifies statements for which open handlers should be closed in the
+ beginning of the statement.
+*/
+#define CF_HA_CLOSE (1U << 11)
+
+/**
+ Identifies statements that can be explained with EXPLAIN.
+*/
+#define CF_CAN_BE_EXPLAINED (1U << 12)
+
+/** Identifies statements which may generate an optimizer trace */
+#define CF_OPTIMIZER_TRACE (1U << 14)
+
+/**
+ Identifies statements that should always be disallowed in
+ read only transactions.
+*/
+#define CF_DISALLOW_IN_RO_TRANS (1U << 15)
+
+/**
+ Statement that need the binlog format to be unchanged.
+*/
+#define CF_FORCE_ORIGINAL_BINLOG_FORMAT (1U << 16)
+
+/**
+ Statement that inserts new rows (INSERT, REPLACE, LOAD, ALTER TABLE)
+*/
+#define CF_INSERTS_DATA (1U << 17)
+
+/**
+ Statement that updates existing rows (UPDATE, multi-update)
+*/
+#define CF_UPDATES_DATA (1U << 18)
+
/* Bits in server_command_flags */
/**
@@ -4273,112 +5013,6 @@ inline void handler::decrement_statistics(ulong SSV::*offset) const
status_var_decrement(table->in_use->status_var.*offset);
}
-inline int handler::ha_index_read_map(uchar * buf, const uchar * key,
- key_part_map keypart_map,
- enum ha_rkey_function find_flag)
-{
- DBUG_ASSERT(inited==INDEX);
- MYSQL_INDEX_READ_ROW_START(table_share->db.str, table_share->table_name.str);
- increment_statistics(&SSV::ha_read_key_count);
- int error= index_read_map(buf, key, keypart_map, find_flag);
- if (!error)
- update_index_statistics();
- table->status=error ? STATUS_NOT_FOUND: 0;
- MYSQL_INDEX_READ_ROW_DONE(error);
- return error;
-}
-
-
-/*
- @note: Other index lookup/navigation functions require prior
- handler->index_init() call. This function is different, it requires
- that the scan is not initialized, and accepts "uint index" as an argument.
-*/
-
-inline int handler::ha_index_read_idx_map(uchar * buf, uint index,
- const uchar * key,
- key_part_map keypart_map,
- enum ha_rkey_function find_flag)
-{
- DBUG_ASSERT(inited==NONE);
- MYSQL_INDEX_READ_ROW_START(table_share->db.str, table_share->table_name.str);
- increment_statistics(&SSV::ha_read_key_count);
- int error= index_read_idx_map(buf, index, key, keypart_map, find_flag);
- if (!error)
- {
- update_rows_read();
- index_rows_read[index]++;
- }
- table->status=error ? STATUS_NOT_FOUND: 0;
- MYSQL_INDEX_READ_ROW_DONE(error);
- return error;
-}
-
-inline int handler::ha_index_next(uchar * buf)
-{
- DBUG_ASSERT(inited==INDEX);
- MYSQL_INDEX_READ_ROW_START(table_share->db.str, table_share->table_name.str);
- increment_statistics(&SSV::ha_read_next_count);
- int error= index_next(buf);
- if (!error)
- update_index_statistics();
- table->status=error ? STATUS_NOT_FOUND: 0;
- MYSQL_INDEX_READ_ROW_DONE(error);
- return error;
-}
-
-inline int handler::ha_index_prev(uchar * buf)
-{
- DBUG_ASSERT(inited==INDEX);
- MYSQL_INDEX_READ_ROW_START(table_share->db.str, table_share->table_name.str);
- increment_statistics(&SSV::ha_read_prev_count);
- int error= index_prev(buf);
- if (!error)
- update_index_statistics();
- table->status=error ? STATUS_NOT_FOUND: 0;
- MYSQL_INDEX_READ_ROW_DONE(error);
- return error;
-}
-
-inline int handler::ha_index_first(uchar * buf)
-{
- DBUG_ASSERT(inited==INDEX);
- MYSQL_INDEX_READ_ROW_START(table_share->db.str, table_share->table_name.str);
- increment_statistics(&SSV::ha_read_first_count);
- int error= index_first(buf);
- if (!error)
- update_index_statistics();
- table->status=error ? STATUS_NOT_FOUND: 0;
- MYSQL_INDEX_READ_ROW_DONE(error);
- return error;
-}
-
-inline int handler::ha_index_last(uchar * buf)
-{
- DBUG_ASSERT(inited==INDEX);
- MYSQL_INDEX_READ_ROW_START(table_share->db.str, table_share->table_name.str);
- increment_statistics(&SSV::ha_read_last_count);
- int error= index_last(buf);
- if (!error)
- update_index_statistics();
- table->status=error ? STATUS_NOT_FOUND: 0;
- MYSQL_INDEX_READ_ROW_DONE(error);
- return error;
-}
-
-inline int handler::ha_index_next_same(uchar *buf, const uchar *key,
- uint keylen)
-{
- DBUG_ASSERT(inited==INDEX);
- MYSQL_INDEX_READ_ROW_START(table_share->db.str, table_share->table_name.str);
- increment_statistics(&SSV::ha_read_next_count);
- int error= index_next_same(buf, key, keylen);
- if (!error)
- update_index_statistics();
- table->status=error ? STATUS_NOT_FOUND: 0;
- MYSQL_INDEX_READ_ROW_DONE(error);
- return error;
-}
inline int handler::ha_ft_read(uchar *buf)
{
@@ -4390,37 +5024,6 @@ inline int handler::ha_ft_read(uchar *buf)
return error;
}
-inline int handler::ha_rnd_next(uchar *buf)
-{
- MYSQL_READ_ROW_START(table_share->db.str, table_share->table_name.str, TRUE);
- int error= rnd_next(buf);
- if (!error)
- {
- update_rows_read();
- increment_statistics(&SSV::ha_read_rnd_next_count);
- }
- else if (error == HA_ERR_RECORD_DELETED)
- increment_statistics(&SSV::ha_read_rnd_deleted_count);
- else
- increment_statistics(&SSV::ha_read_rnd_next_count);
-
- table->status=error ? STATUS_NOT_FOUND: 0;
- MYSQL_READ_ROW_DONE(error);
- return error;
-}
-
-inline int handler::ha_rnd_pos(uchar *buf, uchar *pos)
-{
- MYSQL_READ_ROW_START(table_share->db.str, table_share->table_name.str, FALSE);
- increment_statistics(&SSV::ha_read_rnd_count);
- int error= rnd_pos(buf, pos);
- if (!error)
- update_rows_read();
- table->status=error ? STATUS_NOT_FOUND: 0;
- MYSQL_READ_ROW_DONE(error);
- return error;
-}
-
inline int handler::ha_rnd_pos_by_record(uchar *buf)
{
int error= rnd_pos_by_record(buf);
@@ -4441,24 +5044,73 @@ inline int handler::ha_read_first_row(uchar *buf, uint primary_key)
inline int handler::ha_write_tmp_row(uchar *buf)
{
+ int error;
MYSQL_INSERT_ROW_START(table_share->db.str, table_share->table_name.str);
increment_statistics(&SSV::ha_tmp_write_count);
- int error= write_row(buf);
+ MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,
+ { error= write_row(buf); })
MYSQL_INSERT_ROW_DONE(error);
return error;
}
inline int handler::ha_update_tmp_row(const uchar *old_data, uchar *new_data)
{
+ int error;
MYSQL_UPDATE_ROW_START(table_share->db.str, table_share->table_name.str);
increment_statistics(&SSV::ha_tmp_update_count);
- int error= update_row(old_data, new_data);
+ MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_UPDATE_ROW, active_index, 0,
+ { error= update_row(old_data, new_data);})
MYSQL_UPDATE_ROW_DONE(error);
return error;
}
extern pthread_attr_t *get_connection_attrib(void);
+/**
+ Set thread entering a condition
+
+ This function should be called before putting a thread to wait for
+ a condition. @a mutex should be held before calling this
+ function. After being waken up, @f thd_exit_cond should be called.
+
+ @param thd The thread entering the condition, NULL means current thread
+ @param cond The condition the thread is going to wait for
+ @param mutex The mutex associated with the condition, this must be
+ held before call this function
+ @param stage The new process message for the thread
+ @param old_stage The old process message for the thread
+ @param src_function The caller source function name
+ @param src_file The caller source file name
+ @param src_line The caller source line number
+*/
+void thd_enter_cond(MYSQL_THD thd, mysql_cond_t *cond, mysql_mutex_t *mutex,
+ const PSI_stage_info *stage, PSI_stage_info *old_stage,
+ const char *src_function, const char *src_file,
+ int src_line);
+
+#define THD_ENTER_COND(P1, P2, P3, P4, P5) \
+ thd_enter_cond(P1, P2, P3, P4, P5, __func__, __FILE__, __LINE__)
+
+/**
+ Set thread leaving a condition
+
+ This function should be called after a thread being waken up for a
+ condition.
+
+ @param thd The thread entering the condition, NULL means current thread
+ @param stage The process message, ususally this should be the old process
+ message before calling @f thd_enter_cond
+ @param src_function The caller source function name
+ @param src_file The caller source file name
+ @param src_line The caller source line number
+*/
+void thd_exit_cond(MYSQL_THD thd, const PSI_stage_info *stage,
+ const char *src_function, const char *src_file,
+ int src_line);
+
+#define THD_EXIT_COND(P1, P2) \
+ thd_exit_cond(P1, P2, __func__, __FILE__, __LINE__)
+
#endif /* MYSQL_SERVER */
#endif /* SQL_CLASS_INCLUDED */
diff --git a/sql/sql_client.cc b/sql/sql_client.cc
index eb6c039c065..efac01f9894 100644
--- a/sql/sql_client.cc
+++ b/sql/sql_client.cc
@@ -18,6 +18,7 @@
This files defines some MySQL C API functions that are server specific
*/
+#include <my_global.h>
#include "sql_priv.h"
#include "sql_class.h" // system_variables
@@ -36,7 +37,7 @@ void my_net_local_init(NET *net)
(uint)global_system_variables.net_write_timeout);
net->retry_count= (uint) global_system_variables.net_retry_count;
- net->max_packet_size= max(global_system_variables.net_buffer_length,
+ net->max_packet_size= MY_MAX(global_system_variables.net_buffer_length,
global_system_variables.max_allowed_packet);
#endif
}
diff --git a/sql/sql_cmd.h b/sql/sql_cmd.h
new file mode 100644
index 00000000000..231db2a1d8c
--- /dev/null
+++ b/sql/sql_cmd.h
@@ -0,0 +1,165 @@
+/* Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
+
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+/**
+ @file Representation of an SQL command.
+*/
+
+#ifndef SQL_CMD_INCLUDED
+#define SQL_CMD_INCLUDED
+
+/*
+ When a command is added here, be sure it's also added in mysqld.cc
+ in "struct show_var_st status_vars[]= {" ...
+
+ If the command returns a result set or is not allowed in stored
+ functions or triggers, please also make sure that
+ sp_get_flags_for_command (sp_head.cc) returns proper flags for the
+ added SQLCOM_.
+*/
+
+enum enum_sql_command {
+ SQLCOM_SELECT, SQLCOM_CREATE_TABLE, SQLCOM_CREATE_INDEX, SQLCOM_ALTER_TABLE,
+ SQLCOM_UPDATE, SQLCOM_INSERT, SQLCOM_INSERT_SELECT,
+ SQLCOM_DELETE, SQLCOM_TRUNCATE, SQLCOM_DROP_TABLE, SQLCOM_DROP_INDEX,
+
+ SQLCOM_SHOW_DATABASES, SQLCOM_SHOW_TABLES, SQLCOM_SHOW_FIELDS,
+ SQLCOM_SHOW_KEYS, SQLCOM_SHOW_VARIABLES, SQLCOM_SHOW_STATUS,
+ SQLCOM_SHOW_ENGINE_LOGS, SQLCOM_SHOW_ENGINE_STATUS, SQLCOM_SHOW_ENGINE_MUTEX,
+ SQLCOM_SHOW_PROCESSLIST, SQLCOM_SHOW_MASTER_STAT, SQLCOM_SHOW_SLAVE_STAT,
+ SQLCOM_SHOW_GRANTS, SQLCOM_SHOW_CREATE, SQLCOM_SHOW_CHARSETS,
+ SQLCOM_SHOW_COLLATIONS, SQLCOM_SHOW_CREATE_DB, SQLCOM_SHOW_TABLE_STATUS,
+ SQLCOM_SHOW_TRIGGERS,
+
+ SQLCOM_LOAD,SQLCOM_SET_OPTION,SQLCOM_LOCK_TABLES,SQLCOM_UNLOCK_TABLES,
+ SQLCOM_GRANT,
+ SQLCOM_CHANGE_DB, SQLCOM_CREATE_DB, SQLCOM_DROP_DB, SQLCOM_ALTER_DB,
+ SQLCOM_REPAIR, SQLCOM_REPLACE, SQLCOM_REPLACE_SELECT,
+ SQLCOM_CREATE_FUNCTION, SQLCOM_DROP_FUNCTION,
+ SQLCOM_REVOKE,SQLCOM_OPTIMIZE, SQLCOM_CHECK,
+ SQLCOM_ASSIGN_TO_KEYCACHE, SQLCOM_PRELOAD_KEYS,
+ SQLCOM_FLUSH, SQLCOM_KILL, SQLCOM_ANALYZE,
+ SQLCOM_ROLLBACK, SQLCOM_ROLLBACK_TO_SAVEPOINT,
+ SQLCOM_COMMIT, SQLCOM_SAVEPOINT, SQLCOM_RELEASE_SAVEPOINT,
+ SQLCOM_SLAVE_START, SQLCOM_SLAVE_STOP,
+ SQLCOM_BEGIN, SQLCOM_CHANGE_MASTER,
+ SQLCOM_RENAME_TABLE,
+ SQLCOM_RESET, SQLCOM_PURGE, SQLCOM_PURGE_BEFORE, SQLCOM_SHOW_BINLOGS,
+ SQLCOM_SHOW_OPEN_TABLES,
+ SQLCOM_HA_OPEN, SQLCOM_HA_CLOSE, SQLCOM_HA_READ,
+ SQLCOM_SHOW_SLAVE_HOSTS, SQLCOM_DELETE_MULTI, SQLCOM_UPDATE_MULTI,
+ SQLCOM_SHOW_BINLOG_EVENTS, SQLCOM_DO,
+ SQLCOM_SHOW_WARNS, SQLCOM_EMPTY_QUERY, SQLCOM_SHOW_ERRORS,
+ SQLCOM_SHOW_STORAGE_ENGINES, SQLCOM_SHOW_PRIVILEGES,
+ SQLCOM_HELP, SQLCOM_CREATE_USER, SQLCOM_DROP_USER, SQLCOM_RENAME_USER,
+ SQLCOM_REVOKE_ALL, SQLCOM_CHECKSUM,
+ SQLCOM_CREATE_PROCEDURE, SQLCOM_CREATE_SPFUNCTION, SQLCOM_CALL,
+ SQLCOM_DROP_PROCEDURE, SQLCOM_ALTER_PROCEDURE,SQLCOM_ALTER_FUNCTION,
+ SQLCOM_SHOW_CREATE_PROC, SQLCOM_SHOW_CREATE_FUNC,
+ SQLCOM_SHOW_STATUS_PROC, SQLCOM_SHOW_STATUS_FUNC,
+ SQLCOM_PREPARE, SQLCOM_EXECUTE, SQLCOM_DEALLOCATE_PREPARE,
+ SQLCOM_CREATE_VIEW, SQLCOM_DROP_VIEW,
+ SQLCOM_CREATE_TRIGGER, SQLCOM_DROP_TRIGGER,
+ SQLCOM_XA_START, SQLCOM_XA_END, SQLCOM_XA_PREPARE,
+ SQLCOM_XA_COMMIT, SQLCOM_XA_ROLLBACK, SQLCOM_XA_RECOVER,
+ SQLCOM_SHOW_PROC_CODE, SQLCOM_SHOW_FUNC_CODE,
+ SQLCOM_ALTER_TABLESPACE,
+ SQLCOM_INSTALL_PLUGIN, SQLCOM_UNINSTALL_PLUGIN,
+ SQLCOM_SHOW_AUTHORS, SQLCOM_BINLOG_BASE64_EVENT,
+ SQLCOM_SHOW_PLUGINS,
+ SQLCOM_SHOW_CONTRIBUTORS,
+ SQLCOM_CREATE_SERVER, SQLCOM_DROP_SERVER, SQLCOM_ALTER_SERVER,
+ SQLCOM_CREATE_EVENT, SQLCOM_ALTER_EVENT, SQLCOM_DROP_EVENT,
+ SQLCOM_SHOW_CREATE_EVENT, SQLCOM_SHOW_EVENTS,
+ SQLCOM_SHOW_CREATE_TRIGGER,
+ SQLCOM_ALTER_DB_UPGRADE,
+ SQLCOM_SHOW_PROFILE, SQLCOM_SHOW_PROFILES,
+ SQLCOM_SIGNAL, SQLCOM_RESIGNAL,
+ SQLCOM_SHOW_RELAYLOG_EVENTS,
+ SQLCOM_GET_DIAGNOSTICS,
+ SQLCOM_SHOW_USER_STATS, SQLCOM_SHOW_TABLE_STATS, SQLCOM_SHOW_INDEX_STATS,
+ SQLCOM_SHOW_CLIENT_STATS,
+ SQLCOM_SLAVE_ALL_START, SQLCOM_SLAVE_ALL_STOP,
+ SQLCOM_SHOW_EXPLAIN, SQLCOM_SHUTDOWN,
+ SQLCOM_CREATE_ROLE, SQLCOM_DROP_ROLE, SQLCOM_GRANT_ROLE, SQLCOM_REVOKE_ROLE,
+
+ /*
+ When a command is added here, be sure it's also added in mysqld.cc
+ in "struct show_var_st status_vars[]= {" ...
+ */
+ /* This should be the last !!! */
+ SQLCOM_END
+};
+
+/**
+ @class Sql_cmd - Representation of an SQL command.
+
+ This class is an interface between the parser and the runtime.
+ The parser builds the appropriate derived classes of Sql_cmd
+ to represent a SQL statement in the parsed tree.
+ The execute() method in the derived classes of Sql_cmd contain the runtime
+ implementation.
+ Note that this interface is used for SQL statements recently implemented,
+ the code for older statements tend to load the LEX structure with more
+ attributes instead.
+ Implement new statements by sub-classing Sql_cmd, as this improves
+ code modularity (see the 'big switch' in dispatch_command()), and decreases
+ the total size of the LEX structure (therefore saving memory in stored
+ programs).
+ The recommended name of a derived class of Sql_cmd is Sql_cmd_<derived>.
+
+ Notice that the Sql_cmd class should not be confused with the
+ Statement class. Statement is a class that is used to manage an SQL
+ command or a set of SQL commands. When the SQL statement text is
+ analyzed, the parser will create one or more Sql_cmd objects to
+ represent the actual SQL commands.
+*/
+class Sql_cmd : public Sql_alloc
+{
+private:
+ Sql_cmd(const Sql_cmd &); // No copy constructor wanted
+ void operator=(Sql_cmd &); // No assignment operator wanted
+
+public:
+ /**
+ @brief Return the command code for this statement
+ */
+ virtual enum_sql_command sql_command_code() const = 0;
+
+ /**
+ Execute this SQL statement.
+ @param thd the current thread.
+ @retval false on success.
+ @retval true on error
+ */
+ virtual bool execute(THD *thd) = 0;
+
+protected:
+ Sql_cmd()
+ {}
+
+ virtual ~Sql_cmd()
+ {
+ /*
+ Sql_cmd objects are allocated in thd->mem_root.
+ In MySQL, the C++ destructor is never called, the underlying MEM_ROOT is
+ simply destroyed instead.
+ Do not rely on the destructor for any cleanup.
+ */
+ DBUG_ASSERT(FALSE);
+ }
+};
+
+#endif // SQL_CMD_INCLUDED
diff --git a/sql/sql_connect.cc b/sql/sql_connect.cc
index 44ad0d42d47..096f4616300 100644
--- a/sql/sql_connect.cc
+++ b/sql/sql_connect.cc
@@ -20,7 +20,7 @@
Functions to autenticate and handle reqests for a connection
*/
-#include "my_global.h"
+#include <my_global.h>
#include "sql_priv.h"
#ifndef __WIN__
#include <netdb.h> // getservbyname, servent
@@ -28,7 +28,6 @@
#include "sql_audit.h"
#include "sql_connect.h"
#include "probes_mysql.h"
-#include "unireg.h" // REQUIRED: for other includes
#include "sql_parse.h" // sql_command_flags,
// execute_init_command,
// do_command
@@ -127,6 +126,7 @@ end:
int check_for_max_user_connections(THD *thd, USER_CONN *uc)
{
int error= 1;
+ Host_errors errors;
DBUG_ENTER("check_for_max_user_connections");
mysql_mutex_lock(&LOCK_user_conn);
@@ -138,6 +138,8 @@ int check_for_max_user_connections(THD *thd, USER_CONN *uc)
!(thd->security_ctx->master_access & SUPER_ACL))
{
my_error(ER_TOO_MANY_USER_CONNECTIONS, MYF(0), uc->user);
+ error=1;
+ errors.m_max_user_connection= 1;
goto end;
}
time_out_user_resource_limits(thd, uc);
@@ -147,6 +149,8 @@ int check_for_max_user_connections(THD *thd, USER_CONN *uc)
my_error(ER_USER_LIMIT_REACHED, MYF(0), uc->user,
"max_user_connections",
(long) uc->user_resources.user_conn);
+ error= 1;
+ errors.m_max_user_connection= 1;
goto end;
}
if (uc->user_resources.conn_per_hour &&
@@ -155,6 +159,8 @@ int check_for_max_user_connections(THD *thd, USER_CONN *uc)
my_error(ER_USER_LIMIT_REACHED, MYF(0), uc->user,
"max_connections_per_hour",
(long) uc->user_resources.conn_per_hour);
+ error=1;
+ errors.m_max_user_connection_per_hour= 1;
goto end;
}
uc->conn_per_hour++;
@@ -172,6 +178,10 @@ end:
thd->user_connect= NULL;
}
mysql_mutex_unlock(&LOCK_user_conn);
+ if (error)
+ {
+ inc_host_errors(thd->main_security_ctx.ip, &errors);
+ }
DBUG_RETURN(error);
}
@@ -229,7 +239,7 @@ void time_out_user_resource_limits(THD *thd, USER_CONN *uc)
DBUG_ENTER("time_out_user_resource_limits");
/* If more than a hour since last check, reset resource checking */
- if (check_time - uc->reset_utime >= LL(3600000000))
+ if (check_time - uc->reset_utime >= 3600000000ULL)
{
uc->questions=0;
uc->updates=0;
@@ -434,7 +444,7 @@ void init_user_stats(USER_STATS *user_stats,
DBUG_ENTER("init_user_stats");
DBUG_PRINT("enter", ("user: %s priv_user: %s", user, priv_user));
- user_length= min(user_length, sizeof(user_stats->user)-1);
+ user_length= MY_MIN(user_length, sizeof(user_stats->user)-1);
memcpy(user_stats->user, user, user_length);
user_stats->user[user_length]= 0;
user_stats->user_name_length= user_length;
@@ -707,7 +717,7 @@ static void update_global_user_stats_with_user(THD *thd,
user_stats->cpu_time+= (thd->status_var.cpu_time -
thd->org_status_var.cpu_time);
/*
- This is handle specially as bytes_recieved is incremented BEFORE
+ This is handle specially as bytes_received is incremented BEFORE
org_status_var is copied.
*/
user_stats->bytes_received+= (thd->org_status_var.bytes_received-
@@ -829,14 +839,10 @@ bool thd_init_client_charset(THD *thd, uint cs_number)
Use server character set and collation if
- opt_character_set_client_handshake is not set
- client has not specified a character set
- - client character set is the same as the servers
- client character set doesn't exists in server
*/
if (!opt_character_set_client_handshake ||
- !(cs= get_charset(cs_number, MYF(0))) ||
- !my_strcasecmp(&my_charset_latin1,
- global_system_variables.character_set_client->name,
- cs->name))
+ !(cs= get_charset(cs_number, MYF(0))))
{
thd->variables.character_set_client=
global_system_variables.character_set_client;
@@ -870,7 +876,10 @@ bool init_new_connection_handler_thread()
{
pthread_detach_this_thread();
if (my_thread_init())
+ {
+ statistic_increment(connection_errors_internal, &LOCK_status);
return 1;
+ }
return 0;
}
@@ -890,6 +899,7 @@ bool init_new_connection_handler_thread()
static int check_connection(THD *thd)
{
uint connect_errors= 0;
+ int auth_rc;
NET *net= &thd->net;
DBUG_PRINT("info",
@@ -901,48 +911,116 @@ static int check_connection(THD *thd)
if (!thd->main_security_ctx.host) // If TCP/IP connection
{
+ my_bool peer_rc;
char ip[NI_MAXHOST];
- if (vio_peer_addr(net->vio, ip, &thd->peer_port, NI_MAXHOST))
- {
- my_error(ER_BAD_HOST_ERROR, MYF(0));
- return 1;
- }
- /* BEGIN : DEBUG */
- DBUG_EXECUTE_IF("addr_fake_ipv4",
+ peer_rc= vio_peer_addr(net->vio, ip, &thd->peer_port, NI_MAXHOST);
+
+ /*
+ ===========================================================================
+ DEBUG code only (begin)
+ Simulate various output from vio_peer_addr().
+ ===========================================================================
+ */
+
+ DBUG_EXECUTE_IF("vio_peer_addr_error",
+ {
+ peer_rc= 1;
+ }
+ );
+ DBUG_EXECUTE_IF("vio_peer_addr_fake_ipv4",
{
struct sockaddr *sa= (sockaddr *) &net->vio->remote;
sa->sa_family= AF_INET;
- struct in_addr *ip4= &((struct sockaddr_in *)sa)->sin_addr;
- /* See RFC 5737, 192.0.2.0/23 is reserved */
+ struct in_addr *ip4= &((struct sockaddr_in *) sa)->sin_addr;
+ /* See RFC 5737, 192.0.2.0/24 is reserved. */
const char* fake= "192.0.2.4";
ip4->s_addr= inet_addr(fake);
strcpy(ip, fake);
- };);
- /* END : DEBUG */
+ peer_rc= 0;
+ }
+ );
+
+#ifdef HAVE_IPV6
+ DBUG_EXECUTE_IF("vio_peer_addr_fake_ipv6",
+ {
+ struct sockaddr_in6 *sa= (sockaddr_in6 *) &net->vio->remote;
+ sa->sin6_family= AF_INET6;
+ struct in6_addr *ip6= & sa->sin6_addr;
+ /* See RFC 3849, ipv6 2001:DB8::/32 is reserved. */
+ const char* fake= "2001:db8::6:6";
+ /* inet_pton(AF_INET6, fake, ip6); not available on Windows XP. */
+ ip6->s6_addr[ 0] = 0x20;
+ ip6->s6_addr[ 1] = 0x01;
+ ip6->s6_addr[ 2] = 0x0d;
+ ip6->s6_addr[ 3] = 0xb8;
+ ip6->s6_addr[ 4] = 0x00;
+ ip6->s6_addr[ 5] = 0x00;
+ ip6->s6_addr[ 6] = 0x00;
+ ip6->s6_addr[ 7] = 0x00;
+ ip6->s6_addr[ 8] = 0x00;
+ ip6->s6_addr[ 9] = 0x00;
+ ip6->s6_addr[10] = 0x00;
+ ip6->s6_addr[11] = 0x00;
+ ip6->s6_addr[12] = 0x00;
+ ip6->s6_addr[13] = 0x06;
+ ip6->s6_addr[14] = 0x00;
+ ip6->s6_addr[15] = 0x06;
+ strcpy(ip, fake);
+ peer_rc= 0;
+ }
+ );
+#endif /* HAVE_IPV6 */
+
+ /*
+ ===========================================================================
+ DEBUG code only (end)
+ ===========================================================================
+ */
+ if (peer_rc)
+ {
+ /*
+ Since we can not even get the peer IP address,
+ there is nothing to show in the host_cache,
+ so increment the global status variable for peer address errors.
+ */
+ statistic_increment(connection_errors_peer_addr, &LOCK_status);
+ my_error(ER_BAD_HOST_ERROR, MYF(0));
+ return 1;
+ }
if (!(thd->main_security_ctx.ip= my_strdup(ip,MYF(MY_WME))))
+ {
+ /*
+ No error accounting per IP in host_cache,
+ this is treated as a global server OOM error.
+ TODO: remove the need for my_strdup.
+ */
+ statistic_increment(connection_errors_internal, &LOCK_status);
return 1; /* The error is set by my_strdup(). */
+ }
thd->main_security_ctx.host_or_ip= thd->main_security_ctx.ip;
if (!(specialflag & SPECIAL_NO_RESOLVE))
{
- if (ip_to_hostname(&net->vio->remote, thd->main_security_ctx.ip,
- &thd->main_security_ctx.host, &connect_errors))
- {
- my_error(ER_BAD_HOST_ERROR, MYF(0));
- return 1;
- }
+ int rc;
+
+ rc= ip_to_hostname(&net->vio->remote,
+ thd->main_security_ctx.ip,
+ &thd->main_security_ctx.host,
+ &connect_errors);
/* Cut very long hostnames to avoid possible overflows */
if (thd->main_security_ctx.host)
{
if (thd->main_security_ctx.host != my_localhost)
- thd->main_security_ctx.host[min(strlen(thd->main_security_ctx.host),
+ thd->main_security_ctx.host[MY_MIN(strlen(thd->main_security_ctx.host),
HOSTNAME_LENGTH)]= 0;
thd->main_security_ctx.host_or_ip= thd->main_security_ctx.host;
}
- if (connect_errors > max_connect_errors)
+
+ if (rc == RC_BLOCKED_HOST)
{
+ /* HOST_CACHE stats updated by ip_to_hostname(). */
my_error(ER_HOST_IS_BLOCKED, MYF(0), thd->main_security_ctx.host_or_ip);
return 1;
}
@@ -954,6 +1032,7 @@ static int check_connection(THD *thd)
thd->main_security_ctx.ip : "unknown ip")));
if (acl_check_host(thd->main_security_ctx.host, thd->main_security_ctx.ip))
{
+ /* HOST_CACHE stats updated by acl_check_host(). */
my_error(ER_HOST_NOT_PRIVILEGED, MYF(0),
thd->main_security_ctx.host_or_ip);
return 1;
@@ -970,9 +1049,34 @@ static int check_connection(THD *thd)
vio_keepalive(net->vio, TRUE);
if (thd->packet.alloc(thd->variables.net_buffer_length))
+ {
+ /*
+ Important note:
+ net_buffer_length is a SESSION variable,
+ so it may be tempting to account OOM conditions per IP in the HOST_CACHE,
+ in case some clients are more demanding than others ...
+ However, this session variable is *not* initialized with a per client
+ value during the initial connection, it is initialized from the
+ GLOBAL net_buffer_length variable from the server.
+ Hence, there is no reason to account on OOM conditions per client IP,
+ we count failures in the global server status instead.
+ */
+ statistic_increment(connection_errors_internal, &LOCK_status);
return 1; /* The error is set by alloc(). */
+ }
- return acl_authenticate(thd, connect_errors, 0);
+ auth_rc= acl_authenticate(thd, 0);
+ if (auth_rc == 0 && connect_errors != 0)
+ {
+ /*
+ A client connection from this IP was successful,
+ after some previous failures.
+ Reset the connection error counter.
+ */
+ reset_host_connect_errors(thd->main_security_ctx.ip);
+ }
+
+ return auth_rc;
}
@@ -1055,7 +1159,7 @@ bool login_connection(THD *thd)
}
exit:
- MYSQL_AUDIT_NOTIFY_CONNECTION_CONNECT(thd);
+ mysql_audit_notify_connection_connect(thd);
DBUG_RETURN(error);
}
@@ -1127,7 +1231,7 @@ void prepare_new_connection_state(THD* thd)
TODO: refactor this to avoid code duplication there
*/
thd->proc_info= 0;
- thd->command= COM_SLEEP;
+ thd->set_command(COM_SLEEP);
thd->set_time();
thd->init_for_queries();
@@ -1136,9 +1240,10 @@ void prepare_new_connection_state(THD* thd)
execute_init_command(thd, &opt_init_connect, &LOCK_sys_init_connect);
if (thd->is_error())
{
+ Host_errors errors;
thd->killed= KILL_CONNECTION;
thd->print_aborted_warning(0, "init_connect command failed");
- sql_print_warning("%s", thd->stmt_da->message());
+ sql_print_warning("%s", thd->get_stmt_da()->message());
/*
now let client to send its first command,
@@ -1163,6 +1268,8 @@ void prepare_new_connection_state(THD* thd)
thd->server_status&= ~SERVER_STATUS_CLEAR_SET;
thd->protocol->end_statement();
thd->killed = KILL_CONNECTION;
+ errors.m_init_connect= 1;
+ inc_host_errors(thd->main_security_ctx.ip, &errors);
return;
}
@@ -1274,6 +1381,7 @@ void do_handle_one_connection(THD *thd_arg)
{
bool create_user= TRUE;
+ mysql_socket_set_thread_owner(thd->net.vio->mysql_socket);
if (thd_prepare_connection(thd))
{
create_user= FALSE;
@@ -1295,7 +1403,7 @@ void do_handle_one_connection(THD *thd_arg)
thd->wsrep_query_state= QUERY_EXITING;
mysql_mutex_unlock(&thd->LOCK_wsrep_thd);
}
-#endif
+#endif
end_thread:
close_connection(thd);
diff --git a/sql/sql_const.h b/sql/sql_const.h
index 9d227601a20..5c3a6d9a7cf 100644
--- a/sql/sql_const.h
+++ b/sql/sql_const.h
@@ -17,12 +17,14 @@
@file
File containing constants that can be used throughout the server.
- @note This file shall not contain any includes of any kinds.
+ @note This file shall not contain or include any declarations of any kinds.
*/
#ifndef SQL_CONST_INCLUDED
#define SQL_CONST_INCLUDED
+#include <mysql_version.h>
+
#define LIBLEN FN_REFLEN-FN_LEN /* Max l{ngd p} dev */
/* extra 4+4 bytes for slave tmp tables */
#define MAX_DBKEY_LENGTH (NAME_LEN*2+1+1+4+4)
@@ -38,8 +40,10 @@
#define MAX_REFLENGTH 4 /* Max length for record ref */
#endif
#define MAX_HOSTNAME 61 /* len+1 in mysql.user */
+#define MAX_CONNECTION_NAME NAME_LEN
#define MAX_MBWIDTH 3 /* Max multibyte sequence */
+#define MAX_FILENAME_MBWIDTH 5
#define MAX_FIELD_CHARLENGTH 255
#define MAX_FIELD_VARCHARLENGTH 65535
#define MAX_FIELD_BLOBLENGTH UINT_MAX32 /* cf field_blob::get_length() */
@@ -54,7 +58,7 @@
#define MIN_TIME_WIDTH 10 /* -HHH:MM:SS */
#define MAX_TIME_WIDTH 16 /* -DDDDDD HH:MM:SS */
#define MAX_TIME_FULL_WIDTH 23 /* -DDDDDD HH:MM:SS.###### */
-#define MAX_DATETIME_FULL_WIDTH 29 /* YYYY-MM-DD HH:MM:SS.###### AM */
+#define MAX_DATETIME_FULL_WIDTH 26 /* YYYY-MM-DD HH:MM:SS.###### */
#define MAX_DATETIME_WIDTH 19 /* YYYY-MM-DD HH:MM:SS */
#define MAX_DATETIME_COMPRESSED_WIDTH 14 /* YYYYMMDDHHMMSS */
#define MAX_DATETIME_PRECISION 6
@@ -66,7 +70,7 @@
#define PSEUDO_TABLE_BITS (PARAM_TABLE_BIT | OUTER_REF_TABLE_BIT | \
RAND_TABLE_BIT)
#define MAX_FIELDS 4096 /* Limit in the .frm file */
-#define MAX_PARTITIONS 1024
+#define MAX_PARTITIONS 8192
#define MAX_SELECT_NESTING (sizeof(nesting_map)*8-1)
@@ -75,7 +79,6 @@
/* Some portable defines */
-#define portable_sizeof_char_ptr 8
#define STRING_BUFFER_USUAL_SIZE 80
/* Memory allocated when parsing a statement / saving a statement */
@@ -127,6 +130,13 @@
*/
#define TABLE_DEF_CACHE_MIN 400
+/**
+ Maximum number of connections default value.
+ 151 is larger than Apache's default max children,
+ to avoid "too many connections" error in a common setup.
+*/
+#define MAX_CONNECTIONS_DEFAULT 151
+
/*
Stack reservation.
Feel free to raise this by the smallest amount you can to get the
@@ -134,7 +144,7 @@
*/
#define STACK_MIN_SIZE 16000 // Abort if less stack during eval.
-#define STACK_MIN_SIZE_FOR_OPEN 1024*80
+#define STACK_MIN_SIZE_FOR_OPEN (1024*80)
#define STACK_BUFF_ALLOC 352 ///< For stack overrun checks
#ifndef MYSQLD_NET_RETRY_COUNT
#define MYSQLD_NET_RETRY_COUNT 10 ///< Abort read after this many int.
@@ -230,8 +240,8 @@
#define DEFAULT_CONCURRENCY 10
#define DELAYED_LIMIT 100 /**< pause after xxx inserts */
#define DELAYED_QUEUE_SIZE 1000
-#define DELAYED_WAIT_TIMEOUT 5*60 /**< Wait for delayed insert */
-#define MAX_CONNECT_ERRORS 10 ///< errors before disabling host
+#define DELAYED_WAIT_TIMEOUT (5*60) /**< Wait for delayed insert */
+#define MAX_CONNECT_ERRORS 100 ///< errors before disabling host
#define LONG_TIMEOUT ((ulong) 3600L*24L*365L)
diff --git a/sql/sql_crypt.cc b/sql/sql_crypt.cc
index bcc8ad0b10f..2460a16551d 100644
--- a/sql/sql_crypt.cc
+++ b/sql/sql_crypt.cc
@@ -26,6 +26,7 @@
#pragma implementation // gcc: Class implementation
#endif
+#include <my_global.h>
#include "sql_priv.h"
#include "sql_crypt.h"
#include "password.h"
diff --git a/sql/sql_crypt.h b/sql/sql_crypt.h
index 3a12d603601..3df554e9d31 100644
--- a/sql/sql_crypt.h
+++ b/sql/sql_crypt.h
@@ -22,7 +22,7 @@
#endif
#include "sql_list.h" /* Sql_alloc */
-#include "mysql_com.h" /* rand_struct */
+#include "my_rnd.h" /* rand_struct */
class SQL_CRYPT :public Sql_alloc
{
diff --git a/sql/sql_cursor.cc b/sql/sql_cursor.cc
index 230a8b2c802..d8684086e68 100644
--- a/sql/sql_cursor.cc
+++ b/sql/sql_cursor.cc
@@ -17,6 +17,7 @@
#pragma implementation /* gcc class implementation */
#endif
+#include <my_global.h>
#include "sql_priv.h"
#include "unireg.h"
#include "sql_cursor.h"
@@ -53,6 +54,8 @@ public:
virtual void fetch(ulong num_rows);
virtual void close();
virtual ~Materialized_cursor();
+
+ void on_table_fill_finished();
};
@@ -73,6 +76,18 @@ public:
Select_materialize(select_result *result_arg)
:result(result_arg), materialized_cursor(0) {}
virtual bool send_result_set_metadata(List<Item> &list, uint flags);
+ bool send_eof()
+ {
+ if (materialized_cursor)
+ materialized_cursor->on_table_fill_finished();
+ return false;
+ }
+
+ void abort_result_set()
+ {
+ if (materialized_cursor)
+ materialized_cursor->on_table_fill_finished();
+ }
};
@@ -97,6 +112,8 @@ public:
int mysql_open_cursor(THD *thd, select_result *result,
Server_side_cursor **pcursor)
{
+ sql_digest_state *parent_digest;
+ PSI_statement_locker *parent_locker;
select_result *save_result;
Select_materialize *result_materialize;
LEX *lex= thd->lex;
@@ -115,9 +132,15 @@ int mysql_open_cursor(THD *thd, select_result *result,
&thd->security_ctx->priv_user[0],
(char *) thd->security_ctx->host_or_ip,
2);
+ parent_digest= thd->m_digest;
+ parent_locker= thd->m_statement_psi;
+ thd->m_digest= NULL;
+ thd->m_statement_psi= NULL;
/* Mark that we can't use query cache with cursors */
thd->query_cache_is_applicable= 0;
rc= mysql_execute_command(thd);
+ thd->m_digest= parent_digest;
+ thd->m_statement_psi= parent_locker;
MYSQL_QUERY_EXEC_DONE(rc);
lex->result= save_result;
@@ -379,6 +402,29 @@ Materialized_cursor::~Materialized_cursor()
}
+/*
+ @brief
+ Perform actions that are to be done when cursor materialization has
+ finished.
+
+ @detail
+ This function is called when "OPEN $cursor" has finished filling the
+ temporary table with rows that the cursor will return.
+
+ Temporary table has table->field->orig_table pointing at the tables
+ that are used in the cursor definition query. Pointers to these tables
+ will not be valid after the query finishes. So, we do what is done for
+ regular tables: have orig_table point at the table that the fields belong
+ to.
+*/
+
+void Materialized_cursor::on_table_fill_finished()
+{
+ uint fields= table->s->fields;
+ for (uint i= 0; i < fields; i++)
+ table->field[i]->orig_table= table->field[i]->table;
+}
+
/***************************************************************************
Select_materialize
****************************************************************************/
diff --git a/sql/sql_db.cc b/sql/sql_db.cc
index f72a8918f61..e89c3d9e745 100644
--- a/sql/sql_db.cc
+++ b/sql/sql_db.cc
@@ -18,7 +18,7 @@
/* create and drop of databases */
-#include "my_global.h" /* NO_EMBEDDED_ACCESS_CHECKS */
+#include <my_global.h> /* NO_EMBEDDED_ACCESS_CHECKS */
#include "sql_priv.h"
#include "unireg.h"
#include "sql_db.h"
@@ -32,11 +32,13 @@
#include "log_event.h" // Query_log_event
#include "sql_base.h" // lock_table_names, tdc_remove_table
#include "sql_handler.h" // mysql_ha_rm_tables
+#include "sql_class.h"
#include <mysys_err.h>
#include "sp_head.h"
#include "sp.h"
#include "events.h"
#include "sql_handler.h"
+#include "sql_statistics.h"
#include <my_dir.h>
#include <m_ctype.h>
#include "log.h"
@@ -47,15 +49,12 @@
#define MAX_DROP_TABLE_Q_LEN 1024
-const char *del_exts[]= {".frm", ".BAK", ".TMD",".opt", NullS};
+const char *del_exts[]= {".BAK", ".opt", NullS};
static TYPELIB deletable_extentions=
{array_elements(del_exts)-1,"del_exts", del_exts, NULL};
-static bool find_db_tables_and_rm_known_files(THD *thd, MY_DIR *dirp,
- const char *db,
- const char *path,
- TABLE_LIST **tables,
- bool *found_other_files);
+static bool find_db_tables_and_rm_known_files(THD *, MY_DIR *, char *,
+ const char *, TABLE_LIST **);
long mysql_rm_arc_files(THD *thd, MY_DIR *dirp, const char *org_path);
static my_bool rm_dir_w_symlink(const char *org_path, my_bool send_error);
@@ -79,6 +78,29 @@ typedef struct my_dbopt_st
} my_dbopt_t;
+/**
+ Return TRUE if db1_name is equal to db2_name, FALSE otherwise.
+
+ The function allows to compare database names according to the MariaDB
+ rules. The database names db1 and db2 are equal if:
+ - db1 is NULL and db2 is NULL;
+ or
+ - db1 is not-NULL, db2 is not-NULL, db1 is equal to db2 in
+ table_alias_charset
+
+ This is the same rules as we use for filenames.
+*/
+
+static inline bool
+cmp_db_names(const char *db1_name,
+ const char *db2_name)
+{
+ return ((!db1_name && !db2_name) ||
+ (db1_name && db2_name &&
+ my_strcasecmp(table_alias_charset, db1_name, db2_name) == 0));
+}
+
+
/*
Function we use in the creation of our hash to get key.
*/
@@ -160,8 +182,7 @@ bool my_dboptions_cache_init(void)
if (!dboptions_init)
{
dboptions_init= 1;
- error= my_hash_init(&dboptions, lower_case_table_names ?
- &my_charset_bin : system_charset_info,
+ error= my_hash_init(&dboptions, table_alias_charset,
32, 0, 0, (my_hash_get_key) dboptions_get_key,
free_dbopt,0);
}
@@ -193,8 +214,7 @@ void my_dbopt_cleanup(void)
{
mysql_rwlock_wrlock(&LOCK_dboptions);
my_hash_free(&dboptions);
- my_hash_init(&dboptions, lower_case_table_names ?
- &my_charset_bin : system_charset_info,
+ my_hash_init(&dboptions, table_alias_charset,
32, 0, 0, (my_hash_get_key) dboptions_get_key,
free_dbopt,0);
mysql_rwlock_unlock(&LOCK_dboptions);
@@ -559,7 +579,17 @@ int mysql_create_db(THD *thd, char *db, HA_CREATE_INFO *create_info,
DBUG_RETURN(-1);
}
- if (lock_schema_name(thd, db))
+ char db_tmp[SAFE_NAME_LEN], *dbnorm;
+ if (lower_case_table_names)
+ {
+ strmake_buf(db_tmp, db);
+ my_casedn_str(system_charset_info, db_tmp);
+ dbnorm= db_tmp;
+ }
+ else
+ dbnorm= db;
+
+ if (lock_schema_name(thd, dbnorm))
DBUG_RETURN(-1);
/* Check directory */
@@ -574,7 +604,7 @@ int mysql_create_db(THD *thd, char *db, HA_CREATE_INFO *create_info,
error= -1;
goto exit;
}
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
ER_DB_CREATE_EXISTS, ER(ER_DB_CREATE_EXISTS), db);
error= 0;
goto not_silent;
@@ -758,14 +788,22 @@ bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent)
char path[FN_REFLEN + 16];
MY_DIR *dirp;
uint length;
- bool found_other_files= false;
TABLE_LIST *tables= NULL;
TABLE_LIST *table;
Drop_table_error_handler err_handler;
DBUG_ENTER("mysql_rm_db");
+ char db_tmp[SAFE_NAME_LEN], *dbnorm;
+ if (lower_case_table_names)
+ {
+ strmake_buf(db_tmp, db);
+ my_casedn_str(system_charset_info, db_tmp);
+ dbnorm= db_tmp;
+ }
+ else
+ dbnorm= db;
- if (lock_schema_name(thd, db))
+ if (lock_schema_name(thd, dbnorm))
DBUG_RETURN(true);
length= build_table_filename(path, sizeof(path) - 1, db, "", "", 0);
@@ -783,15 +821,14 @@ bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent)
}
else
{
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
ER_DB_DROP_EXISTS, ER(ER_DB_DROP_EXISTS), db);
error= false;
goto update_binlog;
}
}
- if (find_db_tables_and_rm_known_files(thd, dirp, db, path, &tables,
- &found_other_files))
+ if (find_db_tables_and_rm_known_files(thd, dirp, dbnorm, path, &tables))
goto exit;
/*
@@ -801,37 +838,38 @@ bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent)
if ((my_strcasecmp(system_charset_info, MYSQL_SCHEMA_NAME.str, db) == 0))
{
for (table= tables; table; table= table->next_local)
- {
- if (check_if_log_table(table->db_length, table->db,
- table->table_name_length, table->table_name, true))
- {
- my_error(ER_BAD_LOG_STATEMENT, MYF(0), "DROP");
+ if (check_if_log_table(table, TRUE, "DROP"))
goto exit;
- }
- }
}
/* Lock all tables and stored routines about to be dropped. */
if (lock_table_names(thd, tables, NULL, thd->variables.lock_wait_timeout,
- MYSQL_OPEN_SKIP_TEMPORARY) ||
- lock_db_routines(thd, db))
+ 0) ||
+ lock_db_routines(thd, dbnorm))
goto exit;
+ if (!in_bootstrap)
+ {
+ for (table= tables; table; table= table->next_local)
+ {
+ LEX_STRING db_name= { table->db, table->db_length };
+ LEX_STRING table_name= { table->table_name, table->table_name_length };
+ if (table->open_type == OT_BASE_ONLY || !find_temporary_table(thd, table))
+ (void) delete_statistics_for_table(thd, &db_name, &table_name);
+ }
+ }
+
/* mysql_ha_rm_tables() requires a non-null TABLE_LIST. */
if (tables)
mysql_ha_rm_tables(thd, tables);
for (table= tables; table; table= table->next_local)
- {
- tdc_remove_table(thd, TDC_RT_REMOVE_ALL, table->db, table->table_name,
- false);
deleted_tables++;
- }
thd->push_internal_handler(&err_handler);
if (!thd->killed &&
!(tables &&
- mysql_rm_table_no_locks(thd, tables, true, false, true, true)))
+ mysql_rm_table_no_locks(thd, tables, true, false, true, true, false)))
{
/*
We temporarily disable the binary log while dropping the objects
@@ -854,10 +892,10 @@ bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent)
ha_drop_database(path);
tmp_disable_binlog(thd);
- query_cache_invalidate1(thd, db);
- (void) sp_drop_db_routines(thd, db); /* @todo Do not ignore errors */
+ query_cache_invalidate1(thd, dbnorm);
+ (void) sp_drop_db_routines(thd, dbnorm); /* @todo Do not ignore errors */
#ifdef HAVE_EVENT_SCHEDULER
- Events::drop_schema_events(thd, db);
+ Events::drop_schema_events(thd, dbnorm);
#endif
reenable_binlog(thd);
@@ -865,10 +903,7 @@ bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent)
If the directory is a symbolic link, remove the link first, then
remove the directory the symbolic link pointed at
*/
- if (found_other_files)
- my_error(ER_DB_DROP_RMDIR, MYF(0), path, EEXIST);
- else
- error= rm_dir_w_symlink(path, true);
+ error= rm_dir_w_symlink(path, true);
}
thd->pop_internal_handler();
@@ -924,16 +959,10 @@ update_binlog:
for (tbl= tables; tbl; tbl= tbl->next_local)
{
uint tbl_name_len;
- bool exists;
char quoted_name[FN_REFLEN+3];
// Only write drop table to the binlog for tables that no longer exist.
- if (check_if_table_exists(thd, tbl, 0, &exists))
- {
- error= true;
- goto exit;
- }
- if (exists)
+ if (ha_table_exists(thd, tbl->db, tbl->table_name))
continue;
my_snprintf(quoted_name, sizeof(quoted_name), "%`s", tbl->table_name);
@@ -977,7 +1006,7 @@ exit:
SELECT DATABASE() in the future). For this we free() thd->db and set
it to 0.
*/
- if (thd->db && !strcmp(thd->db, db) && !error)
+ if (thd->db && cmp_db_names(thd->db, db) && !error)
mysql_change_db_impl(thd, NULL, 0, thd->variables.collation_server);
my_dirend(dirp);
DBUG_RETURN(error);
@@ -985,31 +1014,66 @@ exit:
static bool find_db_tables_and_rm_known_files(THD *thd, MY_DIR *dirp,
- const char *db,
+ char *dbname,
const char *path,
- TABLE_LIST **tables,
- bool *found_other_files)
+ TABLE_LIST **tables)
{
char filePath[FN_REFLEN];
+ LEX_STRING db= { dbname, strlen(dbname) };
TABLE_LIST *tot_list=0, **tot_list_next_local, **tot_list_next_global;
DBUG_ENTER("find_db_tables_and_rm_known_files");
DBUG_PRINT("enter",("path: %s", path));
+ /* first, get the list of tables */
+ Dynamic_array<LEX_STRING*> files(dirp->number_of_files);
+ Discovered_table_list tl(thd, &files);
+ if (ha_discover_table_names(thd, &db, dirp, &tl, true))
+ DBUG_RETURN(1);
+
+ /* Now put the tables in the list */
tot_list_next_local= tot_list_next_global= &tot_list;
+ for (size_t idx=0; idx < files.elements(); idx++)
+ {
+ LEX_STRING *table= files.at(idx);
+
+ /* Drop the table nicely */
+ TABLE_LIST *table_list=(TABLE_LIST*)thd->calloc(sizeof(*table_list));
+
+ if (!table_list)
+ DBUG_RETURN(true);
+ table_list->db= db.str;
+ table_list->db_length= db.length;
+ table_list->table_name= table->str;
+ table_list->table_name_length= table->length;
+ table_list->open_type= OT_BASE_ONLY;
+
+ /* To be able to correctly look up the table in the table cache. */
+ if (lower_case_table_names)
+ table_list->table_name_length= my_casedn_str(files_charset_info,
+ table_list->table_name);
+
+ table_list->alias= table_list->table_name; // If lower_case_table_names=2
+ table_list->mdl_request.init(MDL_key::TABLE, table_list->db,
+ table_list->table_name, MDL_EXCLUSIVE,
+ MDL_TRANSACTION);
+ /* Link into list */
+ (*tot_list_next_local)= table_list;
+ (*tot_list_next_global)= table_list;
+ tot_list_next_local= &table_list->next_local;
+ tot_list_next_global= &table_list->next_global;
+ }
+ *tables= tot_list;
+
+ /* and at last delete all non-table files */
for (uint idx=0 ;
- idx < (uint) dirp->number_off_files && !thd->killed ;
+ idx < (uint) dirp->number_of_files && !thd->killed ;
idx++)
{
FILEINFO *file=dirp->dir_entry+idx;
char *extension;
DBUG_PRINT("info",("Examining: %s", file->name));
- /* skiping . and .. */
- if (file->name[0] == '.' && (!file->name[1] ||
- (file->name[1] == '.' && !file->name[2])))
- continue;
-
if (file->name[0] == 'a' && file->name[1] == 'r' &&
file->name[2] == 'c' && file->name[3] == '\0')
{
@@ -1026,59 +1090,12 @@ static bool find_db_tables_and_rm_known_files(THD *thd, MY_DIR *dirp,
DBUG_PRINT("my",("Archive subdir found: %s", newpath));
if ((mysql_rm_arc_files(thd, new_dirp, newpath)) < 0)
DBUG_RETURN(true);
- continue;
}
- *found_other_files= true;
continue;
}
if (!(extension= strrchr(file->name, '.')))
extension= strend(file->name);
- if (find_type(extension, &deletable_extentions, FIND_TYPE_NO_PREFIX) <= 0)
- {
- if (find_type(extension, ha_known_exts(), FIND_TYPE_NO_PREFIX) <= 0)
- *found_other_files= true;
- continue;
- }
- /* just for safety we use files_charset_info */
- if (db && !my_strcasecmp(files_charset_info,
- extension, reg_ext))
- {
- /* Drop the table nicely */
- *extension= 0; // Remove extension
- TABLE_LIST *table_list=(TABLE_LIST*)
- thd->calloc(sizeof(*table_list) +
- strlen(db) + 1 +
- MYSQL50_TABLE_NAME_PREFIX_LENGTH +
- strlen(file->name) + 1);
-
- if (!table_list)
- DBUG_RETURN(true);
- table_list->db= (char*) (table_list+1);
- table_list->db_length= strmov(table_list->db, db) - table_list->db;
- table_list->table_name= table_list->db + table_list->db_length + 1;
- table_list->table_name_length= filename_to_tablename(file->name,
- table_list->table_name,
- MYSQL50_TABLE_NAME_PREFIX_LENGTH +
- strlen(file->name) + 1);
- table_list->open_type= OT_BASE_ONLY;
-
- /* To be able to correctly look up the table in the table cache. */
- if (lower_case_table_names)
- table_list->table_name_length= my_casedn_str(files_charset_info,
- table_list->table_name);
-
- 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);
- table_list->mdl_request.init(MDL_key::TABLE, table_list->db,
- table_list->table_name, MDL_EXCLUSIVE,
- MDL_TRANSACTION);
- /* Link into list */
- (*tot_list_next_local)= table_list;
- (*tot_list_next_global)= table_list;
- tot_list_next_local= &table_list->next_local;
- tot_list_next_global= &table_list->next_global;
- }
- else
+ if (find_type(extension, &deletable_extentions, FIND_TYPE_NO_PREFIX) > 0)
{
strxmov(filePath, path, "/", file->name, NullS);
/*
@@ -1093,7 +1110,7 @@ static bool find_db_tables_and_rm_known_files(THD *thd, MY_DIR *dirp,
}
}
}
- *tables= tot_list;
+
DBUG_RETURN(false);
}
@@ -1177,18 +1194,13 @@ long mysql_rm_arc_files(THD *thd, MY_DIR *dirp, const char *org_path)
DBUG_PRINT("enter", ("path: %s", org_path));
for (uint idx=0 ;
- idx < (uint) dirp->number_off_files && !thd->killed ;
+ idx < (uint) dirp->number_of_files && !thd->killed ;
idx++)
{
FILEINFO *file=dirp->dir_entry+idx;
char *extension, *revision;
DBUG_PRINT("info",("Examining: %s", file->name));
- /* skiping . and .. */
- if (file->name[0] == '.' && (!file->name[1] ||
- (file->name[1] == '.' && !file->name[2])))
- continue;
-
extension= fn_ext(file->name);
if (extension[0] != '.' ||
extension[1] != 'f' || extension[2] != 'r' ||
@@ -1328,27 +1340,6 @@ static void backup_current_db_name(THD *thd,
/**
- Return TRUE if db1_name is equal to db2_name, FALSE otherwise.
-
- The function allows to compare database names according to the MySQL
- rules. The database names db1 and db2 are equal if:
- - db1 is NULL and db2 is NULL;
- or
- - db1 is not-NULL, db2 is not-NULL, db1 is equal (ignoring case) to
- db2 in system character set (UTF8).
-*/
-
-static inline bool
-cmp_db_names(const char *db1_name,
- const char *db2_name)
-{
- return ((!db1_name && !db2_name) ||
- (db1_name && db2_name &&
- my_strcasecmp(system_charset_info, db1_name, db2_name) == 0));
-}
-
-
-/**
@brief Change the current database and its attributes unconditionally.
@param thd thread handle
@@ -1492,14 +1483,18 @@ bool mysql_change_db(THD *thd, const LEX_STRING *new_db_name, bool force_switch)
DBUG_PRINT("info",("Use database: %s", new_db_file_name.str));
#ifndef NO_EMBEDDED_ACCESS_CHECKS
- db_access=
- test_all_bits(sctx->master_access, DB_ACLS) ?
- DB_ACLS :
- acl_get(sctx->host,
- sctx->ip,
- sctx->priv_user,
- new_db_file_name.str,
- FALSE) | sctx->master_access;
+ if (test_all_bits(sctx->master_access, DB_ACLS))
+ db_access= DB_ACLS;
+ else
+ {
+ db_access= acl_get(sctx->host, sctx->ip, sctx->priv_user,
+ new_db_file_name.str, FALSE) | sctx->master_access;
+ if (sctx->priv_role[0])
+ {
+ /* include a possible currently set role for access */
+ db_access|= acl_get("", "", sctx->priv_role, new_db_file_name.str, FALSE);
+ }
+ }
if (!force_switch &&
!(db_access & DB_ACLS) &&
@@ -1524,7 +1519,7 @@ bool mysql_change_db(THD *thd, const LEX_STRING *new_db_name, bool force_switch)
{
/* Throw a warning and free new_db_file_name. */
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
ER_BAD_DB_ERROR, ER(ER_BAD_DB_ERROR),
new_db_file_name.str);
@@ -1674,7 +1669,7 @@ bool mysql_upgrade_db(THD *thd, LEX_STRING *old_db)
/* Step2: Move tables to the new database */
if ((dirp = my_dir(path,MYF(MY_DONT_SORT))))
{
- uint nfiles= (uint) dirp->number_off_files;
+ uint nfiles= (uint) dirp->number_of_files;
for (uint idx=0 ; idx < nfiles && !thd->killed ; idx++)
{
FILEINFO *file= dirp->dir_entry + idx;
@@ -1765,17 +1760,15 @@ bool mysql_upgrade_db(THD *thd, LEX_STRING *old_db)
if ((dirp = my_dir(path,MYF(MY_DONT_SORT))))
{
- uint nfiles= (uint) dirp->number_off_files;
+ uint nfiles= (uint) dirp->number_of_files;
for (uint idx=0 ; idx < nfiles ; idx++)
{
FILEINFO *file= dirp->dir_entry + idx;
char oldname[FN_REFLEN + 1], newname[FN_REFLEN + 1];
DBUG_PRINT("info",("Examining: %s", file->name));
- /* skiping . and .. and MY_DB_OPT_FILE */
- if ((file->name[0] == '.' &&
- (!file->name[1] || (file->name[1] == '.' && !file->name[2]))) ||
- !my_strcasecmp(files_charset_info, file->name, MY_DB_OPT_FILE))
+ /* skiping MY_DB_OPT_FILE */
+ if (!my_strcasecmp(files_charset_info, file->name, MY_DB_OPT_FILE))
continue;
/* pass empty file name, and file->name as extension to avoid encoding */
diff --git a/sql/sql_db.h b/sql/sql_db.h
index 1f447c11a52..62d379c515d 100644
--- a/sql/sql_db.h
+++ b/sql/sql_db.h
@@ -19,7 +19,6 @@
#include "hash.h" /* HASH */
class THD;
-typedef struct st_ha_create_information HA_CREATE_INFO;
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);
diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc
index dbfb44801b0..4de7f88d736 100644
--- a/sql/sql_delete.cc
+++ b/sql/sql_delete.cc
@@ -1,5 +1,6 @@
/*
Copyright (c) 2000, 2010, Oracle and/or its affiliates.
+ Copyright (c) 2010, 2015, MariaDB
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
@@ -20,6 +21,7 @@
Multi-table deletes were introduced by Monty and Sinisa
*/
+#include <my_global.h>
#include "sql_priv.h"
#include "unireg.h"
#include "sql_delete.h"
@@ -35,10 +37,166 @@
#include "sql_select.h"
#include "sp_head.h"
#include "sql_trigger.h"
+#include "sql_statistics.h"
#include "transaction.h"
#include "records.h" // init_read_record,
#include "sql_derived.h" // mysql_handle_list_of_derived
// end_read_record
+#include "sql_partition.h" // make_used_partitions_str
+
+/*
+ @brief
+ Print query plan of a single-table DELETE command
+
+ @detail
+ This function is used by EXPLAIN DELETE and by SHOW EXPLAIN when it is
+ invoked on a running DELETE statement.
+*/
+
+void Delete_plan::save_explain_data(Explain_query *query)
+{
+ Explain_delete* explain= new Explain_delete;
+
+ if (deleting_all_rows)
+ {
+ explain->deleting_all_rows= true;
+ explain->select_type= "SIMPLE";
+ explain->rows= scanned_rows;
+ }
+ else
+ {
+ explain->deleting_all_rows= false;
+ Update_plan::save_explain_data_intern(query, explain);
+ }
+
+ query->add_upd_del_plan(explain);
+}
+
+
+void Update_plan::save_explain_data(Explain_query *query)
+{
+ Explain_update* explain= new Explain_update;
+ save_explain_data_intern(query, explain);
+ query->add_upd_del_plan(explain);
+}
+
+
+void Update_plan::save_explain_data_intern(Explain_query *query,
+ Explain_update *explain)
+{
+ explain->select_type= "SIMPLE";
+ explain->table_name.append(table->pos_in_table_list->alias);
+
+ explain->impossible_where= false;
+ explain->no_partitions= false;
+
+ if (impossible_where)
+ {
+ explain->impossible_where= true;
+ return;
+ }
+
+ if (no_partitions)
+ {
+ explain->no_partitions= true;
+ return;
+ }
+
+ select_lex->set_explain_type(TRUE);
+ explain->select_type= select_lex->type;
+ /* Partitions */
+ {
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+ partition_info *part_info;
+ if ((part_info= table->part_info))
+ {
+ make_used_partitions_str(part_info, &explain->used_partitions);
+ explain->used_partitions_set= true;
+ }
+ else
+ explain->used_partitions_set= false;
+#else
+ /* just produce empty column if partitioning is not compiled in */
+ explain->used_partitions_set= false;
+#endif
+ }
+
+
+ /* Set jtype */
+ if (select && select->quick)
+ {
+ int quick_type= select->quick->get_type();
+ if ((quick_type == QUICK_SELECT_I::QS_TYPE_INDEX_MERGE) ||
+ (quick_type == QUICK_SELECT_I::QS_TYPE_INDEX_INTERSECT) ||
+ (quick_type == QUICK_SELECT_I::QS_TYPE_ROR_INTERSECT) ||
+ (quick_type == QUICK_SELECT_I::QS_TYPE_ROR_UNION))
+ explain->jtype= JT_INDEX_MERGE;
+ else
+ explain->jtype= JT_RANGE;
+ }
+ else
+ {
+ if (index == MAX_KEY)
+ explain->jtype= JT_ALL;
+ else
+ explain->jtype= JT_NEXT;
+ }
+
+ explain->using_where= MY_TEST(select && select->cond);
+ explain->using_filesort= using_filesort;
+ explain->using_io_buffer= using_io_buffer;
+
+ make_possible_keys_line(table, possible_keys, &explain->possible_keys_line);
+
+ explain->quick_info= NULL;
+
+ /* Calculate key_len */
+ if (select && select->quick)
+ {
+ explain->quick_info= select->quick->get_explain(mem_root);
+ }
+ else
+ {
+ if (index != MAX_KEY)
+ {
+ explain->key_str.append(table->key_info[index].name);
+ char buf[64];
+ size_t length;
+ length= longlong10_to_str(table->key_info[index].key_length, buf, 10) - buf;
+ explain->key_len_str.append(buf, length);
+ }
+ }
+ explain->rows= scanned_rows;
+
+ if (select && select->quick &&
+ select->quick->get_type() == QUICK_SELECT_I::QS_TYPE_RANGE)
+ {
+ explain_append_mrr_info((QUICK_RANGE_SELECT*)select->quick,
+ &explain->mrr_type);
+ }
+
+ bool skip= updating_a_view;
+
+ /* Save subquery children */
+ for (SELECT_LEX_UNIT *unit= select_lex->first_inner_unit();
+ unit;
+ unit= unit->next_unit())
+ {
+ if (skip)
+ {
+ skip= false;
+ continue;
+ }
+ /*
+ Display subqueries only if they are not parts of eliminated WHERE/ON
+ clauses.
+ */
+ if (!(unit->item && unit->item->eliminated))
+ explain->add_child(unit->first_select()->select_number);
+ }
+}
+
+
/**
Implement DELETE SQL word.
@@ -48,7 +206,8 @@
*/
bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
- SQL_I_List<ORDER> *order_list, ha_rows limit, ulonglong options)
+ SQL_I_List<ORDER> *order_list, ha_rows limit,
+ ulonglong options, select_result *result)
{
bool will_batch;
int error, loc_error;
@@ -62,12 +221,16 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
bool reverse= FALSE;
ORDER *order= (ORDER *) ((order_list && order_list->elements) ?
order_list->first : NULL);
- uint usable_index= MAX_KEY;
SELECT_LEX *select_lex= &thd->lex->select_lex;
killed_state killed_status= NOT_KILLED;
THD::enum_binlog_query_type query_type= THD::ROW_QUERY_TYPE;
+ bool with_select= !select_lex->item_list.is_empty();
+ Delete_plan query_plan(thd->mem_root);
+ query_plan.index= MAX_KEY;
+ query_plan.using_filesort= FALSE;
DBUG_ENTER("mysql_delete");
+ create_explain_query(thd->lex, thd->mem_root);
if (open_and_lock_tables(thd, table_list, TRUE, 0))
DBUG_RETURN(TRUE);
@@ -87,12 +250,18 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
table_list->view_db.str, table_list->view_name.str);
DBUG_RETURN(TRUE);
}
- thd_proc_info(thd, "init");
+ THD_STAGE_INFO(thd, stage_init);
table->map=1;
+ query_plan.select_lex= &thd->lex->select_lex;
+ query_plan.table= table;
+ query_plan.updating_a_view= MY_TEST(table_list->view);
- if (mysql_prepare_delete(thd, table_list, &conds))
+ if (mysql_prepare_delete(thd, table_list, select_lex->with_wild,
+ select_lex->item_list, &conds))
DBUG_RETURN(TRUE);
+ (void) result->prepare(select_lex->item_list, NULL);
+
if (thd->lex->current_select->first_cond_optimization)
{
thd->lex->current_select->save_leaf_tables(thd);
@@ -124,7 +293,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
DBUG_RETURN(TRUE);
const_cond= (!conds || conds->const_item());
- safe_update=test(thd->variables.option_bits & OPTION_SAFE_UPDATES);
+ safe_update= MY_TEST(thd->variables.option_bits & OPTION_SAFE_UPDATES);
if (safe_update && const_cond)
{
my_message(ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE,
@@ -154,14 +323,19 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
- We should not be binlogging this statement in row-based, and
- there should be no delete triggers associated with the table.
*/
- if (!using_limit && const_cond_result &&
- (!thd->is_current_stmt_binlog_format_row() &&
- !(table->triggers && table->triggers->has_delete_triggers())))
+ if (!with_select && !using_limit && const_cond_result &&
+ (!thd->is_current_stmt_binlog_format_row() &&
+ !(table->triggers && table->triggers->has_delete_triggers())))
{
/* Update the table->file->stats.records number */
table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK);
ha_rows const maybe_deleted= table->file->stats.records;
DBUG_PRINT("debug", ("Trying to use delete_all_rows()"));
+
+ query_plan.set_delete_all_rows(maybe_deleted);
+ if (thd->lex->describe)
+ goto exit_without_my_ok;
+
if (!(error=table->file->ha_delete_all_rows()))
{
/*
@@ -186,20 +360,30 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
Item::cond_result result;
conds= remove_eq_conds(thd, conds, &result);
if (result == Item::COND_FALSE) // Impossible where
+ {
limit= 0;
+ query_plan.set_impossible_where();
+ if (thd->lex->describe)
+ goto exit_without_my_ok;
+ }
}
#ifdef WITH_PARTITION_STORAGE_ENGINE
if (prune_partitions(thd, table, conds))
{
free_underlaid_joins(thd, select_lex);
- // No matching record
+
+ query_plan.set_no_partitions();
+ if (thd->lex->describe)
+ goto exit_without_my_ok;
+
my_ok(thd, 0);
DBUG_RETURN(0);
}
#endif
/* Update the table->file->stats.records number */
table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK);
+ set_statistics_for_table(thd, table);
table->covering_keys.clear_all();
table->quick_keys.clear_all(); // Can't use 'only index'
@@ -209,6 +393,10 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
DBUG_RETURN(TRUE);
if ((select && select->check_quick(thd, safe_update, limit)) || !limit)
{
+ query_plan.set_impossible_where();
+ if (thd->lex->describe)
+ goto exit_without_my_ok;
+
delete select;
free_underlaid_joins(thd, select_lex);
/*
@@ -226,7 +414,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
/* If running in safe sql mode, don't allow updates without keys */
if (table->quick_keys.is_clear_all())
{
- thd->server_status|=SERVER_QUERY_NO_INDEX_USED;
+ thd->set_status_no_index_used();
if (safe_update && !using_limit)
{
delete select;
@@ -239,41 +427,70 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
if (options & OPTION_QUICK)
(void) table->file->extra(HA_EXTRA_QUICK);
+ query_plan.scanned_rows= select? select->records: table->file->stats.records;
if (order)
{
- uint length= 0;
- SORT_FIELD *sortorder;
- ha_rows examined_rows;
-
table->update_const_key_parts(conds);
order= simple_remove_const(order, conds);
- bool need_sort;
if (select && select->quick && select->quick->unique_key_range())
{ // Single row select (always "ordered")
- need_sort= FALSE;
- usable_index= MAX_KEY;
+ query_plan.using_filesort= FALSE;
+ query_plan.index= MAX_KEY;
}
else
- usable_index= get_index_for_order(order, table, select, limit,
- &need_sort, &reverse);
- if (need_sort)
{
- DBUG_ASSERT(usable_index == MAX_KEY);
+ ha_rows scanned_limit= query_plan.scanned_rows;
+ query_plan.index= get_index_for_order(order, table, select, limit,
+ &scanned_limit,
+ &query_plan.using_filesort,
+ &reverse);
+ if (!query_plan.using_filesort)
+ query_plan.scanned_rows= scanned_limit;
+ }
+ }
+
+ query_plan.select= select;
+ query_plan.possible_keys= select? select->possible_keys: key_map(0);
+
+ /*
+ Ok, we have generated a query plan for the DELETE.
+ - if we're running EXPLAIN DELETE, goto produce explain output
+ - otherwise, execute the query plan
+ */
+ if (thd->lex->describe)
+ goto exit_without_my_ok;
+
+ query_plan.save_explain_data(thd->lex->explain);
+
+ DBUG_EXECUTE_IF("show_explain_probe_delete_exec_start",
+ dbug_serve_apcs(thd, 1););
+
+ if (query_plan.using_filesort)
+ {
+ ha_rows examined_rows;
+ ha_rows found_rows;
+ uint length= 0;
+ SORT_FIELD *sortorder;
+
+ {
+ DBUG_ASSERT(query_plan.index == MAX_KEY);
table->sort.io_cache= (IO_CACHE *) my_malloc(sizeof(IO_CACHE),
- MYF(MY_FAE | MY_ZEROFILL));
+ MYF(MY_FAE | MY_ZEROFILL |
+ MY_THREAD_SPECIFIC));
if (!(sortorder= make_unireg_sortorder(order, &length, NULL)) ||
- (table->sort.found_records = filesort(thd, table, sortorder, length,
- select, HA_POS_ERROR, 1,
- &examined_rows))
+ (table->sort.found_records= filesort(thd, table, sortorder, length,
+ select, HA_POS_ERROR,
+ true,
+ &examined_rows, &found_rows))
== HA_POS_ERROR)
{
delete select;
free_underlaid_joins(thd, &thd->lex->select_lex);
DBUG_RETURN(TRUE);
}
- thd->examined_row_count+= examined_rows;
+ thd->inc_examined_row_count(examined_rows);
/*
Filesort has already found and selected the rows we want to delete,
so we don't need the where clause
@@ -291,39 +508,39 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
free_underlaid_joins(thd, select_lex);
DBUG_RETURN(TRUE);
}
- if (usable_index == MAX_KEY || (select && select->quick))
+
+ if (query_plan.index == MAX_KEY || (select && select->quick))
+ error= init_read_record(&info, thd, table, select, 1, 1, FALSE);
+ else
+ error= init_read_record_idx(&info, thd, table, 1, query_plan.index,
+ reverse);
+ if (error)
{
- if (init_read_record(&info, thd, table, select, 1, 1, FALSE))
- {
- delete select;
- free_underlaid_joins(thd, select_lex);
- DBUG_RETURN(TRUE);
- }
+ delete select;
+ free_underlaid_joins(thd, select_lex);
+ DBUG_RETURN(TRUE);
}
- else
- init_read_record_idx(&info, thd, table, 1, usable_index, reverse);
init_ftfuncs(thd, select_lex, 1);
- thd_proc_info(thd, "updating");
+ THD_STAGE_INFO(thd, stage_updating);
- if (table->triggers &&
- table->triggers->has_triggers(TRG_EVENT_DELETE,
- TRG_ACTION_AFTER))
+ if (table->prepare_triggers_for_delete_stmt_or_event())
{
- /*
- The table has AFTER DELETE triggers that might access to subject table
- and therefore might need delete to be done immediately. So we turn-off
- the batching.
- */
- (void) table->file->extra(HA_EXTRA_DELETE_CANNOT_BATCH);
will_batch= FALSE;
}
else
will_batch= !table->file->start_bulk_delete();
-
table->mark_columns_needed_for_delete();
+ if (with_select)
+ {
+ if (result->send_result_set_metadata(select_lex->item_list,
+ Protocol::SEND_NUM_ROWS |
+ Protocol::SEND_EOF))
+ goto cleanup;
+ }
+
while (!(error=info.read_record(&info)) && !thd->killed &&
! thd->is_error())
{
@@ -331,7 +548,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
update_virtual_fields(thd, table,
table->triggers ? VCOL_UPDATE_ALL :
VCOL_UPDATE_FOR_READ);
- thd->examined_row_count++;
+ thd->inc_examined_row_count(1);
// thd->is_error() is tested to disallow delete row on error
if (!select || select->skip_record(thd) > 0)
{
@@ -343,6 +560,12 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
break;
}
+ if (with_select && result->send_data(select_lex->item_list) < 0)
+ {
+ error=1;
+ break;
+ }
+
if (!(error= table->file->ha_delete_row(table->record[0])))
{
deleted++;
@@ -388,7 +611,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
table->file->print_error(loc_error,MYF(0));
error=1;
}
- thd_proc_info(thd, "end");
+ THD_STAGE_INFO(thd, stage_end);
end_read_record(&info);
if (options & OPTION_QUICK)
(void) table->file->extra(HA_EXTRA_NORMAL);
@@ -453,10 +676,23 @@ cleanup:
if (error < 0 ||
(thd->lex->ignore && !thd->is_error() && !thd->is_fatal_error))
{
- my_ok(thd, deleted);
+ if (!with_select)
+ my_ok(thd, deleted);
+ else
+ result->send_eof();
DBUG_PRINT("info",("%ld records deleted",(long) deleted));
}
DBUG_RETURN(error >= 0 || thd->is_error());
+
+ /* Special exits */
+exit_without_my_ok:
+ query_plan.save_explain_data(thd->lex->explain);
+ int err2= thd->lex->explain->send_explain(thd);
+
+ delete select;
+ free_underlaid_joins(thd, select_lex);
+ //table->set_keyread(false);
+ DBUG_RETURN((err2 || thd->is_error() || thd->killed) ? 1 : 0);
}
@@ -467,13 +703,16 @@ cleanup:
mysql_prepare_delete()
thd - thread handler
table_list - global/local table list
+ wild_num - number of wildcards used in optional SELECT clause
+ field_list - list of items in optional SELECT clause
conds - conditions
RETURN VALUE
FALSE OK
TRUE error
*/
-int mysql_prepare_delete(THD *thd, TABLE_LIST *table_list, Item **conds)
+ int mysql_prepare_delete(THD *thd, TABLE_LIST *table_list,
+ uint wild_num, List<Item> &field_list, Item **conds)
{
Item *fake_conds= 0;
SELECT_LEX *select_lex= &thd->lex->select_lex;
@@ -485,7 +724,10 @@ int mysql_prepare_delete(THD *thd, TABLE_LIST *table_list, Item **conds)
&thd->lex->select_lex.top_join_list,
table_list,
select_lex->leaf_tables, FALSE,
- DELETE_ACL, SELECT_ACL, TRUE) ||
+ DELETE_ACL, SELECT_ACL, TRUE))
+ DBUG_RETURN(TRUE);
+ if ((wild_num && setup_wild(thd, table_list, field_list, NULL, wild_num)) ||
+ setup_fields(thd, NULL, field_list, MARK_COLUMNS_READ, NULL, 0) ||
setup_conds(thd, table_list, select_lex->leaf_tables, conds) ||
setup_ftfuncs(select_lex))
DBUG_RETURN(TRUE);
@@ -636,7 +878,7 @@ multi_delete::prepare(List<Item> &values, SELECT_LEX_UNIT *u)
DBUG_ENTER("multi_delete::prepare");
unit= u;
do_delete= 1;
- thd_proc_info(thd, "deleting from main table");
+ THD_STAGE_INFO(thd, stage_deleting_from_main_table);
SELECT_LEX *select_lex= u->first_select();
if (select_lex->first_cond_optimization)
{
@@ -697,17 +939,7 @@ multi_delete::initialize_tables(JOIN *join)
transactional_tables= 1;
else
normal_tables= 1;
- if (tbl->triggers &&
- tbl->triggers->has_triggers(TRG_EVENT_DELETE,
- TRG_ACTION_AFTER))
- {
- /*
- The table has AFTER DELETE triggers that might access to subject
- table and therefore might need delete to be done immediately.
- So we turn-off the batching.
- */
- (void) tbl->file->extra(HA_EXTRA_DELETE_CANNOT_BATCH);
- }
+ tbl->prepare_triggers_for_delete_stmt_or_event();
tbl->prepare_for_position();
tbl->mark_columns_needed_for_delete();
}
@@ -824,17 +1056,6 @@ int multi_delete::send_data(List<Item> &values)
}
-void multi_delete::send_error(uint errcode,const char *err)
-{
- DBUG_ENTER("multi_delete::send_error");
-
- /* First send error what ever it is ... */
- my_message(errcode, err, MYF(0));
-
- DBUG_VOID_RETURN;
-}
-
-
void multi_delete::abort_result_set()
{
DBUG_ENTER("multi_delete::abort_result_set");
@@ -1032,7 +1253,7 @@ int multi_delete::do_table_deletes(TABLE *table, bool ignore)
bool multi_delete::send_eof()
{
killed_state killed_status= NOT_KILLED;
- thd_proc_info(thd, "deleting from reference tables");
+ THD_STAGE_INFO(thd, stage_deleting_from_reference_tables);
/* Does deletes for the last n - 1 tables, returns 0 if ok */
int local_error= do_deletes(); // returns 0 if success
@@ -1041,7 +1262,7 @@ bool multi_delete::send_eof()
local_error= local_error || error;
killed_status= (local_error == 0)? NOT_KILLED : thd->killed;
/* reset used flags */
- thd_proc_info(thd, "end");
+ THD_STAGE_INFO(thd, stage_end);
if (thd->transaction.stmt.modified_non_trans_table)
thd->transaction.all.modified_non_trans_table= TRUE;
@@ -1077,7 +1298,7 @@ bool multi_delete::send_eof()
}
}
if (local_error != 0)
- error_handled= TRUE; // to force early leave from ::send_error()
+ error_handled= TRUE; // to force early leave from ::abort_result_set()
if (!local_error)
{
diff --git a/sql/sql_delete.h b/sql/sql_delete.h
index 6147e0ea367..9cd09dc5722 100644
--- a/sql/sql_delete.h
+++ b/sql/sql_delete.h
@@ -21,12 +21,15 @@
class THD;
struct TABLE_LIST;
class Item;
+class select_result;
typedef class Item COND;
template <typename T> class SQL_I_List;
-int mysql_prepare_delete(THD *thd, TABLE_LIST *table_list, Item **conds);
+int mysql_prepare_delete(THD *thd, TABLE_LIST *table_list,
+ uint wild_num, List<Item> &field_list, Item **conds);
bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
- SQL_I_List<ORDER> *order, ha_rows rows, ulonglong options);
+ SQL_I_List<ORDER> *order, ha_rows rows,
+ ulonglong options, select_result *result);
#endif /* SQL_DELETE_INCLUDED */
diff --git a/sql/sql_derived.cc b/sql/sql_derived.cc
index c6865a7116e..fdc615d0fae 100644
--- a/sql/sql_derived.cc
+++ b/sql/sql_derived.cc
@@ -1,5 +1,6 @@
/*
Copyright (c) 2002, 2011, Oracle and/or its affiliates.
+ Copyright (c) 2010, 2015, MariaDB
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
@@ -21,7 +22,7 @@
*/
-#include "my_global.h" /* NO_EMBEDDED_ACCESS_CHECKS */
+#include <my_global.h> /* NO_EMBEDDED_ACCESS_CHECKS */
#include "sql_priv.h"
#include "unireg.h"
#include "sql_derived.h"
@@ -505,6 +506,15 @@ unconditional_materialization:
bool mysql_derived_merge_for_insert(THD *thd, LEX *lex, TABLE_LIST *derived)
{
DBUG_ENTER("mysql_derived_merge_for_insert");
+ DBUG_PRINT("enter", ("derived: %p", derived));
+ DBUG_PRINT("info", ("merged_for_insert: %d is_materialized_derived: %d "
+ "is_multitable: %d single_table_updatable: %d "
+ "merge_underlying_list: %d",
+ derived->merged_for_insert,
+ derived->is_materialized_derived(),
+ derived->is_multitable(),
+ derived->single_table_updatable(),
+ derived->merge_underlying_list != 0));
if (derived->merged_for_insert)
DBUG_RETURN(FALSE);
if (derived->is_materialized_derived())
@@ -521,8 +531,9 @@ bool mysql_derived_merge_for_insert(THD *thd, LEX *lex, TABLE_LIST *derived)
derived->table= derived->merge_underlying_list->table;
derived->schema_table= derived->merge_underlying_list->schema_table;
derived->merged_for_insert= TRUE;
+ DBUG_ASSERT(derived->table);
}
- }
+ }
DBUG_RETURN(FALSE);
}
@@ -549,6 +560,7 @@ bool mysql_derived_init(THD *thd, LEX *lex, TABLE_LIST *derived)
{
SELECT_LEX_UNIT *unit= derived->get_unit();
DBUG_ENTER("mysql_derived_init");
+ DBUG_PRINT("enter", ("derived: %p", derived));
// Skip already prepared views/DT
if (!unit || unit->prepared)
@@ -694,6 +706,7 @@ bool mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *derived)
thd->create_tmp_table_for_derived= FALSE;
derived->table= derived->derived_result->table;
+ DBUG_ASSERT(derived->table);
if (derived->is_derived() && derived->is_merged_derived())
first_select->mark_as_belong_to_derived(derived);
@@ -702,9 +715,9 @@ exit:
if (derived->view)
{
if (thd->is_error() &&
- (thd->stmt_da->sql_errno() == ER_BAD_FIELD_ERROR ||
- thd->stmt_da->sql_errno() == ER_FUNC_INEXISTENT_NAME_COLLISION ||
- thd->stmt_da->sql_errno() == ER_SP_DOES_NOT_EXIST))
+ (thd->get_stmt_da()->sql_errno() == ER_BAD_FIELD_ERROR ||
+ thd->get_stmt_da()->sql_errno() == ER_FUNC_INEXISTENT_NAME_COLLISION ||
+ thd->get_stmt_da()->sql_errno() == ER_SP_DOES_NOT_EXIST))
{
thd->clear_error();
my_error(ER_VIEW_INVALID, MYF(0), derived->db,
@@ -961,8 +974,7 @@ bool mysql_derived_reinit(THD *thd, LEX *lex, TABLE_LIST *derived)
DBUG_ENTER("mysql_derived_reinit");
st_select_lex_unit *unit= derived->get_unit();
- if (derived->table)
- derived->merged_for_insert= FALSE;
+ derived->merged_for_insert= FALSE;
unit->unclean();
unit->types.empty();
/* for derived tables & PS (which can't be reset by Item_subquery) */
@@ -970,4 +982,3 @@ bool mysql_derived_reinit(THD *thd, LEX *lex, TABLE_LIST *derived)
unit->set_thd(thd);
DBUG_RETURN(FALSE);
}
-
diff --git a/sql/sql_derived.h b/sql/sql_derived.h
index f232445879e..1dffef7235b 100644
--- a/sql/sql_derived.h
+++ b/sql/sql_derived.h
@@ -23,6 +23,7 @@ struct LEX;
bool mysql_handle_derived(LEX *lex, uint phases);
bool mysql_handle_single_derived(LEX *lex, TABLE_LIST *derived, uint phases);
bool mysql_handle_list_of_derived(LEX *lex, TABLE_LIST *dt_list, uint phases);
+bool mysql_derived_reinit(THD *thd, LEX *lex, TABLE_LIST *derived);
/**
Cleans up the SELECT_LEX_UNIT for the derived table (if any).
diff --git a/sql/sql_digest.cc b/sql/sql_digest.cc
new file mode 100644
index 00000000000..c8ba371ea7e
--- /dev/null
+++ b/sql/sql_digest.cc
@@ -0,0 +1,690 @@
+/* Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved.
+
+ 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,
+ 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */
+
+/*
+ This code needs extra visibility in the lexer structures
+*/
+
+#include "my_global.h"
+#include "my_md5.h"
+#include "mysqld_error.h"
+
+#include "sql_string.h"
+#include "sql_class.h"
+#include "sql_lex.h"
+#include "sql_digest.h"
+#include "sql_digest_stream.h"
+
+#include "sql_get_diagnostics.h"
+
+#ifdef NEVER
+#include "my_sys.h"
+#include "sql_signal.h"
+#endif
+
+/* Generated code */
+#include "sql_yacc.h"
+#define LEX_TOKEN_WITH_DEFINITION
+#include "lex_token.h"
+
+/* Name pollution from sql/sql_lex.h */
+#ifdef LEX_YYSTYPE
+#undef LEX_YYSTYPE
+#endif
+
+#define LEX_YYSTYPE YYSTYPE*
+
+#define SIZE_OF_A_TOKEN 2
+
+/**
+ Read a single token from token array.
+*/
+inline uint read_token(const sql_digest_storage *digest_storage,
+ uint index, uint *tok)
+{
+ uint safe_byte_count= digest_storage->m_byte_count;
+
+ if (index + SIZE_OF_A_TOKEN <= safe_byte_count &&
+ safe_byte_count <= digest_storage->m_token_array_length)
+ {
+ const unsigned char *src= & digest_storage->m_token_array[index];
+ *tok= src[0] | (src[1] << 8);
+ return index + SIZE_OF_A_TOKEN;
+ }
+
+ /* The input byte stream is exhausted. */
+ *tok= 0;
+ return MAX_DIGEST_STORAGE_SIZE + 1;
+}
+
+/**
+ Store a single token in token array.
+*/
+inline void store_token(sql_digest_storage* digest_storage, uint token)
+{
+ DBUG_ASSERT(digest_storage->m_byte_count <= digest_storage->m_token_array_length);
+
+ if (digest_storage->m_byte_count + SIZE_OF_A_TOKEN <= digest_storage->m_token_array_length)
+ {
+ unsigned char* dest= & digest_storage->m_token_array[digest_storage->m_byte_count];
+ dest[0]= token & 0xff;
+ dest[1]= (token >> 8) & 0xff;
+ digest_storage->m_byte_count+= SIZE_OF_A_TOKEN;
+ }
+ else
+ {
+ digest_storage->m_full= true;
+ }
+}
+
+/**
+ Read an identifier from token array.
+*/
+inline uint read_identifier(const sql_digest_storage* digest_storage,
+ uint index, char ** id_string, int *id_length)
+{
+ uint new_index;
+ uint safe_byte_count= digest_storage->m_byte_count;
+
+ DBUG_ASSERT(index <= safe_byte_count);
+ DBUG_ASSERT(safe_byte_count <= digest_storage->m_token_array_length);
+
+ /*
+ token + length + string are written in an atomic way,
+ so we do always expect a length + string here
+ */
+
+ uint bytes_needed= SIZE_OF_A_TOKEN;
+ /* If we can read token and identifier length */
+ if ((index + bytes_needed) <= safe_byte_count)
+ {
+ const unsigned char *src= & digest_storage->m_token_array[index];
+ /* Read the length of identifier */
+ uint length= src[0] | (src[1] << 8);
+ bytes_needed+= length;
+ /* If we can read entire identifier from token array */
+ if ((index + bytes_needed) <= safe_byte_count)
+ {
+ *id_string= (char *) (src + 2);
+ *id_length= length;
+
+ new_index= index + bytes_needed;
+ DBUG_ASSERT(new_index <= safe_byte_count);
+ return new_index;
+ }
+ }
+
+ /* The input byte stream is exhausted. */
+ return MAX_DIGEST_STORAGE_SIZE + 1;
+}
+
+/**
+ Store an identifier in token array.
+*/
+inline void store_token_identifier(sql_digest_storage* digest_storage,
+ uint token,
+ size_t id_length, const char *id_name)
+{
+ DBUG_ASSERT(digest_storage->m_byte_count <= digest_storage->m_token_array_length);
+
+ size_t bytes_needed= 2 * SIZE_OF_A_TOKEN + id_length;
+ if (digest_storage->m_byte_count + bytes_needed <= (unsigned int)digest_storage->m_token_array_length)
+ {
+ unsigned char* dest= & digest_storage->m_token_array[digest_storage->m_byte_count];
+ /* Write the token */
+ dest[0]= token & 0xff;
+ dest[1]= (token >> 8) & 0xff;
+ /* Write the string length */
+ dest[2]= id_length & 0xff;
+ dest[3]= (id_length >> 8) & 0xff;
+ /* Write the string data */
+ if (id_length > 0)
+ memcpy((char *)(dest + 4), id_name, id_length);
+ digest_storage->m_byte_count+= bytes_needed;
+ }
+ else
+ {
+ digest_storage->m_full= true;
+ }
+}
+
+void compute_digest_md5(const sql_digest_storage *digest_storage, unsigned char *md5)
+{
+ compute_md5_hash((char *) md5,
+ (const char *) digest_storage->m_token_array,
+ digest_storage->m_byte_count);
+}
+
+/*
+ Iterate token array and updates digest_text.
+*/
+void compute_digest_text(const sql_digest_storage* digest_storage,
+ String *digest_text)
+{
+ DBUG_ASSERT(digest_storage != NULL);
+ uint byte_count= digest_storage->m_byte_count;
+ String *digest_output= digest_text;
+ uint tok= 0;
+ uint current_byte= 0;
+ lex_token_string *tok_data;
+
+ /* Reset existing data */
+ digest_output->length(0);
+
+ if (byte_count > digest_storage->m_token_array_length)
+ {
+ digest_output->append("\0", 1);
+ return;
+ }
+
+ /* Convert text to utf8 */
+ const CHARSET_INFO *from_cs= get_charset(digest_storage->m_charset_number, MYF(0));
+ const CHARSET_INFO *to_cs= &my_charset_utf8_bin;
+
+ if (from_cs == NULL)
+ {
+ /*
+ Can happen, as we do dirty reads on digest_storage,
+ which can be written to in another thread.
+ */
+ digest_output->append("\0", 1);
+ return;
+ }
+
+ char id_buffer[NAME_LEN + 1]= {'\0'};
+ char *id_string;
+ size_t id_length;
+ bool convert_text= !my_charset_same(from_cs, to_cs);
+
+ while (current_byte < byte_count)
+ {
+ current_byte= read_token(digest_storage, current_byte, &tok);
+
+ if (tok <= 0 || tok >= array_elements(lex_token_array)
+ || current_byte > max_digest_length)
+ return;
+
+ tok_data= &lex_token_array[tok];
+
+ switch (tok)
+ {
+ /* All identifiers are printed with their name. */
+ case IDENT:
+ case IDENT_QUOTED:
+ case TOK_IDENT:
+ {
+ char *id_ptr= NULL;
+ int id_len= 0;
+ uint err_cs= 0;
+
+ /* Get the next identifier from the storage buffer. */
+ current_byte= read_identifier(digest_storage, current_byte,
+ &id_ptr, &id_len);
+ if (current_byte > max_digest_length)
+ return;
+
+ if (convert_text)
+ {
+ /* Verify that the converted text will fit. */
+ if (to_cs->mbmaxlen*id_len > NAME_LEN)
+ {
+ digest_output->append("...", 3);
+ break;
+ }
+ /* Convert identifier string into the storage character set. */
+ id_length= my_convert(id_buffer, NAME_LEN, to_cs,
+ id_ptr, id_len, from_cs, &err_cs);
+ id_string= id_buffer;
+ }
+ else
+ {
+ id_string= id_ptr;
+ id_length= id_len;
+ }
+
+ if (id_length == 0 || err_cs != 0)
+ {
+ break;
+ }
+ /* Copy the converted identifier into the digest string. */
+ digest_output->append("`", 1);
+ if (id_length > 0)
+ digest_output->append(id_string, id_length);
+ digest_output->append("` ", 2);
+ }
+ break;
+
+ /* Everything else is printed as is. */
+ default:
+ /*
+ Make sure not to overflow digest_text buffer.
+ +1 is to make sure extra space for ' '.
+ */
+ int tok_length= tok_data->m_token_length;
+
+ digest_output->append(tok_data->m_token_string, tok_length);
+ if (tok_data->m_append_space)
+ digest_output->append(" ", 1);
+ break;
+ }
+ }
+}
+
+static inline uint peek_token(const sql_digest_storage *digest, uint index)
+{
+ uint token;
+ DBUG_ASSERT(index + SIZE_OF_A_TOKEN <= digest->m_byte_count);
+ DBUG_ASSERT(digest->m_byte_count <= digest->m_token_array_length);
+
+ token= ((digest->m_token_array[index + 1])<<8) | digest->m_token_array[index];
+ return token;
+}
+
+/**
+ Function to read last two tokens from token array. If an identifier
+ is found, do not look for token before that.
+*/
+static inline void peek_last_two_tokens(const sql_digest_storage* digest_storage,
+ uint last_id_index, uint *t1, uint *t2)
+{
+ uint byte_count= digest_storage->m_byte_count;
+ uint peek_index= byte_count;
+
+ if (last_id_index + SIZE_OF_A_TOKEN <= peek_index)
+ {
+ /* Take last token. */
+ peek_index-= SIZE_OF_A_TOKEN;
+ *t1= peek_token(digest_storage, peek_index);
+
+ if (last_id_index + SIZE_OF_A_TOKEN <= peek_index)
+ {
+ /* Take 2nd token from last. */
+ peek_index-= SIZE_OF_A_TOKEN;
+ *t2= peek_token(digest_storage, peek_index);
+ }
+ else
+ {
+ *t2= TOK_UNUSED;
+ }
+ }
+ else
+ {
+ *t1= TOK_UNUSED;
+ *t2= TOK_UNUSED;
+ }
+}
+
+/**
+ Function to read last three tokens from token array. If an identifier
+ is found, do not look for token before that.
+*/
+static inline void peek_last_three_tokens(const sql_digest_storage* digest_storage,
+ uint last_id_index, uint *t1, uint *t2, uint *t3)
+{
+ uint byte_count= digest_storage->m_byte_count;
+ uint peek_index= byte_count;
+
+ if (last_id_index + SIZE_OF_A_TOKEN <= peek_index)
+ {
+ /* Take last token. */
+ peek_index-= SIZE_OF_A_TOKEN;
+ *t1= peek_token(digest_storage, peek_index);
+
+ if (last_id_index + SIZE_OF_A_TOKEN <= peek_index)
+ {
+ /* Take 2nd token from last. */
+ peek_index-= SIZE_OF_A_TOKEN;
+ *t2= peek_token(digest_storage, peek_index);
+
+ if (last_id_index + SIZE_OF_A_TOKEN <= peek_index)
+ {
+ /* Take 3rd token from last. */
+ peek_index-= SIZE_OF_A_TOKEN;
+ *t3= peek_token(digest_storage, peek_index);
+ }
+ else
+ {
+ *t3= TOK_UNUSED;
+ }
+ }
+ else
+ {
+ *t2= TOK_UNUSED;
+ *t3= TOK_UNUSED;
+ }
+ }
+ else
+ {
+ *t1= TOK_UNUSED;
+ *t2= TOK_UNUSED;
+ *t3= TOK_UNUSED;
+ }
+}
+
+sql_digest_state* digest_add_token(sql_digest_state *state,
+ uint token,
+ LEX_YYSTYPE yylval)
+{
+ sql_digest_storage *digest_storage= NULL;
+
+ digest_storage= &state->m_digest_storage;
+
+ /*
+ Stop collecting further tokens if digest storage is full or
+ if END token is received.
+ */
+ if (digest_storage->m_full || token == END_OF_INPUT)
+ return NULL;
+
+ /*
+ Take last_token 2 tokens collected till now. These tokens will be used
+ in reduce for normalisation. Make sure not to consider ID tokens in reduce.
+ */
+ uint last_token;
+ uint last_token2;
+
+ switch (token)
+ {
+ case NUM:
+ case LONG_NUM:
+ case ULONGLONG_NUM:
+ case DECIMAL_NUM:
+ case FLOAT_NUM:
+ case BIN_NUM:
+ case HEX_NUM:
+ {
+ bool found_unary;
+ do
+ {
+ found_unary= false;
+ peek_last_two_tokens(digest_storage, state->m_last_id_index,
+ &last_token, &last_token2);
+
+ if ((last_token == '-') || (last_token == '+'))
+ {
+ /*
+ We need to differentiate:
+ - a <unary minus> operator
+ - a <unary plus> operator
+ from
+ - a <binary minus> operator
+ - a <binary plus> operator
+ to only reduce "a = -1" to "a = ?", and not change "b - 1" to "b ?"
+
+ Binary operators are found inside an expression,
+ while unary operators are found at the beginning of an expression, or after operators.
+
+ To achieve this, every token that is followed by an <expr> expression
+ in the SQL grammar is flagged.
+ See sql/sql_yacc.yy
+ See sql/gen_lex_token.cc
+
+ For example,
+ "(-1)" is parsed as "(", "-", NUM, ")", and lex_token_array["("].m_start_expr is true,
+ so reduction of the "-" NUM is done, the result is "(?)".
+ "(a-1)" is parsed as "(", ID, "-", NUM, ")", and lex_token_array[ID].m_start_expr is false,
+ so the operator is binary, no reduction is done, and the result is "(a-?)".
+ */
+ if (lex_token_array[last_token2].m_start_expr)
+ {
+ /*
+ REDUCE:
+ TOK_GENERIC_VALUE := (UNARY_PLUS | UNARY_MINUS) (NUM | LOG_NUM | ... | FLOAT_NUM)
+
+ REDUCE:
+ TOK_GENERIC_VALUE := (UNARY_PLUS | UNARY_MINUS) TOK_GENERIC_VALUE
+ */
+ token= TOK_GENERIC_VALUE;
+ digest_storage->m_byte_count-= SIZE_OF_A_TOKEN;
+ found_unary= true;
+ }
+ }
+ } while (found_unary);
+ }
+ /* fall through, for case NULL_SYM below */
+ case LEX_HOSTNAME:
+ case TEXT_STRING:
+ case NCHAR_STRING:
+ case PARAM_MARKER:
+ {
+ /*
+ REDUCE:
+ TOK_GENERIC_VALUE := BIN_NUM | DECIMAL_NUM | ... | ULONGLONG_NUM
+ */
+ token= TOK_GENERIC_VALUE;
+
+ peek_last_two_tokens(digest_storage, state->m_last_id_index,
+ &last_token, &last_token2);
+
+ if ((last_token2 == TOK_GENERIC_VALUE ||
+ last_token2 == TOK_GENERIC_VALUE_LIST) &&
+ (last_token == ','))
+ {
+ /*
+ REDUCE:
+ TOK_GENERIC_VALUE_LIST :=
+ TOK_GENERIC_VALUE ',' TOK_GENERIC_VALUE
+
+ REDUCE:
+ TOK_GENERIC_VALUE_LIST :=
+ TOK_GENERIC_VALUE_LIST ',' TOK_GENERIC_VALUE
+ */
+ digest_storage->m_byte_count-= 2*SIZE_OF_A_TOKEN;
+ token= TOK_GENERIC_VALUE_LIST;
+ }
+ /*
+ Add this token or the resulting reduce to digest storage.
+ */
+ store_token(digest_storage, token);
+ break;
+ }
+ case ')':
+ {
+ peek_last_two_tokens(digest_storage, state->m_last_id_index,
+ &last_token, &last_token2);
+
+ if (last_token == TOK_GENERIC_VALUE &&
+ last_token2 == '(')
+ {
+ /*
+ REDUCE:
+ TOK_ROW_SINGLE_VALUE :=
+ '(' TOK_GENERIC_VALUE ')'
+ */
+ digest_storage->m_byte_count-= 2*SIZE_OF_A_TOKEN;
+ token= TOK_ROW_SINGLE_VALUE;
+
+ /* Read last two tokens again */
+ peek_last_two_tokens(digest_storage, state->m_last_id_index,
+ &last_token, &last_token2);
+
+ if ((last_token2 == TOK_ROW_SINGLE_VALUE ||
+ last_token2 == TOK_ROW_SINGLE_VALUE_LIST) &&
+ (last_token == ','))
+ {
+ /*
+ REDUCE:
+ TOK_ROW_SINGLE_VALUE_LIST :=
+ TOK_ROW_SINGLE_VALUE ',' TOK_ROW_SINGLE_VALUE
+
+ REDUCE:
+ TOK_ROW_SINGLE_VALUE_LIST :=
+ TOK_ROW_SINGLE_VALUE_LIST ',' TOK_ROW_SINGLE_VALUE
+ */
+ digest_storage->m_byte_count-= 2*SIZE_OF_A_TOKEN;
+ token= TOK_ROW_SINGLE_VALUE_LIST;
+ }
+ }
+ else if (last_token == TOK_GENERIC_VALUE_LIST &&
+ last_token2 == '(')
+ {
+ /*
+ REDUCE:
+ TOK_ROW_MULTIPLE_VALUE :=
+ '(' TOK_GENERIC_VALUE_LIST ')'
+ */
+ digest_storage->m_byte_count-= 2*SIZE_OF_A_TOKEN;
+ token= TOK_ROW_MULTIPLE_VALUE;
+
+ /* Read last two tokens again */
+ peek_last_two_tokens(digest_storage, state->m_last_id_index,
+ &last_token, &last_token2);
+
+ if ((last_token2 == TOK_ROW_MULTIPLE_VALUE ||
+ last_token2 == TOK_ROW_MULTIPLE_VALUE_LIST) &&
+ (last_token == ','))
+ {
+ /*
+ REDUCE:
+ TOK_ROW_MULTIPLE_VALUE_LIST :=
+ TOK_ROW_MULTIPLE_VALUE ',' TOK_ROW_MULTIPLE_VALUE
+
+ REDUCE:
+ TOK_ROW_MULTIPLE_VALUE_LIST :=
+ TOK_ROW_MULTIPLE_VALUE_LIST ',' TOK_ROW_MULTIPLE_VALUE
+ */
+ digest_storage->m_byte_count-= 2*SIZE_OF_A_TOKEN;
+ token= TOK_ROW_MULTIPLE_VALUE_LIST;
+ }
+ }
+ /*
+ Add this token or the resulting reduce to digest storage.
+ */
+ store_token(digest_storage, token);
+ break;
+ }
+ case IDENT:
+ case IDENT_QUOTED:
+ {
+ YYSTYPE *lex_token= yylval;
+ char *yytext= lex_token->lex_str.str;
+ size_t yylen= lex_token->lex_str.length;
+
+ /*
+ REDUCE:
+ TOK_IDENT := IDENT | IDENT_QUOTED
+ The parser gives IDENT or IDENT_TOKEN for the same text,
+ depending on the character set used.
+ We unify both to always print the same digest text,
+ and always have the same digest hash.
+ */
+ token= TOK_IDENT;
+ /* Add this token and identifier string to digest storage. */
+ store_token_identifier(digest_storage, token, yylen, yytext);
+
+ /* Update the index of last identifier found. */
+ state->m_last_id_index= digest_storage->m_byte_count;
+ break;
+ }
+ default:
+ {
+ /* Add this token to digest storage. */
+ store_token(digest_storage, token);
+ break;
+ }
+ }
+
+ return state;
+}
+
+sql_digest_state* digest_reduce_token(sql_digest_state *state,
+ uint token_left, uint token_right)
+{
+ sql_digest_storage *digest_storage= NULL;
+
+ digest_storage= &state->m_digest_storage;
+
+ /*
+ Stop collecting further tokens if digest storage is full.
+ */
+ if (digest_storage->m_full)
+ return NULL;
+
+ uint last_token;
+ uint last_token2;
+ uint last_token3;
+ uint token_to_push= TOK_UNUSED;
+
+ peek_last_two_tokens(digest_storage, state->m_last_id_index,
+ &last_token, &last_token2);
+
+ /*
+ There is only one caller of digest_reduce_token(),
+ see sql/sql_yacc.yy, rule literal := NULL_SYM.
+ REDUCE:
+ token_left := token_right
+ Used for:
+ TOK_GENERIC_VALUE := NULL_SYM
+ */
+
+ if (last_token == token_right)
+ {
+ /*
+ Current stream is like:
+ TOKEN_X TOKEN_RIGHT .
+ REDUCE to
+ TOKEN_X TOKEN_LEFT .
+ */
+ digest_storage->m_byte_count-= SIZE_OF_A_TOKEN;
+ store_token(digest_storage, token_left);
+ }
+ else
+ {
+ /*
+ Current stream is like:
+ TOKEN_X TOKEN_RIGHT TOKEN_Y .
+ Pop TOKEN_Y
+ TOKEN_X TOKEN_RIGHT . TOKEN_Y
+ REDUCE to
+ TOKEN_X TOKEN_LEFT . TOKEN_Y
+ */
+ DBUG_ASSERT(last_token2 == token_right);
+ digest_storage->m_byte_count-= 2 * SIZE_OF_A_TOKEN;
+ store_token(digest_storage, token_left);
+ token_to_push= last_token;
+ }
+
+ peek_last_three_tokens(digest_storage, state->m_last_id_index,
+ &last_token, &last_token2, &last_token3);
+
+ if ((last_token3 == TOK_GENERIC_VALUE ||
+ last_token3 == TOK_GENERIC_VALUE_LIST) &&
+ (last_token2 == ',') &&
+ (last_token == TOK_GENERIC_VALUE))
+ {
+ /*
+ REDUCE:
+ TOK_GENERIC_VALUE_LIST :=
+ TOK_GENERIC_VALUE ',' TOK_GENERIC_VALUE
+
+ REDUCE:
+ TOK_GENERIC_VALUE_LIST :=
+ TOK_GENERIC_VALUE_LIST ',' TOK_GENERIC_VALUE
+ */
+ digest_storage->m_byte_count-= 3*SIZE_OF_A_TOKEN;
+ store_token(digest_storage, TOK_GENERIC_VALUE_LIST);
+ }
+
+ if (token_to_push != TOK_UNUSED)
+ {
+ /*
+ Push TOKEN_Y
+ */
+ store_token(digest_storage, token_to_push);
+ }
+
+ return state;
+}
+
diff --git a/sql/sql_digest.h b/sql/sql_digest.h
new file mode 100644
index 00000000000..eaf74b9542e
--- /dev/null
+++ b/sql/sql_digest.h
@@ -0,0 +1,129 @@
+/* Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved.
+
+ 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,
+ 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */
+
+#ifndef SQL_DIGEST_H
+#define SQL_DIGEST_H
+
+#include <string.h>
+class String;
+#include "my_md5.h"
+
+#define MAX_DIGEST_STORAGE_SIZE (1024*1024)
+
+/**
+ Structure to store token count/array for a statement
+ on which digest is to be calculated.
+*/
+struct sql_digest_storage
+{
+ bool m_full;
+ uint m_byte_count;
+ unsigned char m_md5[MD5_HASH_SIZE];
+ /** Character set number. */
+ uint m_charset_number;
+ /**
+ Token array.
+ Token array is an array of bytes to store tokens received during parsing.
+ Following is the way token array is formed.
+ ... &lt;non-id-token&gt; &lt;non-id-token&gt; &lt;id-token&gt; &lt;id_len&gt; &lt;id_text&gt; ...
+ For Example:
+ SELECT * FROM T1;
+ &lt;SELECT_TOKEN&gt; &lt;*&gt; &lt;FROM_TOKEN&gt; &lt;ID_TOKEN&gt; &lt;2&gt; &lt;T1&gt;
+
+ @note Only the first @c m_byte_count bytes are initialized,
+ out of @c m_token_array_length.
+ */
+ unsigned char *m_token_array;
+ /* Length of the token array to be considered for DIGEST_TEXT calculation. */
+ uint m_token_array_length;
+
+ sql_digest_storage()
+ {
+ reset(NULL, 0);
+ }
+
+ inline void reset(unsigned char *token_array, uint length)
+ {
+ m_token_array= token_array;
+ m_token_array_length= length;
+ reset();
+ }
+
+ inline void reset()
+ {
+ m_full= false;
+ m_byte_count= 0;
+ m_charset_number= 0;
+ memset(m_md5, 0, MD5_HASH_SIZE);
+ }
+
+ inline bool is_empty()
+ {
+ return (m_byte_count == 0);
+ }
+
+ inline void copy(const sql_digest_storage *from)
+ {
+ /*
+ Keep in mind this is a dirty copy of something that may change,
+ as the thread producing the digest is executing concurrently,
+ without any lock enforced.
+ */
+ uint byte_count_copy= m_token_array_length < from->m_byte_count ?
+ m_token_array_length : from->m_byte_count;
+
+ if (byte_count_copy > 0)
+ {
+ m_full= from->m_full;
+ m_byte_count= byte_count_copy;
+ m_charset_number= from->m_charset_number;
+ memcpy(m_token_array, from->m_token_array, m_byte_count);
+ memcpy(m_md5, from->m_md5, MD5_HASH_SIZE);
+ }
+ else
+ {
+ m_full= false;
+ m_byte_count= 0;
+ m_charset_number= 0;
+ }
+ }
+};
+typedef struct sql_digest_storage sql_digest_storage;
+
+/**
+ Compute a digest hash.
+ @param digest_storage The digest
+ @param [out] md5 The computed digest hash. This parameter is a buffer of size @c MD5_HASH_SIZE.
+*/
+void compute_digest_md5(const sql_digest_storage *digest_storage, unsigned char *md5);
+
+/**
+ Compute a digest text.
+ A 'digest text' is a textual representation of a query,
+ where:
+ - comments are removed,
+ - non significant spaces are removed,
+ - literal values are replaced with a special '?' marker,
+ - lists of values are collapsed using a shorter notation
+ @param digest_storage The digest
+ @param [out] digest_text
+ @param digest_text_length Size of @c digest_text.
+ @param [out] truncated true if the text representation was truncated
+*/
+void compute_digest_text(const sql_digest_storage *digest_storage,
+ String *digest_text);
+
+#endif
+
diff --git a/sql/sql_digest_stream.h b/sql/sql_digest_stream.h
new file mode 100644
index 00000000000..55f7e2293c6
--- /dev/null
+++ b/sql/sql_digest_stream.h
@@ -0,0 +1,51 @@
+/* Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved.
+
+ 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,
+ 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */
+
+#ifndef SQL_DIGEST_STREAM_H
+#define SQL_DIGEST_STREAM_H
+
+#include "sql_digest.h"
+
+/**
+ State data storage for @c digest_start, @c digest_add_token.
+ This structure extends the @c sql_digest_storage structure
+ with temporary state used only during parsing.
+*/
+struct sql_digest_state
+{
+ /**
+ Index, in the digest token array, of the last identifier seen.
+ Reduce rules used in the digest computation can not
+ apply to tokens seen before an identifier.
+ @sa digest_add_token
+ */
+ int m_last_id_index;
+ sql_digest_storage m_digest_storage;
+
+ inline void reset(unsigned char *token_array, uint length)
+ {
+ m_last_id_index= 0;
+ m_digest_storage.reset(token_array, length);
+ }
+
+ inline bool is_empty()
+ {
+ return m_digest_storage.is_empty();
+ }
+};
+typedef struct sql_digest_state sql_digest_state;
+
+#endif
+
diff --git a/sql/sql_do.cc b/sql/sql_do.cc
index 4ba887b5ab2..468b1bc33da 100644
--- a/sql/sql_do.cc
+++ b/sql/sql_do.cc
@@ -16,6 +16,7 @@
/* Execute DO statement */
+#include <my_global.h>
#include "sql_priv.h"
#include "transaction.h"
#include "unireg.h"
diff --git a/sql/sql_error.cc b/sql/sql_error.cc
index e12723402a8..b2fa8187925 100644
--- a/sql/sql_error.cc
+++ b/sql/sql_error.cc
@@ -41,18 +41,19 @@ This file contains the implementation of error and warnings related
***********************************************************************/
+#include <my_global.h>
#include "sql_priv.h"
#include "unireg.h"
#include "sql_error.h"
#include "sp_rcontext.h"
/*
- Design notes about MYSQL_ERROR::m_message_text.
+ Design notes about Sql_condition::m_message_text.
- The member MYSQL_ERROR::m_message_text contains the text associated with
+ The member Sql_condition::m_message_text contains the text associated with
an error, warning or note (which are all SQL 'conditions')
- Producer of MYSQL_ERROR::m_message_text:
+ Producer of Sql_condition::m_message_text:
----------------------------------------
(#1) the server implementation itself, when invoking functions like
@@ -78,16 +79,16 @@ This file contains the implementation of error and warnings related
- a RESIGNAL statement,
the message text is provided by the user logic, and is expressed in UTF8.
- Storage of MYSQL_ERROR::m_message_text:
+ Storage of Sql_condition::m_message_text:
---------------------------------------
- (#4) The class MYSQL_ERROR is used to hold the message text member.
+ (#4) The class Sql_condition is used to hold the message text member.
This class represents a single SQL condition.
(#5) The class Warning_info represents a SQL condition area, and contains
a collection of SQL conditions in the Warning_info::m_warn_list
- Consumer of MYSQL_ERROR::m_message_text:
+ Consumer of Sql_condition::m_message_text:
----------------------------------------
(#6) The statements SHOW WARNINGS and SHOW ERRORS display the content of
@@ -97,9 +98,9 @@ This file contains the implementation of error and warnings related
also read the content of:
- the top level statement condition area (when executed in a query),
- a sub statement (when executed in a stored program)
- and return the data stored in a MYSQL_ERROR.
+ and return the data stored in a Sql_condition.
- (#8) The RESIGNAL statement reads the MYSQL_ERROR caught by an exception
+ (#8) The RESIGNAL statement reads the Sql_condition caught by an exception
handler, to raise a new or modified condition (in #3).
The big picture
@@ -113,7 +114,7 @@ This file contains the implementation of error and warnings related
----------------------------|---------------------------- |
| |
V |
- MYSQL_ERROR(#4) |
+ Sql_condition(#4) |
| |
| |
V |
@@ -151,10 +152,10 @@ This file contains the implementation of error and warnings related
As a result, the design choice for (#4) and (#5) is to store data in
the 'error_message_charset_info' CHARSET, to minimize impact on the code base.
- This is implemented by using 'String MYSQL_ERROR::m_message_text'.
+ This is implemented by using 'String Sql_condition::m_message_text'.
The UTF8 -> error_message_charset_info conversion is implemented in
- Signal_common::eval_signal_informations() (for path #B and #C).
+ Sql_cmd_common_signal::eval_signal_informations() (for path #B and #C).
Future work
-----------
@@ -164,14 +165,14 @@ This file contains the implementation of error and warnings related
- Change (#4 and #5) to store message text in UTF8 natively.
In practice, this means changing the type of the message text to
- '<UTF8 String 128 class> MYSQL_ERROR::m_message_text', and is a direct
+ '<UTF8 String 128 class> Sql_condition::m_message_text', and is a direct
consequence of WL#751.
- Implement (#9) (GET DIAGNOSTICS).
See WL#2111 (Stored Procedures: Implement GET DIAGNOSTICS)
*/
-MYSQL_ERROR::MYSQL_ERROR()
+Sql_condition::Sql_condition()
: Sql_alloc(),
m_class_origin((const char*) NULL, 0, & my_charset_utf8_bin),
m_subclass_origin((const char*) NULL, 0, & my_charset_utf8_bin),
@@ -185,21 +186,20 @@ MYSQL_ERROR::MYSQL_ERROR()
m_cursor_name((const char*) NULL, 0, & my_charset_utf8_bin),
m_message_text(),
m_sql_errno(0),
- m_handled(0),
- m_level(MYSQL_ERROR::WARN_LEVEL_ERROR),
+ m_level(Sql_condition::WARN_LEVEL_ERROR),
m_mem_root(NULL)
{
memset(m_returned_sqlstate, 0, sizeof(m_returned_sqlstate));
}
-void MYSQL_ERROR::init(MEM_ROOT *mem_root)
+void Sql_condition::init(MEM_ROOT *mem_root)
{
DBUG_ASSERT(mem_root != NULL);
DBUG_ASSERT(m_mem_root == NULL);
m_mem_root= mem_root;
}
-void MYSQL_ERROR::clear()
+void Sql_condition::clear()
{
m_class_origin.length(0);
m_subclass_origin.length(0);
@@ -213,11 +213,10 @@ void MYSQL_ERROR::clear()
m_cursor_name.length(0);
m_message_text.length(0);
m_sql_errno= 0;
- m_handled= 0;
- m_level= MYSQL_ERROR::WARN_LEVEL_ERROR;
+ m_level= Sql_condition::WARN_LEVEL_ERROR;
}
-MYSQL_ERROR::MYSQL_ERROR(MEM_ROOT *mem_root)
+Sql_condition::Sql_condition(MEM_ROOT *mem_root)
: Sql_alloc(),
m_class_origin((const char*) NULL, 0, & my_charset_utf8_bin),
m_subclass_origin((const char*) NULL, 0, & my_charset_utf8_bin),
@@ -231,8 +230,7 @@ MYSQL_ERROR::MYSQL_ERROR(MEM_ROOT *mem_root)
m_cursor_name((const char*) NULL, 0, & my_charset_utf8_bin),
m_message_text(),
m_sql_errno(0),
- m_handled(0),
- m_level(MYSQL_ERROR::WARN_LEVEL_ERROR),
+ m_level(Sql_condition::WARN_LEVEL_ERROR),
m_mem_root(mem_root)
{
DBUG_ASSERT(mem_root != NULL);
@@ -257,7 +255,7 @@ static void copy_string(MEM_ROOT *mem_root, String* dst, const String* src)
}
void
-MYSQL_ERROR::copy_opt_attributes(const MYSQL_ERROR *cond)
+Sql_condition::copy_opt_attributes(const Sql_condition *cond)
{
DBUG_ASSERT(this != cond);
copy_string(m_mem_root, & m_class_origin, & cond->m_class_origin);
@@ -270,12 +268,11 @@ MYSQL_ERROR::copy_opt_attributes(const MYSQL_ERROR *cond)
copy_string(m_mem_root, & m_table_name, & cond->m_table_name);
copy_string(m_mem_root, & m_column_name, & cond->m_column_name);
copy_string(m_mem_root, & m_cursor_name, & cond->m_cursor_name);
- m_handled= cond->m_handled;
}
void
-MYSQL_ERROR::set(uint sql_errno, const char* sqlstate,
- MYSQL_ERROR::enum_warning_level level, const char* msg)
+Sql_condition::set(uint sql_errno, const char* sqlstate,
+ Sql_condition::enum_warning_level level, const char* msg)
{
DBUG_ASSERT(sql_errno != 0);
DBUG_ASSERT(sqlstate != NULL);
@@ -290,11 +287,11 @@ MYSQL_ERROR::set(uint sql_errno, const char* sqlstate,
}
void
-MYSQL_ERROR::set_builtin_message_text(const char* str)
+Sql_condition::set_builtin_message_text(const char* str)
{
/*
See the comments
- "Design notes about MYSQL_ERROR::m_message_text."
+ "Design notes about Sql_condition::m_message_text."
*/
const char* copy;
@@ -304,24 +301,42 @@ MYSQL_ERROR::set_builtin_message_text(const char* str)
}
const char*
-MYSQL_ERROR::get_message_text() const
+Sql_condition::get_message_text() const
{
return m_message_text.ptr();
}
int
-MYSQL_ERROR::get_message_octet_length() const
+Sql_condition::get_message_octet_length() const
{
return m_message_text.length();
}
void
-MYSQL_ERROR::set_sqlstate(const char* sqlstate)
+Sql_condition::set_sqlstate(const char* sqlstate)
{
memcpy(m_returned_sqlstate, sqlstate, SQLSTATE_LENGTH);
m_returned_sqlstate[SQLSTATE_LENGTH]= '\0';
}
+Diagnostics_area::Diagnostics_area(bool initialize)
+ : m_main_wi(0, false, initialize)
+{
+ push_warning_info(&m_main_wi);
+
+ reset_diagnostics_area();
+}
+
+Diagnostics_area::Diagnostics_area(ulonglong warning_info_id,
+ bool allow_unlimited_warnings,
+ bool initialize)
+ : m_main_wi(warning_info_id, allow_unlimited_warnings, initialize)
+{
+ push_warning_info(&m_main_wi);
+
+ reset_diagnostics_area();
+}
+
/**
Clear this diagnostics area.
@@ -333,7 +348,7 @@ Diagnostics_area::reset_diagnostics_area()
{
DBUG_ENTER("reset_diagnostics_area");
#ifdef DBUG_OFF
- can_overwrite_status= FALSE;
+ m_can_overwrite_status= FALSE;
/** Don't take chances in production */
m_message[0]= '\0';
m_sql_errno= 0;
@@ -341,7 +356,8 @@ Diagnostics_area::reset_diagnostics_area()
m_last_insert_id= 0;
m_statement_warn_count= 0;
#endif
- is_sent= FALSE;
+ get_warning_info()->clear_error_condition();
+ set_is_sent(false);
/** Tiny reset in debug mode to see garbage right away */
m_status= DA_EMPTY;
DBUG_VOID_RETURN;
@@ -354,9 +370,9 @@ Diagnostics_area::reset_diagnostics_area()
*/
void
-Diagnostics_area::set_ok_status(THD *thd, ulonglong affected_rows_arg,
- ulonglong last_insert_id_arg,
- const char *message_arg)
+Diagnostics_area::set_ok_status(ulonglong affected_rows,
+ ulonglong last_insert_id,
+ const char *message)
{
DBUG_ENTER("set_ok_status");
DBUG_ASSERT(! is_set());
@@ -367,11 +383,11 @@ Diagnostics_area::set_ok_status(THD *thd, ulonglong affected_rows_arg,
if (is_error() || is_disabled())
return;
- m_statement_warn_count= thd->warning_info->statement_warn_count();
- m_affected_rows= affected_rows_arg;
- m_last_insert_id= last_insert_id_arg;
- if (message_arg)
- strmake_buf(m_message, message_arg);
+ m_statement_warn_count= current_statement_warn_count();
+ m_affected_rows= affected_rows;
+ m_last_insert_id= last_insert_id;
+ if (message)
+ strmake_buf(m_message, message);
else
m_message[0]= '\0';
m_status= DA_OK;
@@ -402,28 +418,67 @@ Diagnostics_area::set_eof_status(THD *thd)
anyway.
*/
m_statement_warn_count= (thd->spcont ?
- 0 : thd->warning_info->statement_warn_count());
+ 0 :
+ current_statement_warn_count());
m_status= DA_EOF;
DBUG_VOID_RETURN;
}
/**
- Set ERROR status.
+ Set ERROR status in the Diagnostics Area. This function should be used to
+ report fatal errors (such as out-of-memory errors) when no further
+ processing is possible.
+
+ @param sql_errno SQL-condition error number
*/
void
-Diagnostics_area::set_error_status(THD *thd, uint sql_errno_arg,
- const char *message_arg,
- const char *sqlstate)
+Diagnostics_area::set_error_status(uint sql_errno)
+{
+ set_error_status(sql_errno,
+ ER(sql_errno),
+ mysql_errno_to_sqlstate(sql_errno),
+ NULL);
+}
+
+
+/**
+ Set ERROR status in the Diagnostics Area.
+
+ @note error_condition may be NULL. It happens if a) OOM error is being
+ reported; or b) when Warning_info is full.
+
+ @param sql_errno SQL-condition error number
+ @param message SQL-condition message
+ @param sqlstate SQL-condition state
+ @param error_condition SQL-condition object representing the error state
+
+ @note Note, that error_condition may be NULL. It happens if a) OOM error is
+ being reported; or b) when Warning_info is full.
+*/
+
+void
+Diagnostics_area::set_error_status(uint sql_errno,
+ const char *message,
+ const char *sqlstate,
+ const Sql_condition *error_condition)
{
DBUG_ENTER("set_error_status");
+ DBUG_PRINT("enter", ("error: %d", sql_errno));
/*
Only allowed to report error if has not yet reported a success
The only exception is when we flush the message to the client,
an error can happen during the flush.
*/
- DBUG_ASSERT(! is_set() || can_overwrite_status);
+ DBUG_ASSERT(! is_set() || m_can_overwrite_status);
+
+ // message must be set properly by the caller.
+ DBUG_ASSERT(message);
+
+ // sqlstate must be set properly by the caller.
+ DBUG_ASSERT(sqlstate);
+
#ifdef DBUG_OFF
/*
In production, refuse to overwrite a custom response with an
@@ -433,19 +488,17 @@ Diagnostics_area::set_error_status(THD *thd, uint sql_errno_arg,
return;
#endif
- if (sqlstate == NULL)
- sqlstate= mysql_errno_to_sqlstate(sql_errno_arg);
-
- m_sql_errno= sql_errno_arg;
+ m_sql_errno= sql_errno;
memcpy(m_sqlstate, sqlstate, SQLSTATE_LENGTH);
m_sqlstate[SQLSTATE_LENGTH]= '\0';
- strmake_buf(m_message, message_arg);
+ strmake_buf(m_message, message);
+
+ get_warning_info()->set_error_condition(error_condition);
m_status= DA_ERROR;
DBUG_VOID_RETURN;
}
-
/**
Mark the diagnostics area as 'DISABLED'.
@@ -457,116 +510,207 @@ Diagnostics_area::set_error_status(THD *thd, uint sql_errno_arg,
void
Diagnostics_area::disable_status()
{
+ DBUG_ENTER("disable_status");
DBUG_ASSERT(! is_set());
m_status= DA_DISABLED;
+ DBUG_VOID_RETURN;
}
-Warning_info::Warning_info(ulonglong warn_id_arg, bool allow_unlimited_warnings)
- :m_statement_warn_count(0),
+Warning_info::Warning_info(ulonglong warn_id_arg,
+ bool allow_unlimited_warnings, bool initialize)
+ :m_current_statement_warn_count(0),
m_current_row_for_warning(1),
m_warn_id(warn_id_arg),
+ m_error_condition(NULL),
m_allow_unlimited_warnings(allow_unlimited_warnings),
+ initialized(0),
m_read_only(FALSE)
{
- /* Initialize sub structures */
- init_sql_alloc(&m_warn_root, WARN_ALLOC_BLOCK_SIZE, WARN_ALLOC_PREALLOC_SIZE);
m_warn_list.empty();
- bzero((char*) m_warn_count, sizeof(m_warn_count));
+ memset(m_warn_count, 0, sizeof(m_warn_count));
+ if (initialize)
+ init();
}
+void Warning_info::init()
+{
+ /* Initialize sub structures */
+ DBUG_ASSERT(initialized == 0);
+ init_sql_alloc(&m_warn_root, WARN_ALLOC_BLOCK_SIZE,
+ WARN_ALLOC_PREALLOC_SIZE, MYF(MY_THREAD_SPECIFIC));
+ initialized= 1;
+}
+
+void Warning_info::free_memory()
+{
+ if (initialized)
+ free_root(&m_warn_root,MYF(0));
+}
Warning_info::~Warning_info()
{
- free_root(&m_warn_root,MYF(0));
+ free_memory();
}
-/**
- Reset the warning information of this connection.
-*/
+bool Warning_info::has_sql_condition(const char *message_str,
+ ulong message_length) const
+{
+ Diagnostics_area::Sql_condition_iterator it(m_warn_list);
+ const Sql_condition *err;
+
+ while ((err= it++))
+ {
+ if (strncmp(message_str, err->get_message_text(), message_length) == 0)
+ return true;
+ }
+
+ return false;
+}
-void Warning_info::clear_warning_info(ulonglong warn_id_arg)
+
+void Warning_info::clear(ulonglong new_id)
{
- m_warn_id= warn_id_arg;
- free_root(&m_warn_root, MYF(0));
- bzero((char*) m_warn_count, sizeof(m_warn_count));
+ id(new_id);
m_warn_list.empty();
- m_statement_warn_count= 0;
+ m_marked_sql_conditions.empty();
+ free_memory();
+ memset(m_warn_count, 0, sizeof(m_warn_count));
+ m_current_statement_warn_count= 0;
m_current_row_for_warning= 1; /* Start counting from the first row */
+ clear_error_condition();
}
-/**
- Append warnings only if the original contents of the routine
- warning info was replaced.
-*/
-void Warning_info::merge_with_routine_info(THD *thd, Warning_info *source)
+void Warning_info::append_warning_info(THD *thd, const Warning_info *source)
{
- /*
- If a routine body is empty or if a routine did not
- generate any warnings (thus m_warn_id didn't change),
- do not duplicate our own contents by appending the
- contents of the called routine. We know that the called
- routine did not change its warning info.
-
- On the other hand, if the routine body is not empty and
- some statement in the routine generates a warning or
- uses tables, m_warn_id is guaranteed to have changed.
- In this case we know that the routine warning info
- contains only new warnings, and thus we perform a copy.
- */
- if (m_warn_id != source->m_warn_id)
+ const Sql_condition *err;
+ Diagnostics_area::Sql_condition_iterator it(source->m_warn_list);
+ const Sql_condition *src_error_condition = source->get_error_condition();
+
+ while ((err= it++))
{
- /*
- If the invocation of the routine was a standalone statement,
- rather than a sub-statement, in other words, if it's a CALL
- of a procedure, rather than invocation of a function or a
- trigger, we need to clear the current contents of the caller's
- warning info.
-
- This is per MySQL rules: if a statement generates a warning,
- warnings from the previous statement are flushed. Normally
- it's done in push_warning(). However, here we don't use
- push_warning() to avoid invocation of condition handlers or
- escalation of warnings to errors.
- */
- opt_clear_warning_info(thd->query_id);
- append_warning_info(thd, source);
+ // Do not use ::push_warning() to avoid invocation of THD-internal-handlers.
+ Sql_condition *new_error= Warning_info::push_warning(thd, err);
+
+ if (src_error_condition && src_error_condition == err)
+ set_error_condition(new_error);
+
+ if (source->is_marked_for_removal(err))
+ mark_condition_for_removal(new_error);
}
}
+
/**
- Add a warning to the list of warnings. Increment the respective
- counters.
+ Copy Sql_conditions that are not WARN_LEVEL_ERROR from the source
+ Warning_info to the current Warning_info.
+
+ @param thd Thread context.
+ @param sp_wi Stored-program Warning_info
+ @param thd Thread context.
+ @param src_wi Warning_info to copy from.
*/
-MYSQL_ERROR *Warning_info::push_warning(THD *thd,
- uint sql_errno, const char* sqlstate,
- MYSQL_ERROR::enum_warning_level level,
- const char *msg)
+void Diagnostics_area::copy_non_errors_from_wi(THD *thd,
+ const Warning_info *src_wi)
+{
+ Sql_condition_iterator it(src_wi->m_warn_list);
+ const Sql_condition *cond;
+ Warning_info *wi= get_warning_info();
+
+ while ((cond= it++))
+ {
+ if (cond->get_level() == Sql_condition::WARN_LEVEL_ERROR)
+ continue;
+
+ Sql_condition *new_condition= wi->push_warning(thd, cond);
+
+ if (src_wi->is_marked_for_removal(cond))
+ wi->mark_condition_for_removal(new_condition);
+ }
+}
+
+
+void Warning_info::mark_sql_conditions_for_removal()
+{
+ Sql_condition_list::Iterator it(m_warn_list);
+ Sql_condition *cond;
+
+ while ((cond= it++))
+ mark_condition_for_removal(cond);
+}
+
+
+void Warning_info::remove_marked_sql_conditions()
+{
+ List_iterator_fast<Sql_condition> it(m_marked_sql_conditions);
+ Sql_condition *cond;
+
+ while ((cond= it++))
+ {
+ m_warn_list.remove(cond);
+ m_warn_count[cond->get_level()]--;
+ m_current_statement_warn_count--;
+ if (cond == m_error_condition)
+ m_error_condition= NULL;
+ }
+
+ m_marked_sql_conditions.empty();
+}
+
+
+bool Warning_info::is_marked_for_removal(const Sql_condition *cond) const
{
- MYSQL_ERROR *cond= NULL;
+ List_iterator_fast<Sql_condition> it(
+ const_cast<List<Sql_condition>&> (m_marked_sql_conditions));
+ Sql_condition *c;
+
+ while ((c= it++))
+ {
+ if (c == cond)
+ return true;
+ }
+
+ return false;
+}
+
+
+void Warning_info::reserve_space(THD *thd, uint count)
+{
+ while (m_warn_list.elements() &&
+ (m_warn_list.elements() + count) > thd->variables.max_error_count)
+ m_warn_list.remove(m_warn_list.front());
+}
+
+Sql_condition *Warning_info::push_warning(THD *thd,
+ uint sql_errno, const char* sqlstate,
+ Sql_condition::enum_warning_level level,
+ const char *msg)
+{
+ Sql_condition *cond= NULL;
if (! m_read_only)
{
if (m_allow_unlimited_warnings ||
- m_warn_list.elements < thd->variables.max_error_count)
+ m_warn_list.elements() < thd->variables.max_error_count)
{
- cond= new (& m_warn_root) MYSQL_ERROR(& m_warn_root);
+ cond= new (& m_warn_root) Sql_condition(& m_warn_root);
if (cond)
{
cond->set(sql_errno, sqlstate, level, msg);
- m_warn_list.push_back(cond, &m_warn_root);
+ m_warn_list.push_back(cond);
}
}
m_warn_count[(uint) level]++;
}
- m_statement_warn_count++;
+ m_current_statement_warn_count++;
return cond;
}
-MYSQL_ERROR *Warning_info::push_warning(THD *thd, const MYSQL_ERROR *sql_condition)
+
+Sql_condition *Warning_info::push_warning(THD *thd, const Sql_condition *sql_condition)
{
- MYSQL_ERROR *new_condition= push_warning(thd,
+ Sql_condition *new_condition= push_warning(thd,
sql_condition->get_sql_errno(),
sql_condition->get_sqlstate(),
sql_condition->get_level(),
@@ -589,7 +733,7 @@ MYSQL_ERROR *Warning_info::push_warning(THD *thd, const MYSQL_ERROR *sql_conditi
msg Clear error message
*/
-void push_warning(THD *thd, MYSQL_ERROR::enum_warning_level level,
+void push_warning(THD *thd, Sql_condition::enum_warning_level level,
uint code, const char *msg)
{
DBUG_ENTER("push_warning");
@@ -600,15 +744,15 @@ void push_warning(THD *thd, MYSQL_ERROR::enum_warning_level level,
WARN_LEVEL_ERROR *is* a bug. Either use my_printf_error(),
my_error(), or WARN_LEVEL_WARN.
*/
- DBUG_ASSERT(level != MYSQL_ERROR::WARN_LEVEL_ERROR);
+ DBUG_ASSERT(level != Sql_condition::WARN_LEVEL_ERROR);
- if (level == MYSQL_ERROR::WARN_LEVEL_ERROR)
- level= MYSQL_ERROR::WARN_LEVEL_WARN;
+ if (level == Sql_condition::WARN_LEVEL_ERROR)
+ level= Sql_condition::WARN_LEVEL_WARN;
(void) thd->raise_condition(code, NULL, level, msg);
/* Make sure we also count warnings pushed after calling set_ok_status(). */
- thd->stmt_da->increment_warning();
+ thd->get_stmt_da()->increment_warning();
DBUG_VOID_RETURN;
}
@@ -625,7 +769,7 @@ void push_warning(THD *thd, MYSQL_ERROR::enum_warning_level level,
msg Clear error message
*/
-void push_warning_printf(THD *thd, MYSQL_ERROR::enum_warning_level level,
+void push_warning_printf(THD *thd, Sql_condition::enum_warning_level level,
uint code, const char *format, ...)
{
va_list args;
@@ -674,7 +818,7 @@ bool mysqld_show_warnings(THD *thd, ulong levels_to_show)
List<Item> field_list;
DBUG_ENTER("mysqld_show_warnings");
- DBUG_ASSERT(thd->warning_info->is_read_only());
+ DBUG_ASSERT(thd->get_stmt_da()->is_warning_info_read_only());
field_list.push_back(new Item_empty_string("Level", 7));
field_list.push_back(new Item_return_int("Code",4, MYSQL_TYPE_LONG));
@@ -684,7 +828,7 @@ bool mysqld_show_warnings(THD *thd, ulong levels_to_show)
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(TRUE);
- MYSQL_ERROR *err;
+ const Sql_condition *err;
SELECT_LEX *sel= &thd->lex->select_lex;
SELECT_LEX_UNIT *unit= &thd->lex->unit;
ulonglong idx= 0;
@@ -692,7 +836,8 @@ bool mysqld_show_warnings(THD *thd, ulong levels_to_show)
unit->set_limit(sel);
- List_iterator_fast<MYSQL_ERROR> it(thd->warning_info->warn_list());
+ Diagnostics_area::Sql_condition_iterator it=
+ thd->get_stmt_da()->sql_conditions();
while ((err= it++))
{
/* Skip levels that the user is not interested in */
@@ -715,7 +860,7 @@ bool mysqld_show_warnings(THD *thd, ulong levels_to_show)
}
my_eof(thd);
- thd->warning_info->set_read_only(FALSE);
+ thd->get_stmt_da()->set_warning_info_read_only(FALSE);
DBUG_RETURN(FALSE);
}
@@ -823,7 +968,7 @@ uint32 convert_error_message(char *to, uint32 to_length, CHARSET_INFO *to_cs,
if (!to_cs || from_cs == to_cs || to_cs == &my_charset_bin)
{
- length= min(to_length, from_length);
+ length= MY_MIN(to_length, from_length);
memmove(to, from, length);
to[length]= 0;
return length;
@@ -865,3 +1010,32 @@ uint32 convert_error_message(char *to, uint32 to_length, CHARSET_INFO *to_cs,
*errors= error_count;
return (uint32) (to - to_start);
}
+
+
+/**
+ Sanity check for SQLSTATEs. The function does not check if it's really an
+ existing SQL-state (there are just too many), it just checks string length and
+ looks for bad characters.
+
+ @param sqlstate the condition SQLSTATE.
+
+ @retval true if it's ok.
+ @retval false if it's bad.
+*/
+
+bool is_sqlstate_valid(const LEX_STRING *sqlstate)
+{
+ if (sqlstate->length != 5)
+ return false;
+
+ for (int i= 0 ; i < 5 ; ++i)
+ {
+ char c = sqlstate->str[i];
+
+ if ((c < '0' || '9' < c) &&
+ (c < 'A' || 'Z' < c))
+ return false;
+ }
+
+ return true;
+}
diff --git a/sql/sql_error.h b/sql/sql_error.h
index 5f912ad8102..a993e9203c9 100644
--- a/sql/sql_error.h
+++ b/sql/sql_error.h
@@ -19,124 +19,13 @@
#include "sql_list.h" /* Sql_alloc, MEM_ROOT */
#include "m_string.h" /* LEX_STRING */
#include "sql_string.h" /* String */
+#include "sql_plist.h" /* I_P_List */
#include "mysql_com.h" /* MYSQL_ERRMSG_SIZE */
+#include "my_time.h" /* MYSQL_TIME */
+#include "decimal.h"
class THD;
-
-/**
- Stores status of the currently executed statement.
- Cleared at the beginning of the statement, and then
- can hold either OK, ERROR, or EOF status.
- Can not be assigned twice per statement.
-*/
-
-class Diagnostics_area
-{
-public:
- enum enum_diagnostics_status
- {
- /** The area is cleared at start of a statement. */
- DA_EMPTY= 0,
- /** Set whenever one calls my_ok(). */
- DA_OK,
- /** Set whenever one calls my_eof(). */
- DA_EOF,
- /** Set whenever one calls my_error() or my_message(). */
- DA_ERROR,
- /** Set in case of a custom response, such as one from COM_STMT_PREPARE. */
- DA_DISABLED
- };
- /** True if status information is sent to the client. */
- bool is_sent;
- /** Set to make set_error_status after set_{ok,eof}_status possible. */
- bool can_overwrite_status;
-
- void set_ok_status(THD *thd, ulonglong affected_rows_arg,
- ulonglong last_insert_id_arg,
- const char *message);
- void set_eof_status(THD *thd);
- void set_error_status(THD *thd, uint sql_errno_arg, const char *message_arg,
- const char *sqlstate);
-
- void disable_status();
-
- void reset_diagnostics_area();
-
- bool is_set() const { return m_status != DA_EMPTY; }
- bool is_error() const { return m_status == DA_ERROR; }
- bool is_eof() const { return m_status == DA_EOF; }
- bool is_ok() const { return m_status == DA_OK; }
- bool is_disabled() const { return m_status == DA_DISABLED; }
- enum_diagnostics_status status() const { return m_status; }
-
- const char *message() const
- { DBUG_ASSERT(m_status == DA_ERROR || m_status == DA_OK); return m_message; }
-
- uint sql_errno() const
- { DBUG_ASSERT(m_status == DA_ERROR); return m_sql_errno; }
-
- const char* get_sqlstate() const
- { DBUG_ASSERT(m_status == DA_ERROR); return m_sqlstate; }
-
- ulonglong affected_rows() const
- { DBUG_ASSERT(m_status == DA_OK); return m_affected_rows; }
-
- ulonglong last_insert_id() const
- { DBUG_ASSERT(m_status == DA_OK); return m_last_insert_id; }
-
- uint statement_warn_count() const
- {
- DBUG_ASSERT(m_status == DA_OK || m_status == DA_EOF);
- return m_statement_warn_count;
- }
-
- /* Used to count any warnings pushed after calling set_ok_status(). */
- void increment_warning()
- {
- if (m_status != DA_EMPTY)
- m_statement_warn_count++;
- }
-
- Diagnostics_area() { reset_diagnostics_area(); }
-
-private:
- /** Message buffer. Can be used by OK or ERROR status. */
- char m_message[MYSQL_ERRMSG_SIZE];
- /**
- SQL error number. One of ER_ codes from share/errmsg.txt.
- Set by set_error_status.
- */
- uint m_sql_errno;
-
- char m_sqlstate[SQLSTATE_LENGTH+1];
-
- /**
- The number of rows affected by the last statement. This is
- semantically close to thd->row_count_func, but has a different
- life cycle. thd->row_count_func stores the value returned by
- function ROW_COUNT() and is cleared only by statements that
- update its value, such as INSERT, UPDATE, DELETE and few others.
- This member is cleared at the beginning of the next statement.
-
- We could possibly merge the two, but life cycle of thd->row_count_func
- can not be changed.
- */
- ulonglong m_affected_rows;
- /**
- Similarly to the previous member, this is a replacement of
- thd->first_successful_insert_id_in_prev_stmt, which is used
- to implement LAST_INSERT_ID().
- */
- ulonglong m_last_insert_id;
- /**
- Number of warnings of this last statement. May differ from
- the number of warnings returned by SHOW WARNINGS e.g. in case
- the statement doesn't clear the warnings, and doesn't generate
- them.
- */
- uint m_statement_warn_count;
- enum_diagnostics_status m_status;
-};
+class my_decimal;
///////////////////////////////////////////////////////////////////////////
@@ -144,10 +33,8 @@ private:
Representation of a SQL condition.
A SQL condition can be a completion condition (note, warning),
or an exception condition (error, not found).
- @note This class is named MYSQL_ERROR instead of SQL_condition for
- historical reasons, to facilitate merging code with previous releases.
*/
-class MYSQL_ERROR : public Sql_alloc
+class Sql_condition : public Sql_alloc
{
public:
/*
@@ -158,6 +45,17 @@ public:
*/
enum enum_warning_level
{ WARN_LEVEL_NOTE, WARN_LEVEL_WARN, WARN_LEVEL_ERROR, WARN_LEVEL_END};
+
+ /**
+ Convert a bitmask consisting of MYSQL_TIME_{NOTE|WARN}_XXX bits
+ to WARN_LEVEL_XXX
+ */
+ static enum_warning_level time_warn_level(int warnings)
+ {
+ return MYSQL_TIME_WARN_HAVE_WARNINGS(warnings) ?
+ WARN_LEVEL_WARN : WARN_LEVEL_NOTE;
+ }
+
/**
Get the MESSAGE_TEXT of this condition.
@return the message text.
@@ -188,26 +86,15 @@ public:
Get the error level of this condition.
@return the error level condition item.
*/
- MYSQL_ERROR::enum_warning_level get_level() const
+ Sql_condition::enum_warning_level get_level() const
{ return m_level; }
- /** check if condition was handled by a condition handler */
- bool handled() const
- {
- return m_handled;
- }
- /** mark that condition was handled */
- void mark_handled()
- {
- m_handled= 1;
- }
-
private:
/*
- The interface of MYSQL_ERROR is mostly private, by design,
+ The interface of Sql_condition is mostly private, by design,
so that only the following code:
- various raise_error() or raise_warning() methods in class THD,
- - the implementation of SIGNAL / RESIGNAL
+ - the implementation of SIGNAL / RESIGNAL / GET DIAGNOSTICS
- catch / re-throw of SQL conditions in stored procedures (sp_rcontext)
is allowed to create / modify a SQL condition.
Enforcing this policy prevents confusion, since the only public
@@ -217,20 +104,21 @@ private:
*/
friend class THD;
friend class Warning_info;
- friend class Signal_common;
- friend class Signal_statement;
- friend class Resignal_statement;
+ friend class Sql_cmd_common_signal;
+ friend class Sql_cmd_signal;
+ friend class Sql_cmd_resignal;
friend class sp_rcontext;
+ friend class Condition_information_item;
/**
Default constructor.
This constructor is usefull when allocating arrays.
- Note that the init() method should be called to complete the MYSQL_ERROR.
+ Note that the init() method should be called to complete the Sql_condition.
*/
- MYSQL_ERROR();
+ Sql_condition();
/**
- Complete the MYSQL_ERROR initialisation.
+ Complete the Sql_condition initialisation.
@param mem_root The memory root to use for the condition items
of this condition
*/
@@ -241,17 +129,17 @@ private:
@param mem_root The memory root to use for the condition items
of this condition
*/
- MYSQL_ERROR(MEM_ROOT *mem_root);
+ Sql_condition(MEM_ROOT *mem_root);
/** Destructor. */
- ~MYSQL_ERROR()
+ ~Sql_condition()
{}
/**
Copy optional condition items attributes.
@param cond the condition to copy.
*/
- void copy_opt_attributes(const MYSQL_ERROR *cond);
+ void copy_opt_attributes(const Sql_condition *cond);
/**
Set this condition area with a fixed message text.
@@ -262,7 +150,7 @@ private:
@param MyFlags additional flags.
*/
void set(uint sql_errno, const char* sqlstate,
- MYSQL_ERROR::enum_warning_level level,
+ Sql_condition::enum_warning_level level,
const char* msg);
/**
@@ -275,6 +163,12 @@ private:
/** Set the SQLSTATE of this condition. */
void set_sqlstate(const char* sqlstate);
+ /** Set the CLASS_ORIGIN of this condition. */
+ void set_class_origin();
+
+ /** Set the SUBCLASS_ORIGIN of this condition. */
+ void set_subclass_origin();
+
/**
Clear this SQL condition.
*/
@@ -317,9 +211,6 @@ private:
/** MySQL extension, MYSQL_ERRNO condition item. */
uint m_sql_errno;
- /** Marker if error/warning was handled by a continue handler */
- bool m_handled;
-
/**
SQL RETURNED_SQLSTATE condition item.
This member is always NUL terminated.
@@ -327,7 +218,11 @@ private:
char m_returned_sqlstate[SQLSTATE_LENGTH+1];
/** Severity (error, warning, note) of this condition. */
- MYSQL_ERROR::enum_warning_level m_level;
+ Sql_condition::enum_warning_level m_level;
+
+ /** Pointers for participating in the list of conditions. */
+ Sql_condition *next_in_wi;
+ Sql_condition **prev_in_wi;
/** Memory root to use to hold condition item values. */
MEM_ROOT *m_mem_root;
@@ -338,25 +233,33 @@ private:
/**
Information about warnings of the current connection.
*/
-
class Warning_info
{
+ /** The type of the counted and doubly linked list of conditions. */
+ typedef I_P_List<Sql_condition,
+ I_P_List_adapter<Sql_condition,
+ &Sql_condition::next_in_wi,
+ &Sql_condition::prev_in_wi>,
+ I_P_List_counter,
+ I_P_List_fast_push_back<Sql_condition> >
+ Sql_condition_list;
+
/** A memory root to allocate warnings and errors */
MEM_ROOT m_warn_root;
/** List of warnings of all severities (levels). */
- List <MYSQL_ERROR> m_warn_list;
+ Sql_condition_list m_warn_list;
/** A break down of the number of warnings per severity (level). */
- uint m_warn_count[(uint) MYSQL_ERROR::WARN_LEVEL_END];
+ uint m_warn_count[(uint) Sql_condition::WARN_LEVEL_END];
/**
The number of warnings of the current statement. Warning_info
life cycle differs from statement life cycle -- it may span
multiple statements. In that case we get
- m_statement_warn_count 0, whereas m_warn_list is not empty.
+ m_current_statement_warn_count 0, whereas m_warn_list is not empty.
*/
- uint m_statement_warn_count;
+ uint m_current_statement_warn_count;
/*
Row counter, to print in errors and warnings. Not increased in
@@ -367,23 +270,67 @@ class Warning_info
/** Used to optionally clear warnings only once per statement. */
ulonglong m_warn_id;
+ /**
+ A pointer to an element of m_warn_list. It determines SQL-condition
+ instance which corresponds to the error state in Diagnostics_area.
+
+ This is needed for properly processing SQL-conditions in SQL-handlers.
+ When an SQL-handler is found for the current error state in Diagnostics_area,
+ this pointer is needed to remove the corresponding SQL-condition from the
+ Warning_info list.
+
+ @note m_error_condition might be NULL in the following cases:
+ - Diagnostics_area set to fatal error state (like OOM);
+ - Max number of Warning_info elements has been reached (thus, there is
+ no corresponding SQL-condition object in Warning_info).
+ */
+ const Sql_condition *m_error_condition;
+
/** Indicates if push_warning() allows unlimited number of warnings. */
bool m_allow_unlimited_warnings;
+ bool initialized; /* Set to 1 if init() has been called */
+
+ /** Read only status. */
+ bool m_read_only;
+
+ /** Pointers for participating in the stack of Warning_info objects. */
+ Warning_info *m_next_in_da;
+ Warning_info **m_prev_in_da;
+
+ List<Sql_condition> m_marked_sql_conditions;
+
+public:
+ Warning_info(ulonglong warn_id_arg, bool allow_unlimited_warnings,
+ bool initialized);
+ ~Warning_info();
+ /* Allocate memory for structures */
+ void init();
+ void free_memory();
private:
Warning_info(const Warning_info &rhs); /* Not implemented */
Warning_info& operator=(const Warning_info &rhs); /* Not implemented */
-public:
- Warning_info(ulonglong warn_id_arg, bool allow_unlimited_warnings);
- ~Warning_info();
+ /**
+ Checks if Warning_info contains SQL-condition with the given message.
+
+ @param message_str Message string.
+ @param message_length Length of message string.
+
+ @return true if the Warning_info contains an SQL-condition with the given
+ message.
+ */
+ bool has_sql_condition(const char *message_str, ulong message_length) const;
/**
Reset the warning information. Clear all warnings,
the number of warnings, reset current row counter
to point to the first row.
+
+ @param new_id new Warning_info id.
*/
- void clear_warning_info(ulonglong warn_id_arg);
+ void clear(ulonglong new_id);
+
/**
Only clear warning info if haven't yet done that already
for the current query. Allows to be issued at any time
@@ -392,46 +339,72 @@ public:
@todo: This is a sign of sloppy coding. Instead we need to
designate one place in a statement life cycle where we call
- clear_warning_info().
+ Warning_info::clear().
+
+ @param query_id Current query id.
*/
- void opt_clear_warning_info(ulonglong query_id)
+ void opt_clear(ulonglong query_id)
{
if (query_id != m_warn_id)
- clear_warning_info(query_id);
- }
-
- void append_warning_info(THD *thd, Warning_info *source)
- {
- append_warnings(thd, & source->warn_list());
+ clear(query_id);
}
/**
Concatenate the list of warnings.
- It's considered tolerable to lose a warning.
- */
- void append_warnings(THD *thd, List<MYSQL_ERROR> *src)
- {
- MYSQL_ERROR *err;
- List_iterator_fast<MYSQL_ERROR> it(*src);
- /*
- Don't use ::push_warning() to avoid invocation of condition
- handlers or escalation of warnings to errors.
- */
- while ((err= it++))
- Warning_info::push_warning(thd, err);
- }
- /**
- Conditional merge of related warning information areas.
+ It's considered tolerable to lose an SQL-condition in case of OOM-error,
+ or if the number of SQL-conditions in the Warning_info reached top limit.
+
+ @param thd Thread context.
+ @param source Warning_info object to copy SQL-conditions from.
*/
- void merge_with_routine_info(THD *thd, Warning_info *source);
+ void append_warning_info(THD *thd, const Warning_info *source);
/**
Reset between two COM_ commands. Warnings are preserved
between commands, but statement_warn_count indicates
the number of warnings of this particular statement only.
*/
- void reset_for_next_command() { m_statement_warn_count= 0; }
+ void reset_for_next_command()
+ { m_current_statement_warn_count= 0; }
+
+ /**
+ Mark active SQL-conditions for later removal.
+ This is done to simulate stacked DAs for HANDLER statements.
+ */
+ void mark_sql_conditions_for_removal();
+
+ /**
+ Unmark SQL-conditions, which were marked for later removal.
+ This is done to simulate stacked DAs for HANDLER statements.
+ */
+ void unmark_sql_conditions_from_removal()
+ { m_marked_sql_conditions.empty(); }
+
+ /**
+ Remove SQL-conditions that are marked for deletion.
+ This is done to simulate stacked DAs for HANDLER statements.
+ */
+ void remove_marked_sql_conditions();
+
+ /**
+ Check if the given SQL-condition is marked for removal in this Warning_info
+ instance.
+
+ @param cond the SQL-condition.
+
+ @retval true if the given SQL-condition is marked for removal in this
+ Warning_info instance.
+ @retval false otherwise.
+ */
+ bool is_marked_for_removal(const Sql_condition *cond) const;
+
+ /**
+ Mark a single SQL-condition for removal (add the given SQL-condition to the
+ removal list of this Warning_info instance).
+ */
+ void mark_condition_for_removal(Sql_condition *cond)
+ { m_marked_sql_conditions.push_back(cond, &m_warn_root); }
/**
Used for @@warning_count system variable, which prints
@@ -440,52 +413,82 @@ public:
ulong warn_count() const
{
/*
- This may be higher than warn_list.elements if we have
+ This may be higher than warn_list.elements() if we have
had more warnings than thd->variables.max_error_count.
*/
- return (m_warn_count[(uint) MYSQL_ERROR::WARN_LEVEL_NOTE] +
- m_warn_count[(uint) MYSQL_ERROR::WARN_LEVEL_ERROR] +
- m_warn_count[(uint) MYSQL_ERROR::WARN_LEVEL_WARN]);
+ return (m_warn_count[(uint) Sql_condition::WARN_LEVEL_NOTE] +
+ m_warn_count[(uint) Sql_condition::WARN_LEVEL_ERROR] +
+ m_warn_count[(uint) Sql_condition::WARN_LEVEL_WARN]);
}
/**
- This is for iteration purposes. We return a non-constant reference
- since List doesn't have constant iterators.
- */
- List<MYSQL_ERROR> &warn_list() { return m_warn_list; }
-
- /**
The number of errors, or number of rows returned by SHOW ERRORS,
also the value of session variable @@error_count.
*/
ulong error_count() const
+ { return m_warn_count[(uint) Sql_condition::WARN_LEVEL_ERROR]; }
+
+ /**
+ The number of conditions (errors, warnings and notes) in the list.
+ */
+ uint cond_count() const
{
- return m_warn_count[(uint) MYSQL_ERROR::WARN_LEVEL_ERROR];
+ return m_warn_list.elements();
}
/** Id of the warning information area. */
- ulonglong warn_id() const { return m_warn_id; }
+ ulonglong id() const { return m_warn_id; }
+
+ /** Set id of the warning information area. */
+ void id(ulonglong id) { m_warn_id= id; }
/** Do we have any errors and warnings that we can *show*? */
- bool is_empty() const { return m_warn_list.elements == 0; }
+ bool is_empty() const { return m_warn_list.is_empty(); }
/** Increment the current row counter to point at the next row. */
void inc_current_row_for_warning() { m_current_row_for_warning++; }
+
/** Reset the current row counter. Start counting from the first row. */
void reset_current_row_for_warning() { m_current_row_for_warning= 1; }
+
/** Return the current counter value. */
ulong current_row_for_warning() const { return m_current_row_for_warning; }
- ulong statement_warn_count() const { return m_statement_warn_count; }
+ /** Return the number of warnings thrown by the current statement. */
+ ulong current_statement_warn_count() const
+ { return m_current_statement_warn_count; }
- /** Add a new condition to the current list. */
- MYSQL_ERROR *push_warning(THD *thd,
- uint sql_errno, const char* sqlstate,
- MYSQL_ERROR::enum_warning_level level,
- const char* msg);
+ /** Make sure there is room for the given number of conditions. */
+ void reserve_space(THD *thd, uint count);
- /** Add a new condition to the current list. */
- MYSQL_ERROR *push_warning(THD *thd, const MYSQL_ERROR *sql_condition);
+ /**
+ Add a new SQL-condition to the current list and increment the respective
+ counters.
+
+ @param thd Thread context.
+ @param sql_errno SQL-condition error number.
+ @param sqlstate SQL-condition state.
+ @param level SQL-condition level.
+ @param msg SQL-condition message.
+
+ @return a pointer to the added SQL-condition.
+ */
+ Sql_condition *push_warning(THD *thd,
+ uint sql_errno,
+ const char* sqlstate,
+ Sql_condition::enum_warning_level level,
+ const char* msg);
+
+ /**
+ Add a new SQL-condition to the current list and increment the respective
+ counters.
+
+ @param thd Thread context.
+ @param sql_condition SQL-condition to copy values from.
+
+ @return a pointer to the added SQL-condition.
+ */
+ Sql_condition *push_warning(THD *thd, const Sql_condition *sql_condition);
/**
Set the read only status for this statement area.
@@ -496,25 +499,51 @@ public:
- SHOW WARNINGS
- SHOW ERRORS
- GET DIAGNOSTICS
- @param read_only the read only property to set
+ @param read_only the read only property to set.
*/
void set_read_only(bool read_only)
{ m_read_only= read_only; }
/**
Read only status.
- @return the read only property
+ @return the read only property.
*/
bool is_read_only() const
{ return m_read_only; }
-private:
- /** Read only status. */
- bool m_read_only;
+ /**
+ @return SQL-condition, which corresponds to the error state in
+ Diagnostics_area.
- friend class Resignal_statement;
+ @see m_error_condition.
+ */
+ const Sql_condition *get_error_condition() const
+ { return m_error_condition; }
+
+ /**
+ Set SQL-condition, which corresponds to the error state in Diagnostics_area.
+
+ @see m_error_condition.
+ */
+ void set_error_condition(const Sql_condition *error_condition)
+ { m_error_condition= error_condition; }
+
+ /**
+ Reset SQL-condition, which corresponds to the error state in
+ Diagnostics_area.
+
+ @see m_error_condition.
+ */
+ void clear_error_condition()
+ { m_error_condition= NULL; }
+
+ // for:
+ // - m_next_in_da / m_prev_in_da
+ // - is_marked_for_removal()
+ friend class Diagnostics_area;
};
+
extern char *err_conv(char *buff, uint to_length, const char *from,
uint from_length, CHARSET_INFO *from_cs);
@@ -536,6 +565,8 @@ class ErrConvString : public ErrConv
public:
ErrConvString(const char *str_arg, size_t len_arg, CHARSET_INFO *cs_arg)
: ErrConv(), str(str_arg), len(len_arg), cs(cs_arg) {}
+ ErrConvString(const char *str_arg, CHARSET_INFO *cs_arg)
+ : ErrConv(), str(str_arg), len(strlen(str_arg)), cs(cs_arg) {}
ErrConvString(String *s)
: ErrConv(), str(s->ptr()), len(s->length()), cs(s->charset()) {}
const char *ptr() const
@@ -593,15 +624,353 @@ public:
}
};
-void push_warning(THD *thd, MYSQL_ERROR::enum_warning_level level,
+///////////////////////////////////////////////////////////////////////////
+
+/**
+ Stores status of the currently executed statement.
+ Cleared at the beginning of the statement, and then
+ can hold either OK, ERROR, or EOF status.
+ Can not be assigned twice per statement.
+*/
+
+class Diagnostics_area
+{
+private:
+ /** The type of the counted and doubly linked list of conditions. */
+ typedef I_P_List<Warning_info,
+ I_P_List_adapter<Warning_info,
+ &Warning_info::m_next_in_da,
+ &Warning_info::m_prev_in_da>,
+ I_P_List_counter,
+ I_P_List_fast_push_back<Warning_info> >
+ Warning_info_list;
+
+public:
+ /** Const iterator used to iterate through the warning list. */
+ typedef Warning_info::Sql_condition_list::Const_Iterator
+ Sql_condition_iterator;
+
+ enum enum_diagnostics_status
+ {
+ /** The area is cleared at start of a statement. */
+ DA_EMPTY= 0,
+ /** Set whenever one calls my_ok(). */
+ DA_OK,
+ /** Set whenever one calls my_eof(). */
+ DA_EOF,
+ /** Set whenever one calls my_error() or my_message(). */
+ DA_ERROR,
+ /** Set in case of a custom response, such as one from COM_STMT_PREPARE. */
+ DA_DISABLED
+ };
+
+ void set_overwrite_status(bool can_overwrite_status)
+ { m_can_overwrite_status= can_overwrite_status; }
+
+ /** True if status information is sent to the client. */
+ bool is_sent() const { return m_is_sent; }
+
+ void set_is_sent(bool is_sent) { m_is_sent= is_sent; }
+
+ void set_ok_status(ulonglong affected_rows,
+ ulonglong last_insert_id,
+ const char *message);
+
+ void set_eof_status(THD *thd);
+
+ void set_error_status(uint sql_errno);
+
+ void set_error_status(uint sql_errno,
+ const char *message,
+ const char *sqlstate,
+ const Sql_condition *error_condition);
+
+ void disable_status();
+
+ void reset_diagnostics_area();
+
+ bool is_set() const { return m_status != DA_EMPTY; }
+
+ bool is_error() const { return m_status == DA_ERROR; }
+
+ bool is_eof() const { return m_status == DA_EOF; }
+
+ bool is_ok() const { return m_status == DA_OK; }
+
+ bool is_disabled() const { return m_status == DA_DISABLED; }
+
+ enum_diagnostics_status status() const { return m_status; }
+
+ const char *message() const
+ { DBUG_ASSERT(m_status == DA_ERROR || m_status == DA_OK); return m_message; }
+
+ uint sql_errno() const
+ { DBUG_ASSERT(m_status == DA_ERROR); return m_sql_errno; }
+
+ const char* get_sqlstate() const
+ { DBUG_ASSERT(m_status == DA_ERROR); return m_sqlstate; }
+
+ ulonglong affected_rows() const
+ { DBUG_ASSERT(m_status == DA_OK); return m_affected_rows; }
+
+ ulonglong last_insert_id() const
+ { DBUG_ASSERT(m_status == DA_OK); return m_last_insert_id; }
+
+ uint statement_warn_count() const
+ {
+ DBUG_ASSERT(m_status == DA_OK || m_status == DA_EOF);
+ return m_statement_warn_count;
+ }
+
+ /* Used to count any warnings pushed after calling set_ok_status(). */
+ void increment_warning()
+ {
+ if (m_status != DA_EMPTY)
+ m_statement_warn_count++;
+ }
+
+ Diagnostics_area(bool initialize);
+ Diagnostics_area(ulonglong warning_info_id, bool allow_unlimited_warnings,
+ bool initialize);
+ void init() { m_main_wi.init() ; }
+ void free_memory() { m_main_wi.free_memory() ; }
+
+ void push_warning_info(Warning_info *wi)
+ { m_wi_stack.push_front(wi); }
+
+ void pop_warning_info()
+ {
+ DBUG_ASSERT(m_wi_stack.elements() > 0);
+ m_wi_stack.remove(m_wi_stack.front());
+ }
+
+ void set_warning_info_id(ulonglong id)
+ { get_warning_info()->id(id); }
+
+ ulonglong warning_info_id() const
+ { return get_warning_info()->id(); }
+
+ /**
+ Compare given current warning info and current warning info
+ and see if they are different. They will be different if
+ warnings have been generated or statements that use tables
+ have been executed. This is checked by comparing m_warn_id.
+
+ @param wi Warning info to compare with current Warning info.
+
+ @return false if they are equal, true if they are not.
+ */
+ bool warning_info_changed(const Warning_info *wi) const
+ { return get_warning_info()->id() != wi->id(); }
+
+ bool is_warning_info_empty() const
+ { return get_warning_info()->is_empty(); }
+
+ ulong current_statement_warn_count() const
+ { return get_warning_info()->current_statement_warn_count(); }
+
+ bool has_sql_condition(const char *message_str, ulong message_length) const
+ { return get_warning_info()->has_sql_condition(message_str, message_length); }
+
+ void reset_for_next_command()
+ { get_warning_info()->reset_for_next_command(); }
+
+ void clear_warning_info(ulonglong id)
+ { get_warning_info()->clear(id); }
+
+ void opt_clear_warning_info(ulonglong query_id)
+ { get_warning_info()->opt_clear(query_id); }
+
+ ulong current_row_for_warning() const
+ { return get_warning_info()->current_row_for_warning(); }
+
+ void inc_current_row_for_warning()
+ { get_warning_info()->inc_current_row_for_warning(); }
+
+ void reset_current_row_for_warning()
+ { get_warning_info()->reset_current_row_for_warning(); }
+
+ bool is_warning_info_read_only() const
+ { return get_warning_info()->is_read_only(); }
+
+ void set_warning_info_read_only(bool read_only)
+ { get_warning_info()->set_read_only(read_only); }
+
+ ulong error_count() const
+ { return get_warning_info()->error_count(); }
+
+ ulong warn_count() const
+ { return get_warning_info()->warn_count(); }
+
+ uint cond_count() const
+ { return get_warning_info()->cond_count(); }
+
+ Sql_condition_iterator sql_conditions() const
+ { return get_warning_info()->m_warn_list; }
+
+ void reserve_space(THD *thd, uint count)
+ { get_warning_info()->reserve_space(thd, count); }
+
+ Sql_condition *push_warning(THD *thd, const Sql_condition *sql_condition)
+ { return get_warning_info()->push_warning(thd, sql_condition); }
+
+ Sql_condition *push_warning(THD *thd,
+ uint sql_errno,
+ const char* sqlstate,
+ Sql_condition::enum_warning_level level,
+ const char* msg)
+ {
+ return get_warning_info()->push_warning(thd,
+ sql_errno, sqlstate, level, msg);
+ }
+
+ void mark_sql_conditions_for_removal()
+ { get_warning_info()->mark_sql_conditions_for_removal(); }
+
+ void unmark_sql_conditions_from_removal()
+ { get_warning_info()->unmark_sql_conditions_from_removal(); }
+
+ void remove_marked_sql_conditions()
+ { get_warning_info()->remove_marked_sql_conditions(); }
+
+ const Sql_condition *get_error_condition() const
+ { return get_warning_info()->get_error_condition(); }
+
+ void copy_sql_conditions_to_wi(THD *thd, Warning_info *dst_wi) const
+ { dst_wi->append_warning_info(thd, get_warning_info()); }
+
+ void copy_sql_conditions_from_wi(THD *thd, const Warning_info *src_wi)
+ { get_warning_info()->append_warning_info(thd, src_wi); }
+
+ void copy_non_errors_from_wi(THD *thd, const Warning_info *src_wi);
+
+private:
+ Warning_info *get_warning_info() { return m_wi_stack.front(); }
+
+ const Warning_info *get_warning_info() const { return m_wi_stack.front(); }
+
+private:
+ /** True if status information is sent to the client. */
+ bool m_is_sent;
+
+ /** Set to make set_error_status after set_{ok,eof}_status possible. */
+ bool m_can_overwrite_status;
+
+ /** Message buffer. Can be used by OK or ERROR status. */
+ char m_message[MYSQL_ERRMSG_SIZE];
+
+ /**
+ SQL error number. One of ER_ codes from share/errmsg.txt.
+ Set by set_error_status.
+ */
+ uint m_sql_errno;
+
+ char m_sqlstate[SQLSTATE_LENGTH+1];
+
+ /**
+ The number of rows affected by the last statement. This is
+ semantically close to thd->row_count_func, but has a different
+ life cycle. thd->row_count_func stores the value returned by
+ function ROW_COUNT() and is cleared only by statements that
+ update its value, such as INSERT, UPDATE, DELETE and few others.
+ This member is cleared at the beginning of the next statement.
+
+ We could possibly merge the two, but life cycle of thd->row_count_func
+ can not be changed.
+ */
+ ulonglong m_affected_rows;
+
+ /**
+ Similarly to the previous member, this is a replacement of
+ thd->first_successful_insert_id_in_prev_stmt, which is used
+ to implement LAST_INSERT_ID().
+ */
+
+ ulonglong m_last_insert_id;
+ /**
+ Number of warnings of this last statement. May differ from
+ the number of warnings returned by SHOW WARNINGS e.g. in case
+ the statement doesn't clear the warnings, and doesn't generate
+ them.
+ */
+ uint m_statement_warn_count;
+
+ enum_diagnostics_status m_status;
+
+ Warning_info m_main_wi;
+
+ Warning_info_list m_wi_stack;
+};
+
+///////////////////////////////////////////////////////////////////////////
+
+
+void push_warning(THD *thd, Sql_condition::enum_warning_level level,
uint code, const char *msg);
-void push_warning_printf(THD *thd, MYSQL_ERROR::enum_warning_level level,
- uint code, const char *format, ...);
+
+void push_warning_printf(THD *thd, Sql_condition::enum_warning_level level,
+ uint code, const char *format, ...);
+
bool mysqld_show_warnings(THD *thd, ulong levels_to_show);
-uint32 convert_error_message(char *to, uint32 to_length, CHARSET_INFO *to_cs,
+
+uint32 convert_error_message(char *to, uint32 to_length,
+ CHARSET_INFO *to_cs,
const char *from, uint32 from_length,
CHARSET_INFO *from_cs, uint *errors);
extern const LEX_STRING warning_level_names[];
+bool is_sqlstate_valid(const LEX_STRING *sqlstate);
+/**
+ Checks if the specified SQL-state-string defines COMPLETION condition.
+ This function assumes that the given string contains a valid SQL-state.
+
+ @param s the condition SQLSTATE.
+
+ @retval true if the given string defines COMPLETION condition.
+ @retval false otherwise.
+*/
+inline bool is_sqlstate_completion(const char *s)
+{ return s[0] == '0' && s[1] == '0'; }
+
+
+/**
+ Checks if the specified SQL-state-string defines WARNING condition.
+ This function assumes that the given string contains a valid SQL-state.
+
+ @param s the condition SQLSTATE.
+
+ @retval true if the given string defines WARNING condition.
+ @retval false otherwise.
+*/
+inline bool is_sqlstate_warning(const char *s)
+{ return s[0] == '0' && s[1] == '1'; }
+
+
+/**
+ Checks if the specified SQL-state-string defines NOT FOUND condition.
+ This function assumes that the given string contains a valid SQL-state.
+
+ @param s the condition SQLSTATE.
+
+ @retval true if the given string defines NOT FOUND condition.
+ @retval false otherwise.
+*/
+inline bool is_sqlstate_not_found(const char *s)
+{ return s[0] == '0' && s[1] == '2'; }
+
+
+/**
+ Checks if the specified SQL-state-string defines EXCEPTION condition.
+ This function assumes that the given string contains a valid SQL-state.
+
+ @param s the condition SQLSTATE.
+
+ @retval true if the given string defines EXCEPTION condition.
+ @retval false otherwise.
+*/
+inline bool is_sqlstate_exception(const char *s)
+{ return s[0] != '0' || s[1] > '2'; }
+
+
#endif // SQL_ERROR_H
diff --git a/sql/sql_explain.cc b/sql/sql_explain.cc
new file mode 100644
index 00000000000..75f6689ab98
--- /dev/null
+++ b/sql/sql_explain.cc
@@ -0,0 +1,950 @@
+/*
+ Copyright (c) 2013 Monty Program Ab
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ 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 */
+
+#ifdef USE_PRAGMA_IMPLEMENTATION
+#pragma implementation // gcc: Class implementation
+#endif
+
+#include <my_global.h>
+#include "sql_priv.h"
+#include "sql_select.h"
+
+
+Explain_query::Explain_query(THD *thd_arg) :
+ upd_del_plan(NULL), insert_plan(NULL), thd(thd_arg), apc_enabled(false)
+{
+ operations= 0;
+}
+
+
+Explain_query::~Explain_query()
+{
+ if (apc_enabled)
+ thd->apc_target.disable();
+
+ delete upd_del_plan;
+ delete insert_plan;
+ uint i;
+ for (i= 0 ; i < unions.elements(); i++)
+ delete unions.at(i);
+ for (i= 0 ; i < selects.elements(); i++)
+ delete selects.at(i);
+}
+
+
+Explain_node *Explain_query::get_node(uint select_id)
+{
+ Explain_union *u;
+ if ((u= get_union(select_id)))
+ return u;
+ else
+ return get_select(select_id);
+}
+
+Explain_union *Explain_query::get_union(uint select_id)
+{
+ return (unions.elements() > select_id) ? unions.at(select_id) : NULL;
+}
+
+Explain_select *Explain_query::get_select(uint select_id)
+{
+ return (selects.elements() > select_id) ? selects.at(select_id) : NULL;
+}
+
+
+void Explain_query::add_node(Explain_node *node)
+{
+ uint select_id;
+ operations++;
+ if (node->get_type() == Explain_node::EXPLAIN_UNION)
+ {
+ Explain_union *u= (Explain_union*)node;
+ select_id= u->get_select_id();
+ if (unions.elements() <= select_id)
+ unions.resize(MY_MAX(select_id+1, unions.elements()*2), NULL);
+
+ Explain_union *old_node;
+ if ((old_node= get_union(select_id)))
+ delete old_node;
+
+ unions.at(select_id)= u;
+ }
+ else
+ {
+ Explain_select *sel= (Explain_select*)node;
+ if (sel->select_id == FAKE_SELECT_LEX_ID)
+ {
+ DBUG_ASSERT(0); // this is a "fake select" from a UNION.
+ }
+ else
+ {
+ select_id= sel->select_id;
+ Explain_select *old_node;
+
+ if (selects.elements() <= select_id)
+ selects.resize(MY_MAX(select_id+1, selects.elements()*2), NULL);
+
+ if ((old_node= get_select(select_id)))
+ delete old_node;
+
+ selects.at(select_id)= sel;
+ }
+ }
+}
+
+
+void Explain_query::add_insert_plan(Explain_insert *insert_plan_arg)
+{
+ insert_plan= insert_plan_arg;
+ query_plan_ready();
+}
+
+
+void Explain_query::add_upd_del_plan(Explain_update *upd_del_plan_arg)
+{
+ upd_del_plan= upd_del_plan_arg;
+ query_plan_ready();
+}
+
+
+void Explain_query::query_plan_ready()
+{
+ if (!apc_enabled)
+ thd->apc_target.enable();
+ apc_enabled= true;
+}
+
+/*
+ Send EXPLAIN output to the client.
+*/
+
+int Explain_query::send_explain(THD *thd)
+{
+ select_result *result;
+ LEX *lex= thd->lex;
+
+ if (!(result= new select_send()) ||
+ thd->send_explain_fields(result))
+ return 1;
+
+ int res;
+ if ((res= print_explain(result, lex->describe)))
+ result->abort_result_set();
+ else
+ result->send_eof();
+
+ return res;
+}
+
+
+/*
+ The main entry point to print EXPLAIN of the entire query
+*/
+
+int Explain_query::print_explain(select_result_sink *output,
+ uint8 explain_flags)
+{
+ if (upd_del_plan)
+ {
+ upd_del_plan->print_explain(this, output, explain_flags);
+ return 0;
+ }
+ else if (insert_plan)
+ {
+ insert_plan->print_explain(this, output, explain_flags);
+ return 0;
+ }
+ else
+ {
+ /* Start printing from node with id=1 */
+ Explain_node *node= get_node(1);
+ if (!node)
+ return 1; /* No query plan */
+ return node->print_explain(this, output, explain_flags);
+ }
+}
+
+
+bool print_explain_query(LEX *lex, THD *thd, String *str)
+{
+ return lex->explain->print_explain_str(thd, str);
+}
+
+
+/*
+ Return tabular EXPLAIN output as a text string
+*/
+
+bool Explain_query::print_explain_str(THD *thd, String *out_str)
+{
+ List<Item> fields;
+ thd->make_explain_field_list(fields);
+
+ select_result_text_buffer output_buf(thd);
+ output_buf.send_result_set_metadata(fields, thd->lex->describe);
+ if (print_explain(&output_buf, thd->lex->describe))
+ return true;
+ output_buf.save_to(out_str);
+ return false;
+}
+
+
+static void push_str(List<Item> *item_list, const char *str)
+{
+ item_list->push_back(new Item_string_sys(str));
+}
+
+
+static void push_string(List<Item> *item_list, String *str)
+{
+ item_list->push_back(new Item_string_sys(str->ptr(), str->length()));
+}
+
+
+int Explain_union::print_explain(Explain_query *query,
+ select_result_sink *output,
+ uint8 explain_flags)
+{
+ char table_name_buffer[SAFE_NAME_LEN];
+
+ /* print all UNION children, in order */
+ for (int i= 0; i < (int) union_members.elements(); i++)
+ {
+ Explain_select *sel= query->get_select(union_members.at(i));
+ sel->print_explain(query, output, explain_flags);
+ }
+
+ /* Print a line with "UNION RESULT" */
+ List<Item> item_list;
+ Item *item_null= new Item_null();
+
+ /* `id` column */
+ item_list.push_back(item_null);
+
+ /* `select_type` column */
+ push_str(&item_list, fake_select_type);
+
+ /* `table` column: something like "<union1,2>" */
+ {
+ uint childno= 0;
+ uint len= 6, lastop= 0;
+ memcpy(table_name_buffer, STRING_WITH_LEN("<union"));
+
+ for (; childno < union_members.elements() && len + lastop + 5 < NAME_LEN;
+ childno++)
+ {
+ len+= lastop;
+ lastop= my_snprintf(table_name_buffer + len, NAME_LEN - len,
+ "%u,", union_members.at(childno));
+ }
+
+ if (childno < union_members.elements() || len + lastop >= NAME_LEN)
+ {
+ memcpy(table_name_buffer + len, STRING_WITH_LEN("...>") + 1);
+ len+= 4;
+ }
+ else
+ {
+ len+= lastop;
+ table_name_buffer[len - 1]= '>'; // change ',' to '>'
+ }
+ item_list.push_back(new Item_string_sys(table_name_buffer, len));
+ }
+
+ /* `partitions` column */
+ if (explain_flags & DESCRIBE_PARTITIONS)
+ item_list.push_back(item_null);
+
+ /* `type` column */
+ push_str(&item_list, join_type_str[JT_ALL]);
+
+ /* `possible_keys` column */
+ item_list.push_back(item_null);
+
+ /* `key` */
+ item_list.push_back(item_null);
+
+ /* `key_len` */
+ item_list.push_back(item_null);
+
+ /* `ref` */
+ item_list.push_back(item_null);
+
+ /* `rows` */
+ item_list.push_back(item_null);
+
+ /* `filtered` */
+ if (explain_flags & DESCRIBE_EXTENDED)
+ item_list.push_back(item_null);
+
+ /* `Extra` */
+ StringBuffer<256> extra_buf;
+ if (using_filesort)
+ {
+ extra_buf.append(STRING_WITH_LEN("Using filesort"));
+ }
+ item_list.push_back(new Item_string_sys(extra_buf.ptr(), extra_buf.length()));
+
+ //output->unit.offset_limit_cnt= 0;
+ if (output->send_data(item_list))
+ return 1;
+
+ /*
+ Print all subquery children (UNION children have already been printed at
+ the start of this function)
+ */
+ return print_explain_for_children(query, output, explain_flags);
+}
+
+
+/*
+ Print EXPLAINs for all children nodes (i.e. for subqueries)
+*/
+
+int Explain_node::print_explain_for_children(Explain_query *query,
+ select_result_sink *output,
+ uint8 explain_flags)
+{
+ for (int i= 0; i < (int) children.elements(); i++)
+ {
+ Explain_node *node= query->get_node(children.at(i));
+ if (node->print_explain(query, output, explain_flags))
+ return 1;
+ }
+ return 0;
+}
+
+
+Explain_select::~Explain_select()
+{
+ if (join_tabs)
+ {
+ for (uint i= 0; i< n_join_tabs; i++)
+ delete join_tabs[i];
+ my_free(join_tabs);
+ }
+}
+
+
+int Explain_select::print_explain(Explain_query *query,
+ select_result_sink *output,
+ uint8 explain_flags)
+{
+ if (message)
+ {
+ List<Item> item_list;
+ Item *item_null= new Item_null();
+
+ item_list.push_back(new Item_int((int32) select_id));
+ item_list.push_back(new Item_string_sys(select_type));
+ for (uint i=0 ; i < 7; i++)
+ item_list.push_back(item_null);
+ if (explain_flags & DESCRIBE_PARTITIONS)
+ item_list.push_back(item_null);
+ if (explain_flags & DESCRIBE_EXTENDED)
+ item_list.push_back(item_null);
+
+ item_list.push_back(new Item_string_sys(message));
+
+ if (output->send_data(item_list))
+ return 1;
+ }
+ else
+ {
+ bool using_tmp= using_temporary;
+ bool using_fs= using_filesort;
+ for (uint i=0; i< n_join_tabs; i++)
+ {
+ join_tabs[i]->print_explain(output, explain_flags, select_id,
+ select_type, using_tmp, using_fs);
+ if (i == 0)
+ {
+ /*
+ "Using temporary; Using filesort" should only be shown near the 1st
+ table
+ */
+ using_tmp= false;
+ using_fs= false;
+ }
+ }
+ }
+
+ return print_explain_for_children(query, output, explain_flags);
+}
+
+
+void Explain_table_access::push_extra(enum explain_extra_tag extra_tag)
+{
+ extra_tags.append(extra_tag);
+}
+
+
+int Explain_table_access::print_explain(select_result_sink *output, uint8 explain_flags,
+ uint select_id, const char *select_type,
+ bool using_temporary, bool using_filesort)
+{
+ const CHARSET_INFO *cs= system_charset_info;
+ const char *hash_key_prefix= "#hash#";
+ bool is_hj= (type == JT_HASH || type == JT_HASH_NEXT ||
+ type == JT_HASH_RANGE || type == JT_HASH_INDEX_MERGE);
+
+ List<Item> item_list;
+ Item *item_null= new Item_null();
+
+ if (sjm_nest_select_id)
+ select_id= sjm_nest_select_id;
+
+ /* `id` column */
+ item_list.push_back(new Item_int((int32) select_id));
+
+ /* `select_type` column */
+ if (sjm_nest_select_id)
+ push_str(&item_list, "MATERIALIZED");
+ else
+ push_str(&item_list, select_type);
+
+ /* `table` column */
+ push_string(&item_list, &table_name);
+
+ /* `partitions` column */
+ if (explain_flags & DESCRIBE_PARTITIONS)
+ {
+ if (used_partitions_set)
+ {
+ push_string(&item_list, &used_partitions);
+ }
+ else
+ item_list.push_back(item_null);
+ }
+
+ /* `type` column */
+ push_str(&item_list, join_type_str[type]);
+
+ /* `possible_keys` column */
+ if (possible_keys_str.length() > 0)
+ push_string(&item_list, &possible_keys_str);
+ else
+ item_list.push_back(item_null);
+
+ /* `key` */
+ StringBuffer<64> key_str;
+ if (key.get_key_name())
+ {
+ if (is_hj)
+ key_str.append(hash_key_prefix, strlen(hash_key_prefix), cs);
+
+ key_str.append(key.get_key_name());
+
+ if (is_hj && type != JT_HASH)
+ key_str.append(':');
+ }
+
+ if (quick_info)
+ {
+ StringBuffer<64> buf2;
+ quick_info->print_key(&buf2);
+ key_str.append(buf2);
+ }
+ if (type == JT_HASH_NEXT)
+ key_str.append(hash_next_key.get_key_name());
+
+ if (key_str.length() > 0)
+ push_string(&item_list, &key_str);
+ else
+ item_list.push_back(item_null);
+
+ /* `key_len` */
+ StringBuffer<64> key_len_str;
+
+ if (key.get_key_len() != (uint)-1)
+ {
+ char buf[64];
+ size_t length;
+ length= longlong10_to_str(key.get_key_len(), buf, 10) - buf;
+ key_len_str.append(buf, length);
+ if (is_hj && type != JT_HASH)
+ key_len_str.append(':');
+ }
+
+ if (quick_info)
+ {
+ StringBuffer<64> buf2;
+ quick_info->print_key_len(&buf2);
+ key_len_str.append(buf2);
+ }
+
+ if (type == JT_HASH_NEXT)
+ {
+ char buf[64];
+ size_t length;
+ length= longlong10_to_str(hash_next_key.get_key_len(), buf, 10) - buf;
+ key_len_str.append(buf, length);
+ }
+
+ if (key_len_str.length() > 0)
+ push_string(&item_list, &key_len_str);
+ else
+ item_list.push_back(item_null);
+
+ /* `ref` */
+ if (ref_set)
+ push_string(&item_list, &ref);
+ else
+ item_list.push_back(item_null);
+
+ /* `rows` */
+ if (rows_set)
+ {
+ item_list.push_back(new Item_int((longlong) (ulonglong) rows,
+ MY_INT64_NUM_DECIMAL_DIGITS));
+ }
+ else
+ item_list.push_back(item_null);
+
+ /* `filtered` */
+ if (explain_flags & DESCRIBE_EXTENDED)
+ {
+ if (filtered_set)
+ {
+ item_list.push_back(new Item_float(filtered, 2));
+ }
+ else
+ item_list.push_back(item_null);
+ }
+
+ /* `Extra` */
+ StringBuffer<256> extra_buf;
+ bool first= true;
+ for (int i=0; i < (int)extra_tags.elements(); i++)
+ {
+ if (first)
+ first= false;
+ else
+ extra_buf.append(STRING_WITH_LEN("; "));
+ append_tag_name(&extra_buf, extra_tags.at(i));
+ }
+
+ if (using_temporary)
+ {
+ if (first)
+ first= false;
+ else
+ extra_buf.append(STRING_WITH_LEN("; "));
+ extra_buf.append(STRING_WITH_LEN("Using temporary"));
+ }
+
+ if (using_filesort)
+ {
+ if (first)
+ first= false;
+ else
+ extra_buf.append(STRING_WITH_LEN("; "));
+ extra_buf.append(STRING_WITH_LEN("Using filesort"));
+ }
+
+ item_list.push_back(new Item_string_sys(extra_buf.ptr(), extra_buf.length()));
+
+ if (output->send_data(item_list))
+ return 1;
+
+ return 0;
+}
+
+
+/*
+ Elements in this array match members of enum Extra_tag, defined in
+ sql_explain.h
+*/
+
+const char * extra_tag_text[]=
+{
+ "ET_none",
+ "Using index condition",
+ "Using index condition(BKA)",
+ "Using ", // special handling
+ "Range checked for each record (index map: 0x", // special handling
+ "Using where with pushed condition",
+ "Using where",
+ "Not exists",
+
+ "Using index",
+ "Full scan on NULL key",
+ "Skip_open_table",
+ "Open_frm_only",
+ "Open_full_table",
+
+ "Scanned 0 databases",
+ "Scanned 1 database",
+ "Scanned all databases",
+
+ "Using index for group-by", // special handling
+
+ "USING MRR: DONT PRINT ME", // special handling
+
+ "Distinct",
+ "LooseScan",
+ "Start temporary",
+ "End temporary",
+ "FirstMatch", // special handling
+
+ "Using join buffer", // special handling
+
+ "const row not found",
+ "unique row not found",
+ "Impossible ON condition"
+};
+
+
+void Explain_table_access::append_tag_name(String *str, enum explain_extra_tag tag)
+{
+ switch (tag) {
+ case ET_USING:
+ {
+ // quick select
+ str->append(STRING_WITH_LEN("Using "));
+ quick_info->print_extra(str);
+ break;
+ }
+ case ET_RANGE_CHECKED_FOR_EACH_RECORD:
+ {
+ /* 4 bits per 1 hex digit + terminating '\0' */
+ char buf[MAX_KEY / 4 + 1];
+ str->append(STRING_WITH_LEN("Range checked for each "
+ "record (index map: 0x"));
+ str->append(range_checked_map.print(buf));
+ str->append(')');
+ break;
+ }
+ case ET_USING_MRR:
+ {
+ str->append(mrr_type);
+ break;
+ }
+ case ET_USING_JOIN_BUFFER:
+ {
+ str->append(extra_tag_text[tag]);
+
+ str->append(STRING_WITH_LEN(" ("));
+ const char *buffer_type= bka_type.incremental ? "incremental" : "flat";
+ str->append(buffer_type);
+ str->append(STRING_WITH_LEN(", "));
+ str->append(bka_type.join_alg);
+ str->append(STRING_WITH_LEN(" join"));
+ str->append(STRING_WITH_LEN(")"));
+ if (bka_type.mrr_type.length())
+ str->append(bka_type.mrr_type);
+
+ break;
+ }
+ case ET_FIRST_MATCH:
+ {
+ if (firstmatch_table_name.length())
+ {
+ str->append("FirstMatch(");
+ str->append(firstmatch_table_name);
+ str->append(")");
+ }
+ else
+ str->append(extra_tag_text[tag]);
+ break;
+ }
+ case ET_USING_INDEX_FOR_GROUP_BY:
+ {
+ str->append(extra_tag_text[tag]);
+ if (loose_scan_is_scanning)
+ str->append(" (scanning)");
+ break;
+ }
+ default:
+ str->append(extra_tag_text[tag]);
+ }
+}
+
+
+/*
+ This is called for top-level Explain_quick_select only. The point of this
+ function is:
+ - index_merge should print $index_merge_type (child, ...)
+ - 'range' should not print anything.
+*/
+
+void Explain_quick_select::print_extra(String *str)
+{
+ if (quick_type == QUICK_SELECT_I::QS_TYPE_RANGE ||
+ quick_type == QUICK_SELECT_I::QS_TYPE_RANGE_DESC ||
+ quick_type == QUICK_SELECT_I::QS_TYPE_GROUP_MIN_MAX)
+ {
+ /* print nothing */
+ }
+ else
+ print_extra_recursive(str);
+}
+
+
+void Explain_quick_select::print_extra_recursive(String *str)
+{
+ if (quick_type == QUICK_SELECT_I::QS_TYPE_RANGE ||
+ quick_type == QUICK_SELECT_I::QS_TYPE_RANGE_DESC)
+ {
+ str->append(range.get_key_name());
+ }
+ else
+ {
+ str->append(get_name_by_type());
+ str->append('(');
+ List_iterator_fast<Explain_quick_select> it (children);
+ Explain_quick_select* child;
+ bool first= true;
+ while ((child = it++))
+ {
+ if (first)
+ first= false;
+ else
+ str->append(',');
+
+ child->print_extra_recursive(str);
+ }
+ str->append(')');
+ }
+}
+
+
+const char * Explain_quick_select::get_name_by_type()
+{
+ switch (quick_type) {
+ case QUICK_SELECT_I::QS_TYPE_INDEX_MERGE:
+ return "sort_union";
+ case QUICK_SELECT_I::QS_TYPE_ROR_UNION:
+ return "union";
+ case QUICK_SELECT_I::QS_TYPE_ROR_INTERSECT:
+ return "intersect";
+ case QUICK_SELECT_I::QS_TYPE_INDEX_INTERSECT:
+ return "sort_intersect";
+ default:
+ DBUG_ASSERT(0);
+ return "unknown quick select type";
+ }
+}
+
+
+/*
+ This prints a comma-separated list of used indexes, ignoring nesting
+*/
+
+void Explain_quick_select::print_key(String *str)
+{
+ if (quick_type == QUICK_SELECT_I::QS_TYPE_RANGE ||
+ quick_type == QUICK_SELECT_I::QS_TYPE_RANGE_DESC ||
+ quick_type == QUICK_SELECT_I::QS_TYPE_GROUP_MIN_MAX)
+ {
+ if (str->length() > 0)
+ str->append(',');
+ str->append(range.get_key_name());
+ }
+ else
+ {
+ List_iterator_fast<Explain_quick_select> it (children);
+ Explain_quick_select* child;
+ while ((child = it++))
+ {
+ child->print_key(str);
+ }
+ }
+}
+
+
+/*
+ This prints a comma-separated list of used key_lengths, ignoring nesting
+*/
+
+void Explain_quick_select::print_key_len(String *str)
+{
+ if (quick_type == QUICK_SELECT_I::QS_TYPE_RANGE ||
+ quick_type == QUICK_SELECT_I::QS_TYPE_RANGE_DESC ||
+ quick_type == QUICK_SELECT_I::QS_TYPE_GROUP_MIN_MAX)
+ {
+ char buf[64];
+ size_t length;
+ length= longlong10_to_str(range.get_key_len(), buf, 10) - buf;
+ if (str->length() > 0)
+ str->append(',');
+ str->append(buf, length);
+ }
+ else
+ {
+ List_iterator_fast<Explain_quick_select> it (children);
+ Explain_quick_select* child;
+ while ((child = it++))
+ {
+ child->print_key_len(str);
+ }
+ }
+}
+
+
+int Explain_delete::print_explain(Explain_query *query,
+ select_result_sink *output,
+ uint8 explain_flags)
+{
+ if (deleting_all_rows)
+ {
+ const char *msg= "Deleting all rows";
+ int res= print_explain_message_line(output, explain_flags,
+ 1 /*select number*/,
+ select_type, &rows, msg);
+ return res;
+
+ }
+ else
+ {
+ return Explain_update::print_explain(query, output, explain_flags);
+ }
+}
+
+
+int Explain_update::print_explain(Explain_query *query,
+ select_result_sink *output,
+ uint8 explain_flags)
+{
+ StringBuffer<64> key_buf;
+ StringBuffer<64> key_len_buf;
+ StringBuffer<64> extra_str;
+ if (impossible_where || no_partitions)
+ {
+ const char *msg= impossible_where ?
+ "Impossible WHERE" :
+ "No matching rows after partition pruning";
+ int res= print_explain_message_line(output, explain_flags,
+ 1 /*select number*/,
+ select_type,
+ NULL, /* rows */
+ msg);
+ return res;
+ }
+
+
+ if (quick_info)
+ {
+ quick_info->print_key(&key_buf);
+ quick_info->print_key_len(&key_len_buf);
+
+ StringBuffer<64> quick_buf;
+ quick_info->print_extra(&quick_buf);
+ if (quick_buf.length())
+ {
+ extra_str.append(STRING_WITH_LEN("Using "));
+ extra_str.append(quick_buf);
+ }
+ }
+ else
+ {
+ key_buf.copy(key_str);
+ key_len_buf.copy(key_len_str);
+ }
+
+ if (using_where)
+ {
+ if (extra_str.length() !=0)
+ extra_str.append(STRING_WITH_LEN("; "));
+ extra_str.append(STRING_WITH_LEN("Using where"));
+ }
+
+ if (mrr_type.length() != 0)
+ {
+ if (extra_str.length() !=0)
+ extra_str.append(STRING_WITH_LEN("; "));
+ extra_str.append(mrr_type);
+ }
+
+ if (using_filesort)
+ {
+ if (extra_str.length() !=0)
+ extra_str.append(STRING_WITH_LEN("; "));
+ extra_str.append(STRING_WITH_LEN("Using filesort"));
+ }
+
+ if (using_io_buffer)
+ {
+ if (extra_str.length() !=0)
+ extra_str.append(STRING_WITH_LEN("; "));
+ extra_str.append(STRING_WITH_LEN("Using buffer"));
+ }
+
+ /*
+ Single-table DELETE commands do not do "Using temporary".
+ "Using index condition" is also not possible (which is an unjustified limitation)
+ */
+
+ print_explain_row(output, explain_flags,
+ 1, /* id */
+ select_type,
+ table_name.c_ptr(),
+ used_partitions_set? used_partitions.c_ptr() : NULL,
+ jtype,
+ possible_keys_line.length()? possible_keys_line.c_ptr(): NULL,
+ key_buf.length()? key_buf.c_ptr() : NULL,
+ key_len_buf.length() ? key_len_buf.c_ptr() : NULL,
+ NULL, /* 'ref' is always NULL in single-table EXPLAIN DELETE */
+ &rows,
+ extra_str.c_ptr_safe());
+
+ return print_explain_for_children(query, output, explain_flags);
+}
+
+
+int Explain_insert::print_explain(Explain_query *query,
+ select_result_sink *output,
+ uint8 explain_flags)
+{
+ const char *select_type="INSERT";
+ print_explain_row(output, explain_flags,
+ 1, /* id */
+ select_type,
+ table_name.c_ptr(),
+ NULL, // partitions
+ JT_ALL,
+ NULL, // possible_keys
+ NULL, // key
+ NULL, // key_len
+ NULL, // ref
+ NULL, // rows
+ NULL);
+
+ return print_explain_for_children(query, output, explain_flags);
+}
+
+
+void delete_explain_query(LEX *lex)
+{
+ delete lex->explain;
+ lex->explain= NULL;
+}
+
+
+void create_explain_query(LEX *lex, MEM_ROOT *mem_root)
+{
+ DBUG_ASSERT(!lex->explain);
+ lex->explain= new Explain_query(lex->thd);
+ DBUG_ASSERT(mem_root == current_thd->mem_root);
+ lex->explain->mem_root= mem_root;
+}
+
+void create_explain_query_if_not_exists(LEX *lex, MEM_ROOT *mem_root)
+{
+ if (!lex->explain)
+ create_explain_query(lex, mem_root);
+}
+
diff --git a/sql/sql_explain.h b/sql/sql_explain.h
new file mode 100644
index 00000000000..b9f381b867b
--- /dev/null
+++ b/sql/sql_explain.h
@@ -0,0 +1,550 @@
+/*
+ Copyright (c) 2013 Monty Program Ab
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ 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 */
+
+
+/**************************************************************************************
+
+ Data structures for producing EXPLAIN outputs.
+
+ These structures
+ - Can be produced inexpensively from query plan.
+ - Store sufficient information to produce tabular EXPLAIN output (the goal is
+ to be able to produce JSON also)
+
+*************************************************************************************/
+
+
+const int FAKE_SELECT_LEX_ID= (int)UINT_MAX;
+
+class Explain_query;
+
+/*
+ A node can be either a SELECT, or a UNION.
+*/
+class Explain_node : public Sql_alloc
+{
+public:
+ enum explain_node_type
+ {
+ EXPLAIN_UNION,
+ EXPLAIN_SELECT,
+ EXPLAIN_UPDATE,
+ EXPLAIN_DELETE,
+ EXPLAIN_INSERT
+ };
+
+ virtual enum explain_node_type get_type()= 0;
+ virtual int get_select_id()= 0;
+
+ /*
+ A node may have children nodes. When a node's explain structure is
+ created, children nodes may not yet have QPFs. This is why we store ids.
+ */
+ Dynamic_array<int> children;
+ void add_child(int select_no)
+ {
+ children.append(select_no);
+ }
+
+ virtual int print_explain(Explain_query *query, select_result_sink *output,
+ uint8 explain_flags)=0;
+
+ int print_explain_for_children(Explain_query *query, select_result_sink *output,
+ uint8 explain_flags);
+ virtual ~Explain_node(){}
+};
+
+
+class Explain_table_access;
+
+
+/*
+ EXPLAIN structure for a SELECT.
+
+ A select can be:
+ 1. A degenerate case. In this case, message!=NULL, and it contains a
+ description of what kind of degenerate case it is (e.g. "Impossible
+ WHERE").
+ 2. a non-degenrate join. In this case, join_tabs describes the join.
+
+ In the non-degenerate case, a SELECT may have a GROUP BY/ORDER BY operation.
+
+ In both cases, the select may have children nodes. class Explain_node provides
+ a way get node's children.
+*/
+
+class Explain_select : public Explain_node
+{
+public:
+ enum explain_node_type get_type() { return EXPLAIN_SELECT; }
+
+ Explain_select() :
+ message(NULL), join_tabs(NULL),
+ using_temporary(false), using_filesort(false)
+ {}
+
+ ~Explain_select();
+
+ bool add_table(Explain_table_access *tab)
+ {
+ if (!join_tabs)
+ {
+ join_tabs= (Explain_table_access**) my_malloc(sizeof(Explain_table_access*) *
+ MAX_TABLES, MYF(0));
+ n_join_tabs= 0;
+ }
+ join_tabs[n_join_tabs++]= tab;
+ return false;
+ }
+
+public:
+ int select_id;
+ const char *select_type;
+
+ int get_select_id() { return select_id; }
+
+ /*
+ If message != NULL, this is a degenerate join plan, and all subsequent
+ members have no info
+ */
+ const char *message;
+
+ /*
+ A flat array of Explain structs for tables. The order is "just like EXPLAIN
+ would print them".
+ */
+ Explain_table_access** join_tabs;
+ uint n_join_tabs;
+
+ /* Global join attributes. In tabular form, they are printed on the first row */
+ bool using_temporary;
+ bool using_filesort;
+
+ int print_explain(Explain_query *query, select_result_sink *output,
+ uint8 explain_flags);
+};
+
+
+/*
+ Explain structure for a UNION.
+
+ A UNION may or may not have "Using filesort".
+*/
+
+class Explain_union : public Explain_node
+{
+public:
+ enum explain_node_type get_type() { return EXPLAIN_UNION; }
+
+ int get_select_id()
+ {
+ DBUG_ASSERT(union_members.elements() > 0);
+ return union_members.at(0);
+ }
+ /*
+ Members of the UNION. Note: these are different from UNION's "children".
+ Example:
+
+ (select * from t1) union
+ (select * from t2) order by (select col1 from t3 ...)
+
+ here
+ - select-from-t1 and select-from-t2 are "union members",
+ - select-from-t3 is the only "child".
+ */
+ Dynamic_array<int> union_members;
+
+ void add_select(int select_no)
+ {
+ union_members.append(select_no);
+ }
+ int print_explain(Explain_query *query, select_result_sink *output,
+ uint8 explain_flags);
+
+ const char *fake_select_type;
+ bool using_filesort;
+};
+
+
+class Explain_update;
+class Explain_delete;
+class Explain_insert;
+
+/*
+ Explain structure for a query (i.e. a statement).
+
+ This should be able to survive when the query plan was deleted. Currently,
+ we do not intend for it survive until after query's MEM_ROOT is freed. It
+ does surivive freeing of query's items.
+
+ For reference, the process of post-query cleanup is as follows:
+
+ >dispatch_command
+ | >mysql_parse
+ | | ...
+ | | lex_end()
+ | | ...
+ | | >THD::cleanup_after_query
+ | | | ...
+ | | | free_items()
+ | | | ...
+ | | <THD::cleanup_after_query
+ | |
+ | <mysql_parse
+ |
+ | log_slow_statement()
+ |
+ | free_root()
+ |
+ >dispatch_command
+
+ That is, the order of actions is:
+ - free query's Items
+ - write to slow query log
+ - free query's MEM_ROOT
+
+*/
+
+class Explain_query : public Sql_alloc
+{
+public:
+ Explain_query(THD *thd);
+ ~Explain_query();
+
+ /* Add a new node */
+ void add_node(Explain_node *node);
+ void add_insert_plan(Explain_insert *insert_plan_arg);
+ void add_upd_del_plan(Explain_update *upd_del_plan_arg);
+
+ /* This will return a select, or a union */
+ Explain_node *get_node(uint select_id);
+
+ /* This will return a select (even if there is a union with this id) */
+ Explain_select *get_select(uint select_id);
+
+ Explain_union *get_union(uint select_id);
+
+ /* Produce a tabular EXPLAIN output */
+ int print_explain(select_result_sink *output, uint8 explain_flags);
+
+ /* Send tabular EXPLAIN to the client */
+ int send_explain(THD *thd);
+
+ /* Return tabular EXPLAIN output as a text string */
+ bool print_explain_str(THD *thd, String *out_str);
+
+ /* If true, at least part of EXPLAIN can be printed */
+ bool have_query_plan() { return insert_plan || upd_del_plan|| get_node(1) != NULL; }
+
+ void query_plan_ready();
+
+ MEM_ROOT *mem_root;
+private:
+ /* Explain_delete inherits from Explain_update */
+ Explain_update *upd_del_plan;
+
+ /* Query "plan" for INSERTs */
+ Explain_insert *insert_plan;
+
+ Dynamic_array<Explain_union*> unions;
+ Dynamic_array<Explain_select*> selects;
+
+ THD *thd; // for APC start/stop
+ bool apc_enabled;
+ /*
+ Debugging aid: count how many times add_node() was called. Ideally, it
+ should be one, we currently allow O(1) query plan saves for each
+ select or union. The goal is not to have O(#rows_in_some_table), which
+ is unacceptable.
+ */
+ longlong operations;
+};
+
+
+/*
+ Some of the tags have matching text. See extra_tag_text for text names, and
+ Explain_table_access::append_tag_name() for code to convert from tag form to text
+ form.
+*/
+enum explain_extra_tag
+{
+ ET_none= 0, /* not-a-tag */
+ ET_USING_INDEX_CONDITION,
+ ET_USING_INDEX_CONDITION_BKA,
+ ET_USING, /* For quick selects of various kinds */
+ ET_RANGE_CHECKED_FOR_EACH_RECORD,
+ ET_USING_WHERE_WITH_PUSHED_CONDITION,
+ ET_USING_WHERE,
+ ET_NOT_EXISTS,
+
+ ET_USING_INDEX,
+ ET_FULL_SCAN_ON_NULL_KEY,
+ ET_SKIP_OPEN_TABLE,
+ ET_OPEN_FRM_ONLY,
+ ET_OPEN_FULL_TABLE,
+
+ ET_SCANNED_0_DATABASES,
+ ET_SCANNED_1_DATABASE,
+ ET_SCANNED_ALL_DATABASES,
+
+ ET_USING_INDEX_FOR_GROUP_BY,
+
+ ET_USING_MRR, // does not print "Using mrr".
+
+ ET_DISTINCT,
+ ET_LOOSESCAN,
+ ET_START_TEMPORARY,
+ ET_END_TEMPORARY,
+ ET_FIRST_MATCH,
+
+ ET_USING_JOIN_BUFFER,
+
+ ET_CONST_ROW_NOT_FOUND,
+ ET_UNIQUE_ROW_NOT_FOUND,
+ ET_IMPOSSIBLE_ON_CONDITION,
+
+ ET_total
+};
+
+
+typedef struct st_explain_bka_type
+{
+ bool incremental;
+ const char *join_alg;
+ StringBuffer<64> mrr_type;
+
+} EXPLAIN_BKA_TYPE;
+
+
+/*
+ Data about how an index is used by some access method
+*/
+class Explain_index_use : public Sql_alloc
+{
+ char *key_name;
+ uint key_len;
+ /* will add #keyparts here if we implement EXPLAIN FORMAT=JSON */
+public:
+
+ void set(MEM_ROOT *root, const char *key_name_arg, uint key_len_arg)
+ {
+ if (key_name_arg)
+ {
+ size_t name_len= strlen(key_name_arg);
+ if ((key_name= (char*)alloc_root(root, name_len+1)))
+ memcpy(key_name, key_name_arg, name_len+1);
+ }
+ else
+ key_name= NULL;
+ key_len= key_len_arg;
+ }
+
+ inline const char *get_key_name() { return key_name; }
+ inline uint get_key_len() { return key_len; }
+};
+
+
+/*
+ QPF for quick range selects, as well as index_merge select
+*/
+class Explain_quick_select : public Sql_alloc
+{
+public:
+ Explain_quick_select(int quick_type_arg) : quick_type(quick_type_arg)
+ {}
+
+ const int quick_type;
+
+ /* This is used when quick_type == QUICK_SELECT_I::QS_TYPE_RANGE */
+ Explain_index_use range;
+
+ /* Used in all other cases */
+ List<Explain_quick_select> children;
+
+ void print_extra(String *str);
+ void print_key(String *str);
+ void print_key_len(String *str);
+private:
+ void print_extra_recursive(String *str);
+ const char *get_name_by_type();
+};
+
+
+/*
+ EXPLAIN data structure for a single JOIN_TAB.
+*/
+class Explain_table_access : public Sql_alloc
+{
+public:
+ void push_extra(enum explain_extra_tag extra_tag);
+
+ /* Internals */
+public:
+ /*
+ 0 means this tab is not inside SJM nest and should use Explain_select's id
+ other value means the tab is inside an SJM nest.
+ */
+ int sjm_nest_select_id;
+
+ /* id and 'select_type' are cared-of by the parent Explain_select */
+ StringBuffer<32> table_name;
+
+ enum join_type type;
+
+ StringBuffer<32> used_partitions;
+ bool used_partitions_set;
+
+ /* Empty string means "NULL" will be printed */
+ StringBuffer<32> possible_keys_str;
+
+ /*
+ Index use: key name and length.
+ Note: that when one is accessing I_S tables, those may show use of
+ non-existant indexes.
+
+ key.key_name == NULL means 'NULL' will be shown in tabular output.
+ key.key_len == (uint)-1 means 'NULL' will be shown in tabular output.
+ */
+ Explain_index_use key;
+
+ /*
+ when type==JT_HASH_NEXT, 'key' stores the hash join pseudo-key.
+ hash_next_key stores the table's key.
+ */
+ Explain_index_use hash_next_key;
+
+ bool ref_set; /* not set means 'NULL' should be printed */
+ StringBuffer<32> ref;
+
+ bool rows_set; /* not set means 'NULL' should be printed */
+ ha_rows rows;
+
+ bool filtered_set; /* not set means 'NULL' should be printed */
+ double filtered;
+
+ /*
+ Contents of the 'Extra' column. Some are converted into strings, some have
+ parameters, values for which are stored below.
+ */
+ Dynamic_array<enum explain_extra_tag> extra_tags;
+
+ // Valid if ET_USING tag is present
+ Explain_quick_select *quick_info;
+
+ // Valid if ET_USING_INDEX_FOR_GROUP_BY is present
+ bool loose_scan_is_scanning;
+
+ // valid with ET_RANGE_CHECKED_FOR_EACH_RECORD
+ key_map range_checked_map;
+
+ // valid with ET_USING_MRR
+ StringBuffer<32> mrr_type;
+
+ // valid with ET_USING_JOIN_BUFFER
+ EXPLAIN_BKA_TYPE bka_type;
+
+ StringBuffer<32> firstmatch_table_name;
+
+ int print_explain(select_result_sink *output, uint8 explain_flags,
+ uint select_id, const char *select_type,
+ bool using_temporary, bool using_filesort);
+private:
+ void append_tag_name(String *str, enum explain_extra_tag tag);
+};
+
+
+/*
+ EXPLAIN structure for single-table UPDATE.
+
+ This is similar to Explain_table_access, except that it is more restrictive.
+ Also, it can have UPDATE operation options, but currently there aren't any.
+*/
+
+class Explain_update : public Explain_node
+{
+public:
+ virtual enum explain_node_type get_type() { return EXPLAIN_UPDATE; }
+ virtual int get_select_id() { return 1; /* always root */ }
+
+ const char *select_type;
+
+ StringBuffer<32> used_partitions;
+ bool used_partitions_set;
+
+ bool impossible_where;
+ bool no_partitions;
+ StringBuffer<64> table_name;
+
+ enum join_type jtype;
+ StringBuffer<128> possible_keys_line;
+ StringBuffer<128> key_str;
+ StringBuffer<128> key_len_str;
+ StringBuffer<64> mrr_type;
+
+ Explain_quick_select *quick_info;
+
+ bool using_where;
+ ha_rows rows;
+
+ bool using_filesort;
+ bool using_io_buffer;
+
+ virtual int print_explain(Explain_query *query, select_result_sink *output,
+ uint8 explain_flags);
+};
+
+
+/*
+ EXPLAIN data structure for an INSERT.
+
+ At the moment this doesn't do much as we don't really have any query plans
+ for INSERT statements.
+*/
+
+class Explain_insert : public Explain_node
+{
+public:
+ StringBuffer<64> table_name;
+
+ enum explain_node_type get_type() { return EXPLAIN_INSERT; }
+ int get_select_id() { return 1; /* always root */ }
+
+ int print_explain(Explain_query *query, select_result_sink *output,
+ uint8 explain_flags);
+};
+
+
+/*
+ EXPLAIN data of a single-table DELETE.
+*/
+
+class Explain_delete: public Explain_update
+{
+public:
+ /*
+ TRUE means we're going to call handler->delete_all_rows() and not read any
+ rows.
+ */
+ bool deleting_all_rows;
+
+ virtual enum explain_node_type get_type() { return EXPLAIN_DELETE; }
+ virtual int get_select_id() { return 1; /* always root */ }
+
+ virtual int print_explain(Explain_query *query, select_result_sink *output,
+ uint8 explain_flags);
+};
+
+
diff --git a/sql/sql_expression_cache.cc b/sql/sql_expression_cache.cc
index f3c96ee2d2f..824d21eea20 100644
--- a/sql/sql_expression_cache.cc
+++ b/sql/sql_expression_cache.cc
@@ -13,6 +13,7 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+#include <my_global.h>
#include "sql_base.h"
#include "sql_select.h"
#include "sql_expression_cache.h"
@@ -257,7 +258,7 @@ my_bool Expression_cache_tmptable::put_value(Item *value)
}
*(items.head_ref())= value;
- fill_record(table_thd, cache_table->field, items, TRUE, TRUE);
+ fill_record(table_thd, cache_table, cache_table->field, items, TRUE, TRUE);
if (table_thd->is_error())
goto err;;
diff --git a/sql/sql_get_diagnostics.cc b/sql/sql_get_diagnostics.cc
new file mode 100644
index 00000000000..8b0d86aa7d1
--- /dev/null
+++ b/sql/sql_get_diagnostics.cc
@@ -0,0 +1,342 @@
+/* Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02111-1307 USA */
+
+#include "sql_list.h" // Sql_alloc, List, List_iterator
+#include "sql_cmd.h" // Sql_cmd
+#include "sql_class.h" // Diagnostics_area
+#include "sql_get_diagnostics.h" // Sql_cmd_get_diagnostics
+
+/**
+ Execute this GET DIAGNOSTICS statement.
+
+ @param thd The current thread.
+
+ @remark Errors or warnings occurring during the execution of the GET
+ DIAGNOSTICS statement should not affect the diagnostics area
+ of a previous statement as the diagnostics information there
+ would be wiped out. Thus, in order to preserve the contents
+ of the diagnostics area from which information is being
+ retrieved, the GET DIAGNOSTICS statement is executed under
+ a separate diagnostics area. If any errors or warnings occur
+ during the execution of the GET DIAGNOSTICS statement, these
+ error or warnings (conditions) are appended to the list of
+ the original diagnostics area. The only exception to this is
+ fatal errors, which must always cause the statement to fail.
+
+ @retval false on success.
+ @retval true on error
+*/
+
+bool
+Sql_cmd_get_diagnostics::execute(THD *thd)
+{
+ bool rv;
+ Diagnostics_area new_stmt_da(thd->query_id, false, true);
+ Diagnostics_area *save_stmt_da= thd->get_stmt_da();
+ DBUG_ENTER("Sql_cmd_get_diagnostics::execute");
+
+ /* Disable the unneeded read-only mode of the original DA. */
+ save_stmt_da->set_warning_info_read_only(false);
+
+ /* Set new diagnostics area, execute statement and restore. */
+ thd->set_stmt_da(&new_stmt_da);
+ rv= m_info->aggregate(thd, save_stmt_da);
+ thd->set_stmt_da(save_stmt_da);
+
+ /* Bail out early if statement succeeded. */
+ if (! rv)
+ {
+ thd->get_stmt_da()->set_ok_status(0, 0, NULL);
+ DBUG_RETURN(false);
+ }
+
+ /* Statement failed, retrieve the error information for propagation. */
+ uint sql_errno= new_stmt_da.sql_errno();
+ const char *message= new_stmt_da.message();
+ const char *sqlstate= new_stmt_da.get_sqlstate();
+
+ /* In case of a fatal error, set it into the original DA.*/
+ if (thd->is_fatal_error)
+ {
+ save_stmt_da->set_error_status(sql_errno, message, sqlstate, NULL);
+ DBUG_RETURN(true);
+ }
+
+ /* Otherwise, just append the new error as a exception condition. */
+ save_stmt_da->push_warning(thd, sql_errno, sqlstate,
+ Sql_condition::WARN_LEVEL_ERROR,
+ message);
+
+ /* Appending might have failed. */
+ if (! (rv= thd->is_error()))
+ thd->get_stmt_da()->set_ok_status(0, 0, NULL);
+
+ DBUG_RETURN(rv);
+}
+
+
+/**
+ Set a value for this item.
+
+ @param thd The current thread.
+ @param value The obtained value.
+
+ @retval false on success.
+ @retval true on error.
+*/
+
+bool
+Diagnostics_information_item::set_value(THD *thd, Item **value)
+{
+ bool rv;
+ Settable_routine_parameter *srp;
+ DBUG_ENTER("Diagnostics_information_item::set_value");
+
+ /* Get a settable reference to the target. */
+ srp= m_target->get_settable_routine_parameter();
+
+ DBUG_ASSERT(srp);
+
+ /* Set variable/parameter value. */
+ rv= srp->set_value(thd, thd->spcont, value);
+
+ DBUG_RETURN(rv);
+}
+
+
+/**
+ Obtain statement information in the context of a given diagnostics area.
+
+ @param thd The current thread.
+ @param da The diagnostics area.
+
+ @retval false on success.
+ @retval true on error
+*/
+
+bool
+Statement_information::aggregate(THD *thd, const Diagnostics_area *da)
+{
+ bool rv= false;
+ Statement_information_item *stmt_info_item;
+ List_iterator<Statement_information_item> it(*m_items);
+ DBUG_ENTER("Statement_information::aggregate");
+
+ /*
+ Each specified target gets the value of each given
+ information item obtained from the diagnostics area.
+ */
+ while ((stmt_info_item= it++))
+ {
+ if ((rv= evaluate(thd, stmt_info_item, da)))
+ break;
+ }
+
+ DBUG_RETURN(rv);
+}
+
+
+/**
+ Obtain the value of this statement information item in the context of
+ a given diagnostics area.
+
+ @param thd The current thread.
+ @param da The diagnostics area.
+
+ @retval Item representing the value.
+ @retval NULL on error.
+*/
+
+Item *
+Statement_information_item::get_value(THD *thd, const Diagnostics_area *da)
+{
+ Item *value= NULL;
+ DBUG_ENTER("Statement_information_item::get_value");
+
+ switch (m_name)
+ {
+ /*
+ The number of condition areas that have information. That is,
+ the number of errors and warnings within the diagnostics area.
+ */
+ case NUMBER:
+ {
+ ulong count= da->cond_count();
+ value= new (thd->mem_root) Item_uint(count);
+ break;
+ }
+ /*
+ Number that shows how many rows were directly affected by
+ a data-change statement (INSERT, UPDATE, DELETE, MERGE,
+ REPLACE, LOAD).
+ */
+ case ROW_COUNT:
+ value= new (thd->mem_root) Item_int(thd->get_row_count_func());
+ break;
+ }
+
+ DBUG_RETURN(value);
+}
+
+
+/**
+ Obtain condition information in the context of a given diagnostics area.
+
+ @param thd The current thread.
+ @param da The diagnostics area.
+
+ @retval false on success.
+ @retval true on error
+*/
+
+bool
+Condition_information::aggregate(THD *thd, const Diagnostics_area *da)
+{
+ bool rv= false;
+ longlong cond_number;
+ const Sql_condition *cond= NULL;
+ Condition_information_item *cond_info_item;
+ Diagnostics_area::Sql_condition_iterator it_conds= da->sql_conditions();
+ List_iterator_fast<Condition_information_item> it_items(*m_items);
+ DBUG_ENTER("Condition_information::aggregate");
+
+ /* Prepare the expression for evaluation. */
+ if (!m_cond_number_expr->fixed &&
+ m_cond_number_expr->fix_fields(thd, &m_cond_number_expr))
+ DBUG_RETURN(true);
+
+ cond_number= m_cond_number_expr->val_int();
+
+ /*
+ Limit to the number of available conditions. Warning_info::warn_count()
+ is not used because it indicates the number of condition regardless of
+ @@max_error_count, which prevents conditions from being pushed, but not
+ counted.
+ */
+ if (cond_number < 1 || (ulonglong) cond_number > da->cond_count())
+ {
+ my_error(ER_DA_INVALID_CONDITION_NUMBER, MYF(0));
+ DBUG_RETURN(true);
+ }
+
+ /* Advance to the requested condition. */
+ while (cond_number--)
+ cond= it_conds++;
+
+ DBUG_ASSERT(cond);
+
+ /* Evaluate the requested information in the context of the condition. */
+ while ((cond_info_item= it_items++))
+ {
+ if ((rv= evaluate(thd, cond_info_item, cond)))
+ break;
+ }
+
+ DBUG_RETURN(rv);
+}
+
+
+/**
+ Create an UTF-8 string item to represent a condition item string.
+
+ @remark The string might not have a associated charset. For example,
+ this can be the case if the server does not or fails to process
+ the error message file.
+
+ @remark See "Design notes about Sql_condition::m_message_text." in sql_error.cc
+
+ @return Pointer to an string item, NULL on failure.
+*/
+
+Item *
+Condition_information_item::make_utf8_string_item(THD *thd, const String *str)
+{
+ /* Default is utf8 character set and utf8_general_ci collation. */
+ CHARSET_INFO *to_cs= &my_charset_utf8_general_ci;
+ /* If a charset was not set, assume that no conversion is needed. */
+ CHARSET_INFO *from_cs= str->charset() ? str->charset() : to_cs;
+ String tmp(str->ptr(), str->length(), from_cs);
+ /* If necessary, convert the string (ignoring errors), then copy it over. */
+ uint conv_errors;
+ return new Item_string(&tmp, to_cs, &conv_errors,
+ DERIVATION_COERCIBLE, MY_REPERTOIRE_UNICODE30);
+}
+
+
+/**
+ Obtain the value of this condition information item in the context of
+ a given condition.
+
+ @param thd The current thread.
+ @param da The diagnostics area.
+
+ @retval Item representing the value.
+ @retval NULL on error.
+*/
+
+Item *
+Condition_information_item::get_value(THD *thd, const Sql_condition *cond)
+{
+ String str;
+ Item *value= NULL;
+ DBUG_ENTER("Condition_information_item::get_value");
+
+ switch (m_name)
+ {
+ case CLASS_ORIGIN:
+ value= make_utf8_string_item(thd, &(cond->m_class_origin));
+ break;
+ case SUBCLASS_ORIGIN:
+ value= make_utf8_string_item(thd, &(cond->m_subclass_origin));
+ break;
+ case CONSTRAINT_CATALOG:
+ value= make_utf8_string_item(thd, &(cond->m_constraint_catalog));
+ break;
+ case CONSTRAINT_SCHEMA:
+ value= make_utf8_string_item(thd, &(cond->m_constraint_schema));
+ break;
+ case CONSTRAINT_NAME:
+ value= make_utf8_string_item(thd, &(cond->m_constraint_name));
+ break;
+ case CATALOG_NAME:
+ value= make_utf8_string_item(thd, &(cond->m_catalog_name));
+ break;
+ case SCHEMA_NAME:
+ value= make_utf8_string_item(thd, &(cond->m_schema_name));
+ break;
+ case TABLE_NAME:
+ value= make_utf8_string_item(thd, &(cond->m_table_name));
+ break;
+ case COLUMN_NAME:
+ value= make_utf8_string_item(thd, &(cond->m_column_name));
+ break;
+ case CURSOR_NAME:
+ value= make_utf8_string_item(thd, &(cond->m_cursor_name));
+ break;
+ case MESSAGE_TEXT:
+ value= make_utf8_string_item(thd, &(cond->m_message_text));
+ break;
+ case MYSQL_ERRNO:
+ value= new (thd->mem_root) Item_uint(cond->m_sql_errno);
+ break;
+ case RETURNED_SQLSTATE:
+ str.set_ascii(cond->get_sqlstate(), strlen(cond->get_sqlstate()));
+ value= make_utf8_string_item(thd, &str);
+ break;
+ }
+
+ DBUG_RETURN(value);
+}
+
diff --git a/sql/sql_get_diagnostics.h b/sql/sql_get_diagnostics.h
new file mode 100644
index 00000000000..f34820757f5
--- /dev/null
+++ b/sql/sql_get_diagnostics.h
@@ -0,0 +1,318 @@
+/* Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02111-1307 USA */
+
+#ifndef SQL_GET_DIAGNOSTICS_H
+#define SQL_GET_DIAGNOSTICS_H
+
+/** Diagnostics information forward reference. */
+class Diagnostics_information;
+
+
+/**
+ Sql_cmd_get_diagnostics represents a GET DIAGNOSTICS statement.
+
+ The GET DIAGNOSTICS statement retrieves exception or completion
+ condition information from a diagnostics area, usually pertaining
+ to the last non-diagnostic SQL statement that was executed.
+*/
+class Sql_cmd_get_diagnostics : public Sql_cmd
+{
+public:
+ /**
+ Constructor, used to represent a GET DIAGNOSTICS statement.
+
+ @param info Diagnostics information to be obtained.
+ */
+ Sql_cmd_get_diagnostics(Diagnostics_information *info)
+ : m_info(info)
+ {}
+
+ virtual enum_sql_command sql_command_code() const
+ {
+ return SQLCOM_GET_DIAGNOSTICS;
+ }
+
+ virtual bool execute(THD *thd);
+
+private:
+ /** The information to be obtained. */
+ Diagnostics_information *m_info;
+};
+
+
+/**
+ Represents the diagnostics information to be obtained.
+
+ Diagnostic information is made available through statement
+ information and condition information items.
+*/
+class Diagnostics_information : public Sql_alloc
+{
+public:
+ /**
+ Which diagnostics area to access.
+ Only CURRENT is supported for now.
+ */
+ enum Which_area
+ {
+ /** Access the first diagnostics area. */
+ CURRENT_AREA
+ };
+
+ /** Set which diagnostics area to access. */
+ void set_which_da(Which_area area)
+ { m_area= area; }
+
+ /** Get which diagnostics area to access. */
+ Which_area get_which_da(void) const
+ { return m_area; }
+
+ /**
+ Aggregate diagnostics information.
+
+ @param thd The current thread.
+ @param da The diagnostics area.
+
+ @retval false on success.
+ @retval true on error
+ */
+ virtual bool aggregate(THD *thd, const Diagnostics_area *da) = 0;
+
+protected:
+ /**
+ Diagnostics_information objects are allocated in thd->mem_root.
+ Do not rely on the destructor for any cleanup.
+ */
+ virtual ~Diagnostics_information()
+ {
+ DBUG_ASSERT(false);
+ }
+
+ /**
+ Evaluate a diagnostics information item in a specific context.
+
+ @param thd The current thread.
+ @param diag_item The diagnostics information item.
+ @param ctx The context to evaluate the item.
+
+ @retval false on success.
+ @retval true on error.
+ */
+ template <typename Diag_item, typename Context>
+ bool evaluate(THD *thd, Diag_item *diag_item, Context ctx)
+ {
+ Item *value;
+
+ /* Get this item's value. */
+ if (! (value= diag_item->get_value(thd, ctx)))
+ return true;
+
+ /* Set variable/parameter value. */
+ return diag_item->set_value(thd, &value);
+ }
+
+private:
+ /** Which diagnostics area to access. */
+ Which_area m_area;
+};
+
+
+/**
+ A diagnostics information item. Used to associate a specific
+ diagnostics information item to a target variable.
+*/
+class Diagnostics_information_item : public Sql_alloc
+{
+public:
+ /**
+ Set a value for this item.
+
+ @param thd The current thread.
+ @param value The obtained value.
+
+ @retval false on success.
+ @retval true on error.
+ */
+ bool set_value(THD *thd, Item **value);
+
+protected:
+ /**
+ Constructor, used to represent a diagnostics information item.
+
+ @param target A target that gets the value of this item.
+ */
+ Diagnostics_information_item(Item *target)
+ : m_target(target)
+ {}
+
+ /**
+ Diagnostics_information_item objects are allocated in thd->mem_root.
+ Do not rely on the destructor for any cleanup.
+ */
+ virtual ~Diagnostics_information_item()
+ {
+ DBUG_ASSERT(false);
+ }
+
+private:
+ /** The target variable that will receive the value of this item. */
+ Item *m_target;
+};
+
+
+/**
+ A statement information item.
+*/
+class Statement_information_item : public Diagnostics_information_item
+{
+public:
+ /** The name of a statement information item. */
+ enum Name
+ {
+ NUMBER,
+ ROW_COUNT
+ };
+
+ /**
+ Constructor, used to represent a statement information item.
+
+ @param name The name of this item.
+ @param target A target that gets the value of this item.
+ */
+ Statement_information_item(Name name, Item *target)
+ : Diagnostics_information_item(target), m_name(name)
+ {}
+
+ /** Obtain value of this statement information item. */
+ Item *get_value(THD *thd, const Diagnostics_area *da);
+
+private:
+ /** The name of this statement information item. */
+ Name m_name;
+};
+
+
+/**
+ Statement information.
+
+ @remark Provides information about the execution of a statement.
+*/
+class Statement_information : public Diagnostics_information
+{
+public:
+ /**
+ Constructor, used to represent the statement information of a
+ GET DIAGNOSTICS statement.
+
+ @param items List of requested statement information items.
+ */
+ Statement_information(List<Statement_information_item> *items)
+ : m_items(items)
+ {}
+
+ /** Obtain statement information in the context of a diagnostics area. */
+ bool aggregate(THD *thd, const Diagnostics_area *da);
+
+private:
+ /* List of statement information items. */
+ List<Statement_information_item> *m_items;
+};
+
+
+/**
+ A condition information item.
+*/
+class Condition_information_item : public Diagnostics_information_item
+{
+public:
+ /**
+ The name of a condition information item.
+ */
+ enum Name
+ {
+ CLASS_ORIGIN,
+ SUBCLASS_ORIGIN,
+ CONSTRAINT_CATALOG,
+ CONSTRAINT_SCHEMA,
+ CONSTRAINT_NAME,
+ CATALOG_NAME,
+ SCHEMA_NAME,
+ TABLE_NAME,
+ COLUMN_NAME,
+ CURSOR_NAME,
+ MESSAGE_TEXT,
+ MYSQL_ERRNO,
+ RETURNED_SQLSTATE
+ };
+
+ /**
+ Constructor, used to represent a condition information item.
+
+ @param name The name of this item.
+ @param target A target that gets the value of this item.
+ */
+ Condition_information_item(Name name, Item *target)
+ : Diagnostics_information_item(target), m_name(name)
+ {}
+
+ /** Obtain value of this condition information item. */
+ Item *get_value(THD *thd, const Sql_condition *cond);
+
+private:
+ /** The name of this condition information item. */
+ Name m_name;
+
+ /** Create an string item to represent a condition item string. */
+ Item *make_utf8_string_item(THD *thd, const String *str);
+};
+
+
+/**
+ Condition information.
+
+ @remark Provides information about conditions raised during the
+ execution of a statement.
+*/
+class Condition_information : public Diagnostics_information
+{
+public:
+ /**
+ Constructor, used to represent the condition information of a
+ GET DIAGNOSTICS statement.
+
+ @param cond_number_expr Number that identifies the diagnostic condition.
+ @param items List of requested condition information items.
+ */
+ Condition_information(Item *cond_number_expr,
+ List<Condition_information_item> *items)
+ : m_cond_number_expr(cond_number_expr), m_items(items)
+ {}
+
+ /** Obtain condition information in the context of a diagnostics area. */
+ bool aggregate(THD *thd, const Diagnostics_area *da);
+
+private:
+ /**
+ Number that identifies the diagnostic condition for which
+ information is to be obtained.
+ */
+ Item *m_cond_number_expr;
+
+ /** List of condition information items. */
+ List<Condition_information_item> *m_items;
+};
+
+#endif
+
diff --git a/sql/sql_handler.cc b/sql/sql_handler.cc
index f5c79e59bf2..5fc7c20d409 100644
--- a/sql/sql_handler.cc
+++ b/sql/sql_handler.cc
@@ -52,9 +52,9 @@
cursor points at the first record).
*/
+#include <my_global.h>
#include "sql_priv.h"
#include "sql_handler.h"
-#include "unireg.h" // REQUIRED: for other includes
#include "sql_base.h" // close_thread_tables
#include "lock.h" // mysql_unlock_tables
#include "key.h" // key_copy
@@ -171,7 +171,7 @@ static void mysql_ha_close_table(SQL_HANDLER *handler)
table->file->ha_index_or_rnd_end();
table->open_by_handler= 0;
- (void) close_thread_table(thd, &table);
+ close_thread_table(thd, &table);
thd->mdl_context.release_lock(handler->mdl_request.ticket);
}
else
@@ -294,7 +294,8 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, SQL_HANDLER *reopen)
open_ltable() or open_table() because we would like to be able
to open a temporary table.
*/
- error= open_tables(thd, &tables, &counter, 0);
+ error= (open_temporary_tables(thd, tables) ||
+ open_tables(thd, &tables, &counter, 0));
if (error)
goto err;
@@ -304,7 +305,8 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, SQL_HANDLER *reopen)
/* There can be only one table in '*tables'. */
if (! (table->file->ha_table_flags() & HA_CAN_SQL_HANDLER))
{
- my_error(ER_ILLEGAL_HA, MYF(0), tables->alias);
+ my_error(ER_ILLEGAL_HA, MYF(0), table->file->table_type(),
+ table->s->db.str, table->s->table_name.str);
goto err;
}
@@ -323,7 +325,7 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, SQL_HANDLER *reopen)
/* copy data to sql_handler */
if (!(sql_handler= new SQL_HANDLER(thd)))
goto err;
- init_alloc_root(&sql_handler->mem_root, 1024, 0);
+ init_alloc_root(&sql_handler->mem_root, 1024, 0, MYF(MY_THREAD_SPECIFIC));
sql_handler->db.length= strlen(tables->db);
sql_handler->table_name.length= strlen(tables->table_name);
@@ -501,9 +503,9 @@ public:
bool handle_condition(THD *thd,
uint sql_errno,
const char *sqlstate,
- MYSQL_ERROR::enum_warning_level level,
+ Sql_condition::enum_warning_level level,
const char* msg,
- MYSQL_ERROR **cond_hdl);
+ Sql_condition **cond_hdl);
bool need_reopen() const { return m_need_reopen; };
void init() { m_need_reopen= FALSE; };
@@ -522,9 +524,9 @@ Sql_handler_lock_error_handler::
handle_condition(THD *thd,
uint sql_errno,
const char *sqlstate,
- MYSQL_ERROR::enum_warning_level level,
+ Sql_condition::enum_warning_level level,
const char* msg,
- MYSQL_ERROR **cond_hdl)
+ Sql_condition **cond_hdl)
{
*cond_hdl= NULL;
if (sql_errno == ER_LOCK_ABORTED)
@@ -639,9 +641,10 @@ mysql_ha_fix_cond_and_key(SQL_HANDLER *handler,
key_part_map keypart_map;
uint key_len;
- if (key_expr->elements > keyinfo->key_parts)
+ if (key_expr->elements > keyinfo->user_defined_key_parts)
{
- my_error(ER_TOO_MANY_KEY_PARTS, MYF(0), keyinfo->key_parts);
+ my_error(ER_TOO_MANY_KEY_PARTS, MYF(0),
+ keyinfo->user_defined_key_parts);
return 1;
}
for (keypart_map= key_len=0 ; (item=it_ke++) ; key_part++)
@@ -904,7 +907,8 @@ retry:
break;
}
default:
- my_message(ER_ILLEGAL_HA, ER(ER_ILLEGAL_HA), MYF(0));
+ my_error(ER_ILLEGAL_HA, MYF(0), table->file->table_type(),
+ table->s->db.str, table->s->table_name.str);
goto err;
}
@@ -1111,8 +1115,6 @@ void mysql_ha_flush(THD *thd)
SQL_HANDLER *hash_tables;
DBUG_ENTER("mysql_ha_flush");
- mysql_mutex_assert_not_owner(&LOCK_open);
-
/*
Don't try to flush open HANDLERs when we're working with
system tables. The main MDL context is backed up and we can't
@@ -1131,7 +1133,7 @@ void mysql_ha_flush(THD *thd)
((hash_tables->table->mdl_ticket &&
hash_tables->table->mdl_ticket->has_pending_conflicting_lock()) ||
(!hash_tables->table->s->tmp_table &&
- hash_tables->table->s->has_old_version())))
+ hash_tables->table->s->tdc.flushed)))
mysql_ha_close_table(hash_tables);
}
diff --git a/sql/sql_help.cc b/sql/sql_help.cc
index 844810af0f4..afeb9395a55 100644
--- a/sql/sql_help.cc
+++ b/sql/sql_help.cc
@@ -13,6 +13,7 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */
+#include <my_global.h>
#include "sql_priv.h"
#include "unireg.h"
#include "sql_help.h"
@@ -626,7 +627,7 @@ SQL_SELECT *prepare_select_for_name(THD *thd, const char *mask, uint mlen,
{
Item *cond= new Item_func_like(new Item_field(pfname),
new Item_string(mask,mlen,pfname->charset()),
- new Item_string("\\",1,&my_charset_latin1),
+ new Item_string_ascii("\\"),
FALSE);
if (thd->is_fatal_error)
return 0; // OOM
diff --git a/sql/sql_hset.h b/sql/sql_hset.h
index f3a1467737f..dc3bd487ce5 100644
--- a/sql/sql_hset.h
+++ b/sql/sql_hset.h
@@ -23,19 +23,19 @@
A type-safe wrapper around mysys HASH.
*/
-template <typename T, my_hash_get_key K>
+template <typename T>
class Hash_set
{
public:
- typedef T Value_type;
enum { START_SIZE= 8 };
/**
Constructs an empty hash. Does not allocate memory, it is done upon
the first insert. Thus does not cause or return errors.
*/
- Hash_set()
+ Hash_set(uchar *(*K)(const T *, size_t *, my_bool))
{
my_hash_clear(&m_hash);
+ m_hash.get_key= (my_hash_get_key)K;
}
/**
Destroy the hash by freeing the buckets table. Does
@@ -56,13 +56,19 @@ public:
*/
bool insert(T *value)
{
- my_hash_init_opt(&m_hash, &my_charset_bin, START_SIZE, 0, 0, K, 0, MYF(0));
+ my_hash_init_opt(&m_hash, &my_charset_bin, START_SIZE, 0, 0,
+ m_hash.get_key, 0, MYF(0));
size_t key_len;
- const uchar *key= K(reinterpret_cast<uchar*>(value), &key_len, FALSE);
- if (my_hash_search(&m_hash, key, key_len) == NULL)
- return my_hash_insert(&m_hash, reinterpret_cast<uchar *>(value));
+ uchar *v= reinterpret_cast<uchar *>(value);
+ const uchar *key= m_hash.get_key(v, &key_len, FALSE);
+ if (find(key, key_len) == NULL)
+ return my_hash_insert(&m_hash, v);
return FALSE;
}
+ T *find(const void *key, size_t klen) const
+ {
+ return (T*)my_hash_search(&m_hash, reinterpret_cast<const uchar *>(key), klen);
+ }
/** Is this hash set empty? */
bool is_empty() const { return m_hash.records == 0; }
/** Returns the number of unique elements. */
diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc
index 8e8745f322f..c4dc41f7626 100644
--- a/sql/sql_insert.cc
+++ b/sql/sql_insert.cc
@@ -56,9 +56,8 @@
*/
-#include "my_global.h" /* NO_EMBEDDED_ACCESS_CHECKS */
+#include <my_global.h> /* NO_EMBEDDED_ACCESS_CHECKS */
#include "sql_priv.h"
-#include "unireg.h" // REQUIRED: for other includes
#include "sql_insert.h"
#include "sql_update.h" // compare_record
#include "sql_base.h" // close_thread_tables
@@ -148,9 +147,11 @@ bool check_view_single_update(List<Item> &fields, List<Item> *values,
if (view->check_single_table(&tbl, tables, view) || tbl == 0)
goto error;
+ /* view->table should have been set in mysql_derived_merge_for_insert */
+ DBUG_ASSERT(view->table);
+
/*
- A buffer for the insert values was allocated for the merged view.
- Use it.
+ Use buffer for the insert values that was allocated for the merged view.
*/
tbl->table->insert_values= view->table->insert_values;
view->table= tbl->table;
@@ -184,10 +185,6 @@ error:
@param fields_and_values_from_different_maps If 'values' are allowed to
refer to other tables than those of 'fields'
@param map See check_view_single_update
- NOTE
- Clears TIMESTAMP_AUTO_SET_ON_INSERT from table->timestamp_field_type
- or leaves it as is, depending on if timestamp should be updated or
- not.
@returns 0 if success, -1 if error
*/
@@ -199,11 +196,12 @@ static int check_insert_fields(THD *thd, TABLE_LIST *table_list,
table_map *map)
{
TABLE *table= table_list->table;
+ DBUG_ENTER("check_insert_fields");
if (!table_list->single_table_updatable())
{
my_error(ER_NON_INSERTABLE_TABLE, MYF(0), table_list->alias, "INSERT");
- return -1;
+ DBUG_RETURN(-1);
}
if (fields.elements == 0 && values.elements != 0)
@@ -212,21 +210,19 @@ static int check_insert_fields(THD *thd, TABLE_LIST *table_list,
{
my_error(ER_VIEW_NO_INSERT_FIELD_LIST, MYF(0),
table_list->view_db.str, table_list->view_name.str);
- return -1;
+ DBUG_RETURN(-1);
}
if (values.elements != table->s->fields)
{
my_error(ER_WRONG_VALUE_COUNT_ON_ROW, MYF(0), 1L);
- return -1;
+ DBUG_RETURN(-1);
}
#ifndef NO_EMBEDDED_ACCESS_CHECKS
Field_iterator_table_ref field_it;
field_it.set(table_list);
if (check_grant_all_columns(thd, INSERT_ACL, &field_it))
- return -1;
+ DBUG_RETURN(-1);
#endif
- clear_timestamp_auto_bits(table->timestamp_field_type,
- TIMESTAMP_AUTO_SET_ON_INSERT);
/*
No fields are provided so all fields must be provided in the values.
Thus we set all bits in the write set.
@@ -243,7 +239,7 @@ static int check_insert_fields(THD *thd, TABLE_LIST *table_list,
if (fields.elements != values.elements)
{
my_error(ER_WRONG_VALUE_COUNT_ON_ROW, MYF(0), 1L);
- return -1;
+ DBUG_RETURN(-1);
}
thd->dup_field= 0;
@@ -269,7 +265,7 @@ static int check_insert_fields(THD *thd, TABLE_LIST *table_list,
thd->lex->select_lex.no_wrap_view_item= FALSE;
if (res)
- return -1;
+ DBUG_RETURN(-1);
if (table_list->is_view() && table_list->is_merged_derived())
{
@@ -277,27 +273,17 @@ static int check_insert_fields(THD *thd, TABLE_LIST *table_list,
fields_and_values_from_different_maps ?
(List<Item>*) 0 : &values,
table_list, map, true))
- return -1;
+ DBUG_RETURN(-1);
table= table_list->table;
}
if (check_unique && thd->dup_field)
{
my_error(ER_FIELD_SPECIFIED_TWICE, MYF(0), thd->dup_field->field_name);
- return -1;
- }
- if (table->timestamp_field) // Don't automaticly set timestamp if used
- {
- if (bitmap_is_set(table->write_set,
- table->timestamp_field->field_index))
- clear_timestamp_auto_bits(table->timestamp_field_type,
- TIMESTAMP_AUTO_SET_ON_INSERT);
- else
- {
- bitmap_set_bit(table->write_set,
- table->timestamp_field->field_index);
- }
+ DBUG_RETURN(-1);
}
+ if (table->default_field)
+ table->mark_default_fields_for_write();
}
/* Mark virtual columns used in the insert statement */
if (table->vfield)
@@ -312,10 +298,10 @@ static int check_insert_fields(THD *thd, TABLE_LIST *table_list,
check_view_insertability(thd, table_list)))
{
my_error(ER_NON_INSERTABLE_TABLE, MYF(0), table_list->alias, "INSERT");
- return -1;
+ DBUG_RETURN(-1);
}
- return 0;
+ DBUG_RETURN(0);
}
@@ -330,9 +316,9 @@ static int check_insert_fields(THD *thd, TABLE_LIST *table_list,
refer to other tables than those of 'update_fields'
@param map See check_view_single_update
- NOTE
- If the update fields include the timestamp field,
- remove TIMESTAMP_AUTO_SET_ON_UPDATE from table->timestamp_field_type.
+ @note
+ If the update fields include an autoinc field, set the
+ table->next_number_field_updated flag.
@returns 0 if success, -1 if error
*/
@@ -344,21 +330,9 @@ static int check_update_fields(THD *thd, TABLE_LIST *insert_table_list,
table_map *map)
{
TABLE *table= insert_table_list->table;
- my_bool timestamp_mark;
my_bool autoinc_mark;
- LINT_INIT(timestamp_mark);
LINT_INIT(autoinc_mark);
- if (table->timestamp_field)
- {
- /*
- Unmark the timestamp field so that we can check if this is modified
- by update_fields
- */
- timestamp_mark= bitmap_test_and_clear(table->write_set,
- table->timestamp_field->field_index);
- }
-
table->next_number_field_updated= FALSE;
if (table->found_next_number_field)
@@ -384,17 +358,8 @@ static int check_update_fields(THD *thd, TABLE_LIST *insert_table_list,
insert_table_list, map, false))
return -1;
- if (table->timestamp_field)
- {
- /* Don't set timestamp column if this is modified. */
- if (bitmap_is_set(table->write_set,
- table->timestamp_field->field_index))
- clear_timestamp_auto_bits(table->timestamp_field_type,
- TIMESTAMP_AUTO_SET_ON_UPDATE);
- if (timestamp_mark)
- bitmap_set_bit(table->write_set,
- table->timestamp_field->field_index);
- }
+ if (table->default_field)
+ table->mark_default_fields_for_write();
if (table->found_next_number_field)
{
@@ -410,48 +375,6 @@ static int check_update_fields(THD *thd, TABLE_LIST *insert_table_list,
return 0;
}
-/*
- Prepare triggers for INSERT-like statement.
-
- SYNOPSIS
- prepare_triggers_for_insert_stmt()
- table Table to which insert will happen
-
- NOTE
- Prepare triggers for INSERT-like statement by marking fields
- used by triggers and inform handlers that batching of UPDATE/DELETE
- cannot be done if there are BEFORE UPDATE/DELETE triggers.
-*/
-
-void prepare_triggers_for_insert_stmt(TABLE *table)
-{
- if (table->triggers)
- {
- if (table->triggers->has_triggers(TRG_EVENT_DELETE,
- TRG_ACTION_AFTER))
- {
- /*
- The table has AFTER DELETE triggers that might access to
- subject table and therefore might need delete to be done
- immediately. So we turn-off the batching.
- */
- (void) table->file->extra(HA_EXTRA_DELETE_CANNOT_BATCH);
- }
- if (table->triggers->has_triggers(TRG_EVENT_UPDATE,
- TRG_ACTION_AFTER))
- {
- /*
- The table has AFTER UPDATE triggers that might access to subject
- table and therefore might need update to be done immediately.
- So we turn-off the batching.
- */
- (void) table->file->extra(HA_EXTRA_UPDATE_CANNOT_BATCH);
- }
- }
- table->mark_columns_needed_for_insert();
-}
-
-
/**
Upgrade table-level lock of INSERT statement to TL_WRITE if
a more concurrent lock is infeasible for some reason. This is
@@ -498,7 +421,8 @@ void upgrade_lock_type(THD *thd, thr_lock_type *lock_type,
if (specialflag & (SPECIAL_NO_NEW_FUNC | SPECIAL_SAFE_MODE) ||
thd->variables.max_insert_delayed_threads == 0 ||
thd->locked_tables_mode > LTM_LOCK_TABLES ||
- thd->lex->uses_stored_routines())
+ thd->lex->uses_stored_routines() /*||
+ thd->lex->describe*/)
{
*lock_type= TL_WRITE;
return;
@@ -574,6 +498,13 @@ bool open_and_lock_for_insert_delayed(THD *thd, TABLE_LIST *table_list)
DBUG_ENTER("open_and_lock_for_insert_delayed");
#ifndef EMBEDDED_LIBRARY
+ /* INSERT DELAYED is not allowed in a read only transaction. */
+ if (thd->tx_read_only)
+ {
+ my_error(ER_CANT_EXECUTE_IN_READ_ONLY_TRANSACTION, MYF(0));
+ DBUG_RETURN(true);
+ }
+
/*
In order for the deadlock detector to be able to find any deadlocks
caused by the handler thread waiting for GRL or this table, we acquire
@@ -681,6 +612,36 @@ create_insert_stmt_from_insert_delayed(THD *thd, String *buf)
}
+static void save_insert_query_plan(THD* thd, TABLE_LIST *table_list)
+{
+ Explain_insert* explain= new Explain_insert;
+ explain->table_name.append(table_list->table->alias);
+
+ thd->lex->explain->add_insert_plan(explain);
+
+ /* See Update_plan::updating_a_view for details */
+ bool skip= MY_TEST(table_list->view);
+
+ /* Save subquery children */
+ for (SELECT_LEX_UNIT *unit= thd->lex->select_lex.first_inner_unit();
+ unit;
+ unit= unit->next_unit())
+ {
+ if (skip)
+ {
+ skip= false;
+ continue;
+ }
+ /*
+ Table elimination doesn't work for INSERTS, but let's still have this
+ here for consistency
+ */
+ if (!(unit->item && unit->item->eliminated))
+ explain->add_child(unit->first_select()->select_number);
+ }
+}
+
+
/**
INSERT statement implementation
@@ -697,10 +658,11 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
enum_duplicates duplic,
bool ignore)
{
+ bool retval= true;
int error, res;
bool transactional_table, joins_freed= FALSE;
bool changed;
- bool was_insert_delayed= (table_list->lock_type == TL_WRITE_DELAYED);
+ const bool was_insert_delayed= (table_list->lock_type == TL_WRITE_DELAYED);
bool using_bulk_insert= 0;
uint value_count;
ulong counter = 1;
@@ -724,6 +686,7 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
Item *unused_conds= 0;
DBUG_ENTER("mysql_insert");
+ create_explain_query(thd->lex, thd->mem_root);
/*
Upgrade lock type if the requested lock is incompatible with
the current connection mode or table operation.
@@ -764,7 +727,7 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
lock_type= table_list->lock_type;
- thd_proc_info(thd, "init");
+ THD_STAGE_INFO(thd, stage_init);
thd->lex->used_tables=0;
values= its++;
value_count= values->elements;
@@ -774,9 +737,7 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
FALSE,
(fields.elements || !value_count ||
table_list->view != 0),
- !ignore && (thd->variables.sql_mode &
- (MODE_STRICT_TRANS_TABLES |
- MODE_STRICT_ALL_TABLES))))
+ !ignore && thd->is_strict_mode()))
goto abort;
/* mysql_prepare_insert set table_list->table if it was not set */
@@ -817,6 +778,17 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
/* Restore the current context. */
ctx_state.restore_state(context, table_list);
+
+ if (thd->lex->unit.first_select()->optimize_unflattened_subqueries(false))
+ {
+ goto abort;
+ }
+ save_insert_query_plan(thd, table_list);
+ if (thd->lex->describe)
+ {
+ retval= thd->lex->explain->send_explain(thd);
+ goto abort;
+ }
/*
Fill in the given fields and dump it to the table file
@@ -841,15 +813,15 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
table->next_number_field=table->found_next_number_field;
#ifdef HAVE_REPLICATION
- if (thd->slave_thread &&
+ if (thd->rgi_slave &&
(info.handle_duplicates == DUP_UPDATE) &&
(table->next_number_field != NULL) &&
- rpl_master_has_bug(&active_mi->rli, 24432, TRUE, NULL, NULL))
+ rpl_master_has_bug(thd->rgi_slave->rli, 24432, TRUE, NULL, NULL))
goto abort;
#endif
error=0;
- thd_proc_info(thd, "update");
+ THD_STAGE_INFO(thd, stage_update);
if (duplic == DUP_REPLACE &&
(!table->triggers || !table->triggers->has_delete_triggers()))
table->file->extra(HA_EXTRA_WRITE_CAN_REPLACE);
@@ -888,24 +860,24 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
}
}
- thd->abort_on_warning= (!ignore && (thd->variables.sql_mode &
- (MODE_STRICT_TRANS_TABLES |
- MODE_STRICT_ALL_TABLES)));
+ thd->abort_on_warning= !ignore && thd->is_strict_mode();
- prepare_triggers_for_insert_stmt(table);
+ table->prepare_triggers_for_insert_stmt_or_event();
+ table->mark_columns_needed_for_insert();
if (table_list->prepare_where(thd, 0, TRUE) ||
table_list->prepare_check_option(thd))
error= 1;
+ table->reset_default_fields();
+
while ((values= its++))
{
if (fields.elements || !value_count)
{
restore_record(table,s->default_values); // Get empty record
- if (fill_record_n_invoke_before_triggers(thd, fields, *values, 0,
- table->triggers,
+ if (fill_record_n_invoke_before_triggers(thd, table, fields, *values, 0,
TRG_EVENT_INSERT))
{
if (values_list.elements != 1 && ! thd->is_error())
@@ -949,8 +921,7 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
share->default_values[share->null_bytes - 1];
}
}
- if (fill_record_n_invoke_before_triggers(thd, table->field, *values, 0,
- table->triggers,
+ if (fill_record_n_invoke_before_triggers(thd, table, table->field, *values, 0,
TRG_EVENT_INSERT))
{
if (values_list.elements != 1 && ! thd->is_error())
@@ -962,6 +933,11 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
break;
}
}
+ if (table->default_field && table->update_default_fields())
+ {
+ error= 1;
+ break;
+ }
if ((res= table_list->view_check_option(thd,
(values_list.elements == 1 ?
@@ -978,7 +954,9 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
if (lock_type == TL_WRITE_DELAYED)
{
LEX_STRING const st_query = { query, thd->query_length() };
+ DEBUG_SYNC(thd, "before_write_delayed");
error=write_delayed(thd, table, duplic, st_query, ignore, log_on);
+ DEBUG_SYNC(thd, "after_write_delayed");
query=0;
}
else
@@ -986,7 +964,7 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
error=write_record(thd, table ,&info);
if (error)
break;
- thd->warning_info->inc_current_row_for_warning();
+ thd->get_stmt_da()->inc_current_row_for_warning();
}
free_underlaid_joins(thd, &thd->lex->select_lex);
@@ -1104,7 +1082,7 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
DBUG_ASSERT(transactional_table || !changed ||
thd->transaction.stmt.modified_non_trans_table);
}
- thd_proc_info(thd, "end");
+ THD_STAGE_INFO(thd, stage_end);
/*
We'll report to the client this id:
- if the table contains an autoincrement column and we successfully
@@ -1147,11 +1125,11 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
sprintf(buff, ER(ER_INSERT_INFO), (ulong) info.records,
(lock_type == TL_WRITE_DELAYED) ? (ulong) 0 :
(ulong) (info.records - info.copied),
- (ulong) thd->warning_info->statement_warn_count());
+ (long) thd->get_stmt_da()->current_statement_warn_count());
else
sprintf(buff, ER(ER_INSERT_INFO), (ulong) info.records,
(ulong) (info.deleted + updated),
- (ulong) thd->warning_info->statement_warn_count());
+ (long) thd->get_stmt_da()->current_statement_warn_count());
::my_ok(thd, info.copied + info.deleted + updated, id, buff);
}
thd->abort_on_warning= 0;
@@ -1170,10 +1148,11 @@ abort:
#endif
if (table != NULL)
table->file->ha_release_auto_increment();
+
if (!joins_freed)
free_underlaid_joins(thd, &thd->lex->select_lex);
thd->abort_on_warning= 0;
- DBUG_RETURN(TRUE);
+ DBUG_RETURN(retval);
}
@@ -1217,7 +1196,7 @@ static bool check_view_insertability(THD * thd, TABLE_LIST *view)
DBUG_ASSERT(view->table != 0 && view->field_translation != 0);
- (void) bitmap_init(&used_fields, used_fields_buff, table->s->fields, 0);
+ (void) my_bitmap_init(&used_fields, used_fields_buff, table->s->fields, 0);
bitmap_clear_all(&used_fields);
view->contain_auto_increment= 0;
@@ -1236,7 +1215,7 @@ static bool check_view_insertability(THD * thd, TABLE_LIST *view)
}
Item_field *field;
/* simple SELECT list entry (field without expression) */
- if (!(field= trans->item->filed_for_view_update()))
+ if (!(field= trans->item->field_for_view_update()))
{
thd->mark_used_columns= save_mark_used_columns;
DBUG_RETURN(TRUE);
@@ -1672,7 +1651,7 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info)
}
}
key_copy((uchar*) key,table->record[0],table->key_info+key_nr,0);
- key_part_map keypart_map= (1 << table->key_info[key_nr].key_parts) - 1;
+ key_part_map keypart_map= (1 << table->key_info[key_nr].user_defined_key_parts) - 1;
if ((error= (table->file->ha_index_read_idx_map(table->record[1],
key_nr, (uchar*) key,
keypart_map,
@@ -1690,15 +1669,42 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info)
DBUG_ASSERT(table->insert_values != NULL);
store_record(table,insert_values);
restore_record(table,record[1]);
+
+ /*
+ in INSERT ... ON DUPLICATE KEY UPDATE the set of modified fields can
+ change per row. Thus, we have to do reset_default_fields() per row.
+ Twice (before insert and before update).
+ */
+ table->reset_default_fields();
DBUG_ASSERT(info->update_fields->elements ==
info->update_values->elements);
- if (fill_record_n_invoke_before_triggers(thd, *info->update_fields,
+ if (fill_record_n_invoke_before_triggers(thd, table, *info->update_fields,
*info->update_values,
info->ignore,
- table->triggers,
TRG_EVENT_UPDATE))
goto before_trg_err;
+ bool different_records= (!records_are_comparable(table) ||
+ compare_record(table));
+ /*
+ Default fields must be updated before checking view updateability.
+ This branch of INSERT is executed only when a UNIQUE key was violated
+ with the ON DUPLICATE KEY UPDATE option. In this case the INSERT
+ operation is transformed to an UPDATE, and the default fields must
+ be updated as if this is an UPDATE.
+ */
+ if (different_records && table->default_field)
+ {
+ bool res;
+ enum_sql_command cmd= thd->lex->sql_command;
+ thd->lex->sql_command= SQLCOM_UPDATE;
+ res= table->update_default_fields();
+ thd->lex->sql_command= cmd;
+ if (res)
+ goto err;
+ }
+ table->reset_default_fields();
+
/* CHECK OPTION for VIEW ... ON DUPLICATE KEY UPDATE ... */
if (info->view &&
(res= info->view->view_check_option(current_thd, info->ignore)) ==
@@ -1709,7 +1715,7 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info)
table->file->restore_auto_increment(prev_insert_id);
info->touched++;
- if (!records_are_comparable(table) || compare_record(table))
+ if (different_records)
{
if ((error=table->file->ha_update_row(table->record[1],
table->record[0])) &&
@@ -1782,8 +1788,6 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info)
*/
if (last_uniq_key(table,key_nr) &&
!table->file->referenced_by_foreign_key() &&
- (table->timestamp_field_type == TIMESTAMP_NO_AUTO_SET ||
- table->timestamp_field_type == TIMESTAMP_AUTO_SET_ON_BOTH) &&
(!table->triggers || !table->triggers->has_delete_triggers()))
{
if ((error=table->file->ha_update_row(table->record[1],
@@ -1904,11 +1908,11 @@ int check_that_all_fields_are_given_values(THD *thd, TABLE *entry,
if (table_list)
{
table_list= table_list->top_table();
- view= test(table_list->view);
+ view= MY_TEST(table_list->view);
}
if (view)
{
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_NO_DEFAULT_FOR_VIEW_FIELD,
ER(ER_NO_DEFAULT_FOR_VIEW_FIELD),
table_list->view_db.str,
@@ -1916,7 +1920,7 @@ int check_that_all_fields_are_given_values(THD *thd, TABLE *entry,
}
else
{
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_NO_DEFAULT_FOR_FIELD,
ER(ER_NO_DEFAULT_FOR_FIELD),
(*field)->field_name);
@@ -1948,7 +1952,6 @@ public:
ulonglong forced_insert_id;
ulong auto_increment_increment;
ulong auto_increment_offset;
- timestamp_auto_set_type timestamp_field_type;
LEX_STRING query;
Time_zone *time_zone;
@@ -2012,7 +2015,7 @@ public:
thd.security_ctx->host=(char*) my_localhost;
strmake_buf(thd.security_ctx->priv_user, thd.security_ctx->user);
thd.current_tablenr=0;
- thd.command=COM_DELAYED_INSERT;
+ thd.set_command(COM_DELAYED_INSERT);
thd.lex->current_select= current_select;
thd.lex->sql_command= SQLCOM_INSERT; // For innodb::store_lock()
/*
@@ -2055,9 +2058,9 @@ public:
thd.unlink(); // Must be unlinked under lock
my_free(thd.query());
thd.security_ctx->user= thd.security_ctx->host=0;
- thread_count--;
delayed_insert_threads--;
mysql_mutex_unlock(&LOCK_thread_count);
+ thread_safe_decrement32(&thread_count, &thread_count_lock);
mysql_cond_broadcast(&COND_thread_count); /* Tell main we are ready */
}
@@ -2100,7 +2103,7 @@ I_List<Delayed_insert> delayed_threads;
static
Delayed_insert *find_handler(THD *thd, TABLE_LIST *table_list)
{
- thd_proc_info(thd, "waiting for delay_list");
+ THD_STAGE_INFO(thd, stage_waiting_for_delay_list);
mysql_mutex_lock(&LOCK_delayed_insert); // Protect master list
I_List_iterator<Delayed_insert> it(delayed_threads);
Delayed_insert *di;
@@ -2182,7 +2185,7 @@ bool delayed_get_table(THD *thd, MDL_request *grl_protection_request,
*/
if (delayed_insert_threads >= thd->variables.max_insert_delayed_threads)
DBUG_RETURN(0);
- thd_proc_info(thd, "Creating delayed handler");
+ THD_STAGE_INFO(thd, stage_creating_delayed_handler);
mysql_mutex_lock(&LOCK_delayed_create);
/*
The first search above was done without LOCK_delayed_create.
@@ -2192,9 +2195,9 @@ bool delayed_get_table(THD *thd, MDL_request *grl_protection_request,
{
if (!(di= new Delayed_insert(thd->lex->current_select)))
goto end_create;
- mysql_mutex_lock(&LOCK_thread_count);
- thread_count++;
- mysql_mutex_unlock(&LOCK_thread_count);
+
+ thread_safe_increment32(&thread_count, &thread_count_lock);
+
/*
Annotating delayed inserts is not supported.
*/
@@ -2243,14 +2246,14 @@ bool delayed_get_table(THD *thd, MDL_request *grl_protection_request,
handler thread has been properly initialized before exiting. Otherwise
we risk doing clone_ticket() on a ticket that is no longer valid.
*/
- thd_proc_info(thd, "waiting for handler open");
+ THD_STAGE_INFO(thd, stage_waiting_for_handler_open);
while (!di->handler_thread_initialized ||
(!di->thd.killed && !di->table && !thd->killed))
{
mysql_cond_wait(&di->cond_client, &di->mutex);
}
mysql_mutex_unlock(&di->mutex);
- thd_proc_info(thd, "got old table");
+ THD_STAGE_INFO(thd, stage_got_old_table);
if (thd->killed)
{
di->unlock();
@@ -2267,7 +2270,8 @@ bool delayed_get_table(THD *thd, MDL_request *grl_protection_request,
want to send "Server shutdown in progress" in the
INSERT THREAD.
*/
- my_message(di->thd.stmt_da->sql_errno(), di->thd.stmt_da->message(),
+ my_message(di->thd.get_stmt_da()->sql_errno(),
+ di->thd.get_stmt_da()->message(),
MYF(0));
}
di->unlock();
@@ -2318,7 +2322,7 @@ TABLE *Delayed_insert::get_local_table(THD* client_thd)
{
my_ptrdiff_t adjust_ptrs;
Field **field,**org_field, *found_next_number_field;
- Field **UNINIT_VAR(vfield);
+ Field **UNINIT_VAR(vfield), **UNINIT_VAR(dfield_ptr);
TABLE *copy;
TABLE_SHARE *share;
uchar *bitmap;
@@ -2330,13 +2334,13 @@ TABLE *Delayed_insert::get_local_table(THD* client_thd)
tables_in_use++;
if (!thd.lock) // Table is not locked
{
- thd_proc_info(client_thd, "waiting for handler lock");
+ THD_STAGE_INFO(client_thd, stage_waiting_for_handler_lock);
mysql_cond_signal(&cond); // Tell handler to lock table
while (!thd.killed && !thd.lock && ! client_thd->killed)
{
mysql_cond_wait(&cond_client, &mutex);
}
- thd_proc_info(client_thd, "got handler lock");
+ THD_STAGE_INFO(client_thd, stage_got_handler_lock);
if (client_thd->killed)
goto error;
if (thd.killed)
@@ -2357,7 +2361,8 @@ TABLE *Delayed_insert::get_local_table(THD* client_thd)
if (!thd.is_error())
my_message(ER_QUERY_INTERRUPTED, ER(ER_QUERY_INTERRUPTED), MYF(0));
else
- my_message(thd.stmt_da->sql_errno(), thd.stmt_da->message(), MYF(0));
+ my_message(thd.get_stmt_da()->sql_errno(),
+ thd.get_stmt_da()->message(), MYF(0));
goto error;
}
}
@@ -2370,7 +2375,7 @@ TABLE *Delayed_insert::get_local_table(THD* client_thd)
bytes. Since the table copy is used for creating one record only,
the other record buffers and alignment are unnecessary.
*/
- thd_proc_info(client_thd, "allocating local table");
+ THD_STAGE_INFO(client_thd, stage_allocating_local_table);
copy_tmp= (char*) client_thd->alloc(sizeof(*copy)+
(share->fields+1)*sizeof(Field**)+
share->reclength +
@@ -2394,6 +2399,15 @@ TABLE *Delayed_insert::get_local_table(THD* client_thd)
bitmap= (uchar*) (field + share->fields + 1);
copy->record[0]= (bitmap + share->column_bitmap_size*3);
memcpy((char*) copy->record[0], (char*) table->record[0], share->reclength);
+ if (share->default_fields)
+ {
+ copy->default_field= (Field**) client_thd->alloc((share->default_fields+1)*
+ sizeof(Field**));
+ if (!copy->default_field)
+ goto error;
+ dfield_ptr= copy->default_field;
+ }
+
/* Ensure we don't use the table list of the original table */
copy->pos_in_table_list= 0;
@@ -2414,6 +2428,15 @@ TABLE *Delayed_insert::get_local_table(THD* client_thd)
(*field)->move_field_offset(adjust_ptrs); // Point at copy->record[0]
if (*org_field == found_next_number_field)
(*field)->table->found_next_number_field= *field;
+ if (share->default_fields &&
+ ((*org_field)->has_insert_default_function() ||
+ (*org_field)->has_update_default_function()))
+ {
+ /* Put the newly copied field into the set of default fields. */
+ *dfield_ptr= *field;
+ (*dfield_ptr)->unireg_check= (*org_field)->unireg_check;
+ dfield_ptr++;
+ }
}
*field=0;
@@ -2438,15 +2461,8 @@ TABLE *Delayed_insert::get_local_table(THD* client_thd)
*vfield= 0;
}
- /* Adjust timestamp */
- if (table->timestamp_field)
- {
- /* Restore offset as this may have been reset in handle_inserts */
- copy->timestamp_field=
- (Field_timestamp*) copy->field[share->timestamp_field_offset];
- copy->timestamp_field->unireg_check= table->timestamp_field->unireg_check;
- copy->timestamp_field_type= copy->timestamp_field->get_auto_set_type();
- }
+ if (share->default_fields)
+ *dfield_ptr= NULL;
/* Adjust in_use for pointing to client thread */
copy->in_use= client_thd;
@@ -2490,11 +2506,11 @@ int write_delayed(THD *thd, TABLE *table, enum_duplicates duplic,
DBUG_PRINT("enter", ("query = '%s' length %lu", query.str,
(ulong) query.length));
- thd_proc_info(thd, "waiting for handler insert");
+ THD_STAGE_INFO(thd, stage_waiting_for_handler_insert);
mysql_mutex_lock(&di->mutex);
while (di->stacked_inserts >= delayed_queue_size && !thd->killed)
mysql_cond_wait(&di->cond_client, &di->mutex);
- thd_proc_info(thd, "storing row into queue");
+ THD_STAGE_INFO(thd, stage_storing_row_into_queue);
if (thd->killed)
goto err;
@@ -2519,7 +2535,9 @@ int write_delayed(THD *thd, TABLE *table, enum_duplicates duplic,
goto err;
}
- if (!(row->record= (char*) my_malloc(table->s->reclength, MYF(MY_WME))))
+ /* This can't be THREAD_SPECIFIC as it's freed in delayed thread */
+ if (!(row->record= (char*) my_malloc(table->s->reclength,
+ MYF(MY_WME))))
goto err;
memcpy(row->record, table->record[0], table->s->reclength);
row->start_time= thd->start_time;
@@ -2536,7 +2554,6 @@ int write_delayed(THD *thd, TABLE *table, enum_duplicates duplic,
thd->stmt_depends_on_first_successful_insert_id_in_prev_stmt;
row->first_successful_insert_id_in_prev_stmt=
thd->first_successful_insert_id_in_prev_stmt;
- row->timestamp_field_type= table->timestamp_field_type;
/* Add session variable timezone
Time_zone object will not be freed even the thread is ended.
@@ -2771,8 +2788,7 @@ pthread_handler_t handle_delayed_insert(void *arg)
if (my_thread_init())
{
/* Can't use my_error since store_globals has not yet been called */
- thd->stmt_da->set_error_status(thd, ER_OUT_OF_RESOURCES,
- ER(ER_OUT_OF_RESOURCES), NULL);
+ thd->get_stmt_da()->set_error_status(ER_OUT_OF_RESOURCES);
di->handler_thread_initialized= TRUE;
}
else
@@ -2782,8 +2798,7 @@ pthread_handler_t handle_delayed_insert(void *arg)
if (init_thr_lock() || thd->store_globals())
{
/* Can't use my_error since store_globals has perhaps failed */
- thd->stmt_da->set_error_status(thd, ER_OUT_OF_RESOURCES,
- ER(ER_OUT_OF_RESOURCES), NULL);
+ thd->get_stmt_da()->set_error_status(ER_OUT_OF_RESOURCES);
di->handler_thread_initialized= TRUE;
thd->fatal_error();
goto err;
@@ -2869,7 +2884,7 @@ pthread_handler_t handle_delayed_insert(void *arg)
di->thd.mysys_var->current_cond= &di->cond;
mysql_mutex_unlock(&di->thd.mysys_var->mutex);
mysql_mutex_lock(&di->mutex);
- thd_proc_info(&(di->thd), "Waiting for INSERT");
+ THD_STAGE_INFO(&(di->thd), stage_waiting_for_insert);
DBUG_PRINT("info",("Waiting for someone to insert rows"));
while (!thd->killed && !di->status)
@@ -2896,7 +2911,6 @@ pthread_handler_t handle_delayed_insert(void *arg)
mysql_mutex_unlock(&di->thd.mysys_var->mutex);
mysql_mutex_lock(&di->mutex);
}
- thd_proc_info(&(di->thd), 0);
if (di->tables_in_use && ! thd->lock && !thd->killed)
{
@@ -2954,24 +2968,28 @@ pthread_handler_t handle_delayed_insert(void *arg)
DBUG_LEAVE;
}
- di->table=0;
- thd->killed= KILL_CONNECTION; // If error
- mysql_mutex_unlock(&di->mutex);
+ {
+ DBUG_ENTER("handle_delayed_insert-cleanup");
+ di->table=0;
+ thd->killed= KILL_CONNECTION; // If error
+ mysql_mutex_unlock(&di->mutex);
- close_thread_tables(thd); // Free the table
- thd->mdl_context.release_transactional_locks();
- mysql_cond_broadcast(&di->cond_client); // Safety
+ close_thread_tables(thd); // Free the table
+ thd->mdl_context.release_transactional_locks();
+ mysql_cond_broadcast(&di->cond_client); // Safety
- mysql_mutex_lock(&LOCK_delayed_create); // Because of delayed_get_table
- mysql_mutex_lock(&LOCK_delayed_insert);
- /*
- di should be unlinked from the thread handler list and have no active
- clients
- */
- delete di;
- mysql_mutex_unlock(&LOCK_delayed_insert);
- mysql_mutex_unlock(&LOCK_delayed_create);
+ mysql_mutex_lock(&LOCK_delayed_create); // Because of delayed_get_table
+ mysql_mutex_lock(&LOCK_delayed_insert);
+ /*
+ di should be unlinked from the thread handler list and have no active
+ clients
+ */
+ delete di;
+ mysql_mutex_unlock(&LOCK_delayed_insert);
+ mysql_mutex_unlock(&LOCK_delayed_create);
+ DBUG_LEAVE;
+ }
my_thread_end();
pthread_exit(0);
@@ -3023,7 +3041,7 @@ bool Delayed_insert::handle_inserts(void)
table->next_number_field=table->found_next_number_field;
table->use_all_columns();
- thd_proc_info(&thd, "upgrading lock");
+ THD_STAGE_INFO(&thd, stage_upgrading_lock);
if (thr_upgrade_write_delay_lock(*thd.lock->locks, delayed_lock,
thd.variables.lock_wait_timeout))
{
@@ -3037,9 +3055,9 @@ bool Delayed_insert::handle_inserts(void)
goto err;
}
- thd_proc_info(&thd, "insert");
+ THD_STAGE_INFO(&thd, stage_insert);
max_rows= delayed_insert_limit;
- if (thd.killed || table->s->has_old_version())
+ if (thd.killed || table->s->tdc.flushed)
{
thd.killed= KILL_SYSTEM_THREAD;
max_rows= ULONG_MAX; // Do as much as possible
@@ -3096,7 +3114,6 @@ bool Delayed_insert::handle_inserts(void)
row->first_successful_insert_id_in_prev_stmt;
thd.stmt_depends_on_first_successful_insert_id_in_prev_stmt=
row->stmt_depends_on_first_successful_insert_id_in_prev_stmt;
- table->timestamp_field_type= row->timestamp_field_type;
table->auto_increment_field_not_null= row->auto_increment_field_not_null;
/* Copy the session variables. */
@@ -3175,13 +3192,13 @@ bool Delayed_insert::handle_inserts(void)
{
if (tables_in_use)
mysql_cond_broadcast(&cond_client); // If waiting clients
- thd_proc_info(&thd, "reschedule");
+ THD_STAGE_INFO(&thd, stage_reschedule);
mysql_mutex_unlock(&mutex);
if ((error=table->file->extra(HA_EXTRA_NO_CACHE)))
{
/* This should never happen */
table->file->print_error(error,MYF(0));
- sql_print_error("%s", thd.stmt_da->message());
+ sql_print_error("%s", thd.get_stmt_da()->message());
DBUG_PRINT("error", ("HA_EXTRA_NO_CACHE failed in loop"));
goto err;
}
@@ -3198,7 +3215,7 @@ bool Delayed_insert::handle_inserts(void)
if (!using_bin_log)
table->file->extra(HA_EXTRA_WRITE_CACHE);
mysql_mutex_lock(&mutex);
- thd_proc_info(&thd, "insert");
+ THD_STAGE_INFO(&thd, stage_insert);
}
if (tables_in_use)
mysql_cond_broadcast(&cond_client); // If waiting clients
@@ -3233,7 +3250,7 @@ bool Delayed_insert::handle_inserts(void)
if ((error=table->file->extra(HA_EXTRA_NO_CACHE)))
{ // This shouldn't happen
table->file->print_error(error,MYF(0));
- sql_print_error("%s", thd.stmt_da->message());
+ sql_print_error("%s", thd.get_stmt_da()->message());
DBUG_PRINT("error", ("HA_EXTRA_NO_CACHE failed after loop"));
goto err;
}
@@ -3384,9 +3401,7 @@ select_insert::prepare(List<Item> &values, SELECT_LEX_UNIT *u)
if (!res && fields->elements)
{
bool saved_abort_on_warning= thd->abort_on_warning;
- thd->abort_on_warning= !info.ignore && (thd->variables.sql_mode &
- (MODE_STRICT_TRANS_TABLES |
- MODE_STRICT_ALL_TABLES));
+ thd->abort_on_warning= !info.ignore && thd->is_strict_mode();
res= check_that_all_fields_are_given_values(thd, table_list->table,
table_list);
thd->abort_on_warning= saved_abort_on_warning;
@@ -3490,13 +3505,14 @@ select_insert::prepare(List<Item> &values, SELECT_LEX_UNIT *u)
table->file->ha_start_bulk_insert((ha_rows) 0);
}
restore_record(table,s->default_values); // Get empty record
+ table->reset_default_fields();
table->next_number_field=table->found_next_number_field;
#ifdef HAVE_REPLICATION
- if (thd->slave_thread &&
+ if (thd->rgi_slave &&
(info.handle_duplicates == DUP_UPDATE) &&
(table->next_number_field != NULL) &&
- rpl_master_has_bug(&active_mi->rli, 24432, TRUE, NULL, NULL))
+ rpl_master_has_bug(thd->rgi_slave->rli, 24432, TRUE, NULL, NULL))
DBUG_RETURN(1);
#endif
@@ -3508,15 +3524,15 @@ select_insert::prepare(List<Item> &values, SELECT_LEX_UNIT *u)
table->file->extra(HA_EXTRA_WRITE_CAN_REPLACE);
if (info.handle_duplicates == DUP_UPDATE)
table->file->extra(HA_EXTRA_INSERT_WITH_UPDATE);
- thd->abort_on_warning= (!info.ignore &&
- (thd->variables.sql_mode &
- (MODE_STRICT_TRANS_TABLES |
- MODE_STRICT_ALL_TABLES)));
+ thd->abort_on_warning= !info.ignore && thd->is_strict_mode();
res= (table_list->prepare_where(thd, 0, TRUE) ||
table_list->prepare_check_option(thd));
if (!res)
- prepare_triggers_for_insert_stmt(table);
+ {
+ table->prepare_triggers_for_insert_stmt_or_event();
+ table->mark_columns_needed_for_insert();
+ }
DBUG_RETURN(res);
}
@@ -3542,7 +3558,8 @@ int select_insert::prepare2(void)
{
DBUG_ENTER("select_insert::prepare2");
if (thd->lex->current_select->options & OPTION_BUFFER_RESULT &&
- thd->locked_tables_mode <= LTM_LOCK_TABLES)
+ thd->locked_tables_mode <= LTM_LOCK_TABLES &&
+ !thd->lex->describe)
table->file->ha_start_bulk_insert((ha_rows) 0);
DBUG_RETURN(0);
}
@@ -3584,6 +3601,8 @@ int select_insert::send_data(List<Item> &values)
thd->count_cuted_fields= CHECK_FIELD_WARN; // Calculate cuted fields
store_values(values);
+ if (table->default_field && table->update_default_fields())
+ DBUG_RETURN(1);
thd->count_cuted_fields= CHECK_FIELD_ERROR_FOR_NULL;
if (thd->is_error())
{
@@ -3643,23 +3662,13 @@ int select_insert::send_data(List<Item> &values)
void select_insert::store_values(List<Item> &values)
{
if (fields->elements)
- fill_record_n_invoke_before_triggers(thd, *fields, values, 1,
- table->triggers, TRG_EVENT_INSERT);
+ fill_record_n_invoke_before_triggers(thd, table, *fields, values, 1,
+ TRG_EVENT_INSERT);
else
- fill_record_n_invoke_before_triggers(thd, table->field, values, 1,
- table->triggers, TRG_EVENT_INSERT);
-}
-
-void select_insert::send_error(uint errcode,const char *err)
-{
- DBUG_ENTER("select_insert::send_error");
-
- my_message(errcode, err, MYF(0));
-
- DBUG_VOID_RETURN;
+ fill_record_n_invoke_before_triggers(thd, table, table->field, values, 1,
+ TRG_EVENT_INSERT);
}
-
bool select_insert::prepare_eof()
{
int error;
@@ -3681,7 +3690,7 @@ bool select_insert::prepare_eof()
table->file->ha_end_bulk_insert() : 0);
#endif /* WITH_WSREP */
if (!error && thd->is_error())
- error= thd->stmt_da->sql_errno();
+ error= thd->get_stmt_da()->sql_errno();
table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY);
table->file->extra(HA_EXTRA_WRITE_CANNOT_REPLACE);
@@ -3748,11 +3757,11 @@ bool select_insert::send_ok_packet() {
if (info.ignore)
my_snprintf(message, sizeof(message), ER(ER_INSERT_INFO),
(ulong) info.records, (ulong) (info.records - info.copied),
- (ulong) thd->warning_info->statement_warn_count());
+ (long) thd->get_stmt_da()->current_statement_warn_count());
else
my_snprintf(message, sizeof(message), ER(ER_INSERT_INFO),
(ulong) info.records, (ulong) (info.deleted + info.updated),
- (ulong) thd->warning_info->statement_warn_count());
+ (long) thd->get_stmt_da()->current_statement_warn_count());
row_count= info.copied + info.deleted +
((thd->client_capabilities & CLIENT_FOUND_ROWS) ?
@@ -3812,7 +3821,8 @@ void select_insert::abort_result_set() {
*/
changed= (info.copied || info.deleted || info.updated);
transactional_table= table->file->has_transactions();
- if (thd->transaction.stmt.modified_non_trans_table)
+ if (thd->transaction.stmt.modified_non_trans_table ||
+ thd->log_current_statement)
{
if (!can_rollback_data())
thd->transaction.all.modified_non_trans_table= TRUE;
@@ -3901,15 +3911,15 @@ static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info,
DBUG_ENTER("create_table_from_items");
tmp_table.alias= 0;
- tmp_table.timestamp_field= 0;
tmp_table.s= &share;
init_tmp_table_share(thd, &share, "", 0, "", "");
tmp_table.s->db_create_options=0;
- tmp_table.s->blob_ptr_size= portable_sizeof_char_ptr;
tmp_table.null_row= 0;
tmp_table.maybe_null= 0;
+ promote_first_timestamp_column(&alter_info->create_list);
+
while ((item=it++))
{
Field *tmp_table_field;
@@ -3962,6 +3972,16 @@ static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info,
DEBUG_SYNC(thd,"create_table_select_before_create");
+ /* Check if LOCK TABLES + CREATE OR REPLACE of existing normal table*/
+ if (thd->locked_tables_mode && create_table->table &&
+ !create_info->tmp_table())
+ {
+ /* Remember information about the locked table */
+ create_info->pos_in_locked_tables=
+ create_table->table->pos_in_locked_tables;
+ create_info->mdl_ticket= create_table->table->mdl_ticket;
+ }
+
/*
Create and lock table.
@@ -3978,53 +3998,64 @@ static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info,
TABLE, which is a wrong order. So we keep binary logging disabled when we
open_table().
*/
+
+ if (!mysql_create_table_no_lock(thd, create_table->db,
+ create_table->table_name,
+ create_info, alter_info, NULL,
+ select_field_count))
{
- if (!mysql_create_table_no_lock(thd, create_table->db,
- create_table->table_name,
- create_info, alter_info, 0,
- select_field_count, NULL))
+ DEBUG_SYNC(thd,"create_table_select_before_open");
+
+ /*
+ If we had a temporary table or a table used with LOCK TABLES,
+ it was closed by mysql_create()
+ */
+ create_table->table= 0;
+
+ if (!create_info->tmp_table())
{
- DEBUG_SYNC(thd,"create_table_select_before_open");
+ Open_table_context ot_ctx(thd, MYSQL_OPEN_REOPEN);
+ TABLE_LIST::enum_open_strategy save_open_strategy;
- if (!(create_info->options & HA_LEX_CREATE_TMP_TABLE))
+ /* Force the newly created table to be opened */
+ save_open_strategy= create_table->open_strategy;
+ create_table->open_strategy= TABLE_LIST::OPEN_NORMAL;
+ /*
+ Here we open the destination table, on which we already have
+ an exclusive metadata lock.
+ */
+ if (open_table(thd, create_table, thd->mem_root, &ot_ctx))
{
- Open_table_context ot_ctx(thd, MYSQL_OPEN_REOPEN);
- /*
- Here we open the destination table, on which we already have
- an exclusive metadata lock.
- */
- if (open_table(thd, create_table, thd->mem_root, &ot_ctx))
- {
- quick_rm_table(create_info->db_type, create_table->db,
- table_case_name(create_info, create_table->table_name),
- 0);
- }
- else
- table= create_table->table;
- }
- else
- {
- Open_table_context ot_ctx(thd, MYSQL_OPEN_TEMPORARY_ONLY);
- if (open_table(thd, create_table, thd->mem_root, &ot_ctx))
- {
- /*
- This shouldn't happen as creation of temporary table should make
- it preparable for open. But let us do close_temporary_table() here
- just in case.
- */
- drop_temporary_table(thd, create_table, NULL);
- }
- else
- table= create_table->table;
+ quick_rm_table(thd, create_info->db_type, create_table->db,
+ table_case_name(create_info, create_table->table_name),
+ 0);
}
+ /* Restore */
+ create_table->open_strategy= save_open_strategy;
}
- if (!table) // open failed
+ else
{
- if (!thd->is_error()) // CREATE ... IF NOT EXISTS
- my_ok(thd); // succeed, but did nothing
- DBUG_RETURN(NULL);
+ if (open_temporary_table(thd, create_table))
+ {
+ /*
+ This shouldn't happen as creation of temporary table should make
+ it preparable for open. Anyway we can't drop temporary table if
+ we are unable to find it.
+ */
+ DBUG_ASSERT(0);
+ }
+ DBUG_ASSERT(create_table->table == create_info->table);
}
}
+ else
+ create_table->table= 0; // Create failed
+
+ if (!(table= create_table->table))
+ {
+ if (!thd->is_error()) // CREATE ... IF NOT EXISTS
+ my_ok(thd); // succeed, but did nothing
+ DBUG_RETURN(NULL);
+ }
DEBUG_SYNC(thd,"create_table_select_before_lock");
@@ -4041,7 +4072,7 @@ static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info,
/* purecov: begin tested */
/*
This can happen in innodb when you get a deadlock when using same table
- in insert and select
+ in insert and select or when you run out of memory.
*/
my_error(ER_CANT_LOCK, MYF(0), my_errno);
if (*lock)
@@ -4132,15 +4163,13 @@ select_create::prepare(List<Item> &values, SELECT_LEX_UNIT *u)
row-based replication for the statement. If we are creating a
temporary table, we need to start a statement transaction.
*/
- if ((thd->lex->create_info.options & HA_LEX_CREATE_TMP_TABLE) == 0 &&
+ if (!thd->lex->create_info.tmp_table() &&
thd->is_current_stmt_binlog_format_row() &&
mysql_bin_log.is_open())
{
thd->binlog_start_trans_and_stmt();
}
- DBUG_ASSERT(create_table->table == NULL);
-
DEBUG_SYNC(thd,"create_table_select_before_check_if_exists");
if (!(table= create_table_from_items(thd, create_info, create_table,
@@ -4153,7 +4182,7 @@ select_create::prepare(List<Item> &values, SELECT_LEX_UNIT *u)
{
DBUG_ASSERT(m_plock == NULL);
- if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
+ if (create_info->tmp_table())
m_plock= &m_lock;
else
m_plock= &thd->extra_lock;
@@ -4174,8 +4203,6 @@ select_create::prepare(List<Item> &values, SELECT_LEX_UNIT *u)
for (Field **f= field ; *f ; f++)
bitmap_set_bit(table->write_set, (*f)->field_index);
- /* Don't set timestamp if used */
- table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
table->next_number_field=table->found_next_number_field;
restore_record(table,s->default_values); // Get empty record
@@ -4189,10 +4216,7 @@ select_create::prepare(List<Item> &values, SELECT_LEX_UNIT *u)
table->file->extra(HA_EXTRA_INSERT_WITH_UPDATE);
if (thd->locked_tables_mode <= LTM_LOCK_TABLES)
table->file->ha_start_bulk_insert((ha_rows) 0);
- thd->abort_on_warning= (!info.ignore &&
- (thd->variables.sql_mode &
- (MODE_STRICT_TRANS_TABLES |
- MODE_STRICT_ALL_TABLES)));
+ thd->abort_on_warning= !info.ignore && thd->is_strict_mode();
if (check_that_all_fields_are_given_values(thd, table, table_list))
DBUG_RETURN(1);
table->mark_columns_needed_for_insert();
@@ -4207,15 +4231,14 @@ select_create::binlog_show_create_table(TABLE **tables, uint count)
{
/*
Note 1: In RBR mode, we generate a CREATE TABLE statement for the
- created table by calling store_create_info() (behaves as SHOW
- CREATE TABLE). In the event of an error, nothing should be
- written to the binary log, even if the table is non-transactional;
- therefore we pretend that the generated CREATE TABLE statement is
- for a transactional table. The event will then be put in the
- transaction cache, and any subsequent events (e.g., table-map
- events and binrow events) will also be put there. We can then use
- ha_autocommit_or_rollback() to either throw away the entire
- kaboodle of events, or write them to the binary log.
+ created table by calling show_create_table(). In the event of an error,
+ nothing should be written to the binary log, even if the table is
+ non-transactional; therefore we pretend that the generated CREATE TABLE
+ statement is for a transactional table. The event will then be put in the
+ transaction cache, and any subsequent events (e.g., table-map events and
+ binrow events) will also be put there. We can then use
+ ha_autocommit_or_rollback() to either throw away the entire kaboodle of
+ events, or write them to the binary log.
We write the CREATE TABLE statement here and not in prepare()
since there potentially are sub-selects or accesses to information
@@ -4234,9 +4257,9 @@ select_create::binlog_show_create_table(TABLE **tables, uint count)
tmp_table_list.table = *tables;
query.length(0); // Have to zero it since constructor doesn't
- result= store_create_info(thd, &tmp_table_list, &query, create_info,
- /* show_database */ TRUE);
- DBUG_ASSERT(result == 0); /* store_create_info() always return 0 */
+ result= show_create_table(thd, &tmp_table_list, &query, create_info,
+ WITH_DB_NAME);
+ DBUG_ASSERT(result == 0); /* show_create_table() always return 0 */
#ifdef WITH_WSREP
if (WSREP_EMULATE_BINLOG(thd) || mysql_bin_log.is_open())
@@ -4260,38 +4283,8 @@ select_create::binlog_show_create_table(TABLE **tables, uint count)
void select_create::store_values(List<Item> &values)
{
- fill_record_n_invoke_before_triggers(thd, field, values, 1,
- table->triggers, TRG_EVENT_INSERT);
-}
-
-
-void select_create::send_error(uint errcode,const char *err)
-{
- DBUG_ENTER("select_create::send_error");
-
- DBUG_PRINT("info",
- ("Current statement %s row-based",
- thd->is_current_stmt_binlog_format_row() ? "is" : "is NOT"));
- DBUG_PRINT("info",
- ("Current table (at 0x%lu) %s a temporary (or non-existant) table",
- (ulong) table,
- table && !table->s->tmp_table ? "is NOT" : "is"));
- /*
- This will execute any rollbacks that are necessary before writing
- the transcation cache.
-
- We disable the binary log since nothing should be written to the
- binary log. This disabling is important, since we potentially do
- a "roll back" of non-transactional tables by removing the table,
- and the actual rollback might generate events that should not be
- written to the binary log.
-
- */
- tmp_disable_binlog(thd);
- select_insert::send_error(errcode, err);
- reenable_binlog(thd);
-
- DBUG_VOID_RETURN;
+ fill_record_n_invoke_before_triggers(thd, table, field, values, 1,
+ TRG_EVENT_INSERT);
}
@@ -4303,16 +4296,16 @@ bool select_create::send_eof()
abort_result_set();
DBUG_RETURN(true);
}
- else
+
+ /*
+ Do an implicit commit at end of statement for non-temporary
+ tables. This can fail, but we should unlock the table
+ nevertheless.
+ */
+ if (!table->s->tmp_table)
{
- /*
- Do an implicit commit at end of statement for non-temporary
- tables. This can fail, but we should unlock the table
- nevertheless.
- */
- if (!table->s->tmp_table)
- {
- trans_commit_stmt(thd);
+ trans_commit_stmt(thd);
+ if (!(thd->variables.option_bits & OPTION_GTID_BEGIN))
trans_commit_implicit(thd);
#ifdef WITH_WSREP
mysql_mutex_lock(&thd->LOCK_wsrep_thd);
@@ -4326,19 +4319,45 @@ bool select_create::send_eof()
}
mysql_mutex_unlock(&thd->LOCK_wsrep_thd);
#endif /* WITH_WSREP */
- }
+ }
+ else if (!thd->is_current_stmt_binlog_format_row())
+ table->s->table_creation_was_logged= 1;
- table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY);
- table->file->extra(HA_EXTRA_WRITE_CANNOT_REPLACE);
+ /*
+ exit_done must only be set after last potential call to
+ abort_result_set().
+ */
+ exit_done= 1; // Avoid double calls
+
+ table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY);
+ table->file->extra(HA_EXTRA_WRITE_CANNOT_REPLACE);
- send_ok_packet();
+ send_ok_packet();
+
+ if (m_plock)
+ {
+ MYSQL_LOCK *lock= *m_plock;
+ *m_plock= NULL;
+ m_plock= NULL;
- if (m_plock)
+ if (create_info->pos_in_locked_tables)
{
- mysql_unlock_tables(thd, *m_plock);
- *m_plock= NULL;
- m_plock= NULL;
+ /*
+ If we are under lock tables, we have created a table that was
+ originally locked. We should add back the lock to ensure that
+ all tables in the thd->open_list are locked!
+ */
+ table->mdl_ticket= create_info->mdl_ticket;
+
+ /* The following should never fail, except if out of memory */
+ if (!thd->locked_tables_list.restore_lock(thd,
+ create_info->
+ pos_in_locked_tables,
+ table, lock))
+ DBUG_RETURN(false); // ok
+ /* Fail. Continue without locking the table */
}
+ mysql_unlock_tables(thd, lock);
}
DBUG_RETURN(false);
}
@@ -4346,8 +4365,14 @@ bool select_create::send_eof()
void select_create::abort_result_set()
{
+ ulonglong save_option_bits;
DBUG_ENTER("select_create::abort_result_set");
+ /* Avoid double calls, could happen in case of out of memory on cleanup */
+ if (exit_done)
+ DBUG_VOID_RETURN;
+ exit_done= 1;
+
/*
In select_insert::abort_result_set() we roll back the statement, including
truncating the transaction cache of the binary log. To do this, we
@@ -4362,14 +4387,26 @@ void select_create::abort_result_set()
We also roll back the statement regardless of whether the creation
of the table succeeded or not, since we need to reset the binary
log state.
+
+ However if there was an original table that was deleted, as part of
+ create or replace table, then we must log the statement.
*/
- tmp_disable_binlog(thd);
+
+ save_option_bits= thd->variables.option_bits;
+ thd->variables.option_bits&= ~OPTION_BIN_LOG;
select_insert::abort_result_set();
thd->transaction.stmt.modified_non_trans_table= FALSE;
- reenable_binlog(thd);
+ thd->variables.option_bits= save_option_bits;
+
/* possible error of writing binary log is ignored deliberately */
(void) thd->binlog_flush_pending_rows_event(TRUE, TRUE);
+ if (create_info->table_was_deleted)
+ {
+ /* Unlock locked table that was dropped by CREATE */
+ thd->locked_tables_list.unlock_locked_table(thd,
+ create_info->mdl_ticket);
+ }
if (m_plock)
{
mysql_unlock_tables(thd, *m_plock);
@@ -4379,25 +4416,21 @@ void select_create::abort_result_set()
if (table)
{
+ bool tmp_table= table->s->tmp_table;
table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY);
table->file->extra(HA_EXTRA_WRITE_CANNOT_REPLACE);
table->auto_increment_field_not_null= FALSE;
drop_open_table(thd, table, create_table->db, create_table->table_name);
table=0; // Safety
+ if (thd->log_current_statement && mysql_bin_log.is_open())
+ {
+ /* Remove logging of drop, create + insert rows */
+ binlog_reset_cache(thd);
+ /* Original table was deleted. We have to log it */
+ log_drop_table(thd, create_table->db, create_table->db_length,
+ create_table->table_name, create_table->table_name_length,
+ tmp_table);
+ }
}
DBUG_VOID_RETURN;
}
-
-
-/*****************************************************************************
- Instansiate templates
-*****************************************************************************/
-
-#ifdef HAVE_EXPLICIT_TEMPLATE_INSTANTIATION
-template class List_iterator_fast<List_item>;
-#ifndef EMBEDDED_LIBRARY
-template class I_List<Delayed_insert>;
-template class I_List_iterator<Delayed_insert>;
-template class I_List<delayed_row>;
-#endif /* EMBEDDED_LIBRARY */
-#endif /* HAVE_EXPLICIT_TEMPLATE_INSTANTIATION */
diff --git a/sql/sql_insert.h b/sql/sql_insert.h
index 8564f4d103e..cbfc1ea9dcd 100644
--- a/sql/sql_insert.h
+++ b/sql/sql_insert.h
@@ -38,7 +38,6 @@ void upgrade_lock_type_for_insert(THD *thd, thr_lock_type *lock_type,
bool is_multi_insert);
int check_that_all_fields_are_given_values(THD *thd, TABLE *entry,
TABLE_LIST *table_list);
-void prepare_triggers_for_insert_stmt(TABLE *table);
int write_record(THD *thd, TABLE *table, COPY_INFO *info);
void kill_delayed_threads(void);
diff --git a/sql/sql_join_cache.cc b/sql/sql_join_cache.cc
index 9411b3a92c8..254b7026e96 100644
--- a/sql/sql_join_cache.cc
+++ b/sql/sql_join_cache.cc
@@ -222,8 +222,8 @@ void JOIN_CACHE::calc_record_fields()
for (; tab != join_tab ; tab= next_linear_tab(join, tab, WITHOUT_BUSH_ROOTS))
{
tab->calc_used_field_length(FALSE);
- flag_fields+= test(tab->used_null_fields || tab->used_uneven_bit_fields);
- flag_fields+= test(tab->table->maybe_null);
+ flag_fields+= MY_TEST(tab->used_null_fields || tab->used_uneven_bit_fields);
+ flag_fields+= MY_TEST(tab->table->maybe_null);
fields+= tab->used_fields;
blobs+= tab->used_blobs;
}
@@ -696,7 +696,7 @@ void JOIN_CACHE::set_constants()
pack_length_with_blob_ptrs= pack_length + blobs*sizeof(uchar *);
min_buff_size= 0;
min_records= 1;
- buff_size= max(join->thd->variables.join_buff_size,
+ buff_size= MY_MAX(join->thd->variables.join_buff_size,
get_min_join_buffer_size());
size_of_rec_ofs= offset_size(buff_size);
size_of_rec_len= blobs ? size_of_rec_ofs : offset_size(len);
@@ -736,7 +736,7 @@ void JOIN_CACHE::set_constants()
uint JOIN_CACHE::get_record_max_affix_length()
{
uint len= get_prefix_length() +
- test(with_match_flag) +
+ MY_TEST(with_match_flag) +
size_of_fld_ofs * data_field_count;
return len;
}
@@ -929,12 +929,15 @@ int JOIN_CACHE::alloc_buffer()
join->shrink_join_buffers(join_tab, curr_buff_space_sz,
join_buff_space_limit))))
goto fail;
+
+ if (for_explain_only)
+ return 0;
for (ulong buff_size_decr= (buff_size-min_buff_size)/4 + 1; ; )
{
ulong next_buff_size;
- if ((buff= (uchar*) my_malloc(buff_size, MYF(0))))
+ if ((buff= (uchar*) my_malloc(buff_size, MYF(MY_THREAD_SPECIFIC))))
break;
next_buff_size= buff_size > buff_size_decr ? buff_size-buff_size_decr : 0;
@@ -1012,7 +1015,7 @@ int JOIN_CACHE::realloc_buffer()
{
int rc;
free();
- rc= test(!(buff= (uchar*) my_malloc(buff_size, MYF(0))));
+ rc= MY_TEST(!(buff= (uchar*) my_malloc(buff_size, MYF(MY_THREAD_SPECIFIC))));
reset(TRUE);
return rc;
}
@@ -1023,6 +1026,7 @@ int JOIN_CACHE::realloc_buffer()
SYNOPSIS
init()
+ for_explain join buffer is initialized for explain only
DESCRIPTION
The function initializes the join cache structure. It supposed to be called
@@ -1044,10 +1048,12 @@ int JOIN_CACHE::realloc_buffer()
1 otherwise
*/
-int JOIN_CACHE::init()
+int JOIN_CACHE::init(bool for_explain)
{
DBUG_ENTER("JOIN_CACHE::init");
+ for_explain_only= for_explain;
+
calc_record_fields();
collect_info_on_key_args();
@@ -1766,7 +1772,7 @@ uint JOIN_CACHE::read_flag_fields()
CACHE_FIELD *copy_end= copy+flag_fields;
if (with_match_flag)
{
- copy->str[0]= test((Match_flag) pos[0] == MATCH_FOUND);
+ copy->str[0]= MY_TEST((Match_flag) pos[0] == MATCH_FOUND);
pos+= copy->length;
copy++;
}
@@ -2257,7 +2263,7 @@ enum_nested_loop_state JOIN_CACHE::join_matching_records(bool skip_last)
while (!(error= join_tab_scan->next()))
{
- if (join->thd->killed)
+ if (join->thd->check_killed())
{
/* The user has aborted the execution of the query */
join->thd->send_kill_message();
@@ -2520,14 +2526,14 @@ enum_nested_loop_state JOIN_CACHE::join_null_complements(bool skip_last)
if (!records)
DBUG_RETURN(NESTED_LOOP_OK);
- cnt= records - (is_key_access() ? 0 : test(skip_last));
+ cnt= records - (is_key_access() ? 0 : MY_TEST(skip_last));
/* This function may be called only for inner tables of outer joins */
DBUG_ASSERT(join_tab->first_inner);
for ( ; cnt; cnt--)
{
- if (join->thd->killed)
+ if (join->thd->check_killed())
{
/* The user has aborted the execution of the query */
join->thd->send_kill_message();
@@ -2553,49 +2559,41 @@ finish:
/*
- Add a comment on the join algorithm employed by the join cache
+ Save data on the join algorithm employed by the join cache
SYNOPSIS
- print_explain_comment()
+ save_explain_data()
str string to add the comment on the employed join algorithm to
DESCRIPTION
- This function adds info on the type of the used join buffer (flat or
+ This function puts info about the type of the used join buffer (flat or
incremental) and on the type of the the employed join algorithm (BNL,
- BNLH, BKA or BKAH) to the the end of the sring str.
+ BNLH, BKA or BKAH) to the data structure
RETURN VALUE
none
*/
-void JOIN_CACHE::print_explain_comment(String *str)
+void JOIN_CACHE::save_explain_data(struct st_explain_bka_type *explain)
{
- str->append(STRING_WITH_LEN(" ("));
- const char *buffer_type= prev_cache ? "incremental" : "flat";
- str->append(buffer_type);
- str->append(STRING_WITH_LEN(", "));
-
- const char *join_alg="";
+ explain->incremental= MY_TEST(prev_cache);
+
switch (get_join_alg()) {
case BNL_JOIN_ALG:
- join_alg= "BNL";
+ explain->join_alg= "BNL";
break;
case BNLH_JOIN_ALG:
- join_alg= "BNLH";
+ explain->join_alg= "BNLH";
break;
case BKA_JOIN_ALG:
- join_alg= "BKA";
+ explain->join_alg= "BKA";
break;
case BKAH_JOIN_ALG:
- join_alg= "BKAH";
+ explain->join_alg= "BKAH";
break;
default:
DBUG_ASSERT(0);
}
-
- str->append(join_alg);
- str->append(STRING_WITH_LEN(" join"));
- str->append(STRING_WITH_LEN(")"));
}
/**
@@ -2621,18 +2619,17 @@ static void add_mrr_explain_info(String *str, uint mrr_mode, handler *file)
}
}
-
-void JOIN_CACHE_BKA::print_explain_comment(String *str)
+void JOIN_CACHE_BKA::save_explain_data(struct st_explain_bka_type *explain)
{
- JOIN_CACHE::print_explain_comment(str);
- add_mrr_explain_info(str, mrr_mode, join_tab->table->file);
+ JOIN_CACHE::save_explain_data(explain);
+ add_mrr_explain_info(&explain->mrr_type, mrr_mode, join_tab->table->file);
}
-void JOIN_CACHE_BKAH::print_explain_comment(String *str)
+void JOIN_CACHE_BKAH::save_explain_data(struct st_explain_bka_type *explain)
{
- JOIN_CACHE::print_explain_comment(str);
- add_mrr_explain_info(str, mrr_mode, join_tab->table->file);
+ JOIN_CACHE::save_explain_data(explain);
+ add_mrr_explain_info(&explain->mrr_type, mrr_mode, join_tab->table->file);
}
@@ -2641,6 +2638,7 @@ void JOIN_CACHE_BKAH::print_explain_comment(String *str)
SYNOPSIS
init()
+ for_explain join buffer is initialized for explain only
DESCRIPTION
The function initializes the cache structure with a hash table in it.
@@ -2660,7 +2658,7 @@ void JOIN_CACHE_BKAH::print_explain_comment(String *str)
1 otherwise
*/
-int JOIN_CACHE_HASHED::init()
+int JOIN_CACHE_HASHED::init(bool for_explain)
{
int rc= 0;
TABLE_REF *ref= &join_tab->ref;
@@ -2672,8 +2670,8 @@ int JOIN_CACHE_HASHED::init()
key_length= ref->key_length;
- if ((rc= JOIN_CACHE::init()))
- DBUG_RETURN (rc);
+ if ((rc= JOIN_CACHE::init(for_explain)) || for_explain)
+ DBUG_RETURN (rc);
if (!(key_buff= (uchar*) sql_alloc(key_length)))
DBUG_RETURN(1);
@@ -2739,7 +2737,7 @@ int JOIN_CACHE_HASHED::init_hash_table()
key_entries= 0;
/* Calculate the minimal possible value of size_of_key_ofs greater than 1 */
- uint max_size_of_key_ofs= max(2, get_size_of_rec_offset());
+ uint max_size_of_key_ofs= MY_MAX(2, get_size_of_rec_offset());
for (size_of_key_ofs= 2;
size_of_key_ofs <= max_size_of_key_ofs;
size_of_key_ofs+= 2)
@@ -2801,7 +2799,7 @@ int JOIN_CACHE_HASHED::realloc_buffer()
{
int rc;
free();
- rc= test(!(buff= (uchar*) my_malloc(buff_size, MYF(0))));
+ rc= MY_TEST(!(buff= (uchar*) my_malloc(buff_size, MYF(MY_THREAD_SPECIFIC))));
init_hash_table();
reset(TRUE);
return rc;
@@ -3377,7 +3375,7 @@ int JOIN_TAB_SCAN::next()
update_virtual_fields(thd, table);
while (!err && select && (skip_rc= select->skip_record(thd)) <= 0)
{
- if (thd->killed || skip_rc < 0)
+ if (thd->check_killed() || skip_rc < 0)
return 1;
/*
Move to the next record if the last retrieved record does not
@@ -3481,7 +3479,7 @@ bool JOIN_CACHE_BNL::prepare_look_for_matches(bool skip_last)
if (!records)
return TRUE;
reset(FALSE);
- rem_records= records-test(skip_last);
+ rem_records= records - MY_TEST(skip_last);
return rem_records == 0;
}
@@ -3581,6 +3579,7 @@ void JOIN_CACHE_BNL::read_next_candidate_for_match(uchar *rec_ptr)
SYNOPSIS
init
+ for_explain join buffer is initialized for explain only
DESCRIPTION
The function initializes the cache structure. It is supposed to be called
@@ -3595,14 +3594,14 @@ void JOIN_CACHE_BNL::read_next_candidate_for_match(uchar *rec_ptr)
1 otherwise
*/
-int JOIN_CACHE_BNL::init()
+int JOIN_CACHE_BNL::init(bool for_explain)
{
DBUG_ENTER("JOIN_CACHE_BNL::init");
if (!(join_tab_scan= new JOIN_TAB_SCAN(join, join_tab)))
DBUG_RETURN(1);
- DBUG_RETURN(JOIN_CACHE::init());
+ DBUG_RETURN(JOIN_CACHE::init(for_explain));
}
@@ -3767,6 +3766,7 @@ void JOIN_CACHE_BNLH::read_next_candidate_for_match(uchar *rec_ptr)
SYNOPSIS
init
+ for_explain join buffer is initialized for explain only
DESCRIPTION
The function initializes the cache structure. It is supposed to be called
@@ -3781,14 +3781,14 @@ void JOIN_CACHE_BNLH::read_next_candidate_for_match(uchar *rec_ptr)
1 otherwise
*/
-int JOIN_CACHE_BNLH::init()
+int JOIN_CACHE_BNLH::init(bool for_explain)
{
DBUG_ENTER("JOIN_CACHE_BNLH::init");
if (!(join_tab_scan= new JOIN_TAB_SCAN(join, join_tab)))
DBUG_RETURN(1);
- DBUG_RETURN(JOIN_CACHE_HASHED::init());
+ DBUG_RETURN(JOIN_CACHE_HASHED::init(for_explain));
}
@@ -3812,7 +3812,8 @@ uint JOIN_TAB_SCAN_MRR::aux_buffer_incr(ulong recno)
uint incr= 0;
TABLE_REF *ref= &join_tab->ref;
TABLE *tab= join_tab->table;
- uint rec_per_key= tab->key_info[ref->key].rec_per_key[ref->key_parts-1];
+ ha_rows rec_per_key=
+ (ha_rows) tab->key_info[ref->key].actual_rec_per_key(ref->key_parts-1);
set_if_bigger(rec_per_key, 1);
if (recno == 1)
incr= ref->key_length + tab->file->ref_length;
@@ -3893,13 +3894,15 @@ int JOIN_TAB_SCAN_MRR::next()
int rc= join_tab->table->file->multi_range_read_next((range_id_t*)ptr) ? -1 : 0;
if (!rc)
{
- /*
+ /*
If a record in in an incremental cache contains no fields then the
association for the last record in cache will be equal to cache->end_pos
- */
- DBUG_ASSERT((!(mrr_mode & HA_MRR_NO_ASSOCIATION))?
- (cache->buff <= (uchar *) (*ptr) &&
- (uchar *) (*ptr) <= cache->end_pos): TRUE);
+ */
+ /*
+ psergey: this makes no sense where HA_MRR_NO_ASSOC is used.
+ DBUG_ASSERT(cache->buff <= (uchar *) (*ptr) &&
+ (uchar *) (*ptr) <= cache->end_pos);
+ */
if (join_tab->table->vfield)
update_virtual_fields(join->thd, join_tab->table);
}
@@ -3909,663 +3912,665 @@ int JOIN_TAB_SCAN_MRR::next()
static
void bka_range_seq_key_info(void *init_params, uint *length,
- key_part_map *map)
+ key_part_map *map)
{
- TABLE_REF *ref= &(((JOIN_CACHE*)init_params)->join_tab->ref);
- *length= ref->key_length;
- *map= (key_part_map(1) << ref->key_parts) - 1;
+TABLE_REF *ref= &(((JOIN_CACHE*)init_params)->join_tab->ref);
+*length= ref->key_length;
+*map= (key_part_map(1) << ref->key_parts) - 1;
}
/*
- Initialize retrieval of range sequence for BKA join algorithm
-
- SYNOPSIS
- bka_range_seq_init()
- init_params pointer to the BKA join cache object
- n_ranges the number of ranges obtained
- flags combination of MRR flags
-
- DESCRIPTION
- The function interprets init_param as a pointer to a JOIN_CACHE_BKA
- object. The function prepares for an iteration over the join keys
- built for all records from the cache join buffer.
-
- NOTE
- This function are used only as a callback function.
-
- RETURN VALUE
- init_param value that is to be used as a parameter of bka_range_seq_next()
+Initialize retrieval of range sequence for BKA join algorithm
+
+SYNOPSIS
+ bka_range_seq_init()
+ init_params pointer to the BKA join cache object
+ n_ranges the number of ranges obtained
+ flags combination of MRR flags
+
+DESCRIPTION
+ The function interprets init_param as a pointer to a JOIN_CACHE_BKA
+ object. The function prepares for an iteration over the join keys
+ built for all records from the cache join buffer.
+
+NOTE
+ This function are used only as a callback function.
+
+RETURN VALUE
+ init_param value that is to be used as a parameter of bka_range_seq_next()
*/
static
range_seq_t bka_range_seq_init(void *init_param, uint n_ranges, uint flags)
{
- DBUG_ENTER("bka_range_seq_init");
- JOIN_CACHE_BKA *cache= (JOIN_CACHE_BKA *) init_param;
- cache->reset(0);
- DBUG_RETURN((range_seq_t) init_param);
+DBUG_ENTER("bka_range_seq_init");
+JOIN_CACHE_BKA *cache= (JOIN_CACHE_BKA *) init_param;
+cache->reset(0);
+DBUG_RETURN((range_seq_t) init_param);
}
/*
- Get the next range/key over records from the join buffer used by a BKA cache
-
- SYNOPSIS
- bka_range_seq_next()
- seq the value returned by bka_range_seq_init
- range OUT reference to the next range
+Get the next range/key over records from the join buffer used by a BKA cache
- DESCRIPTION
- The function interprets seq as a pointer to a JOIN_CACHE_BKA
- object. The function returns a pointer to the range descriptor
- for the key built over the next record from the join buffer.
-
- NOTE
- This function are used only as a callback function.
-
- RETURN VALUE
- FALSE ok, the range structure filled with info about the next range/key
- TRUE no more ranges
+SYNOPSIS
+ bka_range_seq_next()
+ seq the value returned by bka_range_seq_init
+ range OUT reference to the next range
+
+DESCRIPTION
+ The function interprets seq as a pointer to a JOIN_CACHE_BKA
+ object. The function returns a pointer to the range descriptor
+ for the key built over the next record from the join buffer.
+
+NOTE
+ This function are used only as a callback function.
+
+RETURN VALUE
+ FALSE ok, the range structure filled with info about the next range/key
+ TRUE no more ranges
*/
static
bool bka_range_seq_next(range_seq_t rseq, KEY_MULTI_RANGE *range)
{
- DBUG_ENTER("bka_range_seq_next");
- JOIN_CACHE_BKA *cache= (JOIN_CACHE_BKA *) rseq;
- TABLE_REF *ref= &cache->join_tab->ref;
- key_range *start_key= &range->start_key;
- if ((start_key->length= cache->get_next_key((uchar **) &start_key->key)))
- {
- start_key->keypart_map= (1 << ref->key_parts) - 1;
- start_key->flag= HA_READ_KEY_EXACT;
- range->end_key= *start_key;
- range->end_key.flag= HA_READ_AFTER_KEY;
- range->ptr= (char *) cache->get_curr_rec();
- range->range_flag= EQ_RANGE;
- DBUG_RETURN(0);
- }
- DBUG_RETURN(1);
+DBUG_ENTER("bka_range_seq_next");
+JOIN_CACHE_BKA *cache= (JOIN_CACHE_BKA *) rseq;
+TABLE_REF *ref= &cache->join_tab->ref;
+key_range *start_key= &range->start_key;
+if ((start_key->length= cache->get_next_key((uchar **) &start_key->key)))
+{
+ start_key->keypart_map= (1 << ref->key_parts) - 1;
+ start_key->flag= HA_READ_KEY_EXACT;
+ range->end_key= *start_key;
+ range->end_key.flag= HA_READ_AFTER_KEY;
+ range->ptr= (char *) cache->get_curr_rec();
+ range->range_flag= EQ_RANGE;
+ DBUG_RETURN(0);
+}
+DBUG_RETURN(1);
}
/*
- Check whether range_info orders to skip the next record from BKA buffer
+Check whether range_info orders to skip the next record from BKA buffer
- SYNOPSIS
- bka_range_seq_skip_record()
- seq value returned by bka_range_seq_init()
- range_info information about the next range
- rowid [NOT USED] rowid of the record to be checked
+SYNOPSIS
+ bka_range_seq_skip_record()
+ seq value returned by bka_range_seq_init()
+ range_info information about the next range
+ rowid [NOT USED] rowid of the record to be checked
-
- DESCRIPTION
- The function interprets seq as a pointer to a JOIN_CACHE_BKA object.
- The function returns TRUE if the record with this range_info
- is to be filtered out from the stream of records returned by
- multi_range_read_next().
-
- NOTE
- This function are used only as a callback function.
-
- RETURN VALUE
- 1 record with this range_info is to be filtered out from the stream
- of records returned by multi_range_read_next()
- 0 the record is to be left in the stream
+
+DESCRIPTION
+ The function interprets seq as a pointer to a JOIN_CACHE_BKA object.
+ The function returns TRUE if the record with this range_info
+ is to be filtered out from the stream of records returned by
+ multi_range_read_next().
+
+NOTE
+ This function are used only as a callback function.
+
+RETURN VALUE
+ 1 record with this range_info is to be filtered out from the stream
+ of records returned by multi_range_read_next()
+ 0 the record is to be left in the stream
*/
static
bool bka_range_seq_skip_record(range_seq_t rseq, range_id_t range_info, uchar *rowid)
{
- DBUG_ENTER("bka_range_seq_skip_record");
- JOIN_CACHE_BKA *cache= (JOIN_CACHE_BKA *) rseq;
- bool res= cache->get_match_flag_by_pos((uchar *) range_info) ==
- JOIN_CACHE::MATCH_FOUND;
- DBUG_RETURN(res);
+DBUG_ENTER("bka_range_seq_skip_record");
+JOIN_CACHE_BKA *cache= (JOIN_CACHE_BKA *) rseq;
+bool res= cache->get_match_flag_by_pos((uchar *) range_info) ==
+ JOIN_CACHE::MATCH_FOUND;
+DBUG_RETURN(res);
}
/*
- Check if the record combination from BKA cache matches the index condition
+Check if the record combination from BKA cache matches the index condition
- SYNOPSIS
- bka_skip_index_tuple()
- rseq value returned by bka_range_seq_init()
- range_info record chain for the next range/key returned by MRR
-
- DESCRIPTION
- This is wrapper for JOIN_CACHE_BKA::skip_index_tuple method,
- see comments there.
+SYNOPSIS
+ bka_skip_index_tuple()
+ rseq value returned by bka_range_seq_init()
+ range_info record chain for the next range/key returned by MRR
+
+DESCRIPTION
+ This is wrapper for JOIN_CACHE_BKA::skip_index_tuple method,
+ see comments there.
- NOTE
- This function is used as a RANGE_SEQ_IF::skip_index_tuple callback.
-
- RETURN VALUE
- 0 The record combination satisfies the index condition
- 1 Otherwise
+NOTE
+ This function is used as a RANGE_SEQ_IF::skip_index_tuple callback.
+
+RETURN VALUE
+ 0 The record combination satisfies the index condition
+ 1 Otherwise
*/
static
bool bka_skip_index_tuple(range_seq_t rseq, range_id_t range_info)
{
- DBUG_ENTER("bka_skip_index_tuple");
- JOIN_CACHE_BKA *cache= (JOIN_CACHE_BKA *) rseq;
- THD *thd= cache->thd();
- bool res;
- status_var_increment(thd->status_var.ha_icp_attempts);
- if (!(res= cache->skip_index_tuple(range_info)))
- status_var_increment(thd->status_var.ha_icp_match);
- DBUG_RETURN(res);
+DBUG_ENTER("bka_skip_index_tuple");
+JOIN_CACHE_BKA *cache= (JOIN_CACHE_BKA *) rseq;
+THD *thd= cache->thd();
+bool res;
+status_var_increment(thd->status_var.ha_icp_attempts);
+if (!(res= cache->skip_index_tuple(range_info)))
+ status_var_increment(thd->status_var.ha_icp_match);
+DBUG_RETURN(res);
}
/*
- Prepare to read the record from BKA cache matching the current joined record
-
- SYNOPSIS
- prepare_look_for_matches()
- skip_last <-> ignore the last record in the buffer (always unused here)
-
- DESCRIPTION
- The function prepares to iterate over records in the join cache buffer
- matching the record loaded into the record buffer for join_tab when
- performing join operation by BKA join algorithm. With BKA algorithms the
- record loaded into the record buffer for join_tab always has a direct
- reference to the matching records from the join buffer. When the regular
- BKA join algorithm is employed the record from join_tab can refer to
- only one such record.
- The function sets the counter of the remaining records from the cache
- buffer that would match the current join_tab record to 1.
-
- RETURN VALUE
- TRUE there are no records in the buffer to iterate over
- FALSE otherwise
+Prepare to read the record from BKA cache matching the current joined record
+
+SYNOPSIS
+ prepare_look_for_matches()
+ skip_last <-> ignore the last record in the buffer (always unused here)
+
+DESCRIPTION
+ The function prepares to iterate over records in the join cache buffer
+ matching the record loaded into the record buffer for join_tab when
+ performing join operation by BKA join algorithm. With BKA algorithms the
+ record loaded into the record buffer for join_tab always has a direct
+ reference to the matching records from the join buffer. When the regular
+ BKA join algorithm is employed the record from join_tab can refer to
+ only one such record.
+ The function sets the counter of the remaining records from the cache
+ buffer that would match the current join_tab record to 1.
+
+RETURN VALUE
+ TRUE there are no records in the buffer to iterate over
+ FALSE otherwise
*/
-
+
bool JOIN_CACHE_BKA::prepare_look_for_matches(bool skip_last)
{
- if (!records)
- return TRUE;
- rem_records= 1;
- return FALSE;
+if (!records)
+ return TRUE;
+rem_records= 1;
+return FALSE;
}
/*
- Get the record from the BKA cache matching the current joined record
-
- SYNOPSIS
- get_next_candidate_for_match
-
- DESCRIPTION
- This method is used for iterations over the records from the join
- cache buffer when looking for matches for records from join_tab.
- The method performs the necessary preparations to read the next record
- from the join buffer into the record buffer by the method
- read_next_candidate_for_match, or, to skip the next record from the join
- buffer by the method skip_if_not_needed_match.
- This implementation of the virtual method get_next_candidate_for_match
- just decrements the counter of the records that are to be iterated over
- and returns the value of curr_association as a reference to the position
- of the beginning of the record fields in the buffer.
-
- RETURN VALUE
- pointer to the start of the record fields in the join buffer
- if the there is another record to iterate over, 0 - otherwise.
+Get the record from the BKA cache matching the current joined record
+
+SYNOPSIS
+ get_next_candidate_for_match
+
+DESCRIPTION
+ This method is used for iterations over the records from the join
+ cache buffer when looking for matches for records from join_tab.
+ The method performs the necessary preparations to read the next record
+ from the join buffer into the record buffer by the method
+ read_next_candidate_for_match, or, to skip the next record from the join
+ buffer by the method skip_if_not_needed_match.
+ This implementation of the virtual method get_next_candidate_for_match
+ just decrements the counter of the records that are to be iterated over
+ and returns the value of curr_association as a reference to the position
+ of the beginning of the record fields in the buffer.
+
+RETURN VALUE
+ pointer to the start of the record fields in the join buffer
+ if the there is another record to iterate over, 0 - otherwise.
*/
uchar *JOIN_CACHE_BKA::get_next_candidate_for_match()
{
- if (!rem_records)
- return 0;
- rem_records--;
- return curr_association;
+if (!rem_records)
+ return 0;
+rem_records--;
+return curr_association;
}
/*
- Check whether the matching record from the BKA cache is to be skipped
-
- SYNOPSIS
- skip_next_candidate_for_match
- rec_ptr pointer to the position in the join buffer right after
- the previous record
-
- DESCRIPTION
- This implementation of the virtual function just calls the
- method get_match_flag_by_pos to check whether the record referenced
- by ref_ptr has its match flag set to MATCH_FOUND.
-
- RETURN VALUE
- TRUE the record referenced by rec_ptr has its match flag set to
- MATCH_FOUND
- FALSE otherwise
+Check whether the matching record from the BKA cache is to be skipped
+
+SYNOPSIS
+ skip_next_candidate_for_match
+ rec_ptr pointer to the position in the join buffer right after
+ the previous record
+
+DESCRIPTION
+ This implementation of the virtual function just calls the
+ method get_match_flag_by_pos to check whether the record referenced
+ by ref_ptr has its match flag set to MATCH_FOUND.
+
+RETURN VALUE
+ TRUE the record referenced by rec_ptr has its match flag set to
+ MATCH_FOUND
+ FALSE otherwise
*/
bool JOIN_CACHE_BKA::skip_next_candidate_for_match(uchar *rec_ptr)
{
- return join_tab->check_only_first_match() &&
- (get_match_flag_by_pos(rec_ptr) == MATCH_FOUND);
+return join_tab->check_only_first_match() &&
+ (get_match_flag_by_pos(rec_ptr) == MATCH_FOUND);
}
/*
- Read the next record from the BKA join cache buffer when looking for matches
-
- SYNOPSIS
- read_next_candidate_for_match
- rec_ptr pointer to the position in the join buffer right after
- the previous record
-
- DESCRIPTION
- This implementation of the virtual method read_next_candidate_for_match
- calls the method get_record_by_pos to read the record referenced by rec_ptr
- from the join buffer into the record buffer. If this record refers to
- fields in the other join buffers the call of get_record_by_po ensures that
- these fields are read into the corresponding record buffers as well.
- This function is supposed to be called after a successful call of
- the method get_next_candidate_for_match.
-
- RETURN VALUE
- none
+Read the next record from the BKA join cache buffer when looking for matches
+
+SYNOPSIS
+ read_next_candidate_for_match
+ rec_ptr pointer to the position in the join buffer right after
+ the previous record
+
+DESCRIPTION
+ This implementation of the virtual method read_next_candidate_for_match
+ calls the method get_record_by_pos to read the record referenced by rec_ptr
+ from the join buffer into the record buffer. If this record refers to
+ fields in the other join buffers the call of get_record_by_po ensures that
+ these fields are read into the corresponding record buffers as well.
+ This function is supposed to be called after a successful call of
+ the method get_next_candidate_for_match.
+
+RETURN VALUE
+ none
*/
void JOIN_CACHE_BKA::read_next_candidate_for_match(uchar *rec_ptr)
{
- get_record_by_pos(rec_ptr);
+get_record_by_pos(rec_ptr);
}
/*
- Initialize the BKA join cache
+Initialize the BKA join cache
- SYNOPSIS
- init
+SYNOPSIS
+ init
+ for_explain join buffer is initialized for explain only
- DESCRIPTION
- The function initializes the cache structure. It is supposed to be called
- right after a constructor for the JOIN_CACHE_BKA.
- NOTES
- The function first constructs a companion object of the type
- JOIN_TAB_SCAN_MRR, then it calls the init method of the parent class.
-
- RETURN VALUE
- 0 initialization with buffer allocations has been succeeded
- 1 otherwise
+DESCRIPTION
+ The function initializes the cache structure. It is supposed to be called
+ right after a constructor for the JOIN_CACHE_BKA.
+
+NOTES
+ The function first constructs a companion object of the type
+ JOIN_TAB_SCAN_MRR, then it calls the init method of the parent class.
+
+RETURN VALUE
+ 0 initialization with buffer allocations has been succeeded
+ 1 otherwise
*/
-int JOIN_CACHE_BKA::init()
+int JOIN_CACHE_BKA::init(bool for_explain)
{
- int res;
- bool check_only_first_match= join_tab->check_only_first_match();
+int res;
+bool check_only_first_match= join_tab->check_only_first_match();
- RANGE_SEQ_IF rs_funcs= { bka_range_seq_key_info,
- bka_range_seq_init,
- bka_range_seq_next,
- check_only_first_match ?
- bka_range_seq_skip_record : 0,
- bka_skip_index_tuple };
+RANGE_SEQ_IF rs_funcs= { bka_range_seq_key_info,
+ bka_range_seq_init,
+ bka_range_seq_next,
+ check_only_first_match ?
+ bka_range_seq_skip_record : 0,
+ bka_skip_index_tuple };
- DBUG_ENTER("JOIN_CACHE_BKA::init");
+DBUG_ENTER("JOIN_CACHE_BKA::init");
- JOIN_TAB_SCAN_MRR *jsm;
- if (!(join_tab_scan= jsm= new JOIN_TAB_SCAN_MRR(join, join_tab,
- mrr_mode, rs_funcs)))
- DBUG_RETURN(1);
+JOIN_TAB_SCAN_MRR *jsm;
+if (!(join_tab_scan= jsm= new JOIN_TAB_SCAN_MRR(join, join_tab,
+ mrr_mode, rs_funcs)))
+ DBUG_RETURN(1);
- if ((res= JOIN_CACHE::init()))
- DBUG_RETURN(res);
+if ((res= JOIN_CACHE::init(for_explain)))
+ DBUG_RETURN(res);
- if (use_emb_key)
- jsm->mrr_mode |= HA_MRR_MATERIALIZED_KEYS;
+if (use_emb_key)
+ jsm->mrr_mode |= HA_MRR_MATERIALIZED_KEYS;
- DBUG_RETURN(0);
+DBUG_RETURN(0);
}
/*
- Get the key built over the next record from BKA join buffer
-
- SYNOPSIS
- get_next_key()
- key pointer to the buffer where the key value is to be placed
-
- DESCRIPTION
- The function reads key fields from the current record in the join buffer.
- and builds the key value out of these fields that will be used to access
- the 'join_tab' table. Some of key fields may belong to previous caches.
- They are accessed via record references to the record parts stored in the
- previous join buffers. The other key fields always are placed right after
- the flag fields of the record.
- If the key is embedded, which means that its value can be read directly
- from the join buffer, then *key is set to the beginning of the key in
- this buffer. Otherwise the key is built in the join_tab->ref->key_buff.
- The function returns the length of the key if it succeeds ro read it.
- If is assumed that the functions starts reading at the position of
- the record length which is provided for each records in a BKA cache.
- After the key is built the 'pos' value points to the first position after
- the current record.
- The function just skips the records with MATCH_IMPOSSIBLE in the
- match flag field if there is any.
- The function returns 0 if the initial position is after the beginning
- of the record fields for last record from the join buffer.
-
- RETURN VALUE
- length of the key value - if the starting value of 'pos' points to
- the position before the fields for the last record,
- 0 - otherwise.
+Get the key built over the next record from BKA join buffer
+
+SYNOPSIS
+ get_next_key()
+ key pointer to the buffer where the key value is to be placed
+
+DESCRIPTION
+ The function reads key fields from the current record in the join buffer.
+ and builds the key value out of these fields that will be used to access
+ the 'join_tab' table. Some of key fields may belong to previous caches.
+ They are accessed via record references to the record parts stored in the
+ previous join buffers. The other key fields always are placed right after
+ the flag fields of the record.
+ If the key is embedded, which means that its value can be read directly
+ from the join buffer, then *key is set to the beginning of the key in
+ this buffer. Otherwise the key is built in the join_tab->ref->key_buff.
+ The function returns the length of the key if it succeeds ro read it.
+ If is assumed that the functions starts reading at the position of
+ the record length which is provided for each records in a BKA cache.
+ After the key is built the 'pos' value points to the first position after
+ the current record.
+ The function just skips the records with MATCH_IMPOSSIBLE in the
+ match flag field if there is any.
+ The function returns 0 if the initial position is after the beginning
+ of the record fields for last record from the join buffer.
+
+RETURN VALUE
+ length of the key value - if the starting value of 'pos' points to
+ the position before the fields for the last record,
+ 0 - otherwise.
*/
uint JOIN_CACHE_BKA::get_next_key(uchar ** key)
{
- uint len;
- uint32 rec_len;
- uchar *init_pos;
- JOIN_CACHE *cache;
-
+uint len;
+uint32 rec_len;
+uchar *init_pos;
+JOIN_CACHE *cache;
+
start:
- /* Any record in a BKA cache is prepended with its length */
- DBUG_ASSERT(with_length);
-
- if ((pos+size_of_rec_len) > last_rec_pos || !records)
- return 0;
+/* Any record in a BKA cache is prepended with its length */
+DBUG_ASSERT(with_length);
+
+if ((pos+size_of_rec_len) > last_rec_pos || !records)
+ return 0;
- /* Read the length of the record */
- rec_len= get_rec_length(pos);
- pos+= size_of_rec_len;
- init_pos= pos;
+/* Read the length of the record */
+rec_len= get_rec_length(pos);
+pos+= size_of_rec_len;
+init_pos= pos;
- /* Read a reference to the previous cache if any */
- if (prev_cache)
- pos+= prev_cache->get_size_of_rec_offset();
+/* Read a reference to the previous cache if any */
+if (prev_cache)
+ pos+= prev_cache->get_size_of_rec_offset();
- curr_rec_pos= pos;
+curr_rec_pos= pos;
- /* Read all flag fields of the record */
- read_flag_fields();
+/* Read all flag fields of the record */
+read_flag_fields();
- if (with_match_flag &&
- (Match_flag) curr_rec_pos[0] == MATCH_IMPOSSIBLE )
- {
- pos= init_pos+rec_len;
- goto start;
- }
-
- if (use_emb_key)
- {
- /* An embedded key is taken directly from the join buffer */
- *key= pos;
- len= emb_key_length;
- }
- else
+if (with_match_flag &&
+ (Match_flag) curr_rec_pos[0] == MATCH_IMPOSSIBLE )
+{
+ pos= init_pos+rec_len;
+ goto start;
+}
+
+if (use_emb_key)
+{
+ /* An embedded key is taken directly from the join buffer */
+ *key= pos;
+ len= emb_key_length;
+}
+else
+{
+ /* Read key arguments from previous caches if there are any such fields */
+ if (external_key_arg_fields)
{
- /* Read key arguments from previous caches if there are any such fields */
- if (external_key_arg_fields)
- {
- uchar *rec_ptr= curr_rec_pos;
- uint key_arg_count= external_key_arg_fields;
- CACHE_FIELD **copy_ptr= blob_ptr-key_arg_count;
- for (cache= prev_cache; key_arg_count; cache= cache->prev_cache)
- {
- uint len= 0;
+ uchar *rec_ptr= curr_rec_pos;
+ uint key_arg_count= external_key_arg_fields;
+ CACHE_FIELD **copy_ptr= blob_ptr-key_arg_count;
+ for (cache= prev_cache; key_arg_count; cache= cache->prev_cache)
+ {
+ uint len= 0;
+ DBUG_ASSERT(cache);
+ rec_ptr= cache->get_rec_ref(rec_ptr);
+ while (!cache->referenced_fields)
+ {
+ cache= cache->prev_cache;
DBUG_ASSERT(cache);
rec_ptr= cache->get_rec_ref(rec_ptr);
- while (!cache->referenced_fields)
- {
- cache= cache->prev_cache;
- DBUG_ASSERT(cache);
- rec_ptr= cache->get_rec_ref(rec_ptr);
- }
- while (key_arg_count &&
- cache->read_referenced_field(*copy_ptr, rec_ptr, &len))
- {
- copy_ptr++;
- --key_arg_count;
- }
+ }
+ while (key_arg_count &&
+ cache->read_referenced_field(*copy_ptr, rec_ptr, &len))
+ {
+ copy_ptr++;
+ --key_arg_count;
}
}
-
- /*
- Read the other key arguments from the current record. The fields for
- these arguments are always first in the sequence of the record's fields.
- */
- CACHE_FIELD *copy= field_descr+flag_fields;
- CACHE_FIELD *copy_end= copy+local_key_arg_fields;
- bool blob_in_rec_buff= blob_data_is_in_rec_buff(curr_rec_pos);
- for ( ; copy < copy_end; copy++)
- read_record_field(copy, blob_in_rec_buff);
-
- /* Build the key over the fields read into the record buffers */
- TABLE_REF *ref= &join_tab->ref;
- cp_buffer_from_ref(join->thd, join_tab->table, ref);
- *key= ref->key_buff;
- len= ref->key_length;
}
+
+ /*
+ Read the other key arguments from the current record. The fields for
+ these arguments are always first in the sequence of the record's fields.
+ */
+ CACHE_FIELD *copy= field_descr+flag_fields;
+ CACHE_FIELD *copy_end= copy+local_key_arg_fields;
+ bool blob_in_rec_buff= blob_data_is_in_rec_buff(curr_rec_pos);
+ for ( ; copy < copy_end; copy++)
+ read_record_field(copy, blob_in_rec_buff);
+
+ /* Build the key over the fields read into the record buffers */
+ TABLE_REF *ref= &join_tab->ref;
+ cp_buffer_from_ref(join->thd, join_tab->table, ref);
+ *key= ref->key_buff;
+ len= ref->key_length;
+}
- pos= init_pos+rec_len;
+pos= init_pos+rec_len;
- return len;
+return len;
}
/*
- Check the index condition of the joined table for a record from the BKA cache
-
- SYNOPSIS
- skip_index_tuple()
- range_info pointer to the record returned by MRR
-
- DESCRIPTION
- This function is invoked from MRR implementation to check if an index
- tuple matches the index condition. It is used in the case where the index
- condition actually depends on both columns of the used index and columns
- from previous tables.
-
- NOTES
- Accessing columns of the previous tables requires special handling with
- BKA. The idea of BKA is to collect record combinations in a buffer and
- then do a batch of ref access lookups, i.e. by the time we're doing a
- lookup its previous-records-combination is not in prev_table->record[0]
- but somewhere in the join buffer.
- We need to get it from there back into prev_table(s)->record[0] before we
- can evaluate the index condition, and that's why we need this function
- instead of regular IndexConditionPushdown.
-
- NOTES
- Possible optimization:
- Before we unpack the record from a previous table
- check if this table is used in the condition.
- If so then unpack the record otherwise skip the unpacking.
- This should be done by a special virtual method
- get_partial_record_by_pos().
+Check the index condition of the joined table for a record from the BKA cache
- RETURN VALUE
- 1 the record combination does not satisfies the index condition
- 0 otherwise
+SYNOPSIS
+ skip_index_tuple()
+ range_info pointer to the record returned by MRR
+
+DESCRIPTION
+ This function is invoked from MRR implementation to check if an index
+ tuple matches the index condition. It is used in the case where the index
+ condition actually depends on both columns of the used index and columns
+ from previous tables.
+
+NOTES
+ Accessing columns of the previous tables requires special handling with
+ BKA. The idea of BKA is to collect record combinations in a buffer and
+ then do a batch of ref access lookups, i.e. by the time we're doing a
+ lookup its previous-records-combination is not in prev_table->record[0]
+ but somewhere in the join buffer.
+ We need to get it from there back into prev_table(s)->record[0] before we
+ can evaluate the index condition, and that's why we need this function
+ instead of regular IndexConditionPushdown.
+
+NOTES
+ Possible optimization:
+ Before we unpack the record from a previous table
+ check if this table is used in the condition.
+ If so then unpack the record otherwise skip the unpacking.
+ This should be done by a special virtual method
+ get_partial_record_by_pos().
+
+RETURN VALUE
+ 1 the record combination does not satisfies the index condition
+ 0 otherwise
*/
bool JOIN_CACHE_BKA::skip_index_tuple(range_id_t range_info)
{
- DBUG_ENTER("JOIN_CACHE_BKA::skip_index_tuple");
- get_record_by_pos((uchar*)range_info);
- DBUG_RETURN(!join_tab->cache_idx_cond->val_int());
+DBUG_ENTER("JOIN_CACHE_BKA::skip_index_tuple");
+get_record_by_pos((uchar*)range_info);
+DBUG_RETURN(!join_tab->cache_idx_cond->val_int());
}
/*
- Initialize retrieval of range sequence for the BKAH join algorithm
-
- SYNOPSIS
- bkah_range_seq_init()
- init_params pointer to the BKAH join cache object
- n_ranges the number of ranges obtained
- flags combination of MRR flags
-
- DESCRIPTION
- The function interprets init_param as a pointer to a JOIN_CACHE_BKAH
- object. The function prepares for an iteration over distinct join keys
- built over the records from the cache join buffer.
-
- NOTE
- This function are used only as a callback function.
-
- RETURN VALUE
- init_param value that is to be used as a parameter of
- bkah_range_seq_next()
+Initialize retrieval of range sequence for the BKAH join algorithm
+
+SYNOPSIS
+ bkah_range_seq_init()
+ init_params pointer to the BKAH join cache object
+ n_ranges the number of ranges obtained
+ flags combination of MRR flags
+
+DESCRIPTION
+ The function interprets init_param as a pointer to a JOIN_CACHE_BKAH
+ object. The function prepares for an iteration over distinct join keys
+ built over the records from the cache join buffer.
+
+NOTE
+ This function are used only as a callback function.
+
+RETURN VALUE
+ init_param value that is to be used as a parameter of
+ bkah_range_seq_next()
*/
static
range_seq_t bkah_range_seq_init(void *init_param, uint n_ranges, uint flags)
{
- DBUG_ENTER("bkah_range_seq_init");
- JOIN_CACHE_BKAH *cache= (JOIN_CACHE_BKAH *) init_param;
- cache->reset(0);
- DBUG_RETURN((range_seq_t) init_param);
+DBUG_ENTER("bkah_range_seq_init");
+JOIN_CACHE_BKAH *cache= (JOIN_CACHE_BKAH *) init_param;
+cache->reset(0);
+DBUG_RETURN((range_seq_t) init_param);
}
/*
- Get the next range/key over records from the join buffer of a BKAH cache
-
- SYNOPSIS
- bkah_range_seq_next()
- seq value returned by bkah_range_seq_init()
- range OUT reference to the next range
+Get the next range/key over records from the join buffer of a BKAH cache
- DESCRIPTION
- The function interprets seq as a pointer to a JOIN_CACHE_BKAH
- object. The function returns a pointer to the range descriptor
- for the next unique key built over records from the join buffer.
-
- NOTE
- This function are used only as a callback function.
-
- RETURN VALUE
- FALSE ok, the range structure filled with info about the next range/key
- TRUE no more ranges
+SYNOPSIS
+ bkah_range_seq_next()
+ seq value returned by bkah_range_seq_init()
+ range OUT reference to the next range
+
+DESCRIPTION
+ The function interprets seq as a pointer to a JOIN_CACHE_BKAH
+ object. The function returns a pointer to the range descriptor
+ for the next unique key built over records from the join buffer.
+
+NOTE
+ This function are used only as a callback function.
+
+RETURN VALUE
+ FALSE ok, the range structure filled with info about the next range/key
+ TRUE no more ranges
*/
static
bool bkah_range_seq_next(range_seq_t rseq, KEY_MULTI_RANGE *range)
{
- DBUG_ENTER("bkah_range_seq_next");
- JOIN_CACHE_BKAH *cache= (JOIN_CACHE_BKAH *) rseq;
- TABLE_REF *ref= &cache->join_tab->ref;
- key_range *start_key= &range->start_key;
- if ((start_key->length= cache->get_next_key((uchar **) &start_key->key)))
- {
- start_key->keypart_map= (1 << ref->key_parts) - 1;
- start_key->flag= HA_READ_KEY_EXACT;
- range->end_key= *start_key;
- range->end_key.flag= HA_READ_AFTER_KEY;
- range->ptr= (char *) cache->get_curr_key_chain();
- range->range_flag= EQ_RANGE;
- DBUG_RETURN(0);
- }
- DBUG_RETURN(1);
+DBUG_ENTER("bkah_range_seq_next");
+JOIN_CACHE_BKAH *cache= (JOIN_CACHE_BKAH *) rseq;
+TABLE_REF *ref= &cache->join_tab->ref;
+key_range *start_key= &range->start_key;
+if ((start_key->length= cache->get_next_key((uchar **) &start_key->key)))
+{
+ start_key->keypart_map= (1 << ref->key_parts) - 1;
+ start_key->flag= HA_READ_KEY_EXACT;
+ range->end_key= *start_key;
+ range->end_key.flag= HA_READ_AFTER_KEY;
+ range->ptr= (char *) cache->get_curr_key_chain();
+ range->range_flag= EQ_RANGE;
+ DBUG_RETURN(0);
+}
+DBUG_RETURN(1);
}
/*
- Check whether range_info orders to skip the next record from BKAH join buffer
-
- SYNOPSIS
- bkah_range_seq_skip_record()
- seq value returned by bkah_range_seq_init()
- range_info information about the next range/key returned by MRR
- rowid [NOT USED] rowid of the record to be checked (not used)
-
- DESCRIPTION
- The function interprets seq as a pointer to a JOIN_CACHE_BKAH
- object. The function returns TRUE if the record with this range_info
- is to be filtered out from the stream of records returned by
- multi_range_read_next().
+Check whether range_info orders to skip the next record from BKAH join buffer
- NOTE
- This function are used only as a callback function.
-
- RETURN VALUE
- 1 record with this range_info is to be filtered out from the stream
- of records returned by multi_range_read_next()
- 0 the record is to be left in the stream
+SYNOPSIS
+ bkah_range_seq_skip_record()
+ seq value returned by bkah_range_seq_init()
+ range_info information about the next range/key returned by MRR
+ rowid [NOT USED] rowid of the record to be checked (not used)
+
+DESCRIPTION
+ The function interprets seq as a pointer to a JOIN_CACHE_BKAH
+ object. The function returns TRUE if the record with this range_info
+ is to be filtered out from the stream of records returned by
+ multi_range_read_next().
+
+NOTE
+ This function are used only as a callback function.
+
+RETURN VALUE
+ 1 record with this range_info is to be filtered out from the stream
+ of records returned by multi_range_read_next()
+ 0 the record is to be left in the stream
*/
static
bool bkah_range_seq_skip_record(range_seq_t rseq, range_id_t range_info,
- uchar *rowid)
+ uchar *rowid)
{
- DBUG_ENTER("bkah_range_seq_skip_record");
- JOIN_CACHE_BKAH *cache= (JOIN_CACHE_BKAH *) rseq;
- bool res= cache->check_all_match_flags_for_key((uchar *) range_info);
- DBUG_RETURN(res);
+DBUG_ENTER("bkah_range_seq_skip_record");
+JOIN_CACHE_BKAH *cache= (JOIN_CACHE_BKAH *) rseq;
+bool res= cache->check_all_match_flags_for_key((uchar *) range_info);
+DBUG_RETURN(res);
}
-
+
/*
- Check if the record combination from BKAH cache matches the index condition
+Check if the record combination from BKAH cache matches the index condition
- SYNOPSIS
- bkah_skip_index_tuple()
- rseq value returned by bka_range_seq_init()
- range_info record chain for the next range/key returned by MRR
-
- DESCRIPTION
- This is wrapper for JOIN_CACHE_BKA_UNIQUE::skip_index_tuple method,
- see comments there.
+SYNOPSIS
+ bkah_skip_index_tuple()
+ rseq value returned by bka_range_seq_init()
+ range_info record chain for the next range/key returned by MRR
+
+DESCRIPTION
+ This is wrapper for JOIN_CACHE_BKA_UNIQUE::skip_index_tuple method,
+ see comments there.
- NOTE
- This function is used as a RANGE_SEQ_IF::skip_index_tuple callback.
-
- RETURN VALUE
- 0 some records from the chain satisfy the index condition
- 1 otherwise
+NOTE
+ This function is used as a RANGE_SEQ_IF::skip_index_tuple callback.
+
+RETURN VALUE
+ 0 some records from the chain satisfy the index condition
+ 1 otherwise
*/
static
bool bkah_skip_index_tuple(range_seq_t rseq, range_id_t range_info)
{
- DBUG_ENTER("bka_unique_skip_index_tuple");
- JOIN_CACHE_BKAH *cache= (JOIN_CACHE_BKAH *) rseq;
- THD *thd= cache->thd();
- bool res;
- status_var_increment(thd->status_var.ha_icp_attempts);
- if (!(res= cache->skip_index_tuple(range_info)))
- status_var_increment(thd->status_var.ha_icp_match);
- DBUG_RETURN(res);
+DBUG_ENTER("bka_unique_skip_index_tuple");
+JOIN_CACHE_BKAH *cache= (JOIN_CACHE_BKAH *) rseq;
+THD *thd= cache->thd();
+bool res;
+status_var_increment(thd->status_var.ha_icp_attempts);
+if (!(res= cache->skip_index_tuple(range_info)))
+ status_var_increment(thd->status_var.ha_icp_match);
+DBUG_RETURN(res);
}
/*
- Prepare to read record from BKAH cache matching the current joined record
-
- SYNOPSIS
- prepare_look_for_matches()
- skip_last <-> ignore the last record in the buffer (always unused here)
-
- DESCRIPTION
- The function prepares to iterate over records in the join cache buffer
- matching the record loaded into the record buffer for join_tab when
- performing join operation by BKAH join algorithm. With BKAH algorithm, if
- association labels are used, then record loaded into the record buffer
- for join_tab always has a direct reference to the chain of the mathing
- records from the join buffer. If association labels are not used then
- then the chain of the matching records is obtained by the call of the
- get_key_chain_by_join_key function.
-
- RETURN VALUE
- TRUE there are no records in the buffer to iterate over
- FALSE otherwise
+Prepare to read record from BKAH cache matching the current joined record
+
+SYNOPSIS
+ prepare_look_for_matches()
+ skip_last <-> ignore the last record in the buffer (always unused here)
+
+DESCRIPTION
+ The function prepares to iterate over records in the join cache buffer
+ matching the record loaded into the record buffer for join_tab when
+ performing join operation by BKAH join algorithm. With BKAH algorithm, if
+ association labels are used, then record loaded into the record buffer
+ for join_tab always has a direct reference to the chain of the mathing
+ records from the join buffer. If association labels are not used then
+ then the chain of the matching records is obtained by the call of the
+ get_key_chain_by_join_key function.
+
+RETURN VALUE
+ TRUE there are no records in the buffer to iterate over
+ FALSE otherwise
*/
-
+
bool JOIN_CACHE_BKAH::prepare_look_for_matches(bool skip_last)
{
- last_matching_rec_ref_ptr= next_matching_rec_ref_ptr= 0;
- if (no_association &&
- !(curr_matching_chain= get_matching_chain_by_join_key()))
+last_matching_rec_ref_ptr= next_matching_rec_ref_ptr= 0;
+if (no_association &&
+ !(curr_matching_chain= get_matching_chain_by_join_key())) //psergey: added '!'
return 1;
last_matching_rec_ref_ptr= get_next_rec_ref(curr_matching_chain);
return 0;
@@ -4576,6 +4581,7 @@ bool JOIN_CACHE_BKAH::prepare_look_for_matches(bool skip_last)
SYNOPSIS
init
+ for_explain join buffer is initialized for explain only
DESCRIPTION
The function initializes the cache structure. It is supposed to be called
@@ -4590,11 +4596,11 @@ bool JOIN_CACHE_BKAH::prepare_look_for_matches(bool skip_last)
1 otherwise
*/
-int JOIN_CACHE_BKAH::init()
+int JOIN_CACHE_BKAH::init(bool for_explain)
{
bool check_only_first_match= join_tab->check_only_first_match();
- no_association= test(mrr_mode & HA_MRR_NO_ASSOCIATION);
+ no_association= MY_TEST(mrr_mode & HA_MRR_NO_ASSOCIATION);
RANGE_SEQ_IF rs_funcs= { bka_range_seq_key_info,
bkah_range_seq_init,
@@ -4609,7 +4615,7 @@ int JOIN_CACHE_BKAH::init()
mrr_mode, rs_funcs)))
DBUG_RETURN(1);
- DBUG_RETURN(JOIN_CACHE_HASHED::init());
+ DBUG_RETURN(JOIN_CACHE_HASHED::init(for_explain));
}
diff --git a/sql/sql_join_cache.h b/sql/sql_join_cache.h
index 6953f6881ee..a3e69f92e34 100644
--- a/sql/sql_join_cache.h
+++ b/sql/sql_join_cache.h
@@ -63,12 +63,14 @@ typedef struct st_cache_field {
class JOIN_TAB_SCAN;
+struct st_explain_bka_type;
/*
JOIN_CACHE is the base class to support the implementations of
- Block Nested Loop (BNL) Join Algorithm,
- Block Nested Loop Hash (BNLH) Join Algorithm,
- Batched Key Access (BKA) Join Algorithm.
+
The first algorithm is supported by the derived class JOIN_CACHE_BNL,
the second algorithm is supported by the derived class JOIN_CACHE_BNLH,
while the third algorithm is implemented in two variant supported by
@@ -97,6 +99,9 @@ private:
/* Size of the offset of a field within a record in the cache */
uint size_of_fld_ofs;
+ /* This structure is used only for explain, not for execution */
+ bool for_explain_only;
+
protected:
/* 3 functions below actually do not use the hidden parameter 'this' */
@@ -420,7 +425,7 @@ protected:
/* Shall calculate how much space is remaining in the join buffer */
virtual size_t rem_space()
{
- return max(buff_size-(end_pos-buff)-aux_buff_size,0);
+ return MY_MAX(buff_size-(end_pos-buff)-aux_buff_size,0);
}
/*
@@ -593,7 +598,7 @@ public:
JOIN_CACHE *next_cache;
/* Shall initialize the join cache structure */
- virtual int init();
+ virtual int init(bool for_explain);
/* Get the current size of the cache join buffer */
size_t get_join_buffer_size() { return buff_size; }
@@ -657,7 +662,7 @@ public:
enum_nested_loop_state join_records(bool skip_last);
/* Add a comment on the join algorithm employed by the join cache */
- virtual void print_explain_comment(String *str);
+ virtual void save_explain_data(struct st_explain_bka_type *explain);
THD *thd();
@@ -943,7 +948,7 @@ protected:
*/
size_t rem_space()
{
- return max(last_key_entry-end_pos-aux_buff_size,0);
+ return MY_MAX(last_key_entry-end_pos-aux_buff_size,0);
}
/*
@@ -989,7 +994,7 @@ protected:
public:
/* Initialize a hashed join cache */
- int init();
+ int init(bool for_explain);
/* Reset the buffer of a hashed join cache for reading/writing */
void reset(bool for_writing);
@@ -1125,7 +1130,7 @@ public:
:JOIN_CACHE(j, tab, prev) {}
/* Initialize the BNL cache */
- int init();
+ int init(bool for_explain);
enum Join_algorithm get_join_alg() { return BNL_JOIN_ALG; }
@@ -1192,7 +1197,7 @@ public:
: JOIN_CACHE_HASHED(j, tab, prev) {}
/* Initialize the BNLH cache */
- int init();
+ int init(bool for_explain);
enum Join_algorithm get_join_alg() { return BNLH_JOIN_ALG; }
@@ -1323,7 +1328,7 @@ public:
uchar **get_curr_association_ptr() { return &curr_association; }
/* Initialize the BKA cache */
- int init();
+ int init(bool for_explain);
enum Join_algorithm get_join_alg() { return BKA_JOIN_ALG; }
@@ -1335,7 +1340,7 @@ public:
/* Check index condition of the joined table for a record from BKA cache */
bool skip_index_tuple(range_id_t range_info);
- void print_explain_comment(String *str);
+ void save_explain_data(struct st_explain_bka_type *explain);
};
@@ -1419,12 +1424,12 @@ public:
uchar **get_curr_association_ptr() { return &curr_matching_chain; }
/* Initialize the BKAH cache */
- int init();
+ int init(bool for_explain);
enum Join_algorithm get_join_alg() { return BKAH_JOIN_ALG; }
/* Check index condition of the joined table for a record from BKAH cache */
bool skip_index_tuple(range_id_t range_info);
- void print_explain_comment(String *str);
+ void save_explain_data(struct st_explain_bka_type *explain);
};
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index 2d7926017c2..a0d15093558 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -18,8 +18,8 @@
/* A lexical scanner on a temporary buffer with a yacc interface */
#define MYSQL_LEX 1
+#include <my_global.h>
#include "sql_priv.h"
-#include "unireg.h" // REQUIRED: for other includes
#include "sql_class.h" // sql_lex.h: SQLCOM_END
#include "sql_lex.h"
#include "sql_parse.h" // add_to_list
@@ -299,7 +299,7 @@ Lex_input_stream::reset(char *buffer, unsigned int length)
m_cpp_utf8_processed_ptr= NULL;
next_state= MY_LEX_START;
found_semicolon= NULL;
- ignore_space= test(m_thd->variables.sql_mode & MODE_IGNORE_SPACE);
+ ignore_space= MY_TEST(m_thd->variables.sql_mode & MODE_IGNORE_SPACE);
stmt_prepare_mode= FALSE;
multi_statements= TRUE;
in_comment=NO_COMMENT;
@@ -435,6 +435,21 @@ void Lex_input_stream::body_utf8_append_literal(THD *thd,
m_cpp_utf8_processed_ptr= end_ptr;
}
+void Lex_input_stream::add_digest_token(uint token, LEX_YYSTYPE yylval)
+{
+ if (m_digest != NULL)
+ {
+ m_digest= digest_add_token(m_digest, token, yylval);
+ }
+}
+
+void Lex_input_stream::reduce_digest_token(uint token_left, uint token_right)
+{
+ if (m_digest != NULL)
+ {
+ m_digest= digest_reduce_token(m_digest, token_left, token_right);
+ }
+}
/*
This is called before every query that is to be parsed.
@@ -448,6 +463,8 @@ void lex_start(THD *thd)
DBUG_ENTER("lex_start");
lex->thd= lex->unit.thd= thd;
+
+ DBUG_ASSERT(!lex->explain);
lex->context_stack.empty();
lex->unit.init_query();
@@ -460,6 +477,9 @@ void lex_start(THD *thd)
lex->set_var_list.empty();
lex->param_list.empty();
lex->view_list.empty();
+ lex->with_persistent_for_clause= FALSE;
+ lex->column_list= NULL;
+ lex->index_list= NULL;
lex->prepared_stmt_params.empty();
lex->auxiliary_table_list.empty();
lex->unit.next= lex->unit.master=
@@ -493,12 +513,12 @@ void lex_start(THD *thd)
lex->select_lex.group_list.empty();
lex->select_lex.order_list.empty();
lex->select_lex.gorder_list.empty();
+ lex->m_sql_cmd= NULL;
lex->duplicates= DUP_ERROR;
lex->ignore= 0;
lex->spname= NULL;
lex->sphead= NULL;
lex->spcont= NULL;
- lex->m_stmt= NULL;
lex->proc_list.first= 0;
lex->escape_used= FALSE;
lex->query_tables= 0;
@@ -506,6 +526,8 @@ void lex_start(THD *thd)
lex->expr_allows_subselect= TRUE;
lex->use_only_table_context= FALSE;
lex->parse_vcol_expr= FALSE;
+ lex->check_exists= FALSE;
+ lex->verbose= 0;
lex->name.str= 0;
lex->name.length= 0;
@@ -998,6 +1020,7 @@ int MYSQLlex(YYSTYPE *yylval, THD *thd)
lip->lookahead_token= -1;
*yylval= *(lip->lookahead_yylval);
lip->lookahead_yylval= NULL;
+ lip->add_digest_token(token, yylval);
return token;
}
@@ -1015,8 +1038,10 @@ int MYSQLlex(YYSTYPE *yylval, THD *thd)
token= lex_one_token(yylval, thd);
switch(token) {
case CUBE_SYM:
+ lip->add_digest_token(WITH_CUBE_SYM, yylval);
return WITH_CUBE_SYM;
case ROLLUP_SYM:
+ lip->add_digest_token(WITH_ROLLUP_SYM, yylval);
return WITH_ROLLUP_SYM;
default:
/*
@@ -1025,6 +1050,7 @@ int MYSQLlex(YYSTYPE *yylval, THD *thd)
lip->lookahead_yylval= lip->yylval;
lip->yylval= NULL;
lip->lookahead_token= token;
+ lip->add_digest_token(WITH, yylval);
return WITH;
}
break;
@@ -1032,6 +1058,7 @@ int MYSQLlex(YYSTYPE *yylval, THD *thd)
break;
}
+ lip->add_digest_token(token, yylval);
return token;
}
@@ -1571,7 +1598,14 @@ static int lex_one_token(YYSTYPE *yylval, THD *thd)
version= (ulong) my_strtoll10(lip->get_ptr(), &end_ptr, &error);
- if (version <= MYSQL_VERSION_ID)
+ /*
+ MySQL-5.7 has new features and might have new SQL syntax that
+ MariaDB-10.0 does not understand. Ignore all versioned comments
+ with MySQL versions in the range 50700-999999, but
+ do not ignore MariaDB specific comments for the same versions.
+ */
+ if (version <= MYSQL_VERSION_ID &&
+ (version < 50700 || version > 99999 || maria_comment_syntax))
{
/* Accept 'M' 'm' 'm' 'd' 'd' */
lip->yySkipn(length);
@@ -1766,50 +1800,6 @@ static int lex_one_token(YYSTYPE *yylval, THD *thd)
}
-/**
- Construct a copy of this object to be used for mysql_alter_table
- and mysql_create_table.
-
- Historically, these two functions modify their Alter_info
- arguments. This behaviour breaks re-execution of prepared
- statements and stored procedures and is compensated by always
- supplying a copy of Alter_info to these functions.
-
- @return You need to use check the error in THD for out
- of memory condition after calling this function.
-*/
-
-Alter_info::Alter_info(const Alter_info &rhs, MEM_ROOT *mem_root)
- :drop_list(rhs.drop_list, mem_root),
- alter_list(rhs.alter_list, mem_root),
- key_list(rhs.key_list, mem_root),
- create_list(rhs.create_list, mem_root),
- flags(rhs.flags),
- keys_onoff(rhs.keys_onoff),
- tablespace_op(rhs.tablespace_op),
- partition_names(rhs.partition_names, mem_root),
- num_parts(rhs.num_parts),
- change_level(rhs.change_level),
- datetime_field(rhs.datetime_field),
- error_if_not_empty(rhs.error_if_not_empty)
-{
- /*
- Make deep copies of used objects.
- This is not a fully deep copy - clone() implementations
- of Alter_drop, Alter_column, Key, foreign_key, Key_part_spec
- do not copy string constants. At the same length the only
- reason we make a copy currently is that ALTER/CREATE TABLE
- code changes input Alter_info definitions, but string
- constants never change.
- */
- list_copy_and_replace_each_value(drop_list, mem_root);
- list_copy_and_replace_each_value(alter_list, mem_root);
- list_copy_and_replace_each_value(key_list, mem_root);
- list_copy_and_replace_each_value(create_list, mem_root);
- /* partition_names are not deeply copied currently */
-}
-
-
void trim_whitespace(CHARSET_INFO *cs, LEX_STRING *str)
{
/*
@@ -1904,6 +1894,7 @@ void st_select_lex::init_query()
ref_pointer_array= 0;
ref_pointer_array_size= 0;
select_n_where_fields= 0;
+ select_n_reserved= 0;
select_n_having_items= 0;
n_sum_items= 0;
n_child_sum_items= 0;
@@ -1917,6 +1908,7 @@ void st_select_lex::init_query()
nest_level= 0;
link_next= 0;
prep_leaf_list_state= UNINIT;
+ have_merged_subqueries= FALSE;
bzero((char*) expr_cache_may_be_used, sizeof(expr_cache_may_be_used));
m_non_agg_field_used= false;
m_agg_func_used= false;
@@ -2218,12 +2210,13 @@ bool st_select_lex_node::inc_in_sum_expr() { return 1; }
uint st_select_lex_node::get_in_sum_expr() { return 0; }
TABLE_LIST* st_select_lex_node::get_table_list() { return 0; }
List<Item>* st_select_lex_node::get_item_list() { return 0; }
-TABLE_LIST *st_select_lex_node::add_table_to_list (THD *thd, Table_ident *table,
+TABLE_LIST *st_select_lex_node::add_table_to_list(THD *thd, Table_ident *table,
LEX_STRING *alias,
ulong table_join_options,
thr_lock_type flags,
enum_mdl_type mdl_type,
List<Index_hint> *hints,
+ List<String> *partition_names,
LEX_STRING *option)
{
return 0;
@@ -2352,6 +2345,7 @@ bool st_select_lex::setup_ref_array(THD *thd, uint order_group_num)
const uint n_elems= (n_sum_items +
n_child_sum_items +
item_list.elements +
+ select_n_reserved +
select_n_having_items +
select_n_where_fields +
order_group_num) * 5;
@@ -2597,14 +2591,15 @@ void Query_tables_list::destroy_query_tables_list()
*/
LEX::LEX()
- :result(0), option_type(OPT_DEFAULT), is_lex_started(0),
+ : explain(NULL),
+ result(0), option_type(OPT_DEFAULT), is_lex_started(0),
limit_rows_examined_cnt(ULONGLONG_MAX)
{
my_init_dynamic_array2(&plugins, sizeof(plugin_ref),
plugins_static_buffer,
INITIAL_LEX_PLUGIN_LIST_SIZE,
- INITIAL_LEX_PLUGIN_LIST_SIZE);
+ INITIAL_LEX_PLUGIN_LIST_SIZE, 0);
reset_query_tables_list(TRUE);
mi.init();
}
@@ -2645,7 +2640,8 @@ bool LEX::can_be_merged()
if (tmp_unit->first_select()->parent_lex == this &&
(tmp_unit->item == 0 ||
(tmp_unit->item->place() != IN_WHERE &&
- tmp_unit->item->place() != IN_ON)))
+ tmp_unit->item->place() != IN_ON &&
+ tmp_unit->item->place() != SELECT_LIST)))
{
selects_allow_merge= 0;
break;
@@ -2927,7 +2923,7 @@ void st_select_lex_unit::set_limit(st_select_lex *sl)
val= fix_fields_successful ? item->val_uint() : 0;
}
else
- val= ULL(0);
+ val= 0;
offset_limit_cnt= (ha_rows)val;
#ifndef BIG_TABLES
@@ -3126,7 +3122,7 @@ TABLE_LIST *LEX::unlink_first_table(bool *link_to_local)
/*
and from local list if it is not empty
*/
- if ((*link_to_local= test(select_lex.table_list.first)))
+ if ((*link_to_local= MY_TEST(select_lex.table_list.first)))
{
select_lex.context.table_list=
select_lex.context.first_name_resolution_table= first->next_local;
@@ -3534,7 +3530,7 @@ bool st_select_lex::optimize_unflattened_subqueries(bool const_only)
if (options & SELECT_DESCRIBE)
{
/* Optimize the subquery in the context of EXPLAIN. */
- sl->set_explain_type();
+ sl->set_explain_type(FALSE);
sl->options|= SELECT_DESCRIBE;
inner_join->select_options|= SELECT_DESCRIBE;
}
@@ -3543,6 +3539,18 @@ bool st_select_lex::optimize_unflattened_subqueries(bool const_only)
is_correlated_unit|= sl->is_correlated;
inner_join->select_options= save_options;
un->thd->lex->current_select= save_select;
+
+ Explain_query *eq;
+ if ((eq= inner_join->thd->lex->explain))
+ {
+ Explain_select *expl_sel;
+ if ((expl_sel= eq->get_select(inner_join->select_lex->select_number)))
+ {
+ sl->set_explain_type(TRUE);
+ expl_sel->select_type= sl->type;
+ }
+ }
+
if (empty_union_result)
{
/*
@@ -3865,7 +3873,7 @@ void SELECT_LEX::update_used_tables()
do
{
bool maybe_null;
- if ((maybe_null= test(embedding->outer_join)))
+ if ((maybe_null= MY_TEST(embedding->outer_join)))
{
tl->table->maybe_null= maybe_null;
break;
@@ -3955,37 +3963,40 @@ void st_select_lex::update_correlated_cache()
while ((tl= ti++))
{
if (tl->on_expr)
- is_correlated|= test(tl->on_expr->used_tables() & OUTER_REF_TABLE_BIT);
+ is_correlated|= MY_TEST(tl->on_expr->used_tables() & OUTER_REF_TABLE_BIT);
for (TABLE_LIST *embedding= tl->embedding ; embedding ;
embedding= embedding->embedding)
{
if (embedding->on_expr)
- is_correlated|= test(embedding->on_expr->used_tables() &
- OUTER_REF_TABLE_BIT);
+ is_correlated|= MY_TEST(embedding->on_expr->used_tables() &
+ OUTER_REF_TABLE_BIT);
}
}
if (join->conds)
- is_correlated|= test(join->conds->used_tables() & OUTER_REF_TABLE_BIT);
+ is_correlated|= MY_TEST(join->conds->used_tables() & OUTER_REF_TABLE_BIT);
if (join->having)
- is_correlated|= test(join->having->used_tables() & OUTER_REF_TABLE_BIT);
+ is_correlated|= MY_TEST(join->having->used_tables() & OUTER_REF_TABLE_BIT);
if (join->tmp_having)
- is_correlated|= test(join->tmp_having->used_tables() & OUTER_REF_TABLE_BIT);
+ is_correlated|= MY_TEST(join->tmp_having->used_tables() &
+ OUTER_REF_TABLE_BIT);
Item *item;
List_iterator_fast<Item> it(join->fields_list);
while ((item= it++))
- is_correlated|= test(item->used_tables() & OUTER_REF_TABLE_BIT);
+ is_correlated|= MY_TEST(item->used_tables() & OUTER_REF_TABLE_BIT);
for (ORDER *order= group_list.first; order; order= order->next)
- is_correlated|= test((*order->item)->used_tables() & OUTER_REF_TABLE_BIT);
+ is_correlated|= MY_TEST((*order->item)->used_tables() &
+ OUTER_REF_TABLE_BIT);
if (!master_unit()->is_union())
{
for (ORDER *order= order_list.first; order; order= order->next)
- is_correlated|= test((*order->item)->used_tables() & OUTER_REF_TABLE_BIT);
+ is_correlated|= MY_TEST((*order->item)->used_tables() &
+ OUTER_REF_TABLE_BIT);
}
if (!is_correlated)
@@ -3995,9 +4006,12 @@ void st_select_lex::update_correlated_cache()
/**
Set the EXPLAIN type for this subquery.
+
+ @param on_the_fly TRUE<=> We're running a SHOW EXPLAIN command, so we must
+ not change any variables
*/
-void st_select_lex::set_explain_type()
+void st_select_lex::set_explain_type(bool on_the_fly)
{
bool is_primary= FALSE;
if (next_select())
@@ -4019,6 +4033,9 @@ void st_select_lex::set_explain_type()
}
}
+ if (on_the_fly && !is_primary && have_merged_subqueries)
+ is_primary= TRUE;
+
SELECT_LEX *first= master_unit()->first_select();
/* drop UNCACHEABLE_EXPLAIN, because it is for internal usage only */
uint8 is_uncacheable= (uncacheable & ~UNCACHEABLE_EXPLAIN);
@@ -4071,10 +4088,15 @@ void st_select_lex::set_explain_type()
else
{
type= is_uncacheable ? "UNCACHEABLE UNION": "UNION";
+ if (this == master_unit()->fake_select_lex)
+ type= "UNION RESULT";
+
}
}
}
- options|= SELECT_DESCRIBE;
+
+ if (!on_the_fly)
+ options|= SELECT_DESCRIBE;
}
@@ -4115,7 +4137,8 @@ void SELECT_LEX::increase_derived_records(ha_rows records)
void SELECT_LEX::mark_const_derived(bool empty)
{
TABLE_LIST *derived= master_unit()->derived;
- if (!join->thd->lex->describe && derived)
+ /* join == NULL in DELETE ... RETURNING */
+ if (!(join && join->thd->lex->describe) && derived)
{
if (!empty)
increase_derived_records(1);
@@ -4245,6 +4268,87 @@ bool st_select_lex::is_merged_child_of(st_select_lex *ancestor)
return all_merged;
}
+/*
+ This is used by SHOW EXPLAIN. It assuses query plan has been already
+ collected into QPF structures and we only need to print it out.
+*/
+
+int LEX::print_explain(select_result_sink *output, uint8 explain_flags,
+ bool *printed_anything)
+{
+ int res;
+ if (explain && explain->have_query_plan())
+ {
+ res= explain->print_explain(output, explain_flags);
+ *printed_anything= true;
+ }
+ else
+ {
+ res= 0;
+ *printed_anything= false;
+ }
+ return res;
+}
+
+
+/*
+ Save explain structures of a UNION. The only variable member is whether the
+ union has "Using filesort".
+
+ There is also save_union_explain_part2() function, which is called before we read
+ UNION's output.
+
+ The reason for it is examples like this:
+
+ SELECT col1 FROM t1 UNION SELECT col2 FROM t2 ORDER BY (select ... from t3 ...)
+
+ Here, the (select ... from t3 ...) subquery must be a child of UNION's
+ st_select_lex. However, it is not connected as child until a very late
+ stage in execution.
+*/
+
+int st_select_lex_unit::save_union_explain(Explain_query *output)
+{
+ SELECT_LEX *first= first_select();
+ Explain_union *eu= new (output->mem_root) Explain_union;
+
+ for (SELECT_LEX *sl= first; sl; sl= sl->next_select())
+ eu->add_select(sl->select_number);
+
+ eu->fake_select_type= "UNION RESULT";
+ eu->using_filesort= MY_TEST(global_parameters->order_list.first);
+
+ // Save the UNION node
+ output->add_node(eu);
+
+ if (eu->get_select_id() == 1)
+ output->query_plan_ready();
+
+ return 0;
+}
+
+
+/*
+ @see st_select_lex_unit::save_union_explain
+*/
+
+int st_select_lex_unit::save_union_explain_part2(Explain_query *output)
+{
+ Explain_union *eu= output->get_union(first_select()->select_number);
+ if (fake_select_lex)
+ {
+ for (SELECT_LEX_UNIT *unit= fake_select_lex->first_inner_unit();
+ unit; unit= unit->next_unit())
+ {
+ if (!(unit->item && unit->item->eliminated))
+ {
+ eu->add_child(unit->first_select()->select_number);
+ }
+ }
+ }
+ return 0;
+}
+
/**
A routine used by the parser to decide whether we are specifying a full
@@ -4260,8 +4364,8 @@ bool st_select_lex::is_merged_child_of(st_select_lex *ancestor)
bool LEX::is_partition_management() const
{
return (sql_command == SQLCOM_ALTER_TABLE &&
- (alter_info.flags == ALTER_ADD_PARTITION ||
- alter_info.flags == ALTER_REORGANIZE_PARTITION));
+ (alter_info.flags == Alter_info::ALTER_ADD_PARTITION ||
+ alter_info.flags == Alter_info::ALTER_REORGANIZE_PARTITION));
}
#ifdef MYSQL_SERVER
@@ -4411,6 +4515,3 @@ void binlog_unsafe_map_init()
}
#endif
-#ifdef HAVE_EXPLICIT_TEMPLATE_INSTANTIATION
-template class Mem_root_array<ORDER*, true>;
-#endif
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index b13befd8dbe..6ba384e93a0 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -26,6 +26,8 @@
#include "item.h" /* From item_subselect.h: subselect_union_engine */
#include "thr_lock.h" /* thr_lock_type, TL_UNLOCK */
#include "mem_root_array.h"
+#include "sql_cmd.h"
+#include "sql_alter.h" // Alter_info
/* YACC and LEX Definitions */
@@ -43,11 +45,9 @@ class Event_parse_data;
class set_var_base;
class sys_var;
class Item_func_match;
-class Alter_drop;
-class Alter_column;
-class Key;
class File_parser;
class Key_part_spec;
+struct sql_digest_state;
#ifdef MYSQL_SERVER
/*
@@ -125,6 +125,7 @@ struct LEX_TYPE
#include "lex_symbol.h"
#if MYSQL_LEX
#include "item_func.h" /* Cast_target used in sql_yacc.h */
+#include "sql_get_diagnostics.h" /* Types used in sql_yacc.h */
#include "sql_yacc.h"
#define LEX_YYSTYPE YYSTYPE *
#else
@@ -133,85 +134,6 @@ struct LEX_TYPE
#endif
#endif
-/*
- When a command is added here, be sure it's also added in mysqld.cc
- in "struct show_var_st status_vars[]= {" ...
-
- If the command returns a result set or is not allowed in stored
- functions or triggers, please also make sure that
- sp_get_flags_for_command (sp_head.cc) returns proper flags for the
- added SQLCOM_.
-*/
-
-enum enum_sql_command {
- SQLCOM_SELECT, SQLCOM_CREATE_TABLE, SQLCOM_CREATE_INDEX, SQLCOM_ALTER_TABLE,
- SQLCOM_UPDATE, SQLCOM_INSERT, SQLCOM_INSERT_SELECT,
- SQLCOM_DELETE, SQLCOM_TRUNCATE, SQLCOM_DROP_TABLE, SQLCOM_DROP_INDEX,
-
- SQLCOM_SHOW_DATABASES, SQLCOM_SHOW_TABLES, SQLCOM_SHOW_FIELDS,
- SQLCOM_SHOW_KEYS, SQLCOM_SHOW_VARIABLES, SQLCOM_SHOW_STATUS,
- SQLCOM_SHOW_ENGINE_LOGS, SQLCOM_SHOW_ENGINE_STATUS, SQLCOM_SHOW_ENGINE_MUTEX,
- SQLCOM_SHOW_PROCESSLIST, SQLCOM_SHOW_MASTER_STAT, SQLCOM_SHOW_SLAVE_STAT,
- SQLCOM_SHOW_GRANTS, SQLCOM_SHOW_CREATE, SQLCOM_SHOW_CHARSETS,
- SQLCOM_SHOW_COLLATIONS, SQLCOM_SHOW_CREATE_DB, SQLCOM_SHOW_TABLE_STATUS,
- SQLCOM_SHOW_TRIGGERS,
-
- SQLCOM_LOAD,SQLCOM_SET_OPTION,SQLCOM_LOCK_TABLES,SQLCOM_UNLOCK_TABLES,
- SQLCOM_GRANT,
- SQLCOM_CHANGE_DB, SQLCOM_CREATE_DB, SQLCOM_DROP_DB, SQLCOM_ALTER_DB,
- SQLCOM_REPAIR, SQLCOM_REPLACE, SQLCOM_REPLACE_SELECT,
- SQLCOM_CREATE_FUNCTION, SQLCOM_DROP_FUNCTION,
- SQLCOM_REVOKE,SQLCOM_OPTIMIZE, SQLCOM_CHECK,
- SQLCOM_ASSIGN_TO_KEYCACHE, SQLCOM_PRELOAD_KEYS,
- SQLCOM_FLUSH, SQLCOM_KILL, SQLCOM_ANALYZE,
- SQLCOM_ROLLBACK, SQLCOM_ROLLBACK_TO_SAVEPOINT,
- SQLCOM_COMMIT, SQLCOM_SAVEPOINT, SQLCOM_RELEASE_SAVEPOINT,
- SQLCOM_SLAVE_START, SQLCOM_SLAVE_STOP,
- SQLCOM_BEGIN, SQLCOM_CHANGE_MASTER,
- SQLCOM_RENAME_TABLE,
- SQLCOM_RESET, SQLCOM_PURGE, SQLCOM_PURGE_BEFORE, SQLCOM_SHOW_BINLOGS,
- SQLCOM_SHOW_OPEN_TABLES,
- SQLCOM_HA_OPEN, SQLCOM_HA_CLOSE, SQLCOM_HA_READ,
- SQLCOM_SHOW_SLAVE_HOSTS, SQLCOM_DELETE_MULTI, SQLCOM_UPDATE_MULTI,
- SQLCOM_SHOW_BINLOG_EVENTS, SQLCOM_DO,
- SQLCOM_SHOW_WARNS, SQLCOM_EMPTY_QUERY, SQLCOM_SHOW_ERRORS,
- SQLCOM_SHOW_STORAGE_ENGINES, SQLCOM_SHOW_PRIVILEGES,
- SQLCOM_HELP, SQLCOM_CREATE_USER, SQLCOM_DROP_USER, SQLCOM_RENAME_USER,
- SQLCOM_REVOKE_ALL, SQLCOM_CHECKSUM,
- SQLCOM_CREATE_PROCEDURE, SQLCOM_CREATE_SPFUNCTION, SQLCOM_CALL,
- SQLCOM_DROP_PROCEDURE, SQLCOM_ALTER_PROCEDURE,SQLCOM_ALTER_FUNCTION,
- SQLCOM_SHOW_CREATE_PROC, SQLCOM_SHOW_CREATE_FUNC,
- SQLCOM_SHOW_STATUS_PROC, SQLCOM_SHOW_STATUS_FUNC,
- SQLCOM_PREPARE, SQLCOM_EXECUTE, SQLCOM_DEALLOCATE_PREPARE,
- SQLCOM_CREATE_VIEW, SQLCOM_DROP_VIEW,
- SQLCOM_CREATE_TRIGGER, SQLCOM_DROP_TRIGGER,
- SQLCOM_XA_START, SQLCOM_XA_END, SQLCOM_XA_PREPARE,
- SQLCOM_XA_COMMIT, SQLCOM_XA_ROLLBACK, SQLCOM_XA_RECOVER,
- SQLCOM_SHOW_PROC_CODE, SQLCOM_SHOW_FUNC_CODE,
- SQLCOM_ALTER_TABLESPACE,
- SQLCOM_INSTALL_PLUGIN, SQLCOM_UNINSTALL_PLUGIN,
- SQLCOM_SHOW_AUTHORS, SQLCOM_BINLOG_BASE64_EVENT,
- SQLCOM_SHOW_PLUGINS,
- SQLCOM_SHOW_CONTRIBUTORS,
- SQLCOM_CREATE_SERVER, SQLCOM_DROP_SERVER, SQLCOM_ALTER_SERVER,
- SQLCOM_CREATE_EVENT, SQLCOM_ALTER_EVENT, SQLCOM_DROP_EVENT,
- SQLCOM_SHOW_CREATE_EVENT, SQLCOM_SHOW_EVENTS,
- SQLCOM_SHOW_CREATE_TRIGGER,
- SQLCOM_ALTER_DB_UPGRADE,
- SQLCOM_SHOW_PROFILE, SQLCOM_SHOW_PROFILES,
- SQLCOM_SIGNAL, SQLCOM_RESIGNAL,
- SQLCOM_SHOW_RELAYLOG_EVENTS,
- SQLCOM_SHOW_USER_STATS, SQLCOM_SHOW_TABLE_STATS, SQLCOM_SHOW_INDEX_STATS,
- SQLCOM_SHOW_CLIENT_STATS,
-
- /*
- When a command is added here, be sure it's also added in mysqld.cc
- in "struct show_var_st status_vars[]= {" ...
- */
- /* This should be the last !!! */
- SQLCOM_END
-};
-
// describe/explain types
#define DESCRIBE_NORMAL 1
#define DESCRIBE_EXTENDED 2
@@ -295,7 +217,11 @@ struct LEX_MASTER_INFO
DYNAMIC_ARRAY repl_ignore_server_ids;
char *host, *user, *password, *log_file_name;
char *ssl_key, *ssl_cert, *ssl_ca, *ssl_capath, *ssl_cipher;
+ char *ssl_crl, *ssl_crlpath;
char *relay_log_name;
+ LEX_STRING connection_name;
+ /* Value in START SLAVE UNTIL master_gtid_pos=xxx */
+ LEX_STRING gtid_pos_str;
ulonglong pos;
ulong relay_log_pos;
ulong server_id;
@@ -307,12 +233,15 @@ struct LEX_MASTER_INFO
*/
enum {LEX_MI_UNCHANGED, LEX_MI_DISABLE, LEX_MI_ENABLE}
ssl, ssl_verify_server_cert, heartbeat_opt, repl_ignore_server_ids_opt;
+ enum {
+ LEX_GTID_UNCHANGED, LEX_GTID_NO, LEX_GTID_CURRENT_POS, LEX_GTID_SLAVE_POS
+ } use_gtid_opt;
void init()
{
bzero(this, sizeof(*this));
my_init_dynamic_array(&repl_ignore_server_ids,
- sizeof(::server_id), 0, 16);
+ sizeof(::server_id), 0, 16, MYF(0));
}
void reset()
{
@@ -323,6 +252,9 @@ struct LEX_MASTER_INFO
heartbeat_period= 0;
ssl= ssl_verify_server_cert= heartbeat_opt=
repl_ignore_server_ids_opt= LEX_MI_UNCHANGED;
+ gtid_pos_str.length= 0;
+ gtid_pos_str.str= NULL;
+ use_gtid_opt= LEX_GTID_UNCHANGED;
}
};
@@ -342,11 +274,6 @@ enum olap_type
UNSPECIFIED_OLAP_TYPE, CUBE_TYPE, ROLLUP_TYPE
};
-enum tablespace_op_type
-{
- NO_TABLESPACE_OP, DISCARD_TABLESPACE, IMPORT_TABLESPACE
-};
-
/*
String names used to print a statement with index hints.
Keep in sync with index_hint_type.
@@ -365,6 +292,8 @@ typedef uchar index_clause_map;
#define INDEX_HINT_MASK_ALL (INDEX_HINT_MASK_JOIN | INDEX_HINT_MASK_GROUP | \
INDEX_HINT_MASK_ORDER)
+class select_result_sink;
+
/* Single element of an USE/FORCE/IGNORE INDEX list specified as a SQL hint */
class Index_hint : public Sql_alloc
{
@@ -588,6 +517,7 @@ public:
thr_lock_type flags= TL_UNLOCK,
enum_mdl_type mdl_type= MDL_SHARED_READ,
List<Index_hint> *hints= 0,
+ List<String> *partition_names= 0,
LEX_STRING *option= 0);
virtual void set_lock_for_tables(thr_lock_type lock_type) {}
@@ -614,7 +544,12 @@ class select_result;
class JOIN;
class select_union;
class Procedure;
+class Explain_query;
+void delete_explain_query(LEX *lex);
+void create_explain_query(LEX *lex, MEM_ROOT *mem_root);
+void create_explain_query_if_not_exists(LEX *lex, MEM_ROOT *mem_root);
+bool print_explain_query(LEX *lex, THD *thd, String *str);
class st_select_lex_unit: public st_select_lex_node {
protected:
@@ -725,6 +660,9 @@ public:
friend int subselect_union_engine::exec();
List<Item> *get_unit_column_types();
+
+ int save_union_explain(Explain_query *output);
+ int save_union_explain_part2(Explain_query *output);
};
typedef class st_select_lex_unit SELECT_LEX_UNIT;
@@ -785,6 +723,12 @@ public:
those converted to jtbm nests. The list is emptied when conversion is done.
*/
List<Item_in_subselect> sj_subselects;
+
+ /*
+ Needed to correctly generate 'PRIMARY' or 'SIMPLE' for select_type column
+ of EXPLAIN
+ */
+ bool have_merged_subqueries;
List<TABLE_LIST> leaf_tables;
List<TABLE_LIST> leaf_tables_exec;
@@ -818,6 +762,8 @@ public:
and all inner subselects.
*/
uint select_n_where_fields;
+ /* reserved for exists 2 in */
+ uint select_n_reserved;
enum_parsing_place parsing_place; /* where we are parsing expression */
bool with_sum_func; /* sum function indicator */
@@ -940,6 +886,7 @@ public:
thr_lock_type flags= TL_UNLOCK,
enum_mdl_type mdl_type= MDL_SHARED_READ,
List<Index_hint> *hints= 0,
+ List<String> *partition_names= 0,
LEX_STRING *option= 0);
TABLE_LIST* get_table_list();
bool init_nested_join(THD *thd);
@@ -1011,9 +958,13 @@ public:
void clear_index_hints(void) { index_hints= NULL; }
bool is_part_of_union() { return master_unit()->is_union(); }
+ bool is_top_level_node()
+ {
+ return (select_number == 1) && !is_part_of_union();
+ }
bool optimize_unflattened_subqueries(bool const_only);
/* Set the EXPLAIN type for this subquery. */
- void set_explain_type();
+ void set_explain_type(bool on_the_fly);
bool handle_derived(LEX *lex, uint phases);
void append_table_to_list(TABLE_LIST *TABLE_LIST::*link, TABLE_LIST *table);
bool get_free_table_map(table_map *map, uint *tablenr);
@@ -1037,6 +988,7 @@ public:
bool save_leaf_tables(THD *thd);
bool save_prep_leaf_tables(THD *thd);
+
bool is_merged_child_of(st_select_lex *ancestor);
/*
@@ -1076,110 +1028,6 @@ inline bool st_select_lex_unit::is_union ()
first_select()->next_select()->linkage == UNION_TYPE;
}
-#define ALTER_ADD_COLUMN (1L << 0)
-#define ALTER_DROP_COLUMN (1L << 1)
-#define ALTER_CHANGE_COLUMN (1L << 2)
-#define ALTER_ADD_INDEX (1L << 3)
-#define ALTER_DROP_INDEX (1L << 4)
-#define ALTER_RENAME (1L << 5)
-#define ALTER_ORDER (1L << 6)
-#define ALTER_OPTIONS (1L << 7)
-#define ALTER_CHANGE_COLUMN_DEFAULT (1L << 8)
-#define ALTER_KEYS_ONOFF (1L << 9)
-#define ALTER_CONVERT (1L << 10)
-#define ALTER_RECREATE (1L << 11)
-#define ALTER_ADD_PARTITION (1L << 12)
-#define ALTER_DROP_PARTITION (1L << 13)
-#define ALTER_COALESCE_PARTITION (1L << 14)
-#define ALTER_REORGANIZE_PARTITION (1L << 15)
-#define ALTER_PARTITION (1L << 16)
-#define ALTER_ADMIN_PARTITION (1L << 17)
-#define ALTER_TABLE_REORG (1L << 18)
-#define ALTER_REBUILD_PARTITION (1L << 19)
-#define ALTER_ALL_PARTITION (1L << 20)
-#define ALTER_REMOVE_PARTITIONING (1L << 21)
-#define ALTER_FOREIGN_KEY (1L << 22)
-#define ALTER_TRUNCATE_PARTITION (1L << 23)
-
-enum enum_alter_table_change_level
-{
- ALTER_TABLE_METADATA_ONLY= 0,
- ALTER_TABLE_DATA_CHANGED= 1,
- ALTER_TABLE_INDEX_CHANGED= 2
-};
-
-
-/**
- Temporary hack to enable a class bound forward declaration
- of the enum_alter_table_change_level enumeration. To be
- removed once Alter_info is moved to the sql_alter.h
- header.
-*/
-class Alter_table_change_level
-{
-private:
- typedef enum enum_alter_table_change_level enum_type;
- enum_type value;
-public:
- void operator = (enum_type v) { value = v; }
- operator enum_type () { return value; }
-};
-
-
-/**
- @brief Parsing data for CREATE or ALTER TABLE.
-
- This structure contains a list of columns or indexes to be created,
- altered or dropped.
-*/
-
-class Alter_info
-{
-public:
- List<Alter_drop> drop_list;
- List<Alter_column> alter_list;
- List<Key> key_list;
- List<Create_field> create_list;
- uint flags;
- enum enum_enable_or_disable keys_onoff;
- enum tablespace_op_type tablespace_op;
- List<char> partition_names;
- uint num_parts;
- enum_alter_table_change_level change_level;
- Create_field *datetime_field;
- bool error_if_not_empty;
-
-
- Alter_info() :
- flags(0),
- keys_onoff(LEAVE_AS_IS),
- tablespace_op(NO_TABLESPACE_OP),
- num_parts(0),
- change_level(ALTER_TABLE_METADATA_ONLY),
- datetime_field(NULL),
- error_if_not_empty(FALSE)
- {}
-
- void reset()
- {
- drop_list.empty();
- alter_list.empty();
- key_list.empty();
- create_list.empty();
- flags= 0;
- keys_onoff= LEAVE_AS_IS;
- tablespace_op= NO_TABLESPACE_OP;
- num_parts= 0;
- partition_names.empty();
- change_level= ALTER_TABLE_METADATA_ONLY;
- datetime_field= 0;
- error_if_not_empty= FALSE;
- }
- Alter_info(const Alter_info &rhs, MEM_ROOT *mem_root);
-private:
- Alter_info &operator=(const Alter_info &rhs); // not implemented
- Alter_info(const Alter_info &rhs); // not implemented
-};
struct st_sp_chistics
{
@@ -1262,7 +1110,38 @@ public:
Sroutine_hash_entry **sroutines_list_own_last;
uint sroutines_list_own_elements;
- /*
+ /**
+ Locking state of tables in this particular statement.
+
+ If we under LOCK TABLES or in prelocked mode we consider tables
+ for the statement to be "locked" if there was a call to lock_tables()
+ (which called handler::start_stmt()) for tables of this statement
+ and there was no matching close_thread_tables() call.
+
+ As result this state may differ significantly from one represented
+ by Open_tables_state::lock/locked_tables_mode more, which are always
+ "on" under LOCK TABLES or in prelocked mode.
+ */
+ enum enum_lock_tables_state {
+ LTS_NOT_LOCKED = 0,
+ LTS_LOCKED
+ };
+ enum_lock_tables_state lock_tables_state;
+ bool is_query_tables_locked()
+ {
+ return (lock_tables_state == LTS_LOCKED);
+ }
+
+ /**
+ Number of tables which were open by open_tables() and to be locked
+ by lock_tables().
+ Note that we set this member only in some cases, when this value
+ needs to be passed from open_tables() to lock_tables() which are
+ separated by some amount of code.
+ */
+ uint table_count;
+
+ /*
These constructor and destructor serve for creation/destruction
of Query_tables_list instances which are used as backup storage.
*/
@@ -1289,7 +1168,7 @@ public:
}
bool requires_prelocking()
{
- return test(query_tables_own_last);
+ return MY_TEST(query_tables_own_last);
}
void mark_as_requiring_prelocking(TABLE_LIST **tables_own_last)
{
@@ -2181,6 +2060,10 @@ public:
/** LALR(2) resolution, value of the look ahead token.*/
LEX_YYSTYPE lookahead_yylval;
+ void add_digest_token(uint token, LEX_YYSTYPE yylval);
+
+ void reduce_digest_token(uint token_left, uint token_right);
+
private:
/** Pointer to the current position in the raw input stream. */
char *m_ptr;
@@ -2296,6 +2179,11 @@ public:
NOTE: this member must be used within MYSQLlex() function only.
*/
CHARSET_INFO *m_underscore_cs;
+
+ /**
+ Current statement digest instrumentation.
+ */
+ sql_digest_state* m_digest;
};
/**
@@ -2355,6 +2243,89 @@ protected:
LEX *m_lex;
};
+
+class Delete_plan;
+class SQL_SELECT;
+
+class Explain_query;
+class Explain_update;
+
+/*
+ Query plan of a single-table UPDATE.
+ (This is actually a plan for single-table DELETE also)
+*/
+
+class Update_plan
+{
+protected:
+ bool impossible_where;
+ bool no_partitions;
+public:
+ /*
+ When single-table UPDATE updates a VIEW, that VIEW's select is still
+ listed as the first child. When we print EXPLAIN, it looks like a
+ subquery.
+ In order to get rid of it, updating_a_view=TRUE means that first child
+ select should not be shown when printing EXPLAIN.
+ */
+ bool updating_a_view;
+
+ /* Allocate things there */
+ MEM_ROOT *mem_root;
+
+ TABLE *table;
+ SQL_SELECT *select;
+ uint index;
+ ha_rows scanned_rows;
+ /*
+ Top-level select_lex. Most of its fields are not used, we need it only to
+ get to the subqueries.
+ */
+ SELECT_LEX *select_lex;
+
+ key_map possible_keys;
+ bool using_filesort;
+ bool using_io_buffer;
+
+ /* Set this plan to be a plan to do nothing because of impossible WHERE */
+ void set_impossible_where() { impossible_where= true; }
+ void set_no_partitions() { no_partitions= true; }
+
+ void save_explain_data(Explain_query *query);
+ void save_explain_data_intern(Explain_query *query, Explain_update *eu);
+ virtual ~Update_plan() {}
+
+ Update_plan(MEM_ROOT *mem_root_arg) :
+ impossible_where(false), no_partitions(false),
+ mem_root(mem_root_arg),
+ using_filesort(false), using_io_buffer(false)
+ {}
+};
+
+
+/* Query plan of a single-table DELETE */
+class Delete_plan : public Update_plan
+{
+ bool deleting_all_rows;
+public:
+
+ /* Construction functions */
+ Delete_plan(MEM_ROOT *mem_root_arg) :
+ Update_plan(mem_root_arg),
+ deleting_all_rows(false)
+ {}
+
+ /* Set this query plan to be a plan to make a call to h->delete_all_rows() */
+ void set_delete_all_rows(ha_rows rows_arg)
+ {
+ deleting_all_rows= true;
+ scanned_rows= rows_arg;
+ }
+
+ void save_explain_data(Explain_query *query);
+};
+
+
/* The state of the lex parsing. This is saved in the THD struct */
struct LEX: public Query_tables_list
@@ -2365,6 +2336,9 @@ struct LEX: public Query_tables_list
SELECT_LEX *current_select;
/* list of all SELECT_LEX */
SELECT_LEX *all_selects_list;
+
+ /* Query Plan Footprint of a currently running select */
+ Explain_query *explain;
char *length,*dec,*change;
LEX_STRING name;
@@ -2372,7 +2346,7 @@ struct LEX: public Query_tables_list
char *backup_dir; /* For RESTORE/BACKUP */
char* to_log; /* For PURGE MASTER LOGS TO */
char* x509_subject,*x509_issuer,*ssl_cipher;
- String *wild;
+ String *wild; /* Wildcard in SHOW {something} LIKE 'wild'*/
sql_exchange *exchange;
select_result *result;
Item *default_value, *on_update_value;
@@ -2417,6 +2391,8 @@ struct LEX: public Query_tables_list
List<Item_func_set_user_var> set_var_list; // in-query assignment list
List<Item_param> param_list;
List<LEX_STRING> view_list; // view list (list of field names in view)
+ List<LEX_STRING> *column_list; // list of column names (in ANALYZE)
+ List<LEX_STRING> *index_list; // list of index names (in ANALYZE)
/*
A stack of name resolution contexts for the query. This stack is used
at parse time to set local name resolution contexts for various parts
@@ -2442,9 +2418,10 @@ struct LEX: public Query_tables_list
KEY_CREATE_INFO key_create_info;
LEX_MASTER_INFO mi; // used by CHANGE MASTER
LEX_SERVER_OPTIONS server_options;
+ LEX_STRING relay_log_connection_name;
USER_RESOURCES mqh;
LEX_RESET_SLAVE reset_slave_info;
- ulong type;
+ ulonglong type;
/* The following is used by KILL */
killed_state kill_signal;
killed_type kill_type;
@@ -2459,7 +2436,7 @@ struct LEX: public Query_tables_list
*/
nesting_map allow_sum_func;
- Sql_statement *m_stmt;
+ Sql_cmd *m_sql_cmd;
/*
Usually `expr` rule of yacc is quite reused but some commands better
@@ -2484,6 +2461,8 @@ struct LEX: public Query_tables_list
union {
enum ha_rkey_function ha_rkey_mode;
enum xa_option_words xa_opt;
+ bool with_admin_option; // GRANT role
+ bool with_persistent_for_clause; // uses PERSISTENT FOR clause (in ANALYZE)
};
enum enum_var_type option_type;
enum enum_view_create_mode create_view_mode;
@@ -2514,13 +2493,14 @@ struct LEX: public Query_tables_list
uint16 create_view_algorithm;
uint8 create_view_check;
uint8 context_analysis_only;
- bool drop_if_exists, drop_temporary, local_file, one_shot_set;
+ bool drop_temporary, local_file;
+ bool check_exists;
bool autocommit;
bool verbose, no_write_to_binlog;
enum enum_yes_no_unknown tx_chain, tx_release;
bool safe_to_cache_query;
- bool subqueries, ignore, online;
+ bool subqueries, ignore;
st_parsing_options parsing_options;
Alter_info alter_info;
/*
@@ -2545,6 +2525,7 @@ struct LEX: public Query_tables_list
bool sp_lex_in_use; /* Keep track on lex usage in SPs for error handling */
bool all_privileges;
bool proxy_priv;
+
sp_pcontext *spcont;
st_sp_chistics sp_chistics;
@@ -2779,6 +2760,9 @@ struct LEX: public Query_tables_list
}
bool save_prep_leaf_tables();
+
+ int print_explain(select_result_sink *output, uint8 explain_flags,
+ bool *printed_anything);
};
@@ -2895,6 +2879,18 @@ public:
};
/**
+ Input parameters to the parser.
+*/
+struct Parser_input
+{
+ bool m_compute_digest;
+
+ Parser_input()
+ : m_compute_digest(false)
+ {}
+};
+
+/**
Internal state of the parser.
The complete state consist of:
- state data used during lexical parsing,
@@ -2921,9 +2917,15 @@ public:
~Parser_state()
{}
+ Parser_input m_input;
Lex_input_stream m_lip;
Yacc_state m_yacc;
+ /**
+ Current performance digest instrumentation.
+ */
+ PSI_digest_locker* m_digest_psi;
+
void reset(char *found_semicolon, unsigned int length)
{
m_lip.reset(found_semicolon, length);
@@ -2931,6 +2933,11 @@ public:
}
};
+extern sql_digest_state *
+digest_add_token(sql_digest_state *state, uint token, LEX_YYSTYPE yylval);
+
+extern sql_digest_state *
+digest_reduce_token(sql_digest_state *state, uint token_left, uint token_right);
struct st_lex_local: public LEX
{
diff --git a/sql/sql_load.cc b/sql/sql_load.cc
index 75ddf80ea66..08687b20b00 100644
--- a/sql/sql_load.cc
+++ b/sql/sql_load.cc
@@ -19,6 +19,7 @@
/* Copy data from a textfile to table */
/* 2006-12 Erik Wetterberg : LOAD XML added */
+#include <my_global.h>
#include "sql_priv.h"
#include "unireg.h"
#include "sql_load.h"
@@ -28,7 +29,6 @@
#include <my_dir.h>
#include "sql_view.h" // check_key_in_view
#include "sql_insert.h" // check_that_all_fields_are_given_values,
- // prepare_triggers_for_insert_stmt,
// write_record
#include "sql_acl.h" // INSERT_ACL, UPDATE_ACL
#include "log_event.h" // Delete_file_log_event,
@@ -226,7 +226,7 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
!field_term->is_ascii() ||
!ex->line_term->is_ascii() || !ex->line_start->is_ascii())
{
- push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning(thd, Sql_condition::WARN_LEVEL_WARN,
WARN_NON_ASCII_SEPARATOR_NOT_IMPLEMENTED,
ER(WARN_NON_ASCII_SEPARATOR_NOT_IMPLEMENTED));
}
@@ -265,7 +265,8 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
*/
if (unique_table(thd, table_list, table_list->next_global, 0))
{
- my_error(ER_UPDATE_TABLE_USED, MYF(0), table_list->table_name);
+ my_error(ER_UPDATE_TABLE_USED, MYF(0), table_list->table_name,
+ "LOAD DATA");
DBUG_RETURN(TRUE);
}
@@ -287,7 +288,6 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
fields_vars.push_back(item->real_item());
}
bitmap_set_all(table->write_set);
- table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
/*
Let us also prepare SET clause, altough it is probably empty
in this case.
@@ -303,27 +303,28 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
setup_fields(thd, 0, set_fields, MARK_COLUMNS_WRITE, 0, 0) ||
check_that_all_fields_are_given_values(thd, table, table_list))
DBUG_RETURN(TRUE);
- /*
- Check whenever TIMESTAMP field with auto-set feature specified
- explicitly.
- */
- if (table->timestamp_field)
- {
- if (bitmap_is_set(table->write_set,
- table->timestamp_field->field_index))
- table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
- else
- {
- bitmap_set_bit(table->write_set,
- table->timestamp_field->field_index);
- }
- }
+ /* Add all fields with default functions to table->write_set. */
+ if (table->default_field)
+ table->mark_default_fields_for_write();
/* Fix the expressions in SET clause */
if (setup_fields(thd, 0, set_values, MARK_COLUMNS_READ, 0, 0))
DBUG_RETURN(TRUE);
}
- prepare_triggers_for_insert_stmt(table);
+ table->prepare_triggers_for_insert_stmt_or_event();
+ table->mark_columns_needed_for_insert();
+
+ if (table->vfield)
+ {
+ for (Field **vfield_ptr= table->vfield; *vfield_ptr; vfield_ptr++)
+ {
+ if ((*vfield_ptr)->stored_in_db)
+ {
+ thd->lex->unit.insert_table_with_stored_vcol= table;
+ break;
+ }
+ }
+ }
uint tot_length=0;
bool use_blobs= 0, use_vars= 0;
@@ -389,11 +390,11 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
MY_RETURN_REAL_PATH);
}
- if (thd->slave_thread)
+ if (thd->rgi_slave)
{
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
- if (strncmp(active_mi->rli.slave_patternload_file, name,
- active_mi->rli.slave_patternload_file_size))
+ if (strncmp(thd->rgi_slave->rli->slave_patternload_file, name,
+ thd->rgi_slave->rli->slave_patternload_file_size))
{
/*
LOAD DATA INFILE in the slave SQL Thread can only read from
@@ -487,8 +488,9 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
}
thd_proc_info(thd, "reading file");
- if (!(error=test(read_info.error)))
+ if (!(error= MY_TEST(read_info.error)))
{
+ table->reset_default_fields();
table->next_number_field=table->found_next_number_field;
if (ignore ||
handle_duplicates == DUP_REPLACE)
@@ -501,10 +503,7 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
table->file->ha_start_bulk_insert((ha_rows) 0);
table->copy_blobs=1;
- thd->abort_on_warning= (!ignore &&
- (thd->variables.sql_mode &
- (MODE_STRICT_TRANS_TABLES |
- MODE_STRICT_ALL_TABLES)));
+ thd->abort_on_warning= !ignore && thd->is_strict_mode();
thd_progress_init(thd, 2);
if (ex->filetype == FILETYPE_XML) /* load xml */
@@ -617,7 +616,7 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
}
sprintf(name, ER(ER_LOAD_INFO), (ulong) info.records, (ulong) info.deleted,
(ulong) (info.records - info.copied),
- (ulong) thd->warning_info->statement_warn_count());
+ (long) thd->get_stmt_da()->current_statement_warn_count());
if (thd->transaction.stmt.modified_non_trans_table)
thd->transaction.all.modified_non_trans_table= TRUE;
@@ -858,12 +857,16 @@ read_fixed_length(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
if (pos == read_info.row_end)
{
thd->cuted_fields++; /* Not enough fields */
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_WARN_TOO_FEW_RECORDS,
ER(ER_WARN_TOO_FEW_RECORDS),
- thd->warning_info->current_row_for_warning());
+ thd->get_stmt_da()->current_row_for_warning());
+ /*
+ Timestamp fields that are NOT NULL are autoupdated if there is no
+ corresponding value in the data file.
+ */
if (!field->maybe_null() && field->type() == FIELD_TYPE_TIMESTAMP)
- ((Field_timestamp*) field)->set_time();
+ field->set_time();
}
else
{
@@ -878,21 +881,23 @@ read_fixed_length(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
if ((pos+=length) > read_info.row_end)
pos= read_info.row_end; /* Fills rest with space */
}
+ /* Do not auto-update this field. */
+ field->set_has_explicit_value();
}
if (pos != read_info.row_end)
{
thd->cuted_fields++; /* To long row */
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_WARN_TOO_MANY_RECORDS,
ER(ER_WARN_TOO_MANY_RECORDS),
- thd->warning_info->current_row_for_warning());
+ thd->get_stmt_da()->current_row_for_warning());
}
if (thd->killed ||
- fill_record_n_invoke_before_triggers(thd, set_fields, set_values,
+ fill_record_n_invoke_before_triggers(thd, table, set_fields, set_values,
ignore_check_option_errors,
- table->triggers,
- TRG_EVENT_INSERT))
+ TRG_EVENT_INSERT) ||
+ (table->default_field && table->update_default_fields()))
DBUG_RETURN(1);
switch (table_list->view_check_option(thd,
@@ -918,15 +923,15 @@ read_fixed_length(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
if (read_info.line_cuted)
{
thd->cuted_fields++; /* To long row */
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_WARN_TOO_MANY_RECORDS,
ER(ER_WARN_TOO_MANY_RECORDS),
- thd->warning_info->current_row_for_warning());
+ thd->get_stmt_da()->current_row_for_warning());
}
- thd->warning_info->inc_current_row_for_warning();
+ thd->get_stmt_da()->inc_current_row_for_warning();
continue_loop:;
}
- DBUG_RETURN(test(read_info.error));
+ DBUG_RETURN(MY_TEST(read_info.error));
}
@@ -1003,18 +1008,24 @@ read_sep_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
if (field->reset())
{
my_error(ER_WARN_NULL_TO_NOTNULL, MYF(0), field->field_name,
- thd->warning_info->current_row_for_warning());
+ thd->get_stmt_da()->current_row_for_warning());
DBUG_RETURN(1);
}
field->set_null();
if (!field->maybe_null())
{
+ /*
+ Timestamp fields that are NOT NULL are autoupdated if there is no
+ corresponding value in the data file.
+ */
if (field->type() == MYSQL_TYPE_TIMESTAMP)
- ((Field_timestamp*) field)->set_time();
+ field->set_time();
else if (field != table->next_number_field)
- field->set_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
+ field->set_warning(Sql_condition::WARN_LEVEL_WARN,
ER_WARN_NULL_TO_NOTNULL, 1);
}
+ /* Do not auto-update this field. */
+ field->set_has_explicit_value();
}
else if (item->type() == Item::STRING_ITEM)
{
@@ -1038,6 +1049,7 @@ read_sep_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
if (field == table->next_number_field)
table->auto_increment_field_not_null= TRUE;
field->store((char*) pos, length, read_info.read_charset);
+ field->set_has_explicit_value();
}
else if (item->type() == Item::STRING_ITEM)
{
@@ -1075,11 +1087,12 @@ read_sep_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
if (field->reset())
{
my_error(ER_WARN_NULL_TO_NOTNULL, MYF(0),field->field_name,
- thd->warning_info->current_row_for_warning());
+ thd->get_stmt_da()->current_row_for_warning());
DBUG_RETURN(1);
}
if (!field->maybe_null() && field->type() == FIELD_TYPE_TIMESTAMP)
- ((Field_timestamp*) field)->set_time();
+ field->set_time();
+ field->set_has_explicit_value();
/*
TODO: We probably should not throw warning for each field.
But how about intention to always have the same number
@@ -1087,10 +1100,10 @@ read_sep_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
in the end ?)
*/
thd->cuted_fields++;
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_WARN_TOO_FEW_RECORDS,
ER(ER_WARN_TOO_FEW_RECORDS),
- thd->warning_info->current_row_for_warning());
+ thd->get_stmt_da()->current_row_for_warning());
}
else if (item->type() == Item::STRING_ITEM)
{
@@ -1106,10 +1119,10 @@ read_sep_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
}
if (thd->killed ||
- fill_record_n_invoke_before_triggers(thd, set_fields, set_values,
+ fill_record_n_invoke_before_triggers(thd, table, set_fields, set_values,
ignore_check_option_errors,
- table->triggers,
- TRG_EVENT_INSERT))
+ TRG_EVENT_INSERT) ||
+ (table->default_field && table->update_default_fields()))
DBUG_RETURN(1);
switch (table_list->view_check_option(thd,
@@ -1134,16 +1147,16 @@ read_sep_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
if (read_info.line_cuted)
{
thd->cuted_fields++; /* To long row */
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_WARN_TOO_MANY_RECORDS, ER(ER_WARN_TOO_MANY_RECORDS),
- thd->warning_info->current_row_for_warning());
+ thd->get_stmt_da()->current_row_for_warning());
if (thd->killed)
DBUG_RETURN(1);
}
- thd->warning_info->inc_current_row_for_warning();
+ thd->get_stmt_da()->inc_current_row_for_warning();
continue_loop:;
}
- DBUG_RETURN(test(read_info.error));
+ DBUG_RETURN(MY_TEST(read_info.error));
}
@@ -1219,11 +1232,13 @@ read_xml_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
if (!field->maybe_null())
{
if (field->type() == FIELD_TYPE_TIMESTAMP)
- ((Field_timestamp *) field)->set_time();
+ field->set_time();
else if (field != table->next_number_field)
- field->set_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
+ field->set_warning(Sql_condition::WARN_LEVEL_WARN,
ER_WARN_NULL_TO_NOTNULL, 1);
}
+ /* Do not auto-update this field. */
+ field->set_has_explicit_value();
}
else
((Item_user_var_as_out_param *) item)->set_null_value(cs);
@@ -1238,6 +1253,7 @@ read_xml_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
if (field == table->next_number_field)
table->auto_increment_field_not_null= TRUE;
field->store((char *) tag->value.ptr(), tag->value.length(), cs);
+ field->set_has_explicit_value();
}
else
((Item_user_var_as_out_param *) item)->set_value(
@@ -1271,10 +1287,10 @@ read_xml_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
in the end ?)
*/
thd->cuted_fields++;
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_WARN_TOO_FEW_RECORDS,
ER(ER_WARN_TOO_FEW_RECORDS),
- thd->warning_info->current_row_for_warning());
+ thd->get_stmt_da()->current_row_for_warning());
}
else
((Item_user_var_as_out_param *)item)->set_null_value(cs);
@@ -1282,10 +1298,10 @@ read_xml_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
}
if (thd->killed ||
- fill_record_n_invoke_before_triggers(thd, set_fields, set_values,
+ fill_record_n_invoke_before_triggers(thd, table, set_fields, set_values,
ignore_check_option_errors,
- table->triggers,
- TRG_EVENT_INSERT))
+ TRG_EVENT_INSERT) ||
+ (table->default_field && table->update_default_fields()))
DBUG_RETURN(1);
switch (table_list->view_check_option(thd,
@@ -1305,10 +1321,10 @@ read_xml_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
its default value at the beginning of each loop iteration.
*/
thd->transaction.stmt.modified_non_trans_table= no_trans_update_stmt;
- thd->warning_info->inc_current_row_for_warning();
+ thd->get_stmt_da()->inc_current_row_for_warning();
continue_loop:;
}
- DBUG_RETURN(test(read_info.error) || thd->is_error());
+ DBUG_RETURN(MY_TEST(read_info.error) || thd->is_error());
} /* load xml end */
@@ -1384,11 +1400,11 @@ READ_INFO::READ_INFO(File file_par, uint tot_length, CHARSET_INFO *cs,
line_term_char= line_term_length ? line_term_ptr[0] : INT_MAX;
/* Set of a stack for unget if long terminators */
- uint length= max(cs->mbmaxlen, max(field_term_length, line_term_length)) + 1;
+ uint length= MY_MAX(cs->mbmaxlen, MY_MAX(field_term_length, line_term_length)) + 1;
set_if_bigger(length,line_start.length());
stack=stack_pos=(int*) sql_alloc(sizeof(int)*length);
- if (!(buffer=(uchar*) my_malloc(buff_length+1,MYF(0))))
+ if (!(buffer=(uchar*) my_malloc(buff_length+1,MYF(MY_THREAD_SPECIFIC))))
error=1; /* purecov: inspected */
else
{
@@ -1396,7 +1412,7 @@ READ_INFO::READ_INFO(File file_par, uint tot_length, CHARSET_INFO *cs,
if (init_io_cache(&cache,(get_it_from_net) ? -1 : file, 0,
(get_it_from_net) ? READ_NET :
(is_fifo ? READ_FIFO : READ_CACHE),0L,1,
- MYF(MY_WME)))
+ MYF(MY_WME | MY_THREAD_SPECIFIC)))
{
my_free(buffer); /* purecov: inspected */
buffer= NULL;
@@ -1618,7 +1634,7 @@ int READ_INFO::read_field()
** We come here if buffer is too small. Enlarge it and continue
*/
if (!(new_buffer=(uchar*) my_realloc((char*) buffer,buff_length+1+IO_SIZE,
- MYF(MY_WME))))
+ MYF(MY_WME | MY_THREAD_SPECIFIC))))
return (error=1);
to=new_buffer + (to-buffer);
buffer=new_buffer;
diff --git a/sql/sql_locale.cc b/sql/sql_locale.cc
index 13e00c99f19..d918d5c9cf4 100644
--- a/sql/sql_locale.cc
+++ b/sql/sql_locale.cc
@@ -20,6 +20,7 @@
!! This file is built from my_locale.pl !!
*/
+#include <my_global.h>
#include "sql_priv.h"
#include "unireg.h"
#include "sql_locale.h"
@@ -3247,6 +3248,75 @@ MY_LOCALE my_locale_el_GR
);
/***** LOCALE END el_GR *****/
+
+/***** LOCALE BEGIN rm_CH: Romansh - Switzerland *****/
+static const char *my_locale_month_names_rm_CH[13]=
+{
+ "schaner", "favrer", "mars", "avrigl", "matg", "zercladur",
+ "fanadur", "avust", "settember", "october", "november", "december", NullS
+};
+
+static const char *my_locale_ab_month_names_rm_CH[13]=
+{
+ "schan", "favr", "mars", "avr", "matg", "zercl",
+ "fan", "avust", "sett", "oct", "nov", "dec", NullS
+};
+
+static const char *my_locale_day_names_rm_CH[8]=
+{
+ "glindesdi", "mardi", "mesemna", "gievgia",
+ "venderdi", "sonda", "dumengia", NullS
+};
+
+static const char *my_locale_ab_day_names_rm_CH[8]=
+{
+ "gli", "ma", "me", "gie", "ve", "so", "du", NullS
+};
+
+static TYPELIB my_locale_typelib_month_names_rm_CH=
+{
+ array_elements(my_locale_month_names_rm_CH) - 1,
+ "", my_locale_month_names_rm_CH, NULL
+};
+
+static TYPELIB my_locale_typelib_ab_month_names_rm_CH=
+{
+ array_elements(my_locale_ab_month_names_rm_CH) - 1,
+ "", my_locale_ab_month_names_rm_CH, NULL
+};
+
+static TYPELIB my_locale_typelib_day_names_rm_CH=
+{
+ array_elements(my_locale_day_names_rm_CH) - 1,
+ "", my_locale_day_names_rm_CH, NULL
+};
+
+static TYPELIB my_locale_typelib_ab_day_names_rm_CH=
+{
+ array_elements(my_locale_ab_day_names_rm_CH) - 1,
+ "", my_locale_ab_day_names_rm_CH, NULL
+};
+
+MY_LOCALE my_locale_rm_CH
+(
+ 110,
+ "rm_CH",
+ "Romansh - Switzerland",
+ FALSE,
+ &my_locale_typelib_month_names_rm_CH,
+ &my_locale_typelib_ab_month_names_rm_CH,
+ &my_locale_typelib_day_names_rm_CH,
+ &my_locale_typelib_ab_day_names_rm_CH,
+ 9, /* max mon name length */
+ 9, /* max day name length */
+ ',', /* decimal point rm_CH */
+ '\'', /* thousands_sep rm_CH */
+ "\x03\x03", /* grouping rm_CH */
+ &global_errmsgs[en_US]
+);
+/***** LOCALE END rm_CH *****/
+
+
/*
The list of all locales.
Note, locales must be ordered according to their
@@ -3365,6 +3435,7 @@ MY_LOCALE *my_locales[]=
&my_locale_sv_FI,
&my_locale_zh_HK,
&my_locale_el_GR,
+ &my_locale_rm_CH,
NULL
};
@@ -3422,7 +3493,7 @@ MY_LOCALE *my_locale_by_name(const char *name)
if (thd)
{
// Send a warning to the client
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_WARN_DEPRECATED_SYNTAX, ER(ER_WARN_DEPRECATED_SYNTAX),
name, locale->name);
}
diff --git a/sql/sql_manager.cc b/sql/sql_manager.cc
index 778c732d443..8cf849b97d0 100644
--- a/sql/sql_manager.cc
+++ b/sql/sql_manager.cc
@@ -21,9 +21,9 @@
* o Berkeley DB: removing unneeded log files.
*/
+#include <my_global.h>
#include "sql_priv.h"
#include "sql_manager.h"
-#include "unireg.h" // REQUIRED: for other includes
#include "sql_base.h" // flush_tables
static bool volatile manager_thread_in_use;
@@ -110,7 +110,7 @@ pthread_handler_t handle_manager(void *arg __attribute__((unused)))
if (error == ETIMEDOUT || error == ETIME)
{
- tdc_flush_unused_tables();
+ tc_purge();
error = 0;
reset_flush_time = TRUE;
}
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index 885a5a2f42a..e8c80fed47c 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -15,16 +15,15 @@
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
#define MYSQL_LEX 1
-#include "my_global.h"
+#include <my_global.h>
#include "sql_priv.h"
-#include "unireg.h" // REQUIRED: for other includes
#include "sql_parse.h" // sql_kill, *_precheck, *_prepare
#include "lock.h" // try_transactional_lock,
// check_transactional_lock,
// set_handler_table_locks,
// lock_global_read_lock,
// make_global_read_lock_block_commit
-#include "sql_base.h" // find_temporary_tablesx
+#include "sql_base.h" // find_temporary_table
#include "sql_cache.h" // QUERY_CACHE_FLAGS_SIZE, query_cache_*
#include "sql_show.h" // mysqld_list_*, mysqld_show_*,
// calc_sum_of_all_status
@@ -44,7 +43,6 @@
#include "sql_table.h" // mysql_create_like_table,
// mysql_create_table,
// mysql_alter_table,
- // mysql_recreate_table,
// mysql_backup_table,
// mysql_restore_table
#include "sql_reload.h" // reload_acl_and_cache
@@ -82,6 +80,9 @@
#include <myisam.h>
#include <my_dir.h>
#include "rpl_handler.h"
+#include "rpl_mi.h"
+
+#include "sql_digest.h"
#include "sp_head.h"
#include "sp.h"
@@ -95,6 +96,7 @@
#include "probes_mysql.h"
#include "set_var.h"
#include "log_slow.h"
+#include "sql_bootstrap.h"
#define FLAGSTR(V,F) ((V)&(F)?#F" ":"")
@@ -105,6 +107,7 @@
#ifdef WITH_WSREP
#include "wsrep_mysqld.h"
#include "wsrep_thd.h"
+#include "wsrep_binlog.h"
static void wsrep_mysql_parse(THD *thd, char *rawbuf, uint length,
Parser_state *parser_state);
#endif /* WITH_WSREP */
@@ -124,8 +127,9 @@ static void wsrep_mysql_parse(THD *thd, char *rawbuf, uint length,
"FUNCTION" : "PROCEDURE")
static bool execute_sqlcom_select(THD *thd, TABLE_LIST *all_tables);
-static void sql_kill(THD *thd, ulong id, killed_state state);
+static void sql_kill(THD *thd, longlong id, killed_state state, killed_type type);
static void sql_kill_user(THD *thd, LEX_USER *user, killed_state state);
+static bool lock_tables_precheck(THD *thd, TABLE_LIST *tables);
static bool execute_show_status(THD *, TABLE_LIST *);
static bool check_rename_table(THD *, TABLE_LIST *, TABLE_LIST *);
@@ -175,6 +179,7 @@ const char *xa_state_names[]={
*/
inline bool all_tables_not_ok(THD *thd, TABLE_LIST *tables)
{
+ Rpl_filter *rpl_filter= thd->system_thread_info.rpl_sql_info->rpl_filter;
return rpl_filter->is_on() && tables && !thd->spcont &&
!rpl_filter->tables_ok(thd->db, tables);
}
@@ -199,6 +204,8 @@ static bool some_non_temp_table_to_be_updated(THD *thd, TABLE_LIST *tables)
@param thd Thread handle.
@param mask Bitmask used for the SQL command match.
+ @return 0 No implicit commit
+ @return 1 Do a commit
*/
static bool stmt_causes_implicit_commit(THD *thd, uint mask)
{
@@ -211,12 +218,22 @@ static bool stmt_causes_implicit_commit(THD *thd, uint mask)
switch (lex->sql_command) {
case SQLCOM_DROP_TABLE:
- skip= lex->drop_temporary;
+ skip= (lex->drop_temporary ||
+ (thd->variables.option_bits & OPTION_GTID_BEGIN));
break;
case SQLCOM_ALTER_TABLE:
+ /* If ALTER TABLE of non-temporary table, do implicit commit */
+ skip= (lex->create_info.tmp_table());
+ break;
case SQLCOM_CREATE_TABLE:
- /* If CREATE TABLE of non-temporary table, do implicit commit */
- skip= (lex->create_info.options & HA_LEX_CREATE_TMP_TABLE);
+ /*
+ If CREATE TABLE of non-temporary table and the table is not part
+ if a BEGIN GTID ... COMMIT group, do a implicit commit.
+ This ensures that CREATE ... SELECT will in the same GTID group on the
+ master and slave.
+ */
+ skip= (lex->create_info.tmp_table() ||
+ (thd->variables.option_bits & OPTION_GTID_BEGIN));
break;
case SQLCOM_SET_OPTION:
skip= lex->autocommit ? FALSE : TRUE;
@@ -272,14 +289,16 @@ void init_update_queries(void)
sql_command_flags[SQLCOM_CREATE_TABLE]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE |
CF_AUTO_COMMIT_TRANS | CF_REPORT_PROGRESS |
CF_CAN_GENERATE_ROW_EVENTS;
- sql_command_flags[SQLCOM_CREATE_INDEX]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
+ sql_command_flags[SQLCOM_CREATE_INDEX]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS | CF_REPORT_PROGRESS;
sql_command_flags[SQLCOM_ALTER_TABLE]= CF_CHANGES_DATA | CF_WRITE_LOGS_COMMAND |
- CF_AUTO_COMMIT_TRANS | CF_REPORT_PROGRESS ;
+ CF_AUTO_COMMIT_TRANS | CF_REPORT_PROGRESS |
+ CF_INSERTS_DATA;
sql_command_flags[SQLCOM_TRUNCATE]= CF_CHANGES_DATA | CF_WRITE_LOGS_COMMAND |
CF_AUTO_COMMIT_TRANS;
sql_command_flags[SQLCOM_DROP_TABLE]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
sql_command_flags[SQLCOM_LOAD]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE |
- CF_CAN_GENERATE_ROW_EVENTS | CF_REPORT_PROGRESS;
+ CF_CAN_GENERATE_ROW_EVENTS | CF_REPORT_PROGRESS |
+ CF_INSERTS_DATA;
sql_command_flags[SQLCOM_CREATE_DB]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
sql_command_flags[SQLCOM_DROP_DB]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
sql_command_flags[SQLCOM_ALTER_DB_UPGRADE]= CF_AUTO_COMMIT_TRANS;
@@ -296,26 +315,61 @@ void init_update_queries(void)
sql_command_flags[SQLCOM_DROP_EVENT]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
sql_command_flags[SQLCOM_UPDATE]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE |
- CF_CAN_GENERATE_ROW_EVENTS;
+ CF_CAN_GENERATE_ROW_EVENTS |
+ CF_OPTIMIZER_TRACE |
+ CF_CAN_BE_EXPLAINED |
+ CF_UPDATES_DATA;
sql_command_flags[SQLCOM_UPDATE_MULTI]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE |
- CF_CAN_GENERATE_ROW_EVENTS;
+ CF_CAN_GENERATE_ROW_EVENTS |
+ CF_OPTIMIZER_TRACE |
+ CF_CAN_BE_EXPLAINED |
+ CF_UPDATES_DATA;
sql_command_flags[SQLCOM_INSERT]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE |
- CF_CAN_GENERATE_ROW_EVENTS;
+ CF_CAN_GENERATE_ROW_EVENTS |
+ CF_OPTIMIZER_TRACE |
+ CF_CAN_BE_EXPLAINED |
+ CF_INSERTS_DATA;
sql_command_flags[SQLCOM_INSERT_SELECT]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE |
- CF_CAN_GENERATE_ROW_EVENTS;
+ CF_CAN_GENERATE_ROW_EVENTS |
+ CF_OPTIMIZER_TRACE |
+ CF_CAN_BE_EXPLAINED |
+ CF_INSERTS_DATA;
sql_command_flags[SQLCOM_DELETE]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE |
- CF_CAN_GENERATE_ROW_EVENTS;
+ CF_CAN_GENERATE_ROW_EVENTS |
+ CF_OPTIMIZER_TRACE |
+ CF_CAN_BE_EXPLAINED;
sql_command_flags[SQLCOM_DELETE_MULTI]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE |
- CF_CAN_GENERATE_ROW_EVENTS;
+ CF_CAN_GENERATE_ROW_EVENTS |
+ CF_OPTIMIZER_TRACE |
+ CF_CAN_BE_EXPLAINED;;
sql_command_flags[SQLCOM_REPLACE]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE |
- CF_CAN_GENERATE_ROW_EVENTS;
+ CF_CAN_GENERATE_ROW_EVENTS |
+ CF_OPTIMIZER_TRACE |
+ CF_CAN_BE_EXPLAINED |
+ CF_INSERTS_DATA;;
sql_command_flags[SQLCOM_REPLACE_SELECT]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE |
- CF_CAN_GENERATE_ROW_EVENTS;
+ CF_CAN_GENERATE_ROW_EVENTS |
+ CF_OPTIMIZER_TRACE |
+ CF_CAN_BE_EXPLAINED |
+ CF_INSERTS_DATA;
sql_command_flags[SQLCOM_SELECT]= CF_REEXECUTION_FRAGILE |
- CF_CAN_GENERATE_ROW_EVENTS;
- sql_command_flags[SQLCOM_SET_OPTION]= CF_REEXECUTION_FRAGILE | CF_AUTO_COMMIT_TRANS;
+ CF_CAN_GENERATE_ROW_EVENTS |
+ CF_OPTIMIZER_TRACE |
+ CF_CAN_BE_EXPLAINED;
+ // (1) so that subquery is traced when doing "SET @var = (subquery)"
+ /*
+ @todo SQLCOM_SET_OPTION should have CF_CAN_GENERATE_ROW_EVENTS
+ set, because it may invoke a stored function that generates row
+ events. /Sven
+ */
+ sql_command_flags[SQLCOM_SET_OPTION]= CF_REEXECUTION_FRAGILE |
+ CF_AUTO_COMMIT_TRANS |
+ CF_CAN_GENERATE_ROW_EVENTS |
+ CF_OPTIMIZER_TRACE; // (1)
+ // (1) so that subquery is traced when doing "DO @var := (subquery)"
sql_command_flags[SQLCOM_DO]= CF_REEXECUTION_FRAGILE |
- CF_CAN_GENERATE_ROW_EVENTS;
+ CF_CAN_GENERATE_ROW_EVENTS |
+ CF_OPTIMIZER_TRACE; // (1)
sql_command_flags[SQLCOM_SHOW_STATUS_PROC]= CF_STATUS_COMMAND | CF_REEXECUTION_FRAGILE;
sql_command_flags[SQLCOM_SHOW_STATUS]= CF_STATUS_COMMAND | CF_REEXECUTION_FRAGILE;
@@ -341,6 +395,7 @@ void init_update_queries(void)
sql_command_flags[SQLCOM_SHOW_ENGINE_STATUS]= CF_STATUS_COMMAND;
sql_command_flags[SQLCOM_SHOW_ENGINE_MUTEX]= CF_STATUS_COMMAND;
sql_command_flags[SQLCOM_SHOW_ENGINE_LOGS]= CF_STATUS_COMMAND;
+ sql_command_flags[SQLCOM_SHOW_EXPLAIN]= CF_STATUS_COMMAND;
sql_command_flags[SQLCOM_SHOW_PROCESSLIST]= CF_STATUS_COMMAND;
sql_command_flags[SQLCOM_SHOW_GRANTS]= CF_STATUS_COMMAND;
sql_command_flags[SQLCOM_SHOW_CREATE_DB]= CF_STATUS_COMMAND;
@@ -356,7 +411,7 @@ void init_update_queries(void)
sql_command_flags[SQLCOM_SHOW_CREATE_EVENT]= CF_STATUS_COMMAND;
sql_command_flags[SQLCOM_SHOW_PROFILES]= CF_STATUS_COMMAND;
sql_command_flags[SQLCOM_SHOW_PROFILE]= CF_STATUS_COMMAND;
- sql_command_flags[SQLCOM_BINLOG_BASE64_EVENT]= CF_STATUS_COMMAND;
+ sql_command_flags[SQLCOM_BINLOG_BASE64_EVENT]= CF_STATUS_COMMAND | CF_CAN_GENERATE_ROW_EVENTS;
sql_command_flags[SQLCOM_SHOW_CLIENT_STATS]= CF_STATUS_COMMAND;
sql_command_flags[SQLCOM_SHOW_USER_STATS]= CF_STATUS_COMMAND;
sql_command_flags[SQLCOM_SHOW_TABLE_STATS]= CF_STATUS_COMMAND;
@@ -368,18 +423,21 @@ void init_update_queries(void)
sql_command_flags[SQLCOM_CREATE_USER]= CF_CHANGES_DATA;
sql_command_flags[SQLCOM_RENAME_USER]= CF_CHANGES_DATA;
sql_command_flags[SQLCOM_DROP_USER]= CF_CHANGES_DATA;
+ sql_command_flags[SQLCOM_CREATE_ROLE]= CF_CHANGES_DATA;
sql_command_flags[SQLCOM_GRANT]= CF_CHANGES_DATA;
+ sql_command_flags[SQLCOM_GRANT_ROLE]= CF_CHANGES_DATA;
sql_command_flags[SQLCOM_REVOKE]= CF_CHANGES_DATA;
+ sql_command_flags[SQLCOM_REVOKE_ROLE]= CF_CHANGES_DATA;
sql_command_flags[SQLCOM_OPTIMIZE]= CF_CHANGES_DATA;
- sql_command_flags[SQLCOM_CREATE_FUNCTION]= CF_CHANGES_DATA;
+ sql_command_flags[SQLCOM_CREATE_FUNCTION]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
sql_command_flags[SQLCOM_CREATE_PROCEDURE]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
sql_command_flags[SQLCOM_CREATE_SPFUNCTION]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
sql_command_flags[SQLCOM_DROP_PROCEDURE]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
sql_command_flags[SQLCOM_DROP_FUNCTION]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
sql_command_flags[SQLCOM_ALTER_PROCEDURE]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
sql_command_flags[SQLCOM_ALTER_FUNCTION]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
- sql_command_flags[SQLCOM_INSTALL_PLUGIN]= CF_CHANGES_DATA;
- sql_command_flags[SQLCOM_UNINSTALL_PLUGIN]= CF_CHANGES_DATA;
+ sql_command_flags[SQLCOM_INSTALL_PLUGIN]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
+ sql_command_flags[SQLCOM_UNINSTALL_PLUGIN]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
/*
The following is used to preserver CF_ROW_COUNT during the
@@ -387,11 +445,29 @@ void init_update_queries(void)
last called (or executed) statement is preserved.
See mysql_execute_command() for how CF_ROW_COUNT is used.
*/
+ /*
+ (1): without it, in "CALL some_proc((subq))", subquery would not be
+ traced.
+ */
sql_command_flags[SQLCOM_CALL]= CF_REEXECUTION_FRAGILE |
- CF_CAN_GENERATE_ROW_EVENTS;
+ CF_CAN_GENERATE_ROW_EVENTS |
+ CF_OPTIMIZER_TRACE; // (1)
sql_command_flags[SQLCOM_EXECUTE]= CF_CAN_GENERATE_ROW_EVENTS;
/*
+ We don't want to change to statement based replication for these commands
+ */
+ sql_command_flags[SQLCOM_ROLLBACK]|= CF_FORCE_ORIGINAL_BINLOG_FORMAT;
+ /* We don't want to replicate ALTER TABLE for temp tables in row format */
+ sql_command_flags[SQLCOM_ALTER_TABLE]|= CF_FORCE_ORIGINAL_BINLOG_FORMAT;
+ /* We don't want to replicate TRUNCATE for temp tables in row format */
+ sql_command_flags[SQLCOM_TRUNCATE]|= CF_FORCE_ORIGINAL_BINLOG_FORMAT;
+ /* We don't want to replicate DROP for temp tables in row format */
+ sql_command_flags[SQLCOM_DROP_TABLE]|= CF_FORCE_ORIGINAL_BINLOG_FORMAT;
+ /* One can change replication mode with SET */
+ sql_command_flags[SQLCOM_SET_OPTION]|= CF_FORCE_ORIGINAL_BINLOG_FORMAT;
+
+ /*
The following admin table operations are allowed
on log tables.
*/
@@ -404,18 +480,118 @@ void init_update_queries(void)
sql_command_flags[SQLCOM_CREATE_USER]|= CF_AUTO_COMMIT_TRANS;
sql_command_flags[SQLCOM_DROP_USER]|= CF_AUTO_COMMIT_TRANS;
sql_command_flags[SQLCOM_RENAME_USER]|= CF_AUTO_COMMIT_TRANS;
- sql_command_flags[SQLCOM_REVOKE_ALL]= CF_AUTO_COMMIT_TRANS;
+ sql_command_flags[SQLCOM_CREATE_ROLE]|= CF_AUTO_COMMIT_TRANS;
+ sql_command_flags[SQLCOM_DROP_ROLE]|= CF_AUTO_COMMIT_TRANS;
sql_command_flags[SQLCOM_REVOKE]|= CF_AUTO_COMMIT_TRANS;
+ sql_command_flags[SQLCOM_REVOKE_ALL]= CF_AUTO_COMMIT_TRANS;
+ sql_command_flags[SQLCOM_REVOKE_ROLE]|= CF_AUTO_COMMIT_TRANS;
sql_command_flags[SQLCOM_GRANT]|= CF_AUTO_COMMIT_TRANS;
-
- sql_command_flags[SQLCOM_ASSIGN_TO_KEYCACHE]= CF_AUTO_COMMIT_TRANS;
- sql_command_flags[SQLCOM_PRELOAD_KEYS]= CF_AUTO_COMMIT_TRANS;
+ sql_command_flags[SQLCOM_GRANT_ROLE]|= CF_AUTO_COMMIT_TRANS;
sql_command_flags[SQLCOM_FLUSH]= CF_AUTO_COMMIT_TRANS;
sql_command_flags[SQLCOM_RESET]= CF_AUTO_COMMIT_TRANS;
sql_command_flags[SQLCOM_CREATE_SERVER]= CF_AUTO_COMMIT_TRANS;
sql_command_flags[SQLCOM_ALTER_SERVER]= CF_AUTO_COMMIT_TRANS;
sql_command_flags[SQLCOM_DROP_SERVER]= CF_AUTO_COMMIT_TRANS;
+
+ /*
+ The following statements can deal with temporary tables,
+ so temporary tables should be pre-opened for those statements to
+ simplify privilege checking.
+
+ There are other statements that deal with temporary tables and open
+ them, but which are not listed here. The thing is that the order of
+ pre-opening temporary tables for those statements is somewhat custom.
+ */
+ sql_command_flags[SQLCOM_CREATE_TABLE]|= CF_PREOPEN_TMP_TABLES;
+ sql_command_flags[SQLCOM_DROP_TABLE]|= CF_PREOPEN_TMP_TABLES;
+ sql_command_flags[SQLCOM_CREATE_INDEX]|= CF_PREOPEN_TMP_TABLES;
+ sql_command_flags[SQLCOM_ALTER_TABLE]|= CF_PREOPEN_TMP_TABLES;
+ sql_command_flags[SQLCOM_TRUNCATE]|= CF_PREOPEN_TMP_TABLES;
+ sql_command_flags[SQLCOM_LOAD]|= CF_PREOPEN_TMP_TABLES;
+ sql_command_flags[SQLCOM_DROP_INDEX]|= CF_PREOPEN_TMP_TABLES;
+ sql_command_flags[SQLCOM_UPDATE]|= CF_PREOPEN_TMP_TABLES;
+ sql_command_flags[SQLCOM_UPDATE_MULTI]|= CF_PREOPEN_TMP_TABLES;
+ sql_command_flags[SQLCOM_INSERT_SELECT]|= CF_PREOPEN_TMP_TABLES;
+ sql_command_flags[SQLCOM_DELETE]|= CF_PREOPEN_TMP_TABLES;
+ sql_command_flags[SQLCOM_DELETE_MULTI]|= CF_PREOPEN_TMP_TABLES;
+ sql_command_flags[SQLCOM_REPLACE_SELECT]|= CF_PREOPEN_TMP_TABLES;
+ sql_command_flags[SQLCOM_SELECT]|= CF_PREOPEN_TMP_TABLES;
+ sql_command_flags[SQLCOM_SET_OPTION]|= CF_PREOPEN_TMP_TABLES;
+ sql_command_flags[SQLCOM_DO]|= CF_PREOPEN_TMP_TABLES;
+ sql_command_flags[SQLCOM_HA_OPEN]|= CF_PREOPEN_TMP_TABLES;
+ sql_command_flags[SQLCOM_CALL]|= CF_PREOPEN_TMP_TABLES;
+ sql_command_flags[SQLCOM_CHECKSUM]|= CF_PREOPEN_TMP_TABLES;
+ sql_command_flags[SQLCOM_ANALYZE]|= CF_PREOPEN_TMP_TABLES;
+ sql_command_flags[SQLCOM_CHECK]|= CF_PREOPEN_TMP_TABLES;
+ sql_command_flags[SQLCOM_OPTIMIZE]|= CF_PREOPEN_TMP_TABLES;
+ sql_command_flags[SQLCOM_REPAIR]|= CF_PREOPEN_TMP_TABLES;
+ sql_command_flags[SQLCOM_PRELOAD_KEYS]|= CF_PREOPEN_TMP_TABLES;
+ sql_command_flags[SQLCOM_ASSIGN_TO_KEYCACHE]|= CF_PREOPEN_TMP_TABLES;
+
+ /*
+ DDL statements that should start with closing opened handlers.
+
+ We use this flag only for statements for which open HANDLERs
+ have to be closed before temporary tables are pre-opened.
+ */
+ sql_command_flags[SQLCOM_CREATE_TABLE]|= CF_HA_CLOSE;
+ sql_command_flags[SQLCOM_DROP_TABLE]|= CF_HA_CLOSE;
+ sql_command_flags[SQLCOM_ALTER_TABLE]|= CF_HA_CLOSE;
+ sql_command_flags[SQLCOM_TRUNCATE]|= CF_HA_CLOSE;
+ sql_command_flags[SQLCOM_REPAIR]|= CF_HA_CLOSE;
+ sql_command_flags[SQLCOM_OPTIMIZE]|= CF_HA_CLOSE;
+ sql_command_flags[SQLCOM_ANALYZE]|= CF_HA_CLOSE;
+ sql_command_flags[SQLCOM_CHECK]|= CF_HA_CLOSE;
+ sql_command_flags[SQLCOM_CREATE_INDEX]|= CF_HA_CLOSE;
+ sql_command_flags[SQLCOM_DROP_INDEX]|= CF_HA_CLOSE;
+ sql_command_flags[SQLCOM_PRELOAD_KEYS]|= CF_HA_CLOSE;
+ sql_command_flags[SQLCOM_ASSIGN_TO_KEYCACHE]|= CF_HA_CLOSE;
+
+ /*
+ Mark statements that always are disallowed in read-only
+ transactions. Note that according to the SQL standard,
+ even temporary table DDL should be disallowed.
+ */
+ sql_command_flags[SQLCOM_CREATE_TABLE]|= CF_DISALLOW_IN_RO_TRANS;
+ sql_command_flags[SQLCOM_ALTER_TABLE]|= CF_DISALLOW_IN_RO_TRANS;
+ sql_command_flags[SQLCOM_DROP_TABLE]|= CF_DISALLOW_IN_RO_TRANS;
+ sql_command_flags[SQLCOM_RENAME_TABLE]|= CF_DISALLOW_IN_RO_TRANS;
+ sql_command_flags[SQLCOM_CREATE_INDEX]|= CF_DISALLOW_IN_RO_TRANS;
+ sql_command_flags[SQLCOM_DROP_INDEX]|= CF_DISALLOW_IN_RO_TRANS;
+ sql_command_flags[SQLCOM_CREATE_DB]|= CF_DISALLOW_IN_RO_TRANS;
+ sql_command_flags[SQLCOM_DROP_DB]|= CF_DISALLOW_IN_RO_TRANS;
+ sql_command_flags[SQLCOM_ALTER_DB_UPGRADE]|= CF_DISALLOW_IN_RO_TRANS;
+ sql_command_flags[SQLCOM_ALTER_DB]|= CF_DISALLOW_IN_RO_TRANS;
+ sql_command_flags[SQLCOM_CREATE_VIEW]|= CF_DISALLOW_IN_RO_TRANS;
+ sql_command_flags[SQLCOM_DROP_VIEW]|= CF_DISALLOW_IN_RO_TRANS;
+ sql_command_flags[SQLCOM_CREATE_TRIGGER]|= CF_DISALLOW_IN_RO_TRANS;
+ sql_command_flags[SQLCOM_DROP_TRIGGER]|= CF_DISALLOW_IN_RO_TRANS;
+ sql_command_flags[SQLCOM_CREATE_EVENT]|= CF_DISALLOW_IN_RO_TRANS;
+ sql_command_flags[SQLCOM_ALTER_EVENT]|= CF_DISALLOW_IN_RO_TRANS;
+ sql_command_flags[SQLCOM_DROP_EVENT]|= CF_DISALLOW_IN_RO_TRANS;
+ sql_command_flags[SQLCOM_CREATE_USER]|= CF_DISALLOW_IN_RO_TRANS;
+ sql_command_flags[SQLCOM_RENAME_USER]|= CF_DISALLOW_IN_RO_TRANS;
+ sql_command_flags[SQLCOM_DROP_USER]|= CF_DISALLOW_IN_RO_TRANS;
+ sql_command_flags[SQLCOM_CREATE_SERVER]|= CF_DISALLOW_IN_RO_TRANS;
+ sql_command_flags[SQLCOM_ALTER_SERVER]|= CF_DISALLOW_IN_RO_TRANS;
+ sql_command_flags[SQLCOM_DROP_SERVER]|= CF_DISALLOW_IN_RO_TRANS;
+ sql_command_flags[SQLCOM_CREATE_FUNCTION]|= CF_DISALLOW_IN_RO_TRANS;
+ sql_command_flags[SQLCOM_CREATE_PROCEDURE]|= CF_DISALLOW_IN_RO_TRANS;
+ sql_command_flags[SQLCOM_CREATE_SPFUNCTION]|=CF_DISALLOW_IN_RO_TRANS;
+ sql_command_flags[SQLCOM_DROP_PROCEDURE]|= CF_DISALLOW_IN_RO_TRANS;
+ sql_command_flags[SQLCOM_DROP_FUNCTION]|= CF_DISALLOW_IN_RO_TRANS;
+ sql_command_flags[SQLCOM_ALTER_PROCEDURE]|= CF_DISALLOW_IN_RO_TRANS;
+ sql_command_flags[SQLCOM_ALTER_FUNCTION]|= CF_DISALLOW_IN_RO_TRANS;
+ sql_command_flags[SQLCOM_TRUNCATE]|= CF_DISALLOW_IN_RO_TRANS;
+ sql_command_flags[SQLCOM_ALTER_TABLESPACE]|= CF_DISALLOW_IN_RO_TRANS;
+ sql_command_flags[SQLCOM_REPAIR]|= CF_DISALLOW_IN_RO_TRANS;
+ sql_command_flags[SQLCOM_OPTIMIZE]|= CF_DISALLOW_IN_RO_TRANS;
+ sql_command_flags[SQLCOM_GRANT]|= CF_DISALLOW_IN_RO_TRANS;
+ sql_command_flags[SQLCOM_REVOKE]|= CF_DISALLOW_IN_RO_TRANS;
+ sql_command_flags[SQLCOM_REVOKE_ALL]|= CF_DISALLOW_IN_RO_TRANS;
+ sql_command_flags[SQLCOM_INSTALL_PLUGIN]|= CF_DISALLOW_IN_RO_TRANS;
+ sql_command_flags[SQLCOM_UNINSTALL_PLUGIN]|= CF_DISALLOW_IN_RO_TRANS;
}
bool sqlcom_can_generate_row_events(const THD *thd)
@@ -468,7 +644,7 @@ void execute_init_command(THD *thd, LEX_STRING *init_command,
thd->profiling.set_query_source(buf, len);
#endif
- thd_proc_info(thd, "Execution of init_command");
+ THD_STAGE_INFO(thd, stage_execution_of_init_command);
save_client_capabilities= thd->client_capabilities;
thd->client_capabilities|= CLIENT_MULTI_QUERIES;
/*
@@ -487,21 +663,29 @@ void execute_init_command(THD *thd, LEX_STRING *init_command,
}
+static char *fgets_fn(char *buffer, size_t size, fgets_input_t input, int *error)
+{
+ MYSQL_FILE *in= static_cast<MYSQL_FILE*> (input);
+ char *line= mysql_file_fgets(buffer, size, in);
+ if (error)
+ *error= (line == NULL) ? ferror(in->m_file) : 0;
+ return line;
+}
+
+
static void handle_bootstrap_impl(THD *thd)
{
MYSQL_FILE *file= bootstrap_file;
- char *buff, *res;
-
- DBUG_ENTER("handle_bootstrap");
+ DBUG_ENTER("handle_bootstrap_impl");
#ifndef EMBEDDED_LIBRARY
pthread_detach_this_thread();
thd->thread_stack= (char*) &thd;
#endif /* EMBEDDED_LIBRARY */
- thd_proc_info(thd, 0);
thd->security_ctx->user= (char*) my_strdup("boot", MYF(MY_WME));
- thd->security_ctx->priv_user[0]= thd->security_ctx->priv_host[0]=0;
+ thd->security_ctx->priv_user[0]= thd->security_ctx->priv_host[0]=
+ thd->security_ctx->priv_role[0]= 0;
/*
Make the "client" handle multiple results. This is necessary
to enable stored procedures with SELECTs and Dynamic SQL
@@ -509,50 +693,58 @@ static void handle_bootstrap_impl(THD *thd)
*/
thd->client_capabilities|= CLIENT_MULTI_RESULTS;
- buff= (char*) thd->net.buff;
thd->init_for_queries();
- while (mysql_file_fgets(buff, thd->net.max_packet, file))
+
+ for ( ; ; )
{
+ char buffer[MAX_BOOTSTRAP_QUERY_SIZE] = "";
+ int rc, length;
char *query;
- /* strlen() can't be deleted because mysql_file_fgets() doesn't return length */
- ulong length= (ulong) strlen(buff);
- while (buff[length-1] != '\n' && !mysql_file_feof(file))
+ int error= 0;
+
+ rc= read_bootstrap_query(buffer, &length, file, fgets_fn, &error);
+
+ if (rc == READ_BOOTSTRAP_EOF)
+ break;
+ /*
+ Check for bootstrap file errors. SQL syntax errors will be
+ caught below.
+ */
+ if (rc != READ_BOOTSTRAP_SUCCESS)
{
/*
- We got only a part of the current string. Will try to increase
- net buffer then read the rest of the current string.
+ mysql_parse() may have set a successful error status for the previous
+ query. We must clear the error status to report the bootstrap error.
*/
- /* purecov: begin tested */
- if (net_realloc(&(thd->net), 2 * thd->net.max_packet))
+ thd->get_stmt_da()->reset_diagnostics_area();
+
+ /* Get the nearest query text for reference. */
+ char *err_ptr= buffer + (length <= MAX_BOOTSTRAP_ERROR_LEN ?
+ 0 : (length - MAX_BOOTSTRAP_ERROR_LEN));
+ switch (rc)
{
- thd->protocol->end_statement();
- bootstrap_error= 1;
+ case READ_BOOTSTRAP_ERROR:
+ my_printf_error(ER_UNKNOWN_ERROR, "Bootstrap file error, return code (%d). "
+ "Nearest query: '%s'", MYF(0), error, err_ptr);
break;
- }
- buff= (char*) thd->net.buff;
- res= mysql_file_fgets(buff + length, thd->net.max_packet - length, file);
- if (!res && !mysql_file_feof(file))
- {
- thd->protocol->end_statement();
- bootstrap_error= 1;
+
+ case READ_BOOTSTRAP_QUERY_SIZE:
+ my_printf_error(ER_UNKNOWN_ERROR, "Boostrap file error. Query size "
+ "exceeded %d bytes near '%s'.", MYF(0),
+ MAX_BOOTSTRAP_LINE_SIZE, err_ptr);
break;
- }
- length+= (ulong) strlen(buff + length);
- /* purecov: end */
- }
- if (bootstrap_error)
- break; /* purecov: inspected */
- while (length && (my_isspace(thd->charset(), buff[length-1]) ||
- buff[length-1] == ';'))
- length--;
- buff[length]=0;
+ default:
+ DBUG_ASSERT(false);
+ break;
+ }
- /* Skip lines starting with delimiter */
- if (strncmp(buff, STRING_WITH_LEN("delimiter")) == 0)
- continue;
+ thd->protocol->end_statement();
+ bootstrap_error= 1;
+ break;
+ }
- query= (char *) thd->memdup_w_gap(buff, length + 1,
+ query= (char *) thd->memdup_w_gap(buffer, length + 1,
thd->db_length + 1 +
QUERY_CACHE_DB_LENGTH_SIZE +
QUERY_CACHE_FLAGS_SIZE);
@@ -587,6 +779,7 @@ static void handle_bootstrap_impl(THD *thd)
#if defined(ENABLED_PROFILING)
thd->profiling.finish_current_query();
#endif
+ delete_explain_query(thd->lex);
if (bootstrap_error)
break;
@@ -631,14 +824,13 @@ void do_handle_bootstrap(THD *thd)
handle_bootstrap_impl(thd);
end:
- net_end(&thd->net);
- thd->cleanup();
delete thd;
#ifndef EMBEDDED_LIBRARY
- mysql_mutex_lock(&LOCK_thread_count);
- thread_count--;
+ thread_safe_decrement32(&thread_count, &thread_count_lock);
in_bootstrap= FALSE;
+
+ mysql_mutex_lock(&LOCK_thread_count);
mysql_cond_broadcast(&COND_thread_count);
mysql_mutex_unlock(&LOCK_thread_count);
my_thread_end();
@@ -706,7 +898,7 @@ bool do_command(THD *thd)
{
mysql_mutex_lock(&thd->LOCK_wsrep_thd);
thd->wsrep_query_state= QUERY_IDLE;
- if (thd->wsrep_conflict_state==MUST_ABORT)
+ if (thd->wsrep_conflict_state==MUST_ABORT)
{
wsrep_client_rollback(thd);
}
@@ -734,7 +926,7 @@ bool do_command(THD *thd)
Consider moving to init_connect() instead.
*/
thd->clear_error(); // Clear error message
- thd->stmt_da->reset_diagnostics_area();
+ thd->get_stmt_da()->reset_diagnostics_area();
net_new_transaction(net);
@@ -757,10 +949,12 @@ bool do_command(THD *thd)
*/
DEBUG_SYNC(thd, "before_do_command_net_read");
+ packet_length= my_net_read_packet(net, 1);
+
#ifdef WITH_WSREP
if (WSREP(thd)) {
- packet_length= my_net_read(net);
mysql_mutex_lock(&thd->LOCK_wsrep_thd);
+
/* these THD's are aborted or are aborting during being idle */
if (thd->wsrep_conflict_state == ABORTING)
{
@@ -774,21 +968,18 @@ bool do_command(THD *thd)
else if (thd->wsrep_conflict_state == ABORTED)
{
thd->store_globals();
- thd->wsrep_bf_thd = NULL;
}
thd->wsrep_query_state= QUERY_EXEC;
mysql_mutex_unlock(&thd->LOCK_wsrep_thd);
}
- if ((WSREP(thd) && packet_length == packet_error) ||
- (!WSREP(thd) && (packet_length= my_net_read(net)) == packet_error))
-#else
- if ((packet_length= my_net_read(net)) == packet_error)
#endif /* WITH_WSREP */
+ if (packet_length == packet_error)
{
DBUG_PRINT("info",("Got error %d reading command from socket %s",
net->error,
vio_description(net->vio)));
+
#ifdef WITH_WSREP
if (WSREP(thd)) {
mysql_mutex_lock(&thd->LOCK_wsrep_thd);
@@ -800,6 +991,11 @@ bool do_command(THD *thd)
mysql_mutex_unlock(&thd->LOCK_wsrep_thd);
}
#endif /* WITH_WSREP */
+ /* Instrument this broken statement as "statement/com/error" */
+ thd->m_statement_psi= MYSQL_REFINE_STATEMENT(thd->m_statement_psi,
+ com_statement_info[COM_END].
+ m_key);
+
/* Check if we can continue without closing the connection */
@@ -807,6 +1003,11 @@ bool do_command(THD *thd)
DBUG_ASSERT(thd->is_error());
thd->protocol->end_statement();
+ /* Mark the statement completed. */
+ MYSQL_END_STATEMENT(thd->m_statement_psi, thd->get_stmt_da());
+ thd->m_statement_psi= NULL;
+ thd->m_digest= NULL;
+
if (net->error != 3)
{
return_value= TRUE; // We have to close it.
@@ -873,6 +1074,12 @@ bool do_command(THD *thd)
"WSREP has not yet prepared node for application use",
MYF(0));
thd->protocol->end_statement();
+
+ /* Performance Schema Interface instrumentation end */
+ MYSQL_END_STATEMENT(thd->m_statement_psi, thd->get_stmt_da());
+ thd->m_statement_psi= NULL;
+ thd->m_digest= NULL;
+
return_value= FALSE;
goto out;
}
@@ -882,18 +1089,22 @@ bool do_command(THD *thd)
my_net_set_read_timeout(net, thd->variables.net_read_timeout);
DBUG_ASSERT(packet_length);
+ DBUG_ASSERT(!thd->apc_target.is_enabled());
return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));
#ifdef WITH_WSREP
if (WSREP(thd)) {
while (thd->wsrep_conflict_state== RETRY_AUTOCOMMIT)
{
+ WSREP_DEBUG("Retry autocommit for: %s\n", thd->wsrep_retry_query);
CHARSET_INFO *current_charset = thd->variables.character_set_client;
if (!is_supported_parser_charset(current_charset))
{
/* Do not use non-supported parser character sets */
- WSREP_WARN("Current client character set is non-supported parser character set: %s", current_charset->csname);
+ WSREP_WARN("Current client character set is non-supported parser "
+ "character set: %s", current_charset->csname);
thd->variables.character_set_client = &my_charset_latin1;
- WSREP_WARN("For retry temporally setting character set to : %s", my_charset_latin1.csname);
+ WSREP_WARN("For retry temporally setting character set to : %s",
+ my_charset_latin1.csname);
}
return_value= dispatch_command(command, thd, thd->wsrep_retry_query,
thd->wsrep_retry_query_len);
@@ -908,7 +1119,12 @@ bool do_command(THD *thd)
thd->wsrep_retry_command = COM_CONNECT;
}
#endif /* WITH_WSREP */
+ DBUG_ASSERT(!thd->apc_target.is_enabled());
+
out:
+ /* The statement instrumentation must be closed in all cases. */
+ DBUG_ASSERT(thd->m_digest == NULL);
+ DBUG_ASSERT(thd->m_statement_psi == NULL);
DBUG_RETURN(return_value);
}
#endif /* EMBEDDED_LIBRARY */
@@ -953,7 +1169,7 @@ static my_bool deny_updates_if_read_only_option(THD *thd,
const my_bool create_temp_tables=
(lex->sql_command == SQLCOM_CREATE_TABLE) &&
- (lex->create_info.options & HA_LEX_CREATE_TMP_TABLE);
+ lex->create_info.tmp_table();
const my_bool drop_temp_tables=
(lex->sql_command == SQLCOM_DROP_TABLE) &&
@@ -984,7 +1200,7 @@ static my_bool deny_updates_if_read_only_option(THD *thd,
#ifdef WITH_WSREP
static void wsrep_copy_query(THD *thd)
{
- thd->wsrep_retry_command = thd->command;
+ thd->wsrep_retry_command = thd->get_command();
thd->wsrep_retry_query_len = thd->query_length();
if (thd->wsrep_retry_query) {
my_free(thd->wsrep_retry_query);
@@ -1051,7 +1267,6 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
thd->mysys_var->abort = 0;
thd->wsrep_conflict_state = NO_CONFLICT;
thd->wsrep_retry_counter = 0;
- thd->wsrep_bf_thd = NULL;
/*
Increment threads running to compensate dec_thread_running() called
after dispatch_end label.
@@ -1073,7 +1288,12 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
{ DBUG_PRINT("crash_dispatch_command_before", ("now"));
DBUG_ABORT(); });
- thd->command=command;
+ /* Performance Schema Interface instrumentation, begin */
+ thd->m_statement_psi= MYSQL_REFINE_STATEMENT(thd->m_statement_psi,
+ com_statement_info[command].
+ m_key);
+ thd->set_command(command);
+
/*
Commands which always take a long time are logged into
the slow log only if opt_log_slow_admin_statements is set.
@@ -1081,14 +1301,21 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
thd->enable_slow_log= TRUE;
thd->query_plan_flags= QPLAN_INIT;
thd->lex->sql_command= SQLCOM_END; /* to avoid confusing VIEW detectors */
+ thd->reset_kill_query();
DEBUG_SYNC(thd,"dispatch_command_before_set_time");
thd->set_time();
- if (server_command_flags[command] & CF_SKIP_QUERY_ID)
- thd->set_query_id(get_query_id());
- else
+ if (!(server_command_flags[command] & CF_SKIP_QUERY_ID))
thd->set_query_id(next_query_id());
+ else
+ {
+ /*
+ ping, get statistics or similar stateless command.
+ No reason to increase query id here.
+ */
+ thd->set_query_id(get_query_id());
+ }
inc_thread_running();
if (!(server_command_flags[command] & CF_SKIP_QUESTIONS))
@@ -1124,6 +1351,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
#ifdef HAVE_REPLICATION
case COM_REGISTER_SLAVE:
{
+ status_var_increment(thd->status_var.com_register_slave);
if (!register_slave(thd, (uchar*)packet, packet_length))
my_ok(thd);
break;
@@ -1131,7 +1359,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
#endif
case COM_CHANGE_USER:
{
- bool rc;
+ int auth_rc;
status_var_increment(thd->status_var.com_other);
thd->change_user();
@@ -1162,13 +1390,13 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
if (thd->failed_com_change_user >= 3)
{
my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
- rc= 1;
+ auth_rc= 1;
}
else
- rc= acl_authenticate(thd, 0, packet_length);
+ auth_rc= acl_authenticate(thd, packet_length);
- MYSQL_AUDIT_NOTIFY_CONNECTION_CHANGE_USER(thd);
- if (rc)
+ mysql_audit_notify_connection_change_user(thd);
+ if (auth_rc)
{
/* Free user if allocated by acl_authenticate */
my_free(thd->security_ctx->user);
@@ -1228,6 +1456,10 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
}
case COM_QUERY:
{
+ DBUG_ASSERT(thd->m_digest == NULL);
+ thd->m_digest= & thd->m_digest_state;
+ thd->m_digest->reset(thd->m_token_array, max_digest_length);
+
if (alloc_query(thd, packet, packet_length))
break; // fatal error is set
MYSQL_QUERY_START(thd->query(), thd->thread_id,
@@ -1240,6 +1472,9 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
#if defined(ENABLED_PROFILING)
thd->profiling.set_query_source(thd->query(), thd->query_length());
#endif
+ MYSQL_SET_STATEMENT_TEXT(thd->m_statement_psi, thd->query(),
+ thd->query_length());
+
Parser_state parser_state;
if (parser_state.init(thd, thd->query(), thd->query_length()))
break;
@@ -1268,12 +1503,15 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
query_cache_end_of_result(thd);
mysql_audit_general(thd, MYSQL_AUDIT_GENERAL_STATUS,
- thd->stmt_da->is_error() ? thd->stmt_da->sql_errno()
- : 0, command_name[command].str);
+ thd->get_stmt_da()->is_error()
+ ? thd->get_stmt_da()->sql_errno()
+ : 0,
+ command_name[command].str);
ulong length= (ulong)(packet_end - beginning_of_next_stmt);
log_slow_statement(thd);
+ DBUG_ASSERT(!thd->apc_target.is_enabled());
/* Remove garbage at start of query */
while (length > 0 && my_isspace(thd->charset(), *beginning_of_next_stmt))
@@ -1282,6 +1520,12 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
length--;
}
+ /* PSI end */
+ MYSQL_END_STATEMENT(thd->m_statement_psi, thd->get_stmt_da());
+ thd->m_statement_psi= NULL;
+ thd->m_digest= NULL;
+
+ /* DTRACE end */
if (MYSQL_QUERY_DONE_ENABLED())
{
MYSQL_QUERY_DONE(thd->is_error());
@@ -1293,11 +1537,23 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
thd->profiling.set_query_source(beginning_of_next_stmt, length);
#endif
+ /* DTRACE begin */
MYSQL_QUERY_START(beginning_of_next_stmt, thd->thread_id,
(char *) (thd->db ? thd->db : ""),
&thd->security_ctx->priv_user[0],
(char *) thd->security_ctx->host_or_ip);
+ /* PSI begin */
+ thd->m_digest= & thd->m_digest_state;
+
+ thd->m_statement_psi= MYSQL_START_STATEMENT(&thd->m_statement_state,
+ com_statement_info[command].m_key,
+ thd->db, thd->db_length,
+ thd->charset());
+ THD_STAGE_INFO(thd, stage_init);
+ MYSQL_SET_STATEMENT_TEXT(thd->m_statement_psi, beginning_of_next_stmt,
+ length);
+
thd->set_query_and_id(beginning_of_next_stmt, length,
thd->charset(), next_query_id());
/*
@@ -1369,7 +1625,10 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
lex_start(thd);
/* Must be before we init the table list. */
if (lower_case_table_names)
+ {
table_name.length= my_casedn_str(files_charset_info, table_name.str);
+ db.length= my_casedn_str(files_charset_info, db.str);
+ }
table_list.init_one_table(db.str, db.length, table_name.str,
table_name.length, table_name.str, TL_READ);
/*
@@ -1395,6 +1654,9 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
thd->set_query(fields, query_length);
general_log_print(thd, command, "%s %s", table_list.table_name, fields);
+ if (open_temporary_tables(thd, &table_list))
+ break;
+
if (check_table_access(thd, SELECT_ACL, &table_list,
TRUE, UINT_MAX, FALSE))
break;
@@ -1431,7 +1693,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
/* We don't calculate statistics for this command */
general_log_print(thd, command, NullS);
net->error=0; // Don't give 'abort' message
- thd->stmt_da->disable_status(); // Don't send anything back
+ thd->get_stmt_da()->disable_status(); // Don't send anything back
error=TRUE; // End server
break;
#ifndef EMBEDDED_LIBRARY
@@ -1451,10 +1713,10 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
/* TODO: The following has to be changed to an 8 byte integer */
pos = uint4korr(packet);
flags = uint2korr(packet + 4);
- thd->server_id=0; /* avoid suicide */
+ thd->variables.server_id=0; /* avoid suicide */
if ((slave_server_id= uint4korr(packet+6))) // mysqlbinlog.server_id==0
kill_zombie_dump_threads(slave_server_id);
- thd->server_id = slave_server_id;
+ thd->variables.server_id = slave_server_id;
general_log_print(thd, command, "Log: '%s' Pos: %ld", packet+10,
(long) pos);
@@ -1477,7 +1739,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
lex_start(thd);
status_var_increment(thd->status_var.com_stat[SQLCOM_FLUSH]);
- ulong options= (ulong) (uchar) packet[0];
+ ulonglong options= (ulonglong) (uchar) packet[0];
if (trans_commit_implicit(thd))
break;
thd->mdl_context.release_transactional_locks();
@@ -1496,17 +1758,21 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
and flushes tables.
*/
bool res;
- my_pthread_setspecific_ptr(THR_THD, NULL);
+ set_current_thd(0);
res= reload_acl_and_cache(NULL, options | REFRESH_FAST,
NULL, &not_used);
- my_pthread_setspecific_ptr(THR_THD, thd);
+ set_current_thd(thd);
if (res)
break;
}
else
#endif
- if (reload_acl_and_cache(thd, options, (TABLE_LIST*) 0, &not_used))
- break;
+ {
+ thd->lex->relay_log_connection_name.str= (char*) "";
+ thd->lex->relay_log_connection_name.length= 0;
+ if (reload_acl_and_cache(thd, options, (TABLE_LIST*) 0, &not_used))
+ break;
+ }
if (trans_commit_implicit(thd))
break;
close_thread_tables(thd);
@@ -1561,7 +1827,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
if (!(uptime= (ulong) (thd->start_time - server_start_time)))
queries_per_second1000= 0;
else
- queries_per_second1000= thd->query_id * LL(1000) / uptime;
+ queries_per_second1000= thd->query_id * 1000 / uptime;
length= my_snprintf(buff, buff_len - 1,
"Uptime: %lu Threads: %d Questions: %lu "
@@ -1571,8 +1837,8 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
(int) thread_count, (ulong) thd->query_id,
current_global_status_var->long_query_count,
current_global_status_var->opened_tables,
- refresh_version,
- cached_open_tables(),
+ tdc_refresh_version(),
+ tc_records(),
(uint) (queries_per_second1000 / 1000),
(uint) (queries_per_second1000 % 1000));
#ifdef EMBEDDED_LIBRARY
@@ -1581,7 +1847,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
#else
(void) my_net_write(net, (uchar*) buff, length);
(void) net_flush(net);
- thd->stmt_da->disable_status();
+ thd->get_stmt_da()->disable_status();
#endif
break;
}
@@ -1603,7 +1869,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
{
status_var_increment(thd->status_var.com_stat[SQLCOM_KILL]);
ulong id=(ulong) uint4korr(packet);
- sql_kill(thd,id, KILL_CONNECTION_HARD);
+ sql_kill(thd, id, KILL_CONNECTION_HARD, KILL_TYPE_ID);
break;
}
case COM_SET_OPTION:
@@ -1650,9 +1916,9 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
/* wsrep BF abort in query exec phase */
mysql_mutex_lock(&thd->LOCK_wsrep_thd);
if ((thd->wsrep_conflict_state != REPLAYING) &&
- (thd->wsrep_conflict_state != RETRY_AUTOCOMMIT)) {
+ (thd->wsrep_conflict_state != RETRY_AUTOCOMMIT))
+ {
mysql_mutex_unlock(&thd->LOCK_wsrep_thd);
-
thd->update_server_status();
thd->protocol->end_statement();
query_cache_end_of_result(thd);
@@ -1680,28 +1946,26 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
mysql_audit_general(thd, MYSQL_AUDIT_GENERAL_RESULT, 0, 0);
mysql_audit_general(thd, MYSQL_AUDIT_GENERAL_STATUS,
- thd->stmt_da->is_error() ? thd->stmt_da->sql_errno() : 0,
+ thd->get_stmt_da()->is_error() ?
+ thd->get_stmt_da()->sql_errno() : 0,
command_name[command].str);
thd->update_all_stats();
log_slow_statement(thd);
- thd_proc_info(thd, "cleaning up");
+ THD_STAGE_INFO(thd, stage_cleaning_up);
thd->reset_query();
- thd->command=COM_SLEEP;
+ thd->set_examined_row_count(0); // For processlist
+ thd->set_command(COM_SLEEP);
+
+ /* Performance Schema Interface instrumentation, end */
+ MYSQL_END_STATEMENT(thd->m_statement_psi, thd->get_stmt_da());
+ thd->m_statement_psi= NULL;
+ thd->m_digest= NULL;
+
thd->set_time();
dec_thread_running();
-#ifdef WITH_WSREP
- if (WSREP(thd)) {
- thd_proc_info(thd, "sleeping");
- } else {
-#endif /* WITH_WSREP */
- thd_proc_info(thd, 0);
-#ifdef WITH_WSREP
- }
-#endif /* WITH_WSREP */
-
thd->packet.shrink(thd->variables.net_buffer_length); // Reclaim some memory
free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC));
@@ -1726,30 +1990,38 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
}
+/*
+ @note
+ This function must call delete_explain_query().
+*/
void log_slow_statement(THD *thd)
{
DBUG_ENTER("log_slow_statement");
+
/*
The following should never be true with our current code base,
but better to keep this here so we don't accidently try to log a
statement in a trigger or stored function
*/
if (unlikely(thd->in_sub_stmt))
- DBUG_VOID_RETURN; // Don't set time for sub stmt
+ goto end; // Don't set time for sub stmt
+
/* Follow the slow log filter configuration. */
if (!thd->enable_slow_log ||
(thd->variables.log_slow_filter
&& !(thd->variables.log_slow_filter & thd->query_plan_flags)))
- DBUG_VOID_RETURN;
+ {
+ goto end;
+ }
if (((thd->server_status & SERVER_QUERY_WAS_SLOW) ||
((thd->server_status &
(SERVER_QUERY_NO_INDEX_USED | SERVER_QUERY_NO_GOOD_INDEX_USED)) &&
opt_log_queries_not_using_indexes &&
!(sql_command_flags[thd->lex->sql_command] & CF_STATUS_COMMAND))) &&
- thd->examined_row_count >= thd->variables.min_examined_row_limit)
+ thd->get_examined_row_count() >= thd->variables.min_examined_row_limit)
{
thd->status_var.long_query_count++;
/*
@@ -1758,13 +2030,15 @@ void log_slow_statement(THD *thd)
*/
if (thd->variables.log_slow_rate_limit > 1 &&
(global_query_id % thd->variables.log_slow_rate_limit) != 0)
- DBUG_VOID_RETURN;
+ goto end;
- thd_proc_info(thd, "logging slow query");
+ THD_STAGE_INFO(thd, stage_logging_slow_query);
slow_log_print(thd, thd->query(), thd->query_length(),
thd->utime_after_query);
- thd_proc_info(thd, 0);
}
+
+end:
+ delete_explain_query(thd->lex);
DBUG_VOID_RETURN;
}
@@ -1861,8 +2135,8 @@ int prepare_schema_table(THD *thd, LEX *lex, Table_ident *table_ident,
DBUG_RETURN(1);
lex->query_tables_last= query_tables_last;
break;
- }
#endif
+ }
case SCH_PROFILES:
/*
Mark this current profiling record to be discarded. We don't
@@ -1971,25 +2245,7 @@ bool alloc_query(THD *thd, const char *packet, uint packet_length)
return FALSE;
}
-static void reset_one_shot_variables(THD *thd)
-{
- thd->variables.character_set_client=
- global_system_variables.character_set_client;
- thd->variables.collation_connection=
- global_system_variables.collation_connection;
- thd->variables.collation_database=
- global_system_variables.collation_database;
- thd->variables.collation_server=
- global_system_variables.collation_server;
- thd->update_charset();
- thd->variables.time_zone=
- global_system_variables.time_zone;
- thd->variables.lc_time_names= &my_locale_en_US;
- thd->one_shot_set= 0;
-}
-
-static
bool sp_process_definer(THD *thd)
{
DBUG_ENTER("sp_process_definer");
@@ -2026,7 +2282,7 @@ bool sp_process_definer(THD *thd)
Query_arena original_arena;
Query_arena *ps_arena= thd->activate_stmt_arena_if_needed(&original_arena);
- lex->definer= create_default_definer(thd);
+ lex->definer= create_default_definer(thd, false);
if (ps_arena)
thd->restore_active_arena(ps_arena, &original_arena);
@@ -2040,20 +2296,24 @@ bool sp_process_definer(THD *thd)
}
else
{
+ LEX_USER *d= lex->definer= get_current_user(thd, lex->definer);
+ if (!d)
+ DBUG_RETURN(TRUE);
+
/*
- If the specified definer differs from the current user, we
+ If the specified definer differs from the current user or role, we
should check that the current user has SUPER privilege (in order
to create a stored routine under another user one must have
SUPER privilege).
*/
- if ((strcmp(lex->definer->user.str, thd->security_ctx->priv_user) ||
- my_strcasecmp(system_charset_info, lex->definer->host.str,
- thd->security_ctx->priv_host)) &&
- check_global_access(thd, SUPER_ACL, true))
- {
- my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), "SUPER");
+ bool curuser= !strcmp(d->user.str, thd->security_ctx->priv_user);
+ bool currole= !curuser && !strcmp(d->user.str, thd->security_ctx->priv_role);
+ bool curuserhost= curuser && d->host.str &&
+ !my_strcasecmp(system_charset_info, d->host.str,
+ thd->security_ctx->priv_host);
+ if (!curuserhost && !currole &&
+ check_global_access(thd, SUPER_ACL, false))
DBUG_RETURN(TRUE);
- }
}
/* Check that the specified definer exists. Emit a warning if not. */
@@ -2062,7 +2322,7 @@ bool sp_process_definer(THD *thd)
if (!is_acl_user(lex->definer->host.str, lex->definer->user.str))
{
push_warning_printf(thd,
- MYSQL_ERROR::WARN_LEVEL_NOTE,
+ Sql_condition::WARN_LEVEL_NOTE,
ER_NO_SUCH_USER,
ER(ER_NO_SUCH_USER),
lex->definer->user.str,
@@ -2177,6 +2437,8 @@ mysql_execute_command(THD *thd)
#ifdef HAVE_REPLICATION
/* have table map for update for multi-update statement (BUG#37051) */
bool have_table_map_for_update= FALSE;
+ /* */
+ Rpl_filter *rpl_filter;
#endif
DBUG_ENTER("mysql_execute_command");
@@ -2222,12 +2484,12 @@ mysql_execute_command(THD *thd)
variables, but for now this is probably good enough.
*/
if ((sql_command_flags[lex->sql_command] & CF_DIAGNOSTIC_STMT) != 0)
- thd->warning_info->set_read_only(TRUE);
+ thd->get_stmt_da()->set_warning_info_read_only(TRUE);
else
{
- thd->warning_info->set_read_only(FALSE);
+ thd->get_stmt_da()->set_warning_info_read_only(FALSE);
if (all_tables)
- thd->warning_info->opt_clear_warning_info(thd->query_id);
+ thd->get_stmt_da()->opt_clear_warning_info(thd->query_id);
}
#ifdef HAVE_REPLICATION
@@ -2250,11 +2512,15 @@ mysql_execute_command(THD *thd)
according to slave filtering rules.
Returning success without producing any errors in this case.
*/
- DBUG_RETURN(0);
+ if (!thd->lex->check_exists)
+ DBUG_RETURN(0);
+ /*
+ DROP TRIGGER IF NOT EXISTS will return without an error later
+ after possibly writing the query to a binlog
+ */
}
-
- // force searching in slave.cc:tables_ok()
- all_tables->updating= 1;
+ else // force searching in slave.cc:tables_ok()
+ all_tables->updating= 1;
}
/*
@@ -2290,9 +2556,6 @@ mysql_execute_command(THD *thd)
{
/* we warn the slave SQL thread */
my_message(ER_SLAVE_IGNORED_TABLE, ER(ER_SLAVE_IGNORED_TABLE), MYF(0));
- if (thd->one_shot_set)
- reset_one_shot_variables(thd);
- DBUG_RETURN(0);
}
for (table=all_tables; table; table=table->next_global)
@@ -2315,28 +2578,11 @@ mysql_execute_command(THD *thd)
if (!(lex->sql_command == SQLCOM_UPDATE_MULTI) &&
!(lex->sql_command == SQLCOM_SET_OPTION) &&
!(lex->sql_command == SQLCOM_DROP_TABLE &&
- lex->drop_temporary && lex->drop_if_exists) &&
+ lex->drop_temporary && lex->check_exists) &&
all_tables_not_ok(thd, all_tables))
{
/* we warn the slave SQL thread */
my_message(ER_SLAVE_IGNORED_TABLE, ER(ER_SLAVE_IGNORED_TABLE), MYF(0));
- if (thd->one_shot_set)
- {
- /*
- It's ok to check thd->one_shot_set here:
-
- The charsets in a MySQL 5.0 slave can change by both a binlogged
- SET ONE_SHOT statement and the event-internal charset setting,
- and these two ways to change charsets do not seems to work
- together.
-
- At least there seems to be problems in the rli cache for
- charsets if we are using ONE_SHOT. Note that this is normally no
- problem because either the >= 5.0 slave reads a 4.1 binlog (with
- ONE_SHOT) *or* or 5.0 binlog (without ONE_SHOT) but never both."
- */
- reset_one_shot_variables(thd);
- }
DBUG_RETURN(0);
}
/*
@@ -2424,11 +2670,27 @@ mysql_execute_command(THD *thd)
}
#endif /* WITH_WSREP */
status_var_increment(thd->status_var.com_stat[lex->sql_command]);
- thd->progress.report_to_client= test(sql_command_flags[lex->sql_command] &
- CF_REPORT_PROGRESS);
+ thd->progress.report_to_client= MY_TEST(sql_command_flags[lex->sql_command] &
+ CF_REPORT_PROGRESS);
DBUG_ASSERT(thd->transaction.stmt.modified_non_trans_table == FALSE);
+ /* store old value of binlog format */
+ enum_binlog_format orig_binlog_format,orig_current_stmt_binlog_format;
+
+ thd->get_binlog_format(&orig_binlog_format,
+ &orig_current_stmt_binlog_format);
+
+ /*
+ Force statement logging for DDL commands to allow us to update
+ privilege, system or statistic tables directly without the updates
+ getting logged.
+ */
+ if (!(sql_command_flags[lex->sql_command] &
+ (CF_CAN_GENERATE_ROW_EVENTS | CF_FORCE_ORIGINAL_BINLOG_FORMAT |
+ CF_STATUS_COMMAND)))
+ thd->set_binlog_format_stmt();
+
/*
End a active transaction so that this command will have it's
own transaction and will also sync the binary log. If a DDL is
@@ -2444,24 +2706,65 @@ mysql_execute_command(THD *thd)
DBUG_ASSERT(! thd->in_sub_stmt);
/* Statement transaction still should not be started. */
DBUG_ASSERT(thd->transaction.stmt.is_empty());
- /* Commit the normal transaction if one is active. */
- if (trans_commit_implicit(thd))
+ if (!(thd->variables.option_bits & OPTION_GTID_BEGIN))
{
- thd->mdl_context.release_transactional_locks();
+ /* Commit the normal transaction if one is active. */
+ if (trans_commit_implicit(thd))
+ {
+ thd->mdl_context.release_transactional_locks();
#ifdef WITH_WSREP
- WSREP_DEBUG("implicit commit failed, MDL released: %lu", thd->thread_id);
+ WSREP_DEBUG("implicit commit failed, MDL released: %lu", thd->thread_id);
#endif /* WITH_WSREP */
- goto error;
+ goto error;
+ }
+ /* Release metadata locks acquired in this transaction. */
+ thd->mdl_context.release_transactional_locks();
}
- /* Release metadata locks acquired in this transaction. */
- thd->mdl_context.release_transactional_locks();
}
-
+
#ifndef DBUG_OFF
if (lex->sql_command != SQLCOM_SET_OPTION)
DEBUG_SYNC(thd,"before_execute_sql_command");
#endif
+ /*
+ Check if we are in a read-only transaction and we're trying to
+ execute a statement which should always be disallowed in such cases.
+
+ Note that this check is done after any implicit commits.
+ */
+ if (thd->tx_read_only &&
+ (sql_command_flags[lex->sql_command] & CF_DISALLOW_IN_RO_TRANS))
+ {
+ my_error(ER_CANT_EXECUTE_IN_READ_ONLY_TRANSACTION, MYF(0));
+ goto error;
+ }
+
+ /*
+ Close tables open by HANDLERs before executing DDL statement
+ which is going to affect those tables.
+
+ This should happen before temporary tables are pre-opened as
+ otherwise we will get errors about attempt to re-open tables
+ if table to be changed is open through HANDLER.
+
+ Note that even although this is done before any privilege
+ checks there is no security problem here as closing open
+ HANDLER doesn't require any privileges anyway.
+ */
+ if (sql_command_flags[lex->sql_command] & CF_HA_CLOSE)
+ mysql_ha_rm_tables(thd, all_tables);
+
+ /*
+ Pre-open temporary tables to simplify privilege checking
+ for statements which need this.
+ */
+ if (sql_command_flags[lex->sql_command] & CF_PREOPEN_TMP_TABLES)
+ {
+ if (open_temporary_tables(thd, all_tables))
+ goto error;
+ }
+
switch (lex->sql_command) {
case SQLCOM_SHOW_EVENTS:
@@ -2469,24 +2772,44 @@ mysql_execute_command(THD *thd)
my_error(ER_NOT_SUPPORTED_YET, MYF(0), "embedded server");
break;
#endif
- case SQLCOM_SHOW_STATUS_PROC:
- case SQLCOM_SHOW_STATUS_FUNC:
-#ifdef WITH_WSREP
- if (WSREP_CLIENT(thd) && wsrep_sync_wait(thd)) goto error;
-#endif /* WITH_WSREP */
- if ((res= check_table_access(thd, SELECT_ACL, all_tables, FALSE,
- UINT_MAX, FALSE)))
- goto error;
- res= execute_sqlcom_select(thd, all_tables);
- break;
case SQLCOM_SHOW_STATUS:
{
execute_show_status(thd, all_tables);
-#ifdef WITH_WSREP
- if (lex->sql_command == SQLCOM_SHOW_STATUS) wsrep_free_status(thd);
-#endif /* WITH_WSREP */
break;
}
+ case SQLCOM_SHOW_EXPLAIN:
+ {
+ if (!thd->security_ctx->priv_user[0] &&
+ check_global_access(thd,PROCESS_ACL))
+ break;
+
+ /*
+ The select should use only one table, it's the SHOW EXPLAIN pseudo-table
+ */
+ if (lex->sroutines.records || lex->query_tables->next_global)
+ {
+ my_message(ER_SET_CONSTANTS_ONLY, ER(ER_SET_CONSTANTS_ONLY),
+ MYF(0));
+ goto error;
+ }
+
+ Item **it= lex->value_list.head_ref();
+ if (!(*it)->basic_const_item() ||
+ (!(*it)->fixed && (*it)->fix_fields(lex->thd, it)) ||
+ (*it)->check_cols(1))
+ {
+ my_message(ER_SET_CONSTANTS_ONLY, ER(ER_SET_CONSTANTS_ONLY),
+ MYF(0));
+ goto error;
+ }
+ /* no break; fall through */
+ }
+ case SQLCOM_SHOW_STATUS_PROC:
+ case SQLCOM_SHOW_STATUS_FUNC:
+#ifdef WITH_WSREP
+ if (WSREP_CLIENT(thd) && wsrep_sync_wait(thd)) goto error;
+#endif /* WITH_WSREP */
+
case SQLCOM_SHOW_DATABASES:
case SQLCOM_SHOW_TABLES:
case SQLCOM_SHOW_TRIGGERS:
@@ -2515,7 +2838,7 @@ mysql_execute_command(THD *thd)
case SQLCOM_SHOW_STORAGE_ENGINES:
case SQLCOM_SHOW_PROFILE:
#endif /* WITH_WSREP */
- {
+ {
thd->status_var.last_query_cost= 0.0;
/*
@@ -2601,16 +2924,16 @@ case SQLCOM_PREPARE:
case SQLCOM_SHOW_WARNS:
{
res= mysqld_show_warnings(thd, (ulong)
- ((1L << (uint) MYSQL_ERROR::WARN_LEVEL_NOTE) |
- (1L << (uint) MYSQL_ERROR::WARN_LEVEL_WARN) |
- (1L << (uint) MYSQL_ERROR::WARN_LEVEL_ERROR)
+ ((1L << (uint) Sql_condition::WARN_LEVEL_NOTE) |
+ (1L << (uint) Sql_condition::WARN_LEVEL_WARN) |
+ (1L << (uint) Sql_condition::WARN_LEVEL_ERROR)
));
break;
}
case SQLCOM_SHOW_ERRORS:
{
res= mysqld_show_warnings(thd, (ulong)
- (1L << (uint) MYSQL_ERROR::WARN_LEVEL_ERROR));
+ (1L << (uint) Sql_condition::WARN_LEVEL_ERROR));
break;
}
case SQLCOM_SHOW_PROFILES:
@@ -2670,10 +2993,55 @@ case SQLCOM_PREPARE:
#ifdef HAVE_REPLICATION
case SQLCOM_CHANGE_MASTER:
{
+ LEX_MASTER_INFO *lex_mi= &thd->lex->mi;
+ Master_info *mi;
+ bool new_master= 0;
+ bool master_info_added;
+
if (check_global_access(thd, SUPER_ACL))
goto error;
mysql_mutex_lock(&LOCK_active_mi);
- res = change_master(thd,active_mi);
+
+ if (!master_info_index)
+ goto error;
+
+ mi= master_info_index->get_master_info(&lex_mi->connection_name,
+ Sql_condition::WARN_LEVEL_NOTE);
+
+ if (mi == NULL)
+ {
+ /* New replication created */
+ mi= new Master_info(&lex_mi->connection_name, relay_log_recovery);
+ if (!mi || mi->error())
+ {
+ delete mi;
+ res= 1;
+ mysql_mutex_unlock(&LOCK_active_mi);
+ break;
+ }
+ new_master= 1;
+ }
+
+ res= change_master(thd, mi, &master_info_added);
+ if (res && new_master)
+ {
+ /*
+ If the new master was added by change_master(), remove it as it didn't
+ work (this will free mi as well).
+
+ If new master was not added, we still need to free mi.
+ */
+ if (master_info_added)
+ master_info_index->remove_master_info(&lex_mi->connection_name);
+ else
+ delete mi;
+ }
+ else
+ {
+ mi->rpl_filter= get_or_create_rpl_filter(lex_mi->connection_name.str,
+ lex_mi->connection_name.length);
+ }
+
mysql_mutex_unlock(&LOCK_active_mi);
break;
}
@@ -2683,15 +3051,19 @@ case SQLCOM_PREPARE:
if (check_global_access(thd, SUPER_ACL | REPL_CLIENT_ACL))
goto error;
mysql_mutex_lock(&LOCK_active_mi);
- if (active_mi != NULL)
- {
- res = show_master_info(thd, active_mi);
- }
+
+ if (lex->verbose)
+ res= show_all_master_info(thd);
else
{
- push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
- WARN_NO_MASTER_INFO, ER(WARN_NO_MASTER_INFO));
- my_ok(thd);
+ LEX_MASTER_INFO *lex_mi= &thd->lex->mi;
+ Master_info *mi;
+ mi= master_info_index->get_master_info(&lex_mi->connection_name,
+ Sql_condition::WARN_LEVEL_ERROR);
+ if (mi != NULL)
+ {
+ res= show_master_info(thd, mi, 0);
+ }
}
mysql_mutex_unlock(&LOCK_active_mi);
break;
@@ -2749,20 +3121,20 @@ case SQLCOM_PREPARE:
goto end_with_restore_list;
}
+ /* Check privileges */
if ((res= create_table_precheck(thd, select_tables, create_table)))
goto end_with_restore_list;
/* Might have been updated in create_table_precheck */
create_info.alias= create_table->alias;
-#ifdef HAVE_READLINK
- /* Fix names if symlinked tables */
+ /* Fix names if symlinked or relocated tables */
if (append_file_to_dir(thd, &create_info.data_file_name,
create_table->table_name) ||
append_file_to_dir(thd, &create_info.index_file_name,
create_table->table_name))
goto end_with_restore_list;
-#endif
+
/*
If no engine type was given, work out the default now
rather than at parse-time.
@@ -2783,6 +3155,24 @@ case SQLCOM_PREPARE:
create_info.table_charset= 0;
}
+ /*
+ For CREATE TABLE we should not open the table even if it exists.
+ If the table exists, we should either not create it or replace it
+ */
+ lex->query_tables->open_strategy= TABLE_LIST::OPEN_STUB;
+
+ /*
+ If we are a slave, we should add OR REPLACE if we don't have
+ IF EXISTS. This will help a slave to recover from
+ CREATE TABLE OR EXISTS failures by dropping the table and
+ retrying the create.
+ */
+ create_info.org_options= create_info.options;
+ if (thd->slave_thread &&
+ slave_ddl_exec_mode_options == SLAVE_EXEC_MODE_IDEMPOTENT &&
+ !(lex->create_info.options & HA_LEX_CREATE_IF_NOT_EXISTS))
+ create_info.options|= HA_LEX_CREATE_REPLACE;
+
#ifdef WITH_PARTITION_STORAGE_ENGINE
{
partition_info *part_info= thd->lex->part_info;
@@ -2795,9 +3185,6 @@ case SQLCOM_PREPARE:
}
#endif
- /* Close any open handlers for the table. */
- mysql_ha_rm_tables(thd, create_table);
-
if (select_lex->item_list.elements) // With select
{
select_result *result;
@@ -2825,7 +3212,7 @@ case SQLCOM_PREPARE:
*/
if (thd->query_name_consts &&
mysql_bin_log.is_open() &&
- WSREP_BINLOG_FORMAT(thd->variables.binlog_format) == BINLOG_FORMAT_STMT &&
+ WSREP_FORMAT(thd->variables.binlog_format) == BINLOG_FORMAT_STMT &&
!mysql_bin_log.is_query_in_union(thd, thd->query_id))
{
List_iterator_fast<Item> it(select_lex->item_list);
@@ -2845,7 +3232,7 @@ case SQLCOM_PREPARE:
*/
if (splocal_refs != thd->query_name_consts)
push_warning(thd,
- MYSQL_ERROR::WARN_LEVEL_WARN,
+ Sql_condition::WARN_LEVEL_WARN,
ER_UNKNOWN_ERROR,
"Invoked routine ran a statement that may cause problems with "
"binary log, see 'NAME_CONST issues' in 'Binary Logging of Stored Programs' "
@@ -2868,34 +3255,35 @@ case SQLCOM_PREPARE:
goto end_with_restore_list;
}
+ /* Copy temporarily the statement flags to thd for lock_table_names() */
+ uint save_thd_create_info_options= thd->lex->create_info.options;
+ thd->lex->create_info.options|= create_info.options;
res= open_and_lock_tables(thd, lex->query_tables, TRUE, 0);
+ thd->lex->create_info.options= save_thd_create_info_options;
if (res)
{
/* Got error or warning. Set res to 1 if error */
if (!(res= thd->is_error()))
my_ok(thd); // CREATE ... IF NOT EXISTS
+ goto end_with_restore_list;
}
- else
+
+ /* Ensure we don't try to create something from which we select from */
+ if ((create_info.options & HA_LEX_CREATE_REPLACE) &&
+ !create_info.tmp_table())
{
- /* The table already exists */
- if (create_table->table)
+ TABLE_LIST *duplicate;
+ if ((duplicate= unique_table(thd, lex->query_tables,
+ lex->query_tables->next_global,
+ 0)))
{
- if (create_info.options & HA_LEX_CREATE_IF_NOT_EXISTS)
- {
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
- ER_TABLE_EXISTS_ERROR,
- ER(ER_TABLE_EXISTS_ERROR),
- create_info.alias);
- my_ok(thd);
- }
- else
- {
- my_error(ER_TABLE_EXISTS_ERROR, MYF(0), create_info.alias);
- res= 1;
- }
+ update_non_unique_table_error(lex->query_tables, "CREATE",
+ duplicate);
+ res= TRUE;
goto end_with_restore_list;
}
-
+ }
+ {
/*
Remove target table from main select and name resolution
context. This can't be done earlier as it will break view merging in
@@ -2903,9 +3291,8 @@ case SQLCOM_PREPARE:
*/
lex->unlink_first_table(&link_to_local);
- /* So that CREATE TEMPORARY TABLE gets to binlog at commit/rollback */
- if (create_info.options & HA_LEX_CREATE_TMP_TABLE)
- thd->variables.option_bits|= OPTION_KEEP_LOG;
+ /* Store reference to table in case of LOCK TABLES */
+ create_info.table= create_table->table;
/*
select_create is currently not re-execution friendly and
@@ -2924,18 +3311,18 @@ case SQLCOM_PREPARE:
CREATE from SELECT give its SELECT_LEX for SELECT,
and item_list belong to SELECT
*/
- res= handle_select(thd, lex, result, 0);
+ if (!(res= handle_select(thd, lex, result, 0)))
+ {
+ if (create_info.tmp_table())
+ thd->variables.option_bits|= OPTION_KEEP_LOG;
+ }
delete result;
}
-
lex->link_first_table_back(create_table, link_to_local);
}
}
else
{
- /* So that CREATE TEMPORARY TABLE gets to binlog at commit/rollback */
- if (create_info.options & HA_LEX_CREATE_TMP_TABLE)
- thd->variables.option_bits|= OPTION_KEEP_LOG;
/* regular create */
if (create_info.options & HA_LEX_CREATE_TABLE_LIKE)
{
@@ -2950,7 +3337,7 @@ case SQLCOM_PREPARE:
tables, like mysql replication does
*/
if (!thd->is_current_stmt_binlog_format_row() ||
- !(create_info.options & HA_LEX_CREATE_TMP_TABLE))
+ !(create_info.options & HA_LEX_CREATE_TMP_TABLE))
WSREP_TO_ISOLATION_BEGIN(create_table->db, create_table->table_name,
NULL)
#endif /* WITH_WSREP */
@@ -2959,14 +3346,18 @@ case SQLCOM_PREPARE:
&create_info, &alter_info);
}
if (!res)
+ {
+ /* So that CREATE TEMPORARY TABLE gets to binlog at commit/rollback */
+ if (create_info.tmp_table())
+ thd->variables.option_bits|= OPTION_KEEP_LOG;
my_ok(thd);
+ }
}
end_with_restore_list:
break;
}
case SQLCOM_CREATE_INDEX:
- /* Fall through */
case SQLCOM_DROP_INDEX:
/*
CREATE INDEX and DROP INDEX are implemented by calling ALTER
@@ -3003,46 +3394,106 @@ end_with_restore_list:
res= mysql_alter_table(thd, first_table->db, first_table->table_name,
&create_info, first_table, &alter_info,
- 0, (ORDER*) 0, 0, 0);
+ 0, (ORDER*) 0, 0);
break;
}
#ifdef HAVE_REPLICATION
case SQLCOM_SLAVE_START:
{
+ LEX_MASTER_INFO* lex_mi= &thd->lex->mi;
+ Master_info *mi;
+ int load_error;
+
+ load_error= rpl_load_gtid_slave_state(thd);
+
mysql_mutex_lock(&LOCK_active_mi);
- start_slave(thd,active_mi,1 /* net report*/);
+
+ if ((mi= (master_info_index->
+ get_master_info(&lex_mi->connection_name,
+ Sql_condition::WARN_LEVEL_ERROR))))
+ {
+ if (load_error)
+ {
+ /*
+ We cannot start a slave using GTID if we cannot load the GTID position
+ from the mysql.gtid_slave_pos table. But we can allow non-GTID
+ replication (useful eg. during upgrade).
+ */
+ if (mi->using_gtid != Master_info::USE_GTID_NO)
+ {
+ mysql_mutex_unlock(&LOCK_active_mi);
+ break;
+ }
+ else
+ thd->clear_error();
+ }
+ if (!start_slave(thd, mi, 1 /* net report*/))
+ my_ok(thd);
+ }
mysql_mutex_unlock(&LOCK_active_mi);
break;
}
case SQLCOM_SLAVE_STOP:
- /*
- If the client thread has locked tables, a deadlock is possible.
- Assume that
- - the client thread does LOCK TABLE t READ.
- - then the master updates t.
- - then the SQL slave thread wants to update t,
- so it waits for the client thread because t is locked by it.
+ {
+ LEX_MASTER_INFO *lex_mi;
+ Master_info *mi;
+ /*
+ If the client thread has locked tables, a deadlock is possible.
+ Assume that
+ - the client thread does LOCK TABLE t READ.
+ - then the master updates t.
+ - then the SQL slave thread wants to update t,
+ so it waits for the client thread because t is locked by it.
- then the client thread does SLAVE STOP.
SLAVE STOP waits for the SQL slave thread to terminate its
update t, which waits for the client thread because t is locked by it.
- To prevent that, refuse SLAVE STOP if the
- client thread has locked tables
- */
- if (thd->locked_tables_mode ||
- thd->in_active_multi_stmt_transaction() || thd->global_read_lock.is_acquired())
+ To prevent that, refuse SLAVE STOP if the
+ client thread has locked tables
+ */
+ if (thd->locked_tables_mode ||
+ thd->in_active_multi_stmt_transaction() ||
+ thd->global_read_lock.is_acquired())
+ {
+ my_message(ER_LOCK_OR_ACTIVE_TRANSACTION,
+ ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
+ goto error;
+ }
+
+ lex_mi= &thd->lex->mi;
+ mysql_mutex_lock(&LOCK_active_mi);
+ if ((mi= (master_info_index->
+ get_master_info(&lex_mi->connection_name,
+ Sql_condition::WARN_LEVEL_ERROR))))
+ if (!stop_slave(thd, mi, 1/* net report*/))
+ my_ok(thd);
+ mysql_mutex_unlock(&LOCK_active_mi);
+ break;
+ }
+ case SQLCOM_SLAVE_ALL_START:
{
- my_message(ER_LOCK_OR_ACTIVE_TRANSACTION,
- ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
- goto error;
+ mysql_mutex_lock(&LOCK_active_mi);
+ if (master_info_index && !master_info_index->start_all_slaves(thd))
+ my_ok(thd);
+ mysql_mutex_unlock(&LOCK_active_mi);
+ break;
}
+ case SQLCOM_SLAVE_ALL_STOP:
{
+ if (thd->locked_tables_mode ||
+ thd->in_active_multi_stmt_transaction() ||
+ thd->global_read_lock.is_acquired())
+ {
+ my_message(ER_LOCK_OR_ACTIVE_TRANSACTION,
+ ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
+ goto error;
+ }
mysql_mutex_lock(&LOCK_active_mi);
- stop_slave(thd,active_mi,1/* net report*/);
+ if (master_info_index && !master_info_index->stop_all_slaves(thd))
+ my_ok(thd);
mysql_mutex_unlock(&LOCK_active_mi);
break;
}
#endif /* HAVE_REPLICATION */
-
case SQLCOM_RENAME_TABLE:
{
if (check_rename_table(thd, first_table, all_tables))
@@ -3112,6 +3563,13 @@ end_with_restore_list:
else
{
/*
+ Temporary tables should be opened for SHOW CREATE TABLE, but not
+ for SHOW CREATE VIEW.
+ */
+ if (open_temporary_tables(thd, all_tables))
+ goto error;
+
+ /*
The fact that check_some_access() returned FALSE does not mean that
access is granted. We need to check if first_table->grant.privilege
contains any table-specific privilege.
@@ -3309,6 +3767,18 @@ end_with_restore_list:
#endif /* WITH_WSREP */
{
DBUG_ASSERT(first_table == all_tables && first_table != 0);
+
+ /*
+ Since INSERT DELAYED doesn't support temporary tables, we could
+ not pre-open temporary tables for SQLCOM_INSERT / SQLCOM_REPLACE.
+ Open them here instead.
+ */
+ if (first_table->lock_type != TL_WRITE_DELAYED)
+ {
+ if ((res= open_temporary_tables(thd, all_tables)))
+ break;
+ }
+
if ((res= insert_precheck(thd, all_tables)))
break;
@@ -3354,6 +3824,7 @@ end_with_restore_list:
#endif /* WITH_WSREP */
{
select_result *sel_result;
+ bool explain= MY_TEST(lex->describe);
DBUG_ASSERT(first_table == all_tables && first_table != 0);
if ((res= insert_precheck(thd, all_tables)))
break;
@@ -3413,7 +3884,10 @@ end_with_restore_list:
lex->duplicates,
lex->ignore)))
{
- res= handle_select(thd, lex, sel_result, OPTION_SETUP_TABLES_DONE);
+ if (explain)
+ res= mysql_explain_union(thd, &thd->lex->unit, sel_result);
+ else
+ res= handle_select(thd, lex, sel_result, OPTION_SETUP_TABLES_DONE);
/*
Invalidate the table in the query cache if something changed
after unlocking when changes become visible.
@@ -3429,8 +3903,22 @@ end_with_restore_list:
query_cache_invalidate3(thd, first_table, 1);
first_table->next_local= save_table;
}
+ if (explain)
+ {
+ /*
+ sel_result needs to be cleaned up properly.
+ INSERT... SELECT statement will call either send_eof() or
+ abort_result_set(). EXPLAIN doesn't call either, so we need
+ to cleanup manually.
+ */
+ sel_result->abort_result_set();
+ }
delete sel_result;
}
+
+ if (!res && explain)
+ res= thd->lex->explain->send_explain(thd);
+
/* revert changes for SP */
MYSQL_INSERT_SELECT_DONE(res, (ulong) thd->get_row_count_func());
select_lex->table_list.first= first_table;
@@ -3453,6 +3941,7 @@ end_with_restore_list:
wsrep_sync_wait(thd, WSREP_SYNC_WAIT_BEFORE_UPDATE_DELETE)) goto error;
#endif /* WITH_WSREP */
{
+ select_result *sel_result=lex->result;
DBUG_ASSERT(first_table == all_tables && first_table != 0);
if ((res= delete_precheck(thd, all_tables)))
break;
@@ -3460,9 +3949,13 @@ end_with_restore_list:
unit->set_limit(select_lex);
MYSQL_DELETE_START(thd->query());
- res = mysql_delete(thd, all_tables, select_lex->where,
- &select_lex->order_list,
- unit->select_limit_cnt, select_lex->options);
+ if (!(sel_result= lex->result) && !(sel_result= new select_send()))
+ return 1;
+ res = mysql_delete(thd, all_tables,
+ select_lex->where, &select_lex->order_list,
+ unit->select_limit_cnt, select_lex->options,
+ sel_result);
+ delete sel_result;
MYSQL_DELETE_DONE(res, (ulong) thd->get_row_count_func());
break;
}
@@ -3474,7 +3967,8 @@ end_with_restore_list:
{
DBUG_ASSERT(first_table == all_tables && first_table != 0);
TABLE_LIST *aux_tables= thd->lex->auxiliary_table_list.first;
- multi_delete *del_result;
+ bool explain= MY_TEST(lex->describe);
+ multi_delete *result;
if ((res= multi_delete_precheck(thd, all_tables)))
break;
@@ -3485,7 +3979,7 @@ end_with_restore_list:
if (add_item_to_list(thd, new Item_null()))
goto error;
- thd_proc_info(thd, "init");
+ THD_STAGE_INFO(thd, stage_init);
if ((res= open_and_lock_tables(thd, all_tables, TRUE, 0)))
break;
@@ -3496,25 +3990,34 @@ end_with_restore_list:
goto error;
}
- if (!thd->is_fatal_error &&
- (del_result= new multi_delete(aux_tables, lex->table_count)))
- {
- res= mysql_select(thd, &select_lex->ref_pointer_array,
- select_lex->get_table_list(),
- select_lex->with_wild,
- select_lex->item_list,
- select_lex->where,
- 0, (ORDER *)NULL, (ORDER *)NULL, (Item *)NULL,
- (ORDER *)NULL,
- (select_lex->options | thd->variables.option_bits |
- SELECT_NO_JOIN_CACHE | SELECT_NO_UNLOCK |
- OPTION_SETUP_TABLES_DONE) & ~OPTION_BUFFER_RESULT,
- del_result, unit, select_lex);
- res|= thd->is_error();
- MYSQL_MULTI_DELETE_DONE(res, del_result->num_deleted());
- if (res)
- del_result->abort_result_set();
- delete del_result;
+ if (!thd->is_fatal_error)
+ {
+ result= new multi_delete(aux_tables, lex->table_count);
+ if (result)
+ {
+ res= mysql_select(thd, &select_lex->ref_pointer_array,
+ select_lex->get_table_list(),
+ select_lex->with_wild,
+ select_lex->item_list,
+ select_lex->where,
+ 0, (ORDER *)NULL, (ORDER *)NULL, (Item *)NULL,
+ (ORDER *)NULL,
+ (select_lex->options | thd->variables.option_bits |
+ SELECT_NO_JOIN_CACHE | SELECT_NO_UNLOCK |
+ OPTION_SETUP_TABLES_DONE) & ~OPTION_BUFFER_RESULT,
+ result, unit, select_lex);
+ res|= thd->is_error();
+
+ MYSQL_MULTI_DELETE_DONE(res, result->num_deleted());
+ if (res)
+ result->abort_result_set(); /* for both DELETE and EXPLAIN DELETE */
+ else
+ {
+ if (explain)
+ res= thd->lex->explain->send_explain(thd);
+ }
+ delete result;
+ }
}
else
{
@@ -3548,8 +4051,18 @@ end_with_restore_list:
}
}
#endif /* WITH_WSREP */
+ /*
+ If we are a slave, we should add IF EXISTS if the query executed
+ on the master without an error. This will help a slave to
+ recover from multi-table DROP TABLE that was aborted in the
+ middle.
+ */
+ if (thd->slave_thread && !thd->slave_expected_error &&
+ slave_ddl_exec_mode_options == SLAVE_EXEC_MODE_IDEMPOTENT)
+ lex->check_exists= 1;
+
/* DDL and binlog write order are protected by metadata locks. */
- res= mysql_rm_table(thd, first_table, lex->drop_if_exists,
+ res= mysql_rm_table(thd, first_table, lex->check_exists,
lex->drop_temporary);
}
break;
@@ -3629,11 +4142,6 @@ end_with_restore_list:
goto error;
if (!(res= sql_set_variables(thd, lex_var_list)))
{
- /*
- If the previous command was a SET ONE_SHOT, we don't want to forget
- about the ONE_SHOT property of that SET. So we use a |= instead of = .
- */
- thd->one_shot_set|= lex->one_shot_set;
my_ok(thd);
}
else
@@ -3679,8 +4187,20 @@ end_with_restore_list:
thd->mdl_context.release_transactional_locks();
if (res)
goto error;
- if (check_table_access(thd, LOCK_TABLES_ACL | SELECT_ACL, all_tables,
- FALSE, UINT_MAX, FALSE))
+
+ /*
+ Here we have to pre-open temporary tables for LOCK TABLES.
+
+ CF_PREOPEN_TMP_TABLES is not set for this SQL statement simply
+ because LOCK TABLES calls close_thread_tables() as a first thing
+ (it's called from unlock_locked_tables() above). So even if
+ CF_PREOPEN_TMP_TABLES was set and the tables would be pre-opened
+ in a usual way, they would have been closed.
+ */
+ if (open_temporary_tables(thd, all_tables))
+ goto error;
+
+ if (lock_tables_precheck(thd, all_tables))
goto error;
thd->variables.option_bits|= OPTION_TABLE_LOCK;
@@ -3708,9 +4228,7 @@ end_with_restore_list:
prepared statement- safe.
*/
HA_CREATE_INFO create_info(lex->create_info);
- char *alias;
- if (!(alias=thd->strmake(lex->name.str, lex->name.length)) ||
- check_db_name(&lex->name))
+ if (check_db_name(&lex->name))
{
my_error(ER_WRONG_DB_NAME, MYF(0), lex->name.str);
break;
@@ -3723,19 +4241,21 @@ end_with_restore_list:
above was not called. So we have to check rules again here.
*/
#ifdef HAVE_REPLICATION
- if (thd->slave_thread &&
- (!rpl_filter->db_ok(lex->name.str) ||
- !rpl_filter->db_ok_with_wild_table(lex->name.str)))
+ if (thd->slave_thread)
{
- my_message(ER_SLAVE_IGNORED_TABLE, ER(ER_SLAVE_IGNORED_TABLE), MYF(0));
- break;
+ rpl_filter= thd->system_thread_info.rpl_sql_info->rpl_filter;
+ if (!rpl_filter->db_ok(lex->name.str) ||
+ !rpl_filter->db_ok_with_wild_table(lex->name.str))
+ {
+ my_message(ER_SLAVE_IGNORED_TABLE, ER(ER_SLAVE_IGNORED_TABLE), MYF(0));
+ break;
+ }
}
#endif
if (check_access(thd, CREATE_ACL, lex->name.str, NULL, NULL, 1, 0))
break;
WSREP_TO_ISOLATION_BEGIN(lex->name.str, NULL, NULL)
- res= mysql_create_db(thd,(lower_case_table_names == 2 ? alias :
- lex->name.str), &create_info, 0);
+ res= mysql_create_db(thd, lex->name.str, &create_info, 0);
break;
}
case SQLCOM_DROP_DB:
@@ -3753,31 +4273,37 @@ end_with_restore_list:
above was not called. So we have to check rules again here.
*/
#ifdef HAVE_REPLICATION
- if (thd->slave_thread &&
- (!rpl_filter->db_ok(lex->name.str) ||
- !rpl_filter->db_ok_with_wild_table(lex->name.str)))
+ if (thd->slave_thread)
{
- my_message(ER_SLAVE_IGNORED_TABLE, ER(ER_SLAVE_IGNORED_TABLE), MYF(0));
- break;
+ rpl_filter= thd->system_thread_info.rpl_sql_info->rpl_filter;
+ if (!rpl_filter->db_ok(lex->name.str) ||
+ !rpl_filter->db_ok_with_wild_table(lex->name.str))
+ {
+ my_message(ER_SLAVE_IGNORED_TABLE, ER(ER_SLAVE_IGNORED_TABLE), MYF(0));
+ break;
+ }
}
#endif
if (check_access(thd, DROP_ACL, lex->name.str, NULL, NULL, 1, 0))
break;
WSREP_TO_ISOLATION_BEGIN(lex->name.str, NULL, NULL)
- res= mysql_rm_db(thd, lex->name.str, lex->drop_if_exists, 0);
+ res= mysql_rm_db(thd, lex->name.str, lex->check_exists, 0);
break;
}
case SQLCOM_ALTER_DB_UPGRADE:
{
LEX_STRING *db= & lex->name;
#ifdef HAVE_REPLICATION
- if (thd->slave_thread &&
- (!rpl_filter->db_ok(db->str) ||
- !rpl_filter->db_ok_with_wild_table(db->str)))
+ if (thd->slave_thread)
{
- res= 1;
- my_message(ER_SLAVE_IGNORED_TABLE, ER(ER_SLAVE_IGNORED_TABLE), MYF(0));
- break;
+ rpl_filter= thd->system_thread_info.rpl_sql_info->rpl_filter;
+ if (!rpl_filter->db_ok(db->str) ||
+ !rpl_filter->db_ok_with_wild_table(db->str))
+ {
+ res= 1;
+ my_message(ER_SLAVE_IGNORED_TABLE, ER(ER_SLAVE_IGNORED_TABLE), MYF(0));
+ break;
+ }
}
#endif
if (check_db_name(db))
@@ -3815,12 +4341,15 @@ end_with_restore_list:
above was not called. So we have to check rules again here.
*/
#ifdef HAVE_REPLICATION
- if (thd->slave_thread &&
- (!rpl_filter->db_ok(db->str) ||
- !rpl_filter->db_ok_with_wild_table(db->str)))
+ if (thd->slave_thread)
{
- my_message(ER_SLAVE_IGNORED_TABLE, ER(ER_SLAVE_IGNORED_TABLE), MYF(0));
- break;
+ rpl_filter= thd->system_thread_info.rpl_sql_info->rpl_filter;
+ if (!rpl_filter->db_ok(db->str) ||
+ !rpl_filter->db_ok_with_wild_table(db->str))
+ {
+ my_message(ER_SLAVE_IGNORED_TABLE, ER(ER_SLAVE_IGNORED_TABLE), MYF(0));
+ break;
+ }
}
#endif
if (check_access(thd, ALTER_ACL, db->str, NULL, NULL, 1, 0))
@@ -3831,14 +4360,25 @@ end_with_restore_list:
}
case SQLCOM_SHOW_CREATE_DB:
{
+ char db_name_buff[NAME_LEN+1];
+ LEX_STRING db_name;
DBUG_EXECUTE_IF("4x_server_emul",
my_error(ER_UNKNOWN_ERROR, MYF(0)); goto error;);
- if (check_db_name(&lex->name))
+
+ db_name.str= db_name_buff;
+ db_name.length= lex->name.length;
+ strmov(db_name.str, lex->name.str);
+
+#ifdef WITH_WSREP
+ if (WSREP_CLIENT(thd) && wsrep_sync_wait(thd)) goto error;
+#endif /* WITH_WSREP */
+
+ if (check_db_name(&db_name))
{
- my_error(ER_WRONG_DB_NAME, MYF(0), lex->name.str);
+ my_error(ER_WRONG_DB_NAME, MYF(0), db_name.str);
break;
}
- res= mysqld_show_create_db(thd, lex->name.str, &lex->create_info);
+ res= mysqld_show_create_db(thd, &db_name, &lex->name, &lex->create_info);
break;
}
case SQLCOM_CREATE_EVENT:
@@ -3889,6 +4429,9 @@ end_with_restore_list:
/* lex->unit.cleanup() is called outside, no need to call it here */
break;
case SQLCOM_SHOW_CREATE_EVENT:
+#ifdef WITH_WSREP
+ if (WSREP_CLIENT(thd) && wsrep_sync_wait(thd)) goto error;
+#endif /* WITH_WSREP */
res= Events::show_create_event(thd, lex->spname->m_db,
lex->spname->m_name);
break;
@@ -3896,7 +4439,7 @@ end_with_restore_list:
WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL)
if (!(res= Events::drop_event(thd,
lex->spname->m_db, lex->spname->m_name,
- lex->drop_if_exists)))
+ lex->check_exists)))
my_ok(thd);
break;
#else
@@ -3919,24 +4462,28 @@ end_with_restore_list:
}
#ifndef NO_EMBEDDED_ACCESS_CHECKS
case SQLCOM_CREATE_USER:
+ case SQLCOM_CREATE_ROLE:
{
if (check_access(thd, INSERT_ACL, "mysql", NULL, NULL, 1, 1) &&
check_global_access(thd,CREATE_USER_ACL))
break;
WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL)
/* Conditionally writes to binlog */
- if (!(res= mysql_create_user(thd, lex->users_list)))
+ if (!(res= mysql_create_user(thd, lex->users_list,
+ lex->sql_command == SQLCOM_CREATE_ROLE)))
my_ok(thd);
break;
}
case SQLCOM_DROP_USER:
+ case SQLCOM_DROP_ROLE:
{
if (check_access(thd, DELETE_ACL, "mysql", NULL, NULL, 1, 1) &&
check_global_access(thd,CREATE_USER_ACL))
break;
/* Conditionally writes to binlog */
WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL)
- if (!(res= mysql_drop_user(thd, lex->users_list)))
+ if (!(res= mysql_drop_user(thd, lex->users_list,
+ lex->sql_command == SQLCOM_DROP_ROLE)))
my_ok(thd);
break;
}
@@ -3957,9 +4504,6 @@ end_with_restore_list:
check_global_access(thd,CREATE_USER_ACL))
break;
- /* Replicate current user as grantor */
- thd->binlog_invoker();
-
/* Conditionally writes to binlog */
WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL)
if (!(res = mysql_revoke_all(thd, lex->users_list)))
@@ -3978,42 +4522,58 @@ end_with_restore_list:
goto error;
/* Replicate current user as grantor */
- thd->binlog_invoker();
+ thd->binlog_invoker(false);
if (thd->security_ctx->user) // If not replication
{
- LEX_USER *user, *tmp_user;
+ LEX_USER *user;
bool first_user= TRUE;
List_iterator <LEX_USER> user_list(lex->users_list);
- while ((tmp_user= user_list++))
+ while ((user= user_list++))
{
- if (!(user= get_current_user(thd, tmp_user)))
- goto error;
if (specialflag & SPECIAL_NO_RESOLVE &&
hostname_requires_resolving(user->host.str))
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_WARN_HOSTNAME_WONT_WORK,
ER(ER_WARN_HOSTNAME_WONT_WORK));
- // Are we trying to change a password of another user
- DBUG_ASSERT(user->host.str != 0);
/*
GRANT/REVOKE PROXY has the target user as a first entry in the list.
*/
if (lex->type == TYPE_ENUM_PROXY && first_user)
{
+ if (!(user= get_current_user(thd, user)) || !user->host.str)
+ goto error;
+
first_user= FALSE;
if (acl_check_proxy_grant_access (thd, user->host.str, user->user.str,
lex->grant & GRANT_ACL))
goto error;
}
- else if (is_acl_user(user->host.str, user->user.str) &&
- user->password.str &&
- check_change_password (thd, user->host.str, user->user.str,
- user->password.str,
- user->password.length))
- goto error;
+ else if (user->password.str)
+ {
+ // Are we trying to change a password of another user?
+ const char *hostname= user->host.str, *username=user->user.str;
+ bool userok;
+ if (username == current_user.str)
+ {
+ username= thd->security_ctx->priv_user;
+ hostname= thd->security_ctx->priv_host;
+ userok= true;
+ }
+ else
+ {
+ if (!hostname)
+ hostname= host_not_specified.str;
+ userok= is_acl_user(hostname, username);
+ }
+
+ if (userok && check_change_password (thd, hostname, username,
+ user->password.str,
+ user->password.length))
+ goto error;
+ }
}
}
if (first_table)
@@ -4060,9 +4620,9 @@ end_with_restore_list:
{
WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL)
/* Conditionally writes to binlog */
- res = mysql_grant(thd, select_lex->db, lex->users_list, lex->grant,
- lex->sql_command == SQLCOM_REVOKE,
- lex->type == TYPE_ENUM_PROXY);
+ res= mysql_grant(thd, select_lex->db, lex->users_list, lex->grant,
+ lex->sql_command == SQLCOM_REVOKE,
+ lex->type == TYPE_ENUM_PROXY);
}
if (!res)
{
@@ -4081,6 +4641,15 @@ end_with_restore_list:
}
break;
}
+ case SQLCOM_REVOKE_ROLE:
+ case SQLCOM_GRANT_ROLE:
+ {
+ WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL)
+ if (!(res= mysql_grant_role(thd, lex->users_list,
+ lex->sql_command != SQLCOM_GRANT_ROLE)))
+ my_ok(thd);
+ break;
+ }
#endif /*!NO_EMBEDDED_ACCESS_CHECKS*/
case SQLCOM_RESET:
/*
@@ -4094,39 +4663,91 @@ end_with_restore_list:
if (check_global_access(thd,RELOAD_ACL))
goto error;
- if (first_table && lex->type & REFRESH_READ_LOCK)
+ if (first_table && lex->type & (REFRESH_READ_LOCK|REFRESH_FOR_EXPORT))
{
/* Check table-level privileges. */
if (check_table_access(thd, LOCK_TABLES_ACL | SELECT_ACL, all_tables,
FALSE, UINT_MAX, FALSE))
goto error;
+
if (flush_tables_with_read_lock(thd, all_tables))
goto error;
+
my_ok(thd);
break;
}
#ifdef WITH_WSREP
if (lex->type & (
- REFRESH_GRANT |
- REFRESH_HOSTS |
- REFRESH_DES_KEY_FILE |
+ REFRESH_GRANT |
+ REFRESH_HOSTS |
+#ifdef HAVE_OPENSSL
+ REFRESH_DES_KEY_FILE |
+#endif
+ /*
+ Write all flush log statements except
+ FLUSH LOGS
+ FLUSH BINARY LOGS
+ Check reload_acl_and_cache for why.
+ */
+ REFRESH_RELAY_LOG |
+ REFRESH_SLOW_LOG |
+ REFRESH_GENERAL_LOG |
+ REFRESH_ENGINE_LOG |
+ REFRESH_ERROR_LOG |
#ifdef HAVE_QUERY_CACHE
- REFRESH_QUERY_CACHE_FREE |
+ REFRESH_QUERY_CACHE_FREE |
#endif /* HAVE_QUERY_CACHE */
- REFRESH_STATUS |
- REFRESH_USER_RESOURCES))
+ REFRESH_STATUS |
+ REFRESH_USER_RESOURCES |
+ REFRESH_TABLE_STATS |
+ REFRESH_INDEX_STATS |
+ REFRESH_USER_STATS |
+ REFRESH_CLIENT_STATS))
{
- WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL)
+ WSREP_TO_ISOLATION_BEGIN_WRTCHK(WSREP_MYSQL_DB, NULL, NULL)
}
#endif /* WITH_WSREP*/
+#ifdef HAVE_REPLICATION
+ if (lex->type & REFRESH_READ_LOCK)
+ {
+ /*
+ We need to pause any parallel replication slave workers during FLUSH
+ TABLES WITH READ LOCK. Otherwise we might cause a deadlock, as
+ worker threads eun run in arbitrary order but need to commit in a
+ specific given order.
+ */
+ if (rpl_pause_for_ftwrl(thd))
+ goto error;
+ }
+#endif
+
/*
reload_acl_and_cache() will tell us if we are allowed to write to the
binlog or not.
*/
if (!reload_acl_and_cache(thd, lex->type, first_table, &write_to_binlog))
{
+#ifdef WITH_WSREP
+ if ((lex->type & REFRESH_TABLES) && !(lex->type & (REFRESH_FOR_EXPORT|REFRESH_READ_LOCK)))
+ {
+ /*
+ This is done after reload_acl_and_cache is because
+ LOCK TABLES is not replicated in galera, the upgrade of which
+ is checked in reload_acl_and_cache.
+ Hence, done after/if we are able to upgrade locks.
+ */
+ if (first_table)
+ {
+ WSREP_TO_ISOLATION_BEGIN_WRTCHK(NULL, NULL, first_table);
+ }
+ else
+ {
+ WSREP_TO_ISOLATION_BEGIN_WRTCHK(WSREP_MYSQL_DB, NULL, NULL);
+ }
+ }
+#endif /* WITH_WSREP */
/*
We WANT to write and we CAN write.
! we write after unlocking the table.
@@ -4151,6 +4772,10 @@ end_with_restore_list:
if (!res)
my_ok(thd);
}
+#ifdef HAVE_REPLICATION
+ if (lex->type & REFRESH_READ_LOCK)
+ rpl_unpause_after_ftwrl(thd);
+#endif
break;
}
@@ -4163,7 +4788,7 @@ end_with_restore_list:
break;
}
- if (lex->kill_type == KILL_TYPE_ID)
+ if (lex->kill_type == KILL_TYPE_ID || lex->kill_type == KILL_TYPE_QUERY)
{
Item *it= (Item *)lex->value_list.head();
if ((!it->fixed && it->fix_fields(lex->thd, &it)) || it->check_cols(1))
@@ -4172,21 +4797,39 @@ end_with_restore_list:
MYF(0));
goto error;
}
- sql_kill(thd, (ulong) it->val_int(), lex->kill_signal);
+ sql_kill(thd, it->val_int(), lex->kill_signal, lex->kill_type);
}
else
sql_kill_user(thd, get_current_user(thd, lex->users_list.head()),
lex->kill_signal);
break;
}
+ case SQLCOM_SHUTDOWN:
+#ifndef EMBEDDED_LIBRARY
+ if (check_global_access(thd,SHUTDOWN_ACL))
+ goto error;
+ kill_mysql();
+ my_ok(thd);
+#else
+ my_error(ER_NOT_SUPPORTED_YET, MYF(0), "embedded server");
+#endif
+ break;
+
#ifndef NO_EMBEDDED_ACCESS_CHECKS
case SQLCOM_SHOW_GRANTS:
{
- LEX_USER *grant_user= get_current_user(thd, lex->grant_user);
+ LEX_USER *grant_user= lex->grant_user;
+ Security_context *sctx= thd->security_ctx;
if (!grant_user)
goto error;
- if ((thd->security_ctx->priv_user &&
- !strcmp(thd->security_ctx->priv_user, grant_user->user.str)) ||
+
+ if (grant_user->user.str && !strcmp(sctx->priv_user, grant_user->user.str) &&
+ grant_user->host.str && !strcmp(sctx->priv_host, grant_user->host.str))
+ grant_user->user= current_user;
+
+ if (grant_user->user.str == current_user.str ||
+ grant_user->user.str == current_role.str ||
+ grant_user->user.str == current_user_and_current_role.str ||
!check_access(thd, SELECT_ACL, "mysql", NULL, NULL, 1, 0))
{
res = mysql_show_grants(thd, grant_user);
@@ -4198,6 +4841,9 @@ end_with_restore_list:
DBUG_ASSERT(first_table == all_tables && first_table != 0);
if (check_table_access(thd, SELECT_ACL, all_tables, FALSE, UINT_MAX, FALSE))
goto error;
+ /* Close temporary tables which were pre-opened for privilege checking. */
+ close_thread_tables(thd);
+ all_tables->table= NULL;
res= mysql_ha_open(thd, first_table, 0);
break;
case SQLCOM_HA_CLOSE:
@@ -4226,6 +4872,7 @@ end_with_restore_list:
break;
case SQLCOM_BEGIN:
+ DBUG_PRINT("info", ("Executing SQLCOM_BEGIN thd: %p", thd));
if (trans_begin(thd, lex->start_transaction_opt))
{
thd->mdl_context.release_transactional_locks();
@@ -4259,12 +4906,13 @@ end_with_restore_list:
if (tx_chain)
{
if (trans_begin(thd))
- goto error;
+ goto error;
}
else
{
- /* Reset the isolation level if no chaining transaction. */
+ /* Reset the isolation level and access mode if no chaining transaction.*/
thd->tx_isolation= (enum_tx_isolation) thd->variables.tx_isolation;
+ thd->tx_read_only= thd->variables.tx_read_only;
}
/* Disconnect the current client connection. */
if (tx_release)
@@ -4298,6 +4946,7 @@ end_with_restore_list:
bool tx_release= (lex->tx_release == TVL_YES ||
(thd->variables.completion_type == 2 &&
lex->tx_release != TVL_NO));
+
if (trans_rollback(thd))
{
thd->mdl_context.release_transactional_locks();
@@ -4315,8 +4964,9 @@ end_with_restore_list:
}
else
{
- /* Reset the isolation level if no chaining transaction. */
+ /* Reset the isolation level and access mode if no chaining transaction.*/
thd->tx_isolation= (enum_tx_isolation) thd->variables.tx_isolation;
+ thd->tx_read_only= thd->variables.tx_read_only;
}
/* Disconnect the current client connection. */
if (tx_release)
@@ -4328,11 +4978,11 @@ end_with_restore_list:
}
} else {
#endif /* WITH_WSREP */
- my_ok(thd);
+ my_ok(thd);
#ifdef WITH_WSREP
}
#endif /* WITH_WSREP */
- break;
+ break;
}
case SQLCOM_RELEASE_SAVEPOINT:
if (trans_release_savepoint(thd, lex->ident))
@@ -4368,6 +5018,10 @@ end_with_restore_list:
goto create_sp_error;
}
+ if (check_access(thd, CREATE_PROC_ACL, lex->sphead->m_db.str,
+ NULL, NULL, 0, 0))
+ goto create_sp_error;
+
/*
Check that a database directory with this name
exists. Design note: This won't work on virtual databases
@@ -4379,10 +5033,6 @@ end_with_restore_list:
goto create_sp_error;
}
- if (check_access(thd, CREATE_PROC_ACL, lex->sphead->m_db.str,
- NULL, NULL, 0, 0))
- goto create_sp_error;
-
name= lex->sphead->name(&namelen);
#ifdef HAVE_DLOPEN
if (lex->sphead->m_type == TYPE_ENUM_FUNCTION)
@@ -4453,7 +5103,7 @@ end_with_restore_list:
{
if (sp_grant_privileges(thd, lex->sphead->m_db.str, name,
lex->sql_command == SQLCOM_CREATE_PROCEDURE))
- push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning(thd, Sql_condition::WARN_LEVEL_WARN,
ER_PROC_AUTO_GRANT_FAIL, ER(ER_PROC_AUTO_GRANT_FAIL));
thd->clear_error();
}
@@ -4509,6 +5159,10 @@ create_sp_error:
open_and_lock_tables(thd, all_tables, TRUE, 0))
goto error;
+ if (check_routine_access(thd, EXECUTE_ACL, lex->spname->m_db.str,
+ lex->spname->m_name.str, TRUE, FALSE))
+ goto error;
+
/*
By this moment all needed SPs should be in cache so no need to look
into DB.
@@ -4558,11 +5212,6 @@ create_sp_error:
thd->server_status|= SERVER_MORE_RESULTS_EXISTS;
}
- if (check_routine_access(thd, EXECUTE_ACL,
- sp->m_db.str, sp->m_name.str, TRUE, FALSE))
- {
- goto error;
- }
select_limit= thd->variables.select_limit;
thd->variables.select_limit= HA_POS_ERROR;
@@ -4657,9 +5306,9 @@ create_sp_error:
if (lex->spname->m_db.str == NULL)
{
- if (lex->drop_if_exists)
+ if (lex->check_exists)
{
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
ER_SP_DOES_NOT_EXIST, ER(ER_SP_DOES_NOT_EXIST),
"FUNCTION (UDF)", lex->spname->m_name.str);
res= FALSE;
@@ -4713,7 +5362,7 @@ create_sp_error:
sp_revoke_privileges(thd, db, name,
lex->sql_command == SQLCOM_DROP_PROCEDURE))
{
- push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning(thd, Sql_condition::WARN_LEVEL_WARN,
ER_PROC_AUTO_REVOKE_FAIL,
ER(ER_PROC_AUTO_REVOKE_FAIL));
/* If this happens, an error should have been reported. */
@@ -4727,10 +5376,10 @@ create_sp_error:
my_ok(thd);
break;
case SP_KEY_NOT_FOUND:
- if (lex->drop_if_exists)
+ if (lex->check_exists)
{
res= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
ER_SP_DOES_NOT_EXIST, ER(ER_SP_DOES_NOT_EXIST),
SP_COM_STRING(lex), lex->spname->m_qname.str);
if (!res)
@@ -4749,12 +5398,18 @@ create_sp_error:
}
case SQLCOM_SHOW_CREATE_PROC:
{
+#ifdef WITH_WSREP
+ if (WSREP_CLIENT(thd) && wsrep_sync_wait(thd)) goto error;
+#endif /* WITH_WSREP */
if (sp_show_create_routine(thd, TYPE_ENUM_PROCEDURE, lex->spname))
goto error;
break;
}
case SQLCOM_SHOW_CREATE_FUNC:
{
+#ifdef WITH_WSREP
+ if (WSREP_CLIENT(thd) && wsrep_sync_wait(thd)) goto error;
+#endif /* WITH_WSREP */
if (sp_show_create_routine(thd, TYPE_ENUM_FUNCTION, lex->spname))
goto error;
break;
@@ -4767,6 +5422,9 @@ create_sp_error:
stored_procedure_type type= (lex->sql_command == SQLCOM_SHOW_PROC_CODE ?
TYPE_ENUM_PROCEDURE : TYPE_ENUM_FUNCTION);
+#ifdef WITH_WSREP
+ if (WSREP_CLIENT(thd) && wsrep_sync_wait(thd)) goto error;
+#endif /* WITH_WSREP */
if (sp_cache_routine(thd, type, lex->spname, FALSE, &sp))
goto error;
if (!sp || sp->show_routine_code(thd))
@@ -4791,6 +5449,9 @@ create_sp_error:
goto error;
}
+#ifdef WITH_WSREP
+ if (WSREP_CLIENT(thd) && wsrep_sync_wait(thd)) goto error;
+#endif /* WITH_WSREP */
if (show_create_trigger(thd, lex->spname))
goto error; /* Error has been already logged. */
@@ -4857,9 +5518,10 @@ create_sp_error:
thd->mdl_context.release_transactional_locks();
/*
We've just done a commit, reset transaction
- isolation level to the session default.
+ isolation level and access mode to the session default.
*/
thd->tx_isolation= (enum_tx_isolation) thd->variables.tx_isolation;
+ thd->tx_read_only= thd->variables.tx_read_only;
my_ok(thd);
break;
case SQLCOM_XA_ROLLBACK:
@@ -4874,9 +5536,10 @@ create_sp_error:
thd->mdl_context.release_transactional_locks();
/*
We've just done a rollback, reset transaction
- isolation level to the session default.
+ isolation level and access mode to the session default.
*/
thd->tx_isolation= (enum_tx_isolation) thd->variables.tx_isolation;
+ thd->tx_read_only= thd->variables.tx_read_only;
my_ok(thd);
break;
case SQLCOM_XA_RECOVER:
@@ -4958,7 +5621,7 @@ create_sp_error:
if ((err_code= drop_server(thd, &lex->server_options)))
{
- if (! lex->drop_if_exists && err_code == ER_FOREIGN_SERVER_DOESNT_EXIST)
+ if (! lex->check_exists && err_code == ER_FOREIGN_SERVER_DOESNT_EXIST)
{
DBUG_PRINT("info", ("problem dropping server %s",
lex->server_options.server_name));
@@ -4984,32 +5647,21 @@ create_sp_error:
/* fall through */
case SQLCOM_SIGNAL:
case SQLCOM_RESIGNAL:
- DBUG_ASSERT(lex->m_stmt != NULL);
- res= lex->m_stmt->execute(thd);
+ case SQLCOM_GET_DIAGNOSTICS:
+ DBUG_ASSERT(lex->m_sql_cmd != NULL);
+ res= lex->m_sql_cmd->execute(thd);
break;
default:
+
#ifndef EMBEDDED_LIBRARY
DBUG_ASSERT(0); /* Impossible */
#endif
my_ok(thd);
break;
}
- thd_proc_info(thd, "query end");
+ THD_STAGE_INFO(thd, stage_query_end);
thd->update_stats();
- /*
- Binlog-related cleanup:
- Reset system variables temporarily modified by SET ONE SHOT.
-
- Exception: If this is a SET, do nothing. This is to allow
- mysqlbinlog to print many SET commands (in this case we want the
- charset temp setting to live until the real query). This is also
- needed so that SET CHARACTER_SET_CLIENT... does not cancel itself
- immediately.
- */
- if (thd->one_shot_set && lex->sql_command != SQLCOM_SET_OPTION)
- reset_one_shot_variables(thd);
-
goto finish;
error:
@@ -5030,23 +5682,19 @@ finish:
if (thd->killed_errno())
{
/* If we already sent 'ok', we can ignore any kill query statements */
- if (! thd->stmt_da->is_set())
+ if (! thd->get_stmt_da()->is_set())
thd->send_kill_message();
}
- if (thd->killed < KILL_CONNECTION)
- {
- thd->reset_killed();
- thd->mysys_var->abort= 0;
- }
+ thd->reset_kill_query();
}
if (thd->is_error() || (thd->variables.option_bits & OPTION_MASTER_SQL_ERROR))
trans_rollback_stmt(thd);
else
{
/* If commit fails, we should be able to reset the OK status. */
- thd->stmt_da->can_overwrite_status= TRUE;
+ thd->get_stmt_da()->set_overwrite_status(true);
trans_commit_stmt(thd);
- thd->stmt_da->can_overwrite_status= FALSE;
+ thd->get_stmt_da()->set_overwrite_status(false);
}
#ifdef WITH_ARIA_STORAGE_ENGINE
ha_maria::implicit_commit(thd, FALSE);
@@ -5058,12 +5706,16 @@ finish:
#ifdef WITH_WSREP
thd->wsrep_consistency_check= NO_CONSISTENCY_CHECK;
#endif /* WITH_WSREP */
- thd_proc_info(thd, 0);
#ifndef DBUG_OFF
if (lex->sql_command != SQLCOM_SET_OPTION && ! thd->in_sub_stmt)
DEBUG_SYNC(thd, "execute_command_after_close_tables");
#endif
+ if (!(sql_command_flags[lex->sql_command] &
+ (CF_CAN_GENERATE_ROW_EVENTS | CF_FORCE_ORIGINAL_BINLOG_FORMAT |
+ CF_STATUS_COMMAND)))
+ thd->set_binlog_format(orig_binlog_format,
+ orig_current_stmt_binlog_format);
if (! thd->in_sub_stmt && thd->transaction_rollback_request)
{
@@ -5079,12 +5731,15 @@ finish:
{
/* No transaction control allowed in sub-statements. */
DBUG_ASSERT(! thd->in_sub_stmt);
- /* If commit fails, we should be able to reset the OK status. */
- thd->stmt_da->can_overwrite_status= TRUE;
- /* Commit the normal transaction if one is active. */
- trans_commit_implicit(thd);
- thd->stmt_da->can_overwrite_status= FALSE;
- thd->mdl_context.release_transactional_locks();
+ if (!(thd->variables.option_bits & OPTION_GTID_BEGIN))
+ {
+ /* If commit fails, we should be able to reset the OK status. */
+ thd->get_stmt_da()->set_overwrite_status(true);
+ /* Commit the normal transaction if one is active. */
+ trans_commit_implicit(thd);
+ thd->get_stmt_da()->set_overwrite_status(false);
+ thd->mdl_context.release_transactional_locks();
+ }
}
else if (! thd->in_sub_stmt && ! thd->in_multi_stmt_transaction_mode())
{
@@ -5150,24 +5805,37 @@ static bool execute_sqlcom_select(THD *thd, TABLE_LIST *all_tables)
if (!(result= new select_send()))
return 1; /* purecov: inspected */
thd->send_explain_fields(result);
- res= mysql_explain_union(thd, &thd->lex->unit, result);
+
/*
- The code which prints the extended description is not robust
- against malformed queries, so skip it if we have an error.
+ This will call optimize() for all parts of query. The query plan is
+ printed out below.
*/
- if (!res && (lex->describe & DESCRIBE_EXTENDED))
+ res= mysql_explain_union(thd, &thd->lex->unit, result);
+
+ /* Print EXPLAIN only if we don't have an error */
+ if (!res)
{
- char buff[1024];
- String str(buff,(uint32) sizeof(buff), system_charset_info);
- str.length(0);
- /*
- The warnings system requires input in utf8, @see
- mysqld_show_warnings().
- */
- thd->lex->unit.print(&str, QT_TO_SYSTEM_CHARSET);
- push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
- ER_YES, str.c_ptr_safe());
+ /*
+ Do like the original select_describe did: remove OFFSET from the
+ top-level LIMIT
+ */
+ result->reset_offset_limit();
+ thd->lex->explain->print_explain(result, thd->lex->describe);
+ if (lex->describe & DESCRIBE_EXTENDED)
+ {
+ char buff[1024];
+ String str(buff,(uint32) sizeof(buff), system_charset_info);
+ str.length(0);
+ /*
+ The warnings system requires input in utf8, @see
+ mysqld_show_warnings().
+ */
+ thd->lex->unit.print(&str, QT_TO_SYSTEM_CHARSET);
+ push_warning(thd, Sql_condition::WARN_LEVEL_NOTE,
+ ER_YES, str.c_ptr_safe());
+ }
}
+
if (res)
result->abort_result_set();
else
@@ -5185,10 +5853,10 @@ static bool execute_sqlcom_select(THD *thd, TABLE_LIST *all_tables)
}
}
/* Count number of empty select queries */
- if (!thd->sent_row_count)
+ if (!thd->get_sent_row_count())
status_var_increment(thd->status_var.empty_queries);
else
- status_var_add(thd->status_var.rows_sent, thd->sent_row_count);
+ status_var_add(thd->status_var.rows_sent, thd->get_sent_row_count());
#ifdef WITH_WSREP
if (lex->sql_command == SQLCOM_SHOW_STATUS) wsrep_free_status(thd);
#endif /* WITH_WSREP */
@@ -5214,7 +5882,8 @@ static bool execute_show_status(THD *thd, TABLE_LIST *all_tables)
mysql_mutex_lock(&LOCK_status);
add_diff_to_status(&global_status_var, &thd->status_var,
&old_status_var);
- thd->status_var= old_status_var;
+ memcpy(&thd->status_var, &old_status_var,
+ offsetof(STATUS_VAR, last_cleared_system_status_var));
mysql_mutex_unlock(&LOCK_status);
return res;
}
@@ -5255,100 +5924,6 @@ static bool check_rename_table(THD *thd, TABLE_LIST *first_table,
}
-#ifndef NO_EMBEDDED_ACCESS_CHECKS
-/**
- Check grants for commands which work only with one table.
-
- @param thd Thread handler
- @param privilege requested privilege
- @param all_tables global table list of query
- @param no_errors FALSE/TRUE - report/don't report error to
- the client (using my_error() call).
-
- @retval
- 0 OK
- @retval
- 1 access denied, error is sent to client
-*/
-
-bool check_single_table_access(THD *thd, ulong privilege,
- TABLE_LIST *all_tables, bool no_errors)
-{
- Security_context * backup_ctx= thd->security_ctx;
-
- /* we need to switch to the saved context (if any) */
- if (all_tables->security_ctx)
- thd->security_ctx= all_tables->security_ctx;
-
- const char *db_name;
- if ((all_tables->view || all_tables->field_translation) &&
- !all_tables->schema_table)
- db_name= all_tables->view_db.str;
- else
- db_name= all_tables->db;
-
- if (check_access(thd, privilege, db_name,
- &all_tables->grant.privilege,
- &all_tables->grant.m_internal,
- 0, no_errors))
- goto deny;
-
- /* Show only 1 table for check_grant */
- if (!(all_tables->belong_to_view &&
- (thd->lex->sql_command == SQLCOM_SHOW_FIELDS)) &&
- check_grant(thd, privilege, all_tables, FALSE, 1, no_errors))
- goto deny;
-
- thd->security_ctx= backup_ctx;
- return 0;
-
-deny:
- thd->security_ctx= backup_ctx;
- return 1;
-}
-
-/**
- Check grants for commands which work only with one table and all other
- tables belonging to subselects or implicitly opened tables.
-
- @param thd Thread handler
- @param privilege requested privilege
- @param all_tables global table list of query
-
- @retval
- 0 OK
- @retval
- 1 access denied, error is sent to client
-*/
-
-bool check_one_table_access(THD *thd, ulong privilege, TABLE_LIST *all_tables)
-{
- if (check_single_table_access (thd,privilege,all_tables, FALSE))
- return 1;
-
- /* Check rights on tables of subselects and implictly opened tables */
- TABLE_LIST *subselects_tables, *view= all_tables->view ? all_tables : 0;
- if ((subselects_tables= all_tables->next_global))
- {
- /*
- Access rights asked for the first table of a view should be the same
- as for the view
- */
- if (view && subselects_tables->belong_to_view == view)
- {
- if (check_single_table_access (thd, privilege, subselects_tables, FALSE))
- return 1;
- subselects_tables= subselects_tables->next_global;
- }
- if (subselects_tables &&
- (check_table_access(thd, SELECT_ACL, subselects_tables, FALSE,
- UINT_MAX, FALSE)))
- return 1;
- }
- return 0;
-}
-
-
/**
@brief Compare requested privileges with the privileges acquired from the
User- and Db-tables.
@@ -5381,6 +5956,11 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv,
GRANT_INTERNAL_INFO *grant_internal_info,
bool dont_check_global_grants, bool no_errors)
{
+#ifdef NO_EMBEDDED_ACCESS_CHECKS
+ if (save_priv)
+ *save_priv= GLOBAL_ACLS;
+ return false;
+#else
Security_context *sctx= thd->security_ctx;
ulong db_access;
@@ -5407,7 +5987,7 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv,
dummy= 0;
}
- thd_proc_info(thd, "checking permissions");
+ THD_STAGE_INFO(thd, stage_checking_permissions);
if ((!db || !db[0]) && !thd->db && !dont_check_global_grants)
{
DBUG_PRINT("error",("No database"));
@@ -5419,6 +5999,10 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv,
if ((db != NULL) && (db != any_db))
{
+ /*
+ Check if this is reserved database, like information schema or
+ performance schema
+ */
const ACL_internal_schema_access *access;
access= get_cached_schema_access(grant_internal_info, db);
if (access)
@@ -5461,8 +6045,12 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv,
if (!(sctx->master_access & SELECT_ACL))
{
if (db && (!thd->db || db_is_pattern || strcmp(db, thd->db)))
+ {
db_access= acl_get(sctx->host, sctx->ip, sctx->priv_user, db,
db_is_pattern);
+ if (sctx->priv_role[0])
+ db_access|= acl_get("", "", sctx->priv_role, db, db_is_pattern);
+ }
else
{
/* get access for current db */
@@ -5506,8 +6094,14 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv,
}
if (db && (!thd->db || db_is_pattern || strcmp(db,thd->db)))
+ {
db_access= acl_get(sctx->host, sctx->ip, sctx->priv_user, db,
db_is_pattern);
+ if (sctx->priv_role[0])
+ {
+ db_access|= acl_get("", "", sctx->priv_role, db, db_is_pattern);
+ }
+ }
else
db_access= sctx->db_access;
DBUG_PRINT("info",("db_access: %lu want_access: %lu",
@@ -5559,6 +6153,101 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv,
"unknown")));
}
DBUG_RETURN(TRUE);
+#endif // NO_EMBEDDED_ACCESS_CHECKS
+}
+
+
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+/**
+ Check grants for commands which work only with one table.
+
+ @param thd Thread handler
+ @param privilege requested privilege
+ @param all_tables global table list of query
+ @param no_errors FALSE/TRUE - report/don't report error to
+ the client (using my_error() call).
+
+ @retval
+ 0 OK
+ @retval
+ 1 access denied, error is sent to client
+*/
+
+bool check_single_table_access(THD *thd, ulong privilege,
+ TABLE_LIST *all_tables, bool no_errors)
+{
+ Security_context * backup_ctx= thd->security_ctx;
+
+ /* we need to switch to the saved context (if any) */
+ if (all_tables->security_ctx)
+ thd->security_ctx= all_tables->security_ctx;
+
+ const char *db_name;
+ if ((all_tables->view || all_tables->field_translation) &&
+ !all_tables->schema_table)
+ db_name= all_tables->view_db.str;
+ else
+ db_name= all_tables->db;
+
+ if (check_access(thd, privilege, db_name,
+ &all_tables->grant.privilege,
+ &all_tables->grant.m_internal,
+ 0, no_errors))
+ goto deny;
+
+ /* Show only 1 table for check_grant */
+ if (!(all_tables->belong_to_view &&
+ (thd->lex->sql_command == SQLCOM_SHOW_FIELDS)) &&
+ check_grant(thd, privilege, all_tables, FALSE, 1, no_errors))
+ goto deny;
+
+ thd->security_ctx= backup_ctx;
+ return 0;
+
+deny:
+ thd->security_ctx= backup_ctx;
+ return 1;
+}
+
+/**
+ Check grants for commands which work only with one table and all other
+ tables belonging to subselects or implicitly opened tables.
+
+ @param thd Thread handler
+ @param privilege requested privilege
+ @param all_tables global table list of query
+
+ @retval
+ 0 OK
+ @retval
+ 1 access denied, error is sent to client
+*/
+
+bool check_one_table_access(THD *thd, ulong privilege, TABLE_LIST *all_tables)
+{
+ if (check_single_table_access (thd,privilege,all_tables, FALSE))
+ return 1;
+
+ /* Check rights on tables of subselects and implictly opened tables */
+ TABLE_LIST *subselects_tables, *view= all_tables->view ? all_tables : 0;
+ if ((subselects_tables= all_tables->next_global))
+ {
+ /*
+ Access rights asked for the first table of a view should be the same
+ as for the view
+ */
+ if (view && subselects_tables->belong_to_view == view)
+ {
+ if (check_single_table_access (thd, privilege, subselects_tables, FALSE))
+ return 1;
+ subselects_tables= subselects_tables->next_global;
+ }
+ if (subselects_tables &&
+ (check_table_access(thd, SELECT_ACL, subselects_tables, FALSE,
+ UINT_MAX, FALSE)))
+ return 1;
+ }
+ return 0;
}
@@ -5616,6 +6305,12 @@ static bool check_show_access(THD *thd, TABLE_LIST *table)
DBUG_ASSERT(dst_table);
+ /*
+ Open temporary tables to be able to detect them during privilege check.
+ */
+ if (open_temporary_tables(thd, dst_table))
+ return TRUE;
+
if (check_access(thd, SELECT_ACL, dst_table->db,
&dst_table->grant.privilege,
&dst_table->grant.m_internal,
@@ -5629,6 +6324,9 @@ static bool check_show_access(THD *thd, TABLE_LIST *table)
if (check_grant(thd, SELECT_ACL, dst_table, TRUE, UINT_MAX, FALSE))
return TRUE; /* Access denied */
+ close_thread_tables(thd);
+ dst_table->table= NULL;
+
/* Access granted */
return FALSE;
}
@@ -5717,10 +6415,10 @@ check_table_access(THD *thd, ulong requirements,TABLE_LIST *tables,
DBUG_PRINT("info", ("derived: %d view: %d", table_ref->derived != 0,
table_ref->view != 0));
- if (table_ref->is_anonymous_derived_table() ||
- (table_ref->table && table_ref->table->s &&
- (int)table_ref->table->s->tmp_table))
+
+ if (table_ref->is_anonymous_derived_table())
continue;
+
thd->security_ctx= sctx;
if (check_access(thd, want_access, table_ref->get_db_name(),
@@ -5912,8 +6610,8 @@ bool check_fk_parent_table_access(THD *thd,
bool is_qualified_table_name;
Foreign_key *fk_key= (Foreign_key *)key;
LEX_STRING db_name;
- LEX_STRING table_name= { fk_key->ref_table->table.str,
- fk_key->ref_table->table.length };
+ LEX_STRING table_name= { fk_key->ref_table.str,
+ fk_key->ref_table.length };
const ulong privileges= (SELECT_ACL | INSERT_ACL | UPDATE_ACL |
DELETE_ACL | REFERENCES_ACL);
@@ -5925,15 +6623,15 @@ bool check_fk_parent_table_access(THD *thd,
return true;
}
- if (fk_key->ref_table->db.str)
+ if (fk_key->ref_db.str)
{
is_qualified_table_name= true;
- db_name.str= (char *) thd->memdup(fk_key->ref_table->db.str,
- fk_key->ref_table->db.length+1);
- db_name.length= fk_key->ref_table->db.length;
+ db_name.str= (char *) thd->memdup(fk_key->ref_db.str,
+ fk_key->ref_db.length+1);
+ db_name.length= fk_key->ref_db.length;
// Check if database name is valid or not.
- if (fk_key->ref_table->db.str && check_db_name(&db_name))
+ if (fk_key->ref_db.str && check_db_name(&db_name))
{
my_error(ER_WRONG_DB_NAME, MYF(0), db_name.str);
return true;
@@ -5947,9 +6645,10 @@ bool check_fk_parent_table_access(THD *thd,
// if lower_case_table_names is set then convert tablename to lower case.
if (lower_case_table_names)
{
- table_name.str= (char *) thd->memdup(fk_key->ref_table->table.str,
- fk_key->ref_table->table.length+1);
+ table_name.str= (char *) thd->memdup(fk_key->ref_table.str,
+ fk_key->ref_table.length+1);
table_name.length= my_casedn_str(files_charset_info, table_name.str);
+ db_name.length = my_casedn_str(files_charset_info, db_name.str);
}
parent_table.init_one_table(db_name.str, db_name.length,
@@ -6035,7 +6734,7 @@ bool check_stack_overrun(THD *thd, long margin,
return 1;
}
#ifndef DBUG_OFF
- max_stack_used= max(max_stack_used, stack_used);
+ max_stack_used= MY_MAX(max_stack_used, stack_used);
#endif
return 0;
}
@@ -6103,7 +6802,6 @@ void THD::reset_for_next_command()
DBUG_ENTER("THD::reset_for_next_command");
DBUG_ASSERT(!thd->spcont); /* not for substatements of routines */
DBUG_ASSERT(! thd->in_sub_stmt);
- DBUG_ASSERT(thd->transaction.on);
thd->free_list= 0;
thd->select_number= 1;
/*
@@ -6136,6 +6834,8 @@ void THD::reset_for_next_command()
thd->query_start_used= 0;
thd->query_start_sec_part_used= 0;
thd->is_fatal_error= thd->time_zone_used= 0;
+ thd->log_current_statement= 0;
+
/*
Clear the status flag that are expected to be cleared at the
beginning of each SQL statement.
@@ -6160,10 +6860,10 @@ void THD::reset_for_next_command()
thd->user_var_events_alloc= thd->mem_root;
}
thd->clear_error();
- thd->stmt_da->reset_diagnostics_area();
- thd->warning_info->reset_for_next_command();
+ thd->get_stmt_da()->reset_diagnostics_area();
+ thd->get_stmt_da()->reset_for_next_command();
thd->rand_used= 0;
- thd->sent_row_count= thd->examined_row_count= 0;
+ thd->m_sent_row_count= thd->m_examined_row_count= 0;
thd->accessed_rows_and_keys= 0;
thd->query_plan_flags= QPLAN_INIT;
@@ -6338,7 +7038,7 @@ void mysql_init_multi_delete(LEX *lex)
#ifdef WITH_WSREP
static void wsrep_mysql_parse(THD *thd, char *rawbuf, uint length,
- Parser_state *parser_state)
+ Parser_state *parser_state)
{
bool is_autocommit=
!thd->in_multi_stmt_transaction_mode() &&
@@ -6350,6 +7050,11 @@ static void wsrep_mysql_parse(THD *thd, char *rawbuf, uint length,
if (thd->wsrep_conflict_state== RETRY_AUTOCOMMIT)
{
thd->wsrep_conflict_state= NO_CONFLICT;
+ /* Performance Schema Interface instrumentation, begin */
+ thd->m_statement_psi= MYSQL_REFINE_STATEMENT(thd->m_statement_psi,
+ com_statement_info[thd->get_command()].m_key);
+ MYSQL_SET_STATEMENT_TEXT(thd->m_statement_psi, thd->query(),
+ thd->query_length());
}
mysql_parse(thd, rawbuf, length, parser_state);
@@ -6362,8 +7067,7 @@ static void wsrep_mysql_parse(THD *thd, char *rawbuf, uint length,
WSREP_DEBUG("abort in exec query state, avoiding autocommit");
}
- /* checking if BF trx must be replayed */
- if (thd->wsrep_conflict_state== MUST_REPLAY)
+ if (thd->wsrep_conflict_state == MUST_REPLAY)
{
wsrep_replay_transaction(thd);
}
@@ -6378,9 +7082,13 @@ static void wsrep_mysql_parse(THD *thd, char *rawbuf, uint length,
thd->lex->sql_command != SQLCOM_SELECT &&
(thd->wsrep_retry_counter < thd->variables.wsrep_retry_autocommit))
{
- WSREP_DEBUG("wsrep retrying AC query: %s",
+ WSREP_DEBUG("wsrep retrying AC query: %s",
(thd->query()) ? thd->query() : "void");
+ /* Performance Schema Interface instrumentation, end */
+ MYSQL_END_STATEMENT(thd->m_statement_psi, thd->get_stmt_da());
+ thd->m_statement_psi= NULL;
+ thd->m_digest= NULL;
close_thread_tables(thd);
thd->wsrep_conflict_state= RETRY_AUTOCOMMIT;
@@ -6391,10 +7099,10 @@ static void wsrep_mysql_parse(THD *thd, char *rawbuf, uint length,
}
else
{
- WSREP_DEBUG("%s, thd: %lu is_AC: %d, retry: %lu - %lu SQL: %s",
- (thd->wsrep_conflict_state == ABORTED) ?
+ WSREP_DEBUG("%s, thd: %lu is_AC: %d, retry: %lu - %lu SQL: %s",
+ (thd->wsrep_conflict_state == ABORTED) ?
"BF Aborted" : "cert failure",
- thd->thread_id, is_autocommit, thd->wsrep_retry_counter,
+ thd->thread_id, is_autocommit, thd->wsrep_retry_counter,
thd->variables.wsrep_retry_autocommit, thd->query());
my_error(ER_LOCK_DEADLOCK, MYF(0), "wsrep aborted transaction");
thd->killed= NOT_KILLED;
@@ -6409,16 +7117,20 @@ static void wsrep_mysql_parse(THD *thd, char *rawbuf, uint length,
}
mysql_mutex_unlock(&thd->LOCK_wsrep_thd);
}
+
+ /* If retry is requested clean up explain structure */
+ if (thd->wsrep_conflict_state == RETRY_AUTOCOMMIT && thd->lex->explain)
+ delete_explain_query(thd->lex);
+
} while (thd->wsrep_conflict_state== RETRY_AUTOCOMMIT);
if (thd->wsrep_retry_query)
{
- WSREP_DEBUG("releasing retry_query: "
- "conf %d sent %d kill %d errno %d SQL %s",
+ WSREP_DEBUG("releasing retry_query: conf %d sent %d kill %d errno %d SQL %s",
thd->wsrep_conflict_state,
- thd->stmt_da->is_sent,
+ thd->get_stmt_da()->is_sent(),
thd->killed,
- thd->stmt_da->is_error() ? thd->stmt_da->sql_errno() : 0,
+ thd->get_stmt_da()->is_error() ? thd->get_stmt_da()->sql_errno() : 0,
thd->wsrep_retry_query);
my_free(thd->wsrep_retry_query);
thd->wsrep_retry_query = NULL;
@@ -6473,10 +7185,15 @@ void mysql_parse(THD *thd, char *rawbuf, uint length,
{
LEX *lex= thd->lex;
- bool err= parse_sql(thd, parser_state, NULL);
+ bool err= parse_sql(thd, parser_state, NULL, true);
if (!err)
{
+ thd->m_statement_psi=
+ MYSQL_REFINE_STATEMENT(thd->m_statement_psi,
+ sql_statement_info[thd->lex->sql_command].
+ m_key);
+
#ifndef NO_EMBEDDED_ACCESS_CHECKS
if (mqh_used && thd->user_connect &&
check_mqh(thd, lex->sql_command))
@@ -6525,13 +7242,17 @@ void mysql_parse(THD *thd, char *rawbuf, uint length,
}
else
{
+ /* Instrument this broken statement as "statement/sql/error" */
+ thd->m_statement_psi=
+ MYSQL_REFINE_STATEMENT(thd->m_statement_psi,
+ sql_statement_info[SQLCOM_END].m_key);
DBUG_ASSERT(thd->is_error());
DBUG_PRINT("info",("Command aborted. Fatal_error: %d",
thd->is_fatal_error));
query_cache_abort(&thd->query_cache_tls);
}
- thd_proc_info(thd, "freeing items");
+ THD_STAGE_INFO(thd, stage_freeing_items);
sp_cache_enforce_limit(thd->sp_proc_cache, stored_program_cache_size);
sp_cache_enforce_limit(thd->sp_func_cache, stored_program_cache_size);
thd->end_statement();
@@ -6542,12 +7263,15 @@ void mysql_parse(THD *thd, char *rawbuf, uint length,
{
/* Update statistics for getting the query from the cache */
thd->lex->sql_command= SQLCOM_SELECT;
+ thd->m_statement_psi=
+ MYSQL_REFINE_STATEMENT(thd->m_statement_psi,
+ sql_statement_info[SQLCOM_SELECT].m_key);
status_var_increment(thd->status_var.com_stat[SQLCOM_SELECT]);
thd->update_stats();
#ifdef WITH_WSREP
if (WSREP_CLIENT(thd))
{
- thd->wsrep_sync_wait_gtid = WSREP_GTID_UNDEFINED;
+ thd->wsrep_sync_wait_gtid= WSREP_GTID_UNDEFINED;
}
#endif /* WITH_WSREP */
}
@@ -6578,7 +7302,7 @@ bool mysql_test_parse_for_slave(THD *thd, char *rawbuf, uint length)
lex_start(thd);
mysql_reset_thd_for_next_command(thd);
- if (!parse_sql(thd, & parser_state, NULL) &&
+ if (!parse_sql(thd, & parser_state, NULL, true) &&
all_tables_not_ok(thd, lex->select_lex.table_list.first))
error= 1; /* Ignore question */
thd->end_statement();
@@ -6610,6 +7334,7 @@ bool add_field_to_list(THD *thd, LEX_STRING *field_name, enum_field_types type,
{
register Create_field *new_field;
LEX *lex= thd->lex;
+ uint8 datetime_precision= length ? atoi(length) : 0;
DBUG_ENTER("add_field_to_list");
if (check_string_char_length(field_name, "", NAME_CHAR_LEN,
@@ -6624,7 +7349,7 @@ bool add_field_to_list(THD *thd, LEX_STRING *field_name, enum_field_types type,
lex->col_list.push_back(new Key_part_spec(*field_name, 0));
key= new Key(Key::PRIMARY, null_lex_str,
&default_key_create_info,
- 0, lex->col_list, NULL);
+ 0, lex->col_list, NULL, lex->check_exists);
lex->alter_info.key_list.push_back(key);
lex->col_list.empty();
}
@@ -6634,7 +7359,7 @@ bool add_field_to_list(THD *thd, LEX_STRING *field_name, enum_field_types type,
lex->col_list.push_back(new Key_part_spec(*field_name, 0));
key= new Key(Key::UNIQUE, null_lex_str,
&default_key_create_info, 0,
- lex->col_list, NULL);
+ lex->col_list, NULL, lex->check_exists);
lex->alter_info.key_list.push_back(key);
lex->col_list.empty();
}
@@ -6646,11 +7371,13 @@ bool add_field_to_list(THD *thd, LEX_STRING *field_name, enum_field_types type,
no need fix_fields()
We allow only one function as part of default value -
- NOW() as default for TIMESTAMP type.
+ NOW() as default for TIMESTAMP and DATETIME type.
*/
if (default_value->type() == Item::FUNC_ITEM &&
- !(((Item_func*)default_value)->functype() == Item_func::NOW_FUNC &&
- type == MYSQL_TYPE_TIMESTAMP))
+ (static_cast<Item_func*>(default_value)->functype() !=
+ Item_func::NOW_FUNC ||
+ (mysql_type_to_time_type(type) != MYSQL_TIMESTAMP_DATETIME) ||
+ default_value->decimals < datetime_precision))
{
my_error(ER_INVALID_DEFAULT, MYF(0), field_name->str);
DBUG_RETURN(1);
@@ -6672,7 +7399,9 @@ bool add_field_to_list(THD *thd, LEX_STRING *field_name, enum_field_types type,
}
}
- if (on_update_value && type != MYSQL_TYPE_TIMESTAMP)
+ if (on_update_value &&
+ (mysql_type_to_time_type(type) != MYSQL_TIMESTAMP_DATETIME ||
+ on_update_value->decimals < datetime_precision))
{
my_error(ER_INVALID_ON_UPDATE, MYF(0), field_name->str);
DBUG_RETURN(1);
@@ -6682,7 +7411,7 @@ bool add_field_to_list(THD *thd, LEX_STRING *field_name, enum_field_types type,
new_field->init(thd, field_name->str, type, length, decimals, type_modifier,
default_value, on_update_value, comment, change,
interval_list, cs, uint_geom_type, vcol_info,
- create_options))
+ create_options, lex->check_exists))
DBUG_RETURN(1);
lex->alter_info.create_list.push_back(new_field);
@@ -6731,6 +7460,7 @@ bool add_to_list(THD *thd, SQL_I_List<ORDER> &list, Item *item,bool asc)
order->free_me=0;
order->used=0;
order->counter_used= 0;
+ order->fast_field_copier_setup= 0;
list.link_in_list(order, &order->next);
DBUG_RETURN(0);
}
@@ -6763,6 +7493,7 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd,
thr_lock_type lock_type,
enum_mdl_type mdl_type,
List<Index_hint> *index_hints_arg,
+ List<String> *partition_names,
LEX_STRING *option)
{
register TABLE_LIST *ptr;
@@ -6775,7 +7506,7 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd,
if (!table)
DBUG_RETURN(0); // End of memory
alias_str= alias ? alias->str : table->table.str;
- if (!test(table_options & TL_OPTION_ALIAS) &&
+ if (!MY_TEST(table_options & TL_OPTION_ALIAS) &&
check_table_name(table->table.str, table->table.length, FALSE))
{
my_error(ER_WRONG_TABLE_NAME, MYF(0), table->table.str);
@@ -6815,15 +7546,21 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd,
ptr->alias= alias_str;
ptr->is_alias= alias ? TRUE : FALSE;
- if (lower_case_table_names && table->table.length)
- table->table.length= my_casedn_str(files_charset_info, table->table.str);
+ if (lower_case_table_names)
+ {
+ if (table->table.length)
+ table->table.length= my_casedn_str(files_charset_info, table->table.str);
+ if (ptr->db_length && ptr->db != any_db)
+ ptr->db_length= my_casedn_str(files_charset_info, ptr->db);
+ }
+
ptr->table_name=table->table.str;
ptr->table_name_length=table->table.length;
ptr->lock_type= lock_type;
- ptr->updating= test(table_options & TL_OPTION_UPDATING);
+ ptr->updating= MY_TEST(table_options & TL_OPTION_UPDATING);
/* TODO: remove TL_OPTION_FORCE_INDEX as it looks like it's not used */
- ptr->force_index= test(table_options & TL_OPTION_FORCE_INDEX);
- ptr->ignore_leaves= test(table_options & TL_OPTION_IGNORE_LEAVES);
+ ptr->force_index= MY_TEST(table_options & TL_OPTION_FORCE_INDEX);
+ ptr->ignore_leaves= MY_TEST(table_options & TL_OPTION_IGNORE_LEAVES);
ptr->derived= table->sel;
if (!ptr->derived && is_infoschema_db(ptr->db, ptr->db_length))
{
@@ -6911,11 +7648,14 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd,
*/
table_list.link_in_list(ptr, &ptr->next_local);
ptr->next_name_resolution_table= NULL;
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+ ptr->partition_names= partition_names;
+#endif /* WITH_PARTITION_STORAGE_ENGINE */
/* Link table in global list (all used tables) */
lex->add_to_query_tables(ptr);
// Pure table aliases do not need to be locked:
- if (!test(table_options & TL_OPTION_ALIAS))
+ if (!MY_TEST(table_options & TL_OPTION_ALIAS))
{
ptr->mdl_request.init(MDL_key::TABLE, ptr->db, ptr->table_name, mdl_type,
MDL_TRANSACTION);
@@ -7363,37 +8103,56 @@ void add_join_natural(TABLE_LIST *a, TABLE_LIST *b, List<String> *using_fields,
/**
- kill on thread.
+ Find a thread by id and return it, locking it LOCK_thd_data
- @param thd Thread class
- @param id Thread id
- @param only_kill_query Should it kill the query or the connection
+ @param id Identifier of the thread we're looking for
+ @param query_id If true, search by query_id instead of thread_id
- @note
- This is written such that we have a short lock on LOCK_thread_count
+ @return NULL - not found
+ pointer - thread found, and its LOCK_thd_data is locked.
*/
-uint kill_one_thread(THD *thd, ulong id, killed_state kill_signal)
+THD *find_thread_by_id(longlong id, bool query_id)
{
THD *tmp;
- uint error=ER_NO_SUCH_THREAD;
- DBUG_ENTER("kill_one_thread");
- DBUG_PRINT("enter", ("id: %lu signal: %u", id, (uint) kill_signal));
-
mysql_mutex_lock(&LOCK_thread_count); // For unlink from list
I_List_iterator<THD> it(threads);
while ((tmp=it++))
{
- if (tmp->command == COM_DAEMON)
+ if (tmp->get_command() == COM_DAEMON)
continue;
- if (tmp->thread_id == id)
+ if (id == (query_id ? tmp->query_id : (longlong) tmp->thread_id))
{
mysql_mutex_lock(&tmp->LOCK_thd_data); // Lock from delete
break;
}
}
mysql_mutex_unlock(&LOCK_thread_count);
- if (tmp)
+ return tmp;
+}
+
+
+/**
+ kill one thread.
+
+ @param thd Thread class
+ @param id Thread id or query id
+ @param kill_signal Should it kill the query or the connection
+ @param type Type of id: thread id or query id
+
+ @note
+ This is written such that we have a short lock on LOCK_thread_count
+*/
+
+uint
+kill_one_thread(THD *thd, longlong id, killed_state kill_signal, killed_type type)
+{
+ THD *tmp;
+ uint error= (type == KILL_TYPE_QUERY ? ER_NO_SUCH_QUERY : ER_NO_SUCH_THREAD);
+ DBUG_ENTER("kill_one_thread");
+ DBUG_PRINT("enter", ("id: %lld signal: %u", id, (uint) kill_signal));
+
+ if (id && (tmp= find_thread_by_id(id, type == KILL_TYPE_QUERY)))
{
/*
If we're SUPER, we can KILL anything, including system-threads.
@@ -7487,7 +8246,7 @@ static uint kill_threads_for_user(THD *thd, LEX_USER *user,
mysql_mutex_unlock(&LOCK_thread_count);
DBUG_RETURN(ER_KILL_DENIED_ERROR);
}
- if (!threads_to_kill.push_back(tmp, tmp->mem_root))
+ if (!threads_to_kill.push_back(tmp, thd->mem_root))
mysql_mutex_lock(&tmp->LOCK_thd_data); // Lock from delete
}
}
@@ -7517,21 +8276,20 @@ static uint kill_threads_for_user(THD *thd, LEX_USER *user,
}
-/*
- kills a thread and sends response
+/**
+ kills a thread and sends response.
- SYNOPSIS
- sql_kill()
- thd Thread class
- id Thread id
- only_kill_query Should it kill the query or the connection
+ @param thd Thread class
+ @param id Thread id or query id
+ @param state Should it kill the query or the connection
+ @param type Type of id: thread id or query id
*/
static
-void sql_kill(THD *thd, ulong id, killed_state state)
+void sql_kill(THD *thd, longlong id, killed_state state, killed_type type)
{
uint error;
- if (!(error= kill_one_thread(thd, id, state)))
+ if (!(error= kill_one_thread(thd, id, state, type)))
{
if ((!thd->killed))
my_ok(thd);
@@ -7606,7 +8364,7 @@ bool check_simple_select()
char command[80];
Lex_input_stream *lip= & thd->m_parser_state->m_lip;
strmake(command, lip->yylval->symbol.str,
- min(lip->yylval->symbol.length, sizeof(command)-1));
+ MY_MIN(lip->yylval->symbol.length, sizeof(command)-1));
my_error(ER_CANT_USE_OPTION_HERE, MYF(0), command);
return 1;
}
@@ -7712,6 +8470,8 @@ bool multi_update_precheck(THD *thd, TABLE_LIST *tables)
*/
for (table= tables; table; table= table->next_local)
{
+ if (table->is_jtbm())
+ continue;
if (table->derived)
table->grant.privilege= SELECT_ACL;
else if ((check_access(thd, UPDATE_ACL, table->db,
@@ -7726,6 +8486,7 @@ bool multi_update_precheck(THD *thd, TABLE_LIST *tables)
check_grant(thd, SELECT_ACL, table, FALSE, 1, FALSE)))
DBUG_RETURN(TRUE);
+ table->grant.orig_want_privilege= 0;
table->table_in_first_from_clause= 1;
}
/*
@@ -7779,6 +8540,19 @@ bool multi_delete_precheck(THD *thd, TABLE_LIST *tables)
TABLE_LIST **save_query_tables_own_last= thd->lex->query_tables_own_last;
DBUG_ENTER("multi_delete_precheck");
+ /*
+ Temporary tables are pre-opened in 'tables' list only. Here we need to
+ initialize TABLE instances in 'aux_tables' list.
+ */
+ for (TABLE_LIST *tl= aux_tables; tl; tl= tl->next_global)
+ {
+ if (tl->table)
+ continue;
+
+ if (tl->correspondent_table)
+ tl->table= tl->correspondent_table->table;
+ }
+
/* sql_yacc guarantees that tables and aux_tables are not zero */
DBUG_ASSERT(aux_tables != 0);
if (check_table_access(thd, SELECT_ACL, tables, FALSE, UINT_MAX, FALSE))
@@ -8001,7 +8775,7 @@ void create_table_set_open_action_and_adjust_tables(LEX *lex)
{
TABLE_LIST *create_table= lex->query_tables;
- if (lex->create_info.options & HA_LEX_CREATE_TMP_TABLE)
+ if (lex->create_info.tmp_table())
create_table->open_type= OT_TEMPORARY_ONLY;
else
create_table->open_type= OT_BASE_ONLY;
@@ -8047,10 +8821,14 @@ bool create_table_precheck(THD *thd, TABLE_LIST *tables,
CREATE TABLE ... SELECT, also require INSERT.
*/
- want_priv= ((lex->create_info.options & HA_LEX_CREATE_TMP_TABLE) ?
- CREATE_TMP_ACL : CREATE_ACL) |
- (select_lex->item_list.elements ? INSERT_ACL : 0);
+ want_priv= lex->create_info.tmp_table() ? CREATE_TMP_ACL :
+ (CREATE_ACL | (select_lex->item_list.elements ? INSERT_ACL : 0));
+ /* CREATE OR REPLACE on not temporary tables require DROP_ACL */
+ if ((lex->create_info.options & HA_LEX_CREATE_REPLACE) &&
+ !lex->create_info.tmp_table())
+ want_priv|= DROP_ACL;
+
if (check_access(thd, want_priv, create_table->db,
&create_table->grant.privilege,
&create_table->grant.m_internal,
@@ -8058,11 +8836,48 @@ bool create_table_precheck(THD *thd, TABLE_LIST *tables,
goto err;
/* If it is a merge table, check privileges for merge children. */
- if (lex->create_info.merge_list.first &&
- check_table_access(thd, SELECT_ACL | UPDATE_ACL | DELETE_ACL,
- lex->create_info.merge_list.first,
- FALSE, UINT_MAX, FALSE))
- goto err;
+ if (lex->create_info.merge_list.first)
+ {
+ /*
+ The user must have (SELECT_ACL | UPDATE_ACL | DELETE_ACL) on the
+ underlying base tables, even if there are temporary tables with the same
+ names.
+
+ From user's point of view, it might look as if the user must have these
+ privileges on temporary tables to create a merge table over them. This is
+ one of two cases when a set of privileges is required for operations on
+ temporary tables (see also CREATE TABLE).
+
+ The reason for this behavior stems from the following facts:
+
+ - For merge tables, the underlying table privileges are checked only
+ at CREATE TABLE / ALTER TABLE time.
+
+ In other words, once a merge table is created, the privileges of
+ the underlying tables can be revoked, but the user will still have
+ access to the merge table (provided that the user has privileges on
+ the merge table itself).
+
+ - Temporary tables shadow base tables.
+
+ I.e. there might be temporary and base tables with the same name, and
+ the temporary table takes the precedence in all operations.
+
+ - For temporary MERGE tables we do not track if their child tables are
+ base or temporary. As result we can't guarantee that privilege check
+ which was done in presence of temporary child will stay relevant
+ later as this temporary table might be removed.
+
+ If SELECT_ACL | UPDATE_ACL | DELETE_ACL privileges were not checked for
+ the underlying *base* tables, it would create a security breach as in
+ Bug#12771903.
+ */
+
+ if (check_table_access(thd, SELECT_ACL | UPDATE_ACL | DELETE_ACL,
+ lex->create_info.merge_list.first,
+ FALSE, UINT_MAX, FALSE))
+ goto err;
+ }
if (want_priv != CREATE_TMP_ACL &&
check_grant(thd, want_priv, create_table, FALSE, 1, FALSE))
@@ -8084,13 +8899,49 @@ bool create_table_precheck(THD *thd, TABLE_LIST *tables,
if (check_fk_parent_table_access(thd, &lex->create_info, &lex->alter_info))
goto err;
+ /*
+ For CREATE TABLE we should not open the table even if it exists.
+ If the table exists, we should either not create it or replace it
+ */
+ lex->query_tables->open_strategy= TABLE_LIST::OPEN_STUB;
+
error= FALSE;
+
err:
DBUG_RETURN(error);
}
/**
+ Check privileges for LOCK TABLES statement.
+
+ @param thd Thread context.
+ @param tables List of tables to be locked.
+
+ @retval FALSE - Success.
+ @retval TRUE - Failure.
+*/
+
+static bool lock_tables_precheck(THD *thd, TABLE_LIST *tables)
+{
+ TABLE_LIST *first_not_own_table= thd->lex->first_not_own_table();
+
+ for (TABLE_LIST *table= tables; table != first_not_own_table && table;
+ table= table->next_global)
+ {
+ if (is_temporary_table(table))
+ continue;
+
+ if (check_table_access(thd, LOCK_TABLES_ACL | SELECT_ACL, table,
+ FALSE, 1, FALSE))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+/**
negate given expression.
@param thd thread handler
@@ -8131,16 +8982,23 @@ Item *negate_expression(THD *thd, Item *expr)
@param[out] definer definer
*/
-void get_default_definer(THD *thd, LEX_USER *definer)
+void get_default_definer(THD *thd, LEX_USER *definer, bool role)
{
const Security_context *sctx= thd->security_ctx;
- definer->user.str= (char *) sctx->priv_user;
+ if (role)
+ {
+ definer->user.str= const_cast<char*>(sctx->priv_role);
+ definer->host= empty_lex_str;
+ }
+ else
+ {
+ definer->user.str= const_cast<char*>(sctx->priv_user);
+ definer->host.str= const_cast<char*>(sctx->priv_host);
+ definer->host.length= strlen(definer->host.str);
+ }
definer->user.length= strlen(definer->user.str);
- definer->host.str= (char *) sctx->priv_host;
- definer->host.length= strlen(definer->host.str);
-
definer->password= null_lex_str;
definer->plugin= empty_lex_str;
definer->auth= empty_lex_str;
@@ -8158,16 +9016,22 @@ void get_default_definer(THD *thd, LEX_USER *definer)
- On error, return 0.
*/
-LEX_USER *create_default_definer(THD *thd)
+LEX_USER *create_default_definer(THD *thd, bool role)
{
LEX_USER *definer;
if (! (definer= (LEX_USER*) thd->alloc(sizeof(LEX_USER))))
return 0;
- thd->get_definer(definer);
+ thd->get_definer(definer, role);
- return definer;
+ if (role && definer->user.length == 0)
+ {
+ my_error(ER_MALFORMED_DEFINER, MYF(0));
+ return 0;
+ }
+ else
+ return definer;
}
@@ -8201,28 +9065,6 @@ LEX_USER *create_definer(THD *thd, LEX_STRING *user_name, LEX_STRING *host_name)
return definer;
}
-
-/**
- Retuns information about user or current user.
-
- @param[in] thd thread handler
- @param[in] user user
-
- @return
- - On success, return a valid pointer to initialized
- LEX_USER, which contains user information.
- - On error, return 0.
-*/
-
-LEX_USER *get_current_user(THD *thd, LEX_USER *user)
-{
- if (!user->user.str) // current_user
- return create_default_definer(thd);
-
- return user;
-}
-
-
/**
Check that byte length of a string does not exceed some limit.
@@ -8286,6 +9128,7 @@ bool check_string_char_length(LEX_STRING *str, const char *err_msg,
return TRUE;
}
+C_MODE_START
/*
Check if path does not contain mysql data home directory
@@ -8298,7 +9141,6 @@ bool check_string_char_length(LEX_STRING *str, const char *err_msg,
0 ok
1 error ; Given path contains data directory
*/
-C_MODE_START
int test_if_data_home_dir(const char *dir)
{
@@ -8309,6 +9151,22 @@ int test_if_data_home_dir(const char *dir)
if (!dir)
DBUG_RETURN(0);
+ /*
+ data_file_name and index_file_name include the table name without
+ extension. Mostly this does not refer to an existing file. When
+ comparing data_file_name or index_file_name against the data
+ directory, we try to resolve all symbolic links. On some systems,
+ we use realpath(3) for the resolution. This returns ENOENT if the
+ resolved path does not refer to an existing file. my_realpath()
+ does then copy the requested path verbatim, without symlink
+ resolution. Thereafter the comparison can fail even if the
+ requested path is within the data directory. E.g. if symlinks to
+ another file system are used. To make realpath(3) return the
+ resolved path, we strip the table name and compare the directory
+ path only. If the directory doesn't exist either, table creation
+ will fail anyway.
+ */
+
(void) fn_format(path, dir, "", "",
(MY_RETURN_REAL_PATH|MY_RESOLVE_SYMLINKS));
dir_len= strlen(path);
@@ -8342,6 +9200,22 @@ int test_if_data_home_dir(const char *dir)
C_MODE_END
+int error_if_data_home_dir(const char *path, const char *what)
+{
+ size_t dirlen;
+ char dirpath[FN_REFLEN];
+ if (path)
+ {
+ dirname_part(dirpath, path, &dirlen);
+ if (test_if_data_home_dir(dirpath))
+ {
+ my_error(ER_WRONG_ARGUMENTS, MYF(0), what);
+ return 1;
+ }
+ }
+ return 0;
+}
+
/**
Check that host name string is valid.
@@ -8391,14 +9265,13 @@ extern int MYSQLparse(THD *thd); // from sql_yacc.cc
@retval TRUE on parsing error.
*/
-bool parse_sql(THD *thd,
- Parser_state *parser_state,
- Object_creation_ctx *creation_ctx)
+bool parse_sql(THD *thd, Parser_state *parser_state,
+ Object_creation_ctx *creation_ctx, bool do_pfs_digest)
{
bool ret_value;
DBUG_ENTER("parse_sql");
DBUG_ASSERT(thd->m_parser_state == NULL);
- DBUG_ASSERT(thd->lex->m_stmt == NULL);
+ DBUG_ASSERT(thd->lex->m_sql_cmd == NULL);
MYSQL_QUERY_PARSE_START(thd->query());
/* Backup creation context. */
@@ -8412,6 +9285,28 @@ bool parse_sql(THD *thd,
thd->m_parser_state= parser_state;
+ parser_state->m_digest_psi= NULL;
+ parser_state->m_lip.m_digest= NULL;
+
+ if (do_pfs_digest)
+ {
+ /* Start Digest */
+ parser_state->m_digest_psi= MYSQL_DIGEST_START(thd->m_statement_psi);
+
+ if (parser_state->m_input.m_compute_digest ||
+ (parser_state->m_digest_psi != NULL))
+ {
+ /*
+ If either:
+ - the caller wants to compute a digest
+ - the performance schema wants to compute a digest
+ set the digest listener in the lexer.
+ */
+ parser_state->m_lip.m_digest= thd->m_digest;
+ parser_state->m_lip.m_digest->m_digest_storage.m_charset_number= thd->charset()->number;
+ }
+ }
+
/* Parse the query. */
bool mysql_parse_status= MYSQLparse(thd) != 0;
@@ -8443,6 +9338,18 @@ bool parse_sql(THD *thd,
/* That's it. */
ret_value= mysql_parse_status || thd->is_fatal_error;
+
+ if ((ret_value == 0) && (parser_state->m_digest_psi != NULL))
+ {
+ /*
+ On parsing success, record the digest in the performance schema.
+ */
+ DBUG_ASSERT(do_pfs_digest);
+ DBUG_ASSERT(thd->m_digest != NULL);
+ MYSQL_DIGEST_END(parser_state->m_digest_psi,
+ & thd->m_digest->m_digest_storage);
+ }
+
MYSQL_QUERY_PARSE_DONE(ret_value);
DBUG_RETURN(ret_value);
}
diff --git a/sql/sql_parse.h b/sql/sql_parse.h
index a00a1eaa192..859a1f21202 100644
--- a/sql/sql_parse.h
+++ b/sql/sql_parse.h
@@ -34,6 +34,7 @@ enum enum_mysql_completiontype {
};
extern "C" int test_if_data_home_dir(const char *dir);
+int error_if_data_home_dir(const char *path, const char *what);
bool multi_update_precheck(THD *thd, TABLE_LIST *tables);
bool multi_delete_precheck(THD *thd, TABLE_LIST *tables);
@@ -49,9 +50,8 @@ bool check_fk_parent_table_access(THD *thd,
HA_CREATE_INFO *create_info,
Alter_info *alter_info);
-bool parse_sql(THD *thd,
- Parser_state *parser_state,
- Object_creation_ctx *creation_ctx);
+bool parse_sql(THD *thd, Parser_state *parser_state,
+ Object_creation_ctx *creation_ctx, bool do_pfs_digest=false);
void free_items(Item *item);
void cleanup_items(Item *item);
@@ -65,10 +65,11 @@ Comp_creator *comp_ne_creator(bool invert);
int prepare_schema_table(THD *thd, LEX *lex, Table_ident *table_ident,
enum enum_schema_tables schema_table_idx);
-void get_default_definer(THD *thd, LEX_USER *definer);
-LEX_USER *create_default_definer(THD *thd);
+void get_default_definer(THD *thd, LEX_USER *definer, bool role);
+LEX_USER *create_default_definer(THD *thd, bool role);
LEX_USER *create_definer(THD *thd, LEX_STRING *user_name, LEX_STRING *host_name);
-LEX_USER *get_current_user(THD *thd, LEX_USER *user);
+LEX_USER *get_current_user(THD *thd, LEX_USER *user, bool lock=true);
+bool sp_process_definer(THD *thd);
bool check_string_byte_length(LEX_STRING *str, const char *err_msg,
uint max_byte_length);
bool check_string_char_length(LEX_STRING *str, const char *err_msg,
@@ -150,6 +151,15 @@ inline bool check_identifier_name(LEX_STRING *str)
return check_identifier_name(str, NAME_CHAR_LEN, 0, "");
}
+
+/*
+ check_access() is needed for the connect engine.
+ It cannot be inlined - it must be exported.
+*/
+bool check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv,
+ GRANT_INTERNAL_INFO *grant_internal_info,
+ bool dont_check_global_grants, bool no_errors);
+
#ifndef NO_EMBEDDED_ACCESS_CHECKS
bool check_one_table_access(THD *thd, ulong privilege, TABLE_LIST *tables);
bool check_single_table_access(THD *thd, ulong privilege,
@@ -158,9 +168,6 @@ bool check_routine_access(THD *thd,ulong want_access,char *db,char *name,
bool is_proc, bool no_errors);
bool check_some_access(THD *thd, ulong want_access, TABLE_LIST *table);
bool check_some_routine_access(THD *thd, const char *db, const char *name, bool is_proc);
-bool check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv,
- GRANT_INTERNAL_INFO *grant_internal_info,
- bool dont_check_global_grants, bool no_errors);
bool check_table_access(THD *thd, ulong requirements,TABLE_LIST *tables,
bool any_combination_of_privileges_will_do,
uint number,
@@ -182,13 +189,6 @@ inline bool check_some_access(THD *thd, ulong want_access, TABLE_LIST *table)
inline bool check_some_routine_access(THD *thd, const char *db,
const char *name, bool is_proc)
{ return false; }
-inline bool check_access(THD *, ulong, const char *, ulong *save_priv,
- GRANT_INTERNAL_INFO *, bool, bool)
-{
- if (save_priv)
- *save_priv= GLOBAL_ACLS;
- return false;
-}
inline bool
check_table_access(THD *thd, ulong requirements,TABLE_LIST *tables,
bool any_combination_of_privileges_will_do,
@@ -203,7 +203,7 @@ bool check_global_access(THD *thd, ulong want_access, bool no_errors= false);
inline bool is_supported_parser_charset(CHARSET_INFO *cs)
{
- return test(cs->mbminlen == 1);
+ return MY_TEST(cs->mbminlen == 1);
}
#ifdef WITH_WSREP
@@ -215,10 +215,19 @@ inline bool is_supported_parser_charset(CHARSET_INFO *cs)
if (WSREP(thd) || (thd && thd->wsrep_exec_mode==TOTAL_ORDER)) \
wsrep_to_isolation_end(thd);
+/*
+ Checks if lex->no_write_to_binlog is set for statements that use LOCAL or
+ NO_WRITE_TO_BINLOG.
+*/
+#define WSREP_TO_ISOLATION_BEGIN_WRTCHK(db_, table_, table_list_) \
+ if (WSREP(thd) && !thd->lex->no_write_to_binlog \
+ && wsrep_to_isolation_begin(thd, db_, table_, table_list_)) goto error;
+
#else
#define WSREP_TO_ISOLATION_BEGIN(db_, table_, table_list_)
-#define WSREP_TO_ISOLATION_END
+#define WSREP_TO_ISOLATION_END
+#define WSREP_TO_ISOLATION_BEGIN_WRTCHK(db_, table_, table_list_)
#endif /* WITH_WSREP */
diff --git a/sql/sql_partition.cc b/sql/sql_partition.cc
index b0fb38bf748..be7824aae9e 100644
--- a/sql/sql_partition.cc
+++ b/sql/sql_partition.cc
@@ -47,18 +47,17 @@
/* Some general useful functions */
#define MYSQL_LEX 1
+#include <my_global.h>
#include "sql_priv.h"
-#include "unireg.h" // REQUIRED: for other includes
#include "sql_partition.h"
#include "key.h" // key_restore
#include "sql_parse.h" // parse_sql
#include "sql_cache.h" // query_cache_invalidate3
#include "lock.h" // mysql_lock_remove
#include "sql_show.h" // append_identifier
-#include <errno.h>
#include <m_ctype.h>
-#include "my_md5.h"
#include "transaction.h"
+#include "debug_sync.h"
#include "sql_base.h" // close_all_tables_for_name
#include "sql_table.h" // build_table_filename,
@@ -66,7 +65,11 @@
// table_to_filename
// mysql_*_alter_copy_data
#include "opt_range.h" // store_key_image_to_rec
-#include "sql_analyse.h" // append_escaped
+#include "sql_alter.h" // Alter_table_ctx
+
+#include <algorithm>
+using std::max;
+using std::min;
#ifdef WITH_PARTITION_STORAGE_ENGINE
#include "ha_partition.h"
@@ -190,7 +193,7 @@ static int cmp_rec_and_tuple_prune(part_column_list_val *val,
item New converted item
*/
-Item* convert_charset_partition_constant(Item *item, CHARSET_INFO *cs)
+Item* convert_charset_partition_constant(Item *item, const CHARSET_INFO *cs)
{
THD *thd= current_thd;
Name_resolution_context *context= &thd->lex->current_select->context;
@@ -208,21 +211,18 @@ Item* convert_charset_partition_constant(Item *item, CHARSET_INFO *cs)
}
-/*
- A support function to check if a name is in a list of strings
+/**
+ A support function to check if a name is in a list of strings.
- SYNOPSIS
- is_name_in_list()
- name String searched for
- list_names A list of names searched in
+ @param name String searched for
+ @param list_names A list of names searched in
- RETURN VALUES
- TRUE String found
- FALSE String not found
+ @return True if if the name is in the list.
+ @retval true String found
+ @retval false String not found
*/
-bool is_name_in_list(char *name,
- List<char> list_names)
+static bool is_name_in_list(char *name, List<char> list_names)
{
List_iterator<char> names_it(list_names);
uint num_names= list_names.elements;
@@ -283,62 +283,7 @@ bool partition_default_handling(TABLE *table, partition_info *part_info,
}
}
part_info->set_up_defaults_for_partitioning(table->file,
- (ulonglong)0, (uint)0);
- DBUG_RETURN(FALSE);
-}
-
-
-/*
- Check that the reorganized table will not have duplicate partitions.
-
- SYNOPSIS
- check_reorganise_list()
- new_part_info New partition info
- old_part_info Old partition info
- list_part_names The list of partition names that will go away and
- can be reused in the new table.
-
- RETURN VALUES
- TRUE Inacceptable name conflict detected.
- FALSE New names are OK.
-
- DESCRIPTION
- Can handle that the 'new_part_info' and 'old_part_info' the same
- in which case it checks that the list of names in the partitions
- doesn't contain any duplicated names.
-*/
-
-bool check_reorganise_list(partition_info *new_part_info,
- partition_info *old_part_info,
- List<char> list_part_names)
-{
- uint new_count, old_count;
- uint num_new_parts= new_part_info->partitions.elements;
- uint num_old_parts= old_part_info->partitions.elements;
- List_iterator<partition_element> new_parts_it(new_part_info->partitions);
- bool same_part_info= (new_part_info == old_part_info);
- DBUG_ENTER("check_reorganise_list");
-
- new_count= 0;
- do
- {
- List_iterator<partition_element> old_parts_it(old_part_info->partitions);
- char *new_name= (new_parts_it++)->partition_name;
- new_count++;
- old_count= 0;
- do
- {
- char *old_name= (old_parts_it++)->partition_name;
- old_count++;
- if (same_part_info && old_count == new_count)
- break;
- if (!(my_strcasecmp(system_charset_info, old_name, new_name)))
- {
- if (!is_name_in_list(old_name, list_part_names))
- DBUG_RETURN(TRUE);
- }
- } while (old_count < num_old_parts);
- } while (new_count < num_new_parts);
+ NULL, 0U);
DBUG_RETURN(FALSE);
}
@@ -578,7 +523,13 @@ static bool set_up_field_array(TABLE *table,
} while (++inx < num_fields);
if (inx == num_fields)
{
- mem_alloc_error(1);
+ /*
+ Should not occur since it should already been checked in either
+ add_column_list_values, handle_list_of_fields,
+ check_partition_info etc.
+ */
+ DBUG_ASSERT(0);
+ my_error(ER_FIELD_NOT_FOUND_PART_ERROR, MYF(0));
result= TRUE;
continue;
}
@@ -697,7 +648,7 @@ static bool create_full_part_field_array(THD *thd, TABLE *table,
result= TRUE;
goto end;
}
- if (bitmap_init(&part_info->full_part_field_set, bitmap_buf,
+ if (my_bitmap_init(&part_info->full_part_field_set, bitmap_buf,
table->s->fields, FALSE))
{
mem_alloc_error(table->s->fields);
@@ -742,7 +693,7 @@ end:
static void clear_indicator_in_key_fields(KEY *key_info)
{
KEY_PART_INFO *key_part;
- uint key_parts= key_info->key_parts, i;
+ uint key_parts= key_info->user_defined_key_parts, i;
for (i= 0, key_part=key_info->key_part; i < key_parts; i++, key_part++)
key_part->field->flags&= (~GET_FIXED_FIELDS_FLAG);
}
@@ -762,7 +713,7 @@ static void clear_indicator_in_key_fields(KEY *key_info)
static void set_indicator_in_key_fields(KEY *key_info)
{
KEY_PART_INFO *key_part;
- uint key_parts= key_info->key_parts, i;
+ uint key_parts= key_info->user_defined_key_parts, i;
for (i= 0, key_part=key_info->key_part; i < key_parts; i++, key_part++)
key_part->field->flags|= GET_FIXED_FIELDS_FLAG;
}
@@ -882,7 +833,7 @@ static bool handle_list_of_fields(List_iterator<char> it,
uint primary_key= table->s->primary_key;
if (primary_key != MAX_KEY)
{
- uint num_key_parts= table->key_info[primary_key].key_parts, i;
+ uint num_key_parts= table->key_info[primary_key].user_defined_key_parts, i;
/*
In the case of an empty list we use primary key as partition key.
*/
@@ -1075,7 +1026,7 @@ static bool fix_fields_part_func(THD *thd, Item* func_expr, TABLE *table,
goto end;
}
else
- push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning(thd, Sql_condition::WARN_LEVEL_WARN,
ER_WRONG_EXPR_IN_PARTITION_FUNC_ERROR,
ER(ER_WRONG_EXPR_IN_PARTITION_FUNC_ERROR));
}
@@ -1244,39 +1195,44 @@ void check_range_capable_PF(TABLE *table)
}
-/*
- Set up partition bitmap
+/**
+ Set up partition bitmaps
- SYNOPSIS
- set_up_partition_bitmap()
- thd Thread object
- part_info Reference to partitioning data structure
+ @param thd Thread object
+ @param part_info Reference to partitioning data structure
- RETURN VALUE
- TRUE Memory allocation failure
- FALSE Success
+ @return Operation status
+ @retval TRUE Memory allocation failure
+ @retval FALSE Success
- DESCRIPTION
- Allocate memory for bitmap of the partitioned table
+ Allocate memory for bitmaps of the partitioned table
and initialise it.
*/
-static bool set_up_partition_bitmap(THD *thd, partition_info *part_info)
+static bool set_up_partition_bitmaps(THD *thd, partition_info *part_info)
{
uint32 *bitmap_buf;
uint bitmap_bits= part_info->num_subparts?
(part_info->num_subparts* part_info->num_parts):
part_info->num_parts;
uint bitmap_bytes= bitmap_buffer_size(bitmap_bits);
- DBUG_ENTER("set_up_partition_bitmap");
+ DBUG_ENTER("set_up_partition_bitmaps");
- if (!(bitmap_buf= (uint32*)thd->alloc(bitmap_bytes)))
+ DBUG_ASSERT(!part_info->bitmaps_are_initialized);
+
+ /* Allocate for both read and lock_partitions */
+ if (!(bitmap_buf= (uint32*) alloc_root(&part_info->table->mem_root,
+ bitmap_bytes * 2)))
{
- mem_alloc_error(bitmap_bytes);
+ mem_alloc_error(bitmap_bytes * 2);
DBUG_RETURN(TRUE);
}
- bitmap_init(&part_info->used_partitions, bitmap_buf, bitmap_bytes*8, FALSE);
- bitmap_set_all(&part_info->used_partitions);
+ my_bitmap_init(&part_info->read_partitions, bitmap_buf, bitmap_bits, FALSE);
+ /* Use the second half of the allocated buffer for lock_partitions */
+ my_bitmap_init(&part_info->lock_partitions, bitmap_buf + (bitmap_bytes / 4),
+ bitmap_bits, FALSE);
+ part_info->bitmaps_are_initialized= TRUE;
+ part_info->set_partition_bitmaps(NULL);
DBUG_RETURN(FALSE);
}
@@ -1564,7 +1520,7 @@ bool field_is_partition_charset(Field *field)
!(field->type() == MYSQL_TYPE_VARCHAR))
return FALSE;
{
- CHARSET_INFO *cs= ((Field_str*)field)->charset();
+ CHARSET_INFO *cs= field->charset();
if (!(field->type() == MYSQL_TYPE_STRING) ||
!(cs->state & MY_CS_BINSORT))
return TRUE;
@@ -1607,7 +1563,7 @@ bool check_part_func_fields(Field **ptr, bool ok_with_charsets)
*/
if (field_is_partition_charset(field))
{
- CHARSET_INFO *cs= ((Field_str*)field)->charset();
+ CHARSET_INFO *cs= field->charset();
if (!ok_with_charsets ||
cs->mbmaxlen > 1 ||
cs->strxfrm_multiply > 1)
@@ -1796,7 +1752,7 @@ bool fix_partition_func(THD *thd, TABLE *table,
(table->s->db_type()->partition_flags() & HA_CAN_PARTITION_UNIQUE))) &&
check_unique_keys(table)))
goto end;
- if (unlikely(set_up_partition_bitmap(thd, part_info)))
+ if (unlikely(set_up_partition_bitmaps(thd, part_info)))
goto end;
if (unlikely(part_info->set_up_charset_field_preps()))
{
@@ -1812,6 +1768,7 @@ bool fix_partition_func(THD *thd, TABLE *table,
set_up_partition_key_maps(table, part_info);
set_up_partition_func_pointers(part_info);
set_up_range_analysis_info(part_info);
+ table->file->set_part_info(part_info);
result= FALSE;
end:
thd->mark_used_columns= save_mark_used_columns;
@@ -1975,16 +1932,92 @@ static int add_uint(File fptr, ulonglong number)
*/
static int add_quoted_string(File fptr, const char *quotestr)
{
- String orgstr(quotestr, system_charset_info);
String escapedstr;
int err= add_string(fptr, "'");
- err+= append_escaped(&escapedstr, &orgstr);
+ err+= escapedstr.append_for_single_quote(quotestr);
err+= add_string(fptr, escapedstr.c_ptr_safe());
return err + add_string(fptr, "'");
}
+/**
+ @brief Truncate the partition file name from a path it it exists.
+
+ @note A partition file name will contian one or more '#' characters.
+One of the occurances of '#' will be either "#P#" or "#p#" depending
+on whether the storage engine has converted the filename to lower case.
+*/
+void truncate_partition_filename(char *path)
+{
+ if (path)
+ {
+ char* last_slash= strrchr(path, FN_LIBCHAR);
+
+ if (!last_slash)
+ last_slash= strrchr(path, FN_LIBCHAR2);
+
+ if (last_slash)
+ {
+ /* Look for a partition-type filename */
+ for (char* pound= strchr(last_slash, '#');
+ pound; pound = strchr(pound + 1, '#'))
+ {
+ if ((pound[1] == 'P' || pound[1] == 'p') && pound[2] == '#')
+ {
+ last_slash[0] = '\0'; /* truncate the file name */
+ break;
+ }
+ }
+ }
+ }
+}
+
+
+/**
+ @brief Output a filepath. Similar to add_keyword_string except it
+also converts \ to / on Windows and skips the partition file name at
+the end if found.
+
+ @note When Mysql sends a DATA DIRECTORY from SQL for partitions it does
+not use a file name, but it does for DATA DIRECTORY on a non-partitioned
+table. So when the storage engine is asked for the DATA DIRECTORY string
+after a restart through Handler::update_create_options(), the storage
+engine may include the filename.
+*/
+static int add_keyword_path(File fptr, const char *keyword,
+ const char *path)
+{
+ int err= add_string(fptr, keyword);
+
+ err+= add_space(fptr);
+ err+= add_equal(fptr);
+ err+= add_space(fptr);
+
+ char temp_path[FN_REFLEN];
+ strcpy(temp_path, path);
+#ifdef __WIN__
+ /* Convert \ to / to be able to create table on unix */
+ char *pos, *end;
+ uint length= strlen(temp_path);
+ for (pos= temp_path, end= pos+length ; pos < end ; pos++)
+ {
+ if (*pos == '\\')
+ *pos = '/';
+ }
+#endif
+
+ /*
+ If the partition file name with its "#P#" identifier
+ is found after the last slash, truncate that filename.
+ */
+ truncate_partition_filename(temp_path);
+
+ err+= add_quoted_string(fptr, temp_path);
+
+ return err + add_space(fptr);
+}
+
static int add_keyword_string(File fptr, const char *keyword,
- bool should_use_quotes,
+ bool should_use_quotes,
const char *keystr)
{
int err= add_string(fptr, keyword);
@@ -2035,11 +2068,9 @@ static int add_partition_options(File fptr, partition_element *p_elem)
if (!(current_thd->variables.sql_mode & MODE_NO_DIR_IN_CREATE))
{
if (p_elem->data_file_name)
- err+= add_keyword_string(fptr, "DATA DIRECTORY", TRUE,
- p_elem->data_file_name);
+ err+= add_keyword_path(fptr, "DATA DIRECTORY", p_elem->data_file_name);
if (p_elem->index_file_name)
- err+= add_keyword_string(fptr, "INDEX DIRECTORY", TRUE,
- p_elem->index_file_name);
+ err+= add_keyword_path(fptr, "INDEX DIRECTORY", p_elem->index_file_name);
}
if (p_elem->part_comment)
err+= add_keyword_string(fptr, "COMMENT", TRUE, p_elem->part_comment);
@@ -2091,6 +2122,8 @@ static int check_part_field(enum_field_types sql_type,
case MYSQL_TYPE_DATE:
case MYSQL_TYPE_TIME:
case MYSQL_TYPE_DATETIME:
+ case MYSQL_TYPE_TIME2:
+ case MYSQL_TYPE_DATETIME2:
*result_type= STRING_RESULT;
*need_cs_check= TRUE;
return FALSE;
@@ -2103,6 +2136,7 @@ static int check_part_field(enum_field_types sql_type,
case MYSQL_TYPE_NEWDECIMAL:
case MYSQL_TYPE_DECIMAL:
case MYSQL_TYPE_TIMESTAMP:
+ case MYSQL_TYPE_TIMESTAMP2:
case MYSQL_TYPE_NULL:
case MYSQL_TYPE_FLOAT:
case MYSQL_TYPE_DOUBLE:
@@ -2186,7 +2220,7 @@ static int add_column_list_values(File fptr, partition_info *part_info,
else
{
String *res;
- CHARSET_INFO *field_cs;
+ const CHARSET_INFO *field_cs;
bool need_cs_check= FALSE;
Item_result result_type= STRING_RESULT;
@@ -2374,7 +2408,7 @@ static int add_key_with_algorithm(File fptr, partition_info *part_info,
/* If we already are within a comment, end that comment first. */
if (current_comment_start)
err+= add_string(fptr, "*/ ");
- err+= add_string(fptr, "/*!50531 ");
+ err+= add_string(fptr, "/*!50611 ");
err+= add_part_key_word(fptr, partition_keywords[PKW_ALGORITHM].str);
err+= add_equal(fptr);
err+= add_space(fptr);
@@ -2700,114 +2734,13 @@ static inline int part_val_int(Item *item_expr, longlong *result)
We have a set of support functions for these 14 variants. There are 4
variants of hash functions and there is a function for each. The KEY
- partitioning uses the function calculate_key_value to calculate the hash
+ partitioning uses the function calculate_key_hash_value to calculate the hash
value based on an array of fields. The linear hash variants uses the
method get_part_id_from_linear_hash to get the partition id using the
hash value and some parameters calculated from the number of partitions.
*/
/*
- Calculate hash value for KEY partitioning using an array of fields.
-
- SYNOPSIS
- calculate_key_value()
- field_array An array of the fields in KEY partitioning
-
- RETURN VALUE
- hash_value calculated
-
- DESCRIPTION
- Uses the hash function on the character set of the field. Integer and
- floating point fields use the binary character set by default.
-*/
-
-static uint32 calculate_key_value(Field **field_array)
-{
- ulong nr1= 1;
- ulong nr2= 4;
- bool use_51_hash;
- use_51_hash= test((*field_array)->table->part_info->key_algorithm ==
- partition_info::KEY_ALGORITHM_51);
-
- do
- {
- Field *field= *field_array;
- if (use_51_hash)
- {
- switch (field->real_type()) {
- case MYSQL_TYPE_TINY:
- case MYSQL_TYPE_SHORT:
- case MYSQL_TYPE_LONG:
- case MYSQL_TYPE_FLOAT:
- case MYSQL_TYPE_DOUBLE:
- case MYSQL_TYPE_NEWDECIMAL:
- case MYSQL_TYPE_TIMESTAMP:
- case MYSQL_TYPE_LONGLONG:
- case MYSQL_TYPE_INT24:
- case MYSQL_TYPE_TIME:
- case MYSQL_TYPE_DATETIME:
- case MYSQL_TYPE_YEAR:
- case MYSQL_TYPE_NEWDATE:
- {
- if (field->is_null())
- {
- nr1^= (nr1 << 1) | 1;
- continue;
- }
- /* Force this to my_hash_sort_bin, which was used in 5.1! */
- uint len= field->pack_length();
- my_charset_bin.coll->hash_sort(&my_charset_bin, field->ptr, len,
- &nr1, &nr2);
- /* Done with this field, continue with next one. */
- continue;
- }
- case MYSQL_TYPE_STRING:
- case MYSQL_TYPE_VARCHAR:
- case MYSQL_TYPE_BIT:
- /* Not affected, same in 5.1 and 5.5 */
- break;
- /*
- ENUM/SET uses my_hash_sort_simple in 5.1 (i.e. my_charset_latin1)
- and my_hash_sort_bin in 5.5!
- */
- case MYSQL_TYPE_ENUM:
- case MYSQL_TYPE_SET:
- {
- if (field->is_null())
- {
- nr1^= (nr1 << 1) | 1;
- continue;
- }
- /* Force this to my_hash_sort_bin, which was used in 5.1! */
- uint len= field->pack_length();
- my_charset_latin1.coll->hash_sort(&my_charset_latin1, field->ptr,
- len, &nr1, &nr2);
- continue;
- }
- /* These types should not be allowed for partitioning! */
- case MYSQL_TYPE_NULL:
- case MYSQL_TYPE_DECIMAL:
- case MYSQL_TYPE_DATE:
- case MYSQL_TYPE_TINY_BLOB:
- case MYSQL_TYPE_MEDIUM_BLOB:
- case MYSQL_TYPE_LONG_BLOB:
- case MYSQL_TYPE_BLOB:
- case MYSQL_TYPE_VAR_STRING:
- case MYSQL_TYPE_GEOMETRY:
- /* fall through. */
- default:
- DBUG_ASSERT(0); // New type?
- /* Fall through for default hashing (5.5). */
- }
- /* fall through, use collation based hashing. */
- }
- field->hash(&nr1, &nr2);
- } while (*(++field_array));
- return (uint32) nr1;
-}
-
-
-/*
A simple support function to calculate part_id given local part and
sub part.
@@ -2894,25 +2827,25 @@ static int get_part_id_linear_hash(partition_info *part_info,
}
-/*
+/**
Calculate part_id for (SUB)PARTITION BY KEY
- SYNOPSIS
- get_part_id_key()
- field_array Array of fields for PARTTION KEY
- num_parts Number of KEY partitions
+ @param file Handler to storage engine
+ @param field_array Array of fields for PARTTION KEY
+ @param num_parts Number of KEY partitions
+ @param func_value[out] Returns calculated hash value
- RETURN VALUE
- Calculated partition id
+ @return Calculated partition id
*/
inline
-static uint32 get_part_id_key(Field **field_array,
+static uint32 get_part_id_key(handler *file,
+ Field **field_array,
uint num_parts,
longlong *func_value)
{
DBUG_ENTER("get_part_id_key");
- *func_value= calculate_key_value(field_array);
+ *func_value= ha_partition::calculate_key_hash_value(field_array);
DBUG_RETURN((uint32) (*func_value % num_parts));
}
@@ -2939,7 +2872,7 @@ static uint32 get_part_id_linear_key(partition_info *part_info,
{
DBUG_ENTER("get_part_id_linear_key");
- *func_value= calculate_key_value(field_array);
+ *func_value= ha_partition::calculate_key_hash_value(field_array);
DBUG_RETURN(get_part_id_from_linear_hash(*func_value,
part_info->linear_hash_mask,
num_parts));
@@ -2975,7 +2908,7 @@ static void copy_to_part_field_buffers(Field **ptr,
restore_ptr++;
if (!field->maybe_null() || !field->is_null())
{
- CHARSET_INFO *cs= ((Field_str*)field)->charset();
+ CHARSET_INFO *cs= field->charset();
uint max_len= field->pack_length();
uint data_len= field->data_length();
uchar *field_buf= *field_bufs;
@@ -3378,7 +3311,7 @@ uint32 get_list_array_idx_for_endpoint(partition_info *part_info,
}
else
{
- DBUG_RETURN(list_index + test(left_endpoint ^ include_endpoint));
+ DBUG_RETURN(list_index + MY_TEST(left_endpoint ^ include_endpoint));
}
} while (max_list_index >= min_list_index);
notfound:
@@ -3640,7 +3573,8 @@ int get_partition_id_key_nosub(partition_info *part_info,
uint32 *part_id,
longlong *func_value)
{
- *part_id= get_part_id_key(part_info->part_field_array,
+ *part_id= get_part_id_key(part_info->table->file,
+ part_info->part_field_array,
part_info->num_parts, func_value);
return 0;
}
@@ -3730,7 +3664,8 @@ int get_partition_id_key_sub(partition_info *part_info,
uint32 *part_id)
{
longlong func_value;
- *part_id= get_part_id_key(part_info->subpart_field_array,
+ *part_id= get_part_id_key(part_info->table->file,
+ part_info->subpart_field_array,
part_info->num_subparts, &func_value);
return FALSE;
}
@@ -3967,6 +3902,92 @@ void get_full_part_id_from_key(const TABLE *table, uchar *buf,
DBUG_VOID_RETURN;
}
+
+/**
+ @brief Verify that all rows in a table is in the given partition
+
+ @param table Table which contains the data that will be checked if
+ it is matching the partition definition.
+ @param part_table Partitioned table containing the partition to check.
+ @param part_id Which partition to match with.
+
+ @return Operation status
+ @retval TRUE Not all rows match the given partition
+ @retval FALSE OK
+*/
+bool verify_data_with_partition(TABLE *table, TABLE *part_table,
+ uint32 part_id)
+{
+ uint32 found_part_id;
+ longlong func_value; /* Unused */
+ handler *file;
+ int error;
+ uchar *old_rec;
+ partition_info *part_info;
+ DBUG_ENTER("verify_data_with_partition");
+ DBUG_ASSERT(table && table->file && part_table && part_table->part_info &&
+ part_table->file);
+
+ /*
+ Verify all table rows.
+ First implementation uses full scan + evaluates partition functions for
+ every row. TODO: add optimization to use index if possible, see WL#5397.
+
+ 1) Open both tables (already done) and set the row buffers to use
+ the same buffer (to avoid copy).
+ 2) Init rnd on table.
+ 3) loop over all rows.
+ 3.1) verify that partition_id on the row is correct. Break if error.
+ */
+ file= table->file;
+ part_info= part_table->part_info;
+ bitmap_union(table->read_set, &part_info->full_part_field_set);
+ old_rec= part_table->record[0];
+ part_table->record[0]= table->record[0];
+ set_field_ptr(part_info->full_part_field_array, table->record[0], old_rec);
+ if ((error= file->ha_rnd_init(TRUE)))
+ {
+ file->print_error(error, MYF(0));
+ goto err;
+ }
+
+ do
+ {
+ if ((error= file->ha_rnd_next(table->record[0])))
+ {
+ if (error == HA_ERR_RECORD_DELETED)
+ continue;
+ if (error == HA_ERR_END_OF_FILE)
+ error= 0;
+ else
+ file->print_error(error, MYF(0));
+ break;
+ }
+ if ((error= part_info->get_partition_id(part_info, &found_part_id,
+ &func_value)))
+ {
+ part_table->file->print_error(error, MYF(0));
+ break;
+ }
+ DEBUG_SYNC(current_thd, "swap_partition_first_row_read");
+ if (found_part_id != part_id)
+ {
+ my_error(ER_ROW_DOES_NOT_MATCH_PARTITION, MYF(0));
+ error= 1;
+ break;
+ }
+ } while (TRUE);
+ (void) file->ha_rnd_end();
+err:
+ set_field_ptr(part_info->full_part_field_array, old_rec,
+ table->record[0]);
+ part_table->record[0]= old_rec;
+ if (error)
+ DBUG_RETURN(TRUE);
+ DBUG_RETURN(FALSE);
+}
+
+
/*
Prune the set of partitions to use in query
@@ -3977,7 +3998,7 @@ void get_full_part_id_from_key(const TABLE *table, uchar *buf,
DESCRIPTION
This function is called to prune the range of partitions to scan by
- checking the used_partitions bitmap.
+ checking the read_partitions bitmap.
If start_part > end_part at return it means no partition needs to be
scanned. If start_part == end_part it always means a single partition
needs to be scanned.
@@ -3994,7 +4015,7 @@ void prune_partition_set(const TABLE *table, part_id_range *part_spec)
DBUG_ENTER("prune_partition_set");
for (i= part_spec->start_part; i <= part_spec->end_part; i++)
{
- if (bitmap_is_set(&(part_info->used_partitions), i))
+ if (bitmap_is_set(&(part_info->read_partitions), i))
{
DBUG_PRINT("info", ("Partition %d is set", i));
if (last_partition == -1)
@@ -4076,7 +4097,7 @@ void get_partition_set(const TABLE *table, uchar *buf, const uint index,
*/
get_full_part_id_from_key(table,buf,key_info,key_spec,part_spec);
/*
- Check if range can be adjusted by looking in used_partitions
+ Check if range can be adjusted by looking in read_partitions
*/
prune_partition_set(table, part_spec);
DBUG_VOID_RETURN;
@@ -4128,7 +4149,7 @@ void get_partition_set(const TABLE *table, uchar *buf, const uint index,
get_full_part_id_from_key(table,buf,key_info,key_spec,part_spec);
clear_indicator_in_key_fields(key_info);
/*
- Check if range can be adjusted by looking in used_partitions
+ Check if range can be adjusted by looking in read_partitions
*/
prune_partition_set(table, part_spec);
DBUG_VOID_RETURN;
@@ -4198,7 +4219,7 @@ void get_partition_set(const TABLE *table, uchar *buf, const uint index,
if (found_part_field)
clear_indicator_in_key_fields(key_info);
/*
- Check if range can be adjusted by looking in used_partitions
+ Check if range can be adjusted by looking in read_partitions
*/
prune_partition_set(table, part_spec);
DBUG_VOID_RETURN;
@@ -4269,9 +4290,11 @@ bool mysql_unpack_partition(THD *thd,
{
bool result= TRUE;
partition_info *part_info;
- CHARSET_INFO *old_character_set_client= thd->variables.character_set_client;
+ const CHARSET_INFO *old_character_set_client=
+ thd->variables.character_set_client;
LEX *old_lex= thd->lex;
LEX lex;
+ PSI_statement_locker *parent_locker= thd->m_statement_psi;
DBUG_ENTER("mysql_unpack_partition");
thd->variables.character_set_client= system_charset_info;
@@ -4301,12 +4324,16 @@ bool mysql_unpack_partition(THD *thd,
}
part_info= lex.part_info;
DBUG_PRINT("info", ("Parse: %s", part_buf));
+
+ thd->m_statement_psi= NULL;
if (parse_sql(thd, & parser_state, NULL) ||
part_info->fix_parser_data(thd))
{
thd->free_items();
+ thd->m_statement_psi= parent_locker;
goto end;
}
+ thd->m_statement_psi= parent_locker;
/*
The parsed syntax residing in the frm file can still contain defaults.
The reason is that the frm file is sometimes saved outside of this
@@ -4346,6 +4373,7 @@ bool mysql_unpack_partition(THD *thd,
*work_part_info_used= true;
}
table->part_info= part_info;
+ part_info->table= table;
table->file->set_part_info(part_info);
if (!part_info->default_engine_type)
part_info->default_engine_type= default_db_type;
@@ -4563,7 +4591,7 @@ bool set_part_state(Alter_info *alter_info, partition_info *tab_part_info,
do
{
partition_element *part_elem= part_it++;
- if ((alter_info->flags & ALTER_ALL_PARTITION) ||
+ if ((alter_info->flags & Alter_info::ALTER_ALL_PARTITION) ||
(is_name_in_list(part_elem->partition_name,
alter_info->partition_names)))
{
@@ -4582,7 +4610,7 @@ bool set_part_state(Alter_info *alter_info, partition_info *tab_part_info,
} while (++part_count < tab_part_info->num_parts);
if (num_parts_found != alter_info->partition_names.elements &&
- !(alter_info->flags & ALTER_ALL_PARTITION))
+ !(alter_info->flags & Alter_info::ALTER_ALL_PARTITION))
{
/* Not all given partitions found, revert and return failure */
part_it.rewind();
@@ -4599,16 +4627,60 @@ bool set_part_state(Alter_info *alter_info, partition_info *tab_part_info,
/**
+ @brief Check if partition is exchangable with table by checking table options
+
+ @param table_create_info Table options from table.
+ @param part_elem All the info of the partition.
+
+ @retval FALSE if they are equal, otherwise TRUE.
+
+ @note Any differens that would cause a change in the frm file is prohibited.
+ Such options as data_file_name, index_file_name, min_rows, max_rows etc. are
+ not allowed to differ. But comment is allowed to differ.
+*/
+bool compare_partition_options(HA_CREATE_INFO *table_create_info,
+ partition_element *part_elem)
+{
+#define MAX_COMPARE_PARTITION_OPTION_ERRORS 5
+ const char *option_diffs[MAX_COMPARE_PARTITION_OPTION_ERRORS + 1];
+ int i, errors= 0;
+ DBUG_ENTER("compare_partition_options");
+ DBUG_ASSERT(!part_elem->tablespace_name &&
+ !table_create_info->tablespace);
+
+ /*
+ Note that there are not yet any engine supporting tablespace together
+ with partitioning. TODO: when there are, add compare.
+ */
+ if (part_elem->tablespace_name || table_create_info->tablespace)
+ option_diffs[errors++]= "TABLESPACE";
+ if (part_elem->part_max_rows != table_create_info->max_rows)
+ option_diffs[errors++]= "MAX_ROWS";
+ if (part_elem->part_min_rows != table_create_info->min_rows)
+ option_diffs[errors++]= "MIN_ROWS";
+ if (part_elem->data_file_name || table_create_info->data_file_name)
+ option_diffs[errors++]= "DATA DIRECTORY";
+ if (part_elem->index_file_name || table_create_info->index_file_name)
+ option_diffs[errors++]= "INDEX DIRECTORY";
+
+ for (i= 0; i < errors; i++)
+ my_error(ER_PARTITION_EXCHANGE_DIFFERENT_OPTION, MYF(0),
+ option_diffs[i]);
+ DBUG_RETURN(errors != 0);
+}
+
+
+/*
Prepare for ALTER TABLE of partition structure
@param[in] thd Thread object
@param[in] table Table object
@param[in,out] alter_info Alter information
@param[in,out] create_info Create info for CREATE TABLE
- @param[in] old_db_type Old engine type
+ @param[in] alter_ctx ALTER TABLE runtime context
@param[out] partition_changed Boolean indicating whether partition changed
- @param[out] fast_alter_table Internal temporary table allowing fast
- partition change or NULL if not possible
+ @param[out] fast_alter_table Boolean indicating if fast partition alter is
+ possible.
@return Operation status
@retval TRUE Error
@@ -4626,22 +4698,26 @@ bool set_part_state(Alter_info *alter_info, partition_info *tab_part_info,
uint prep_alter_part_table(THD *thd, TABLE *table, Alter_info *alter_info,
HA_CREATE_INFO *create_info,
- handlerton *old_db_type,
+ Alter_table_ctx *alter_ctx,
bool *partition_changed,
- char *db,
- const char *table_name,
- const char *path,
- TABLE **fast_alter_table)
+ bool *fast_alter_table)
{
- TABLE *new_table= NULL;
DBUG_ENTER("prep_alter_part_table");
/* Foreign keys on partitioned tables are not supported, waits for WL#148 */
- if (table->part_info && (alter_info->flags & ALTER_FOREIGN_KEY))
+ if (table->part_info && (alter_info->flags & Alter_info::ADD_FOREIGN_KEY ||
+ alter_info->flags & Alter_info::DROP_FOREIGN_KEY))
{
my_error(ER_FOREIGN_KEY_ON_PARTITIONED, MYF(0));
DBUG_RETURN(TRUE);
}
+ /* Remove partitioning on a not partitioned table is not possible */
+ if (!table->part_info && (alter_info->flags &
+ Alter_info::ALTER_REMOVE_PARTITIONING))
+ {
+ my_error(ER_PARTITION_MGMT_ON_NONPARTITIONED, MYF(0));
+ DBUG_RETURN(TRUE);
+ }
thd->work_part_info= thd->lex->part_info;
@@ -4650,12 +4726,15 @@ uint prep_alter_part_table(THD *thd, TABLE *table, Alter_info *alter_info,
DBUG_RETURN(TRUE);
/* ALTER_ADMIN_PARTITION is handled in mysql_admin_table */
- DBUG_ASSERT(!(alter_info->flags & ALTER_ADMIN_PARTITION));
+ DBUG_ASSERT(!(alter_info->flags & Alter_info::ALTER_ADMIN_PARTITION));
if (alter_info->flags &
- (ALTER_ADD_PARTITION | ALTER_DROP_PARTITION |
- ALTER_COALESCE_PARTITION | ALTER_REORGANIZE_PARTITION |
- ALTER_TABLE_REORG | ALTER_REBUILD_PARTITION))
+ (Alter_info::ALTER_ADD_PARTITION |
+ Alter_info::ALTER_DROP_PARTITION |
+ Alter_info::ALTER_COALESCE_PARTITION |
+ Alter_info::ALTER_REORGANIZE_PARTITION |
+ Alter_info::ALTER_TABLE_REORG |
+ Alter_info::ALTER_REBUILD_PARTITION))
{
partition_info *tab_part_info;
partition_info *alt_part_info= thd->work_part_info;
@@ -4677,30 +4756,31 @@ uint prep_alter_part_table(THD *thd, TABLE *table, Alter_info *alter_info,
Open it as a copy of the original table, and modify its partition_info
object to allow fast_alter_partition_table to perform the changes.
*/
- DBUG_ASSERT(thd->mdl_context.is_lock_owner(MDL_key::TABLE, db, table_name,
+ DBUG_ASSERT(thd->mdl_context.is_lock_owner(MDL_key::TABLE,
+ alter_ctx->db,
+ alter_ctx->table_name,
MDL_INTENTION_EXCLUSIVE));
- new_table= open_table_uncached(thd, path, db, table_name, 0);
- if (!new_table)
- DBUG_RETURN(TRUE);
- /*
- This table may be used for copy rows between partitions
- and also read/write columns when fixing the partition_info struct.
- */
- new_table->use_all_columns();
-
- tab_part_info= new_table->part_info;
+ tab_part_info= table->part_info;
- if (alter_info->flags & ALTER_TABLE_REORG)
+ if (alter_info->flags & Alter_info::ALTER_TABLE_REORG)
{
uint new_part_no, curr_part_no;
+ /*
+ 'ALTER TABLE t REORG PARTITION' only allowed with auto partition
+ if default partitioning is used.
+ */
+
if (tab_part_info->part_type != HASH_PARTITION ||
- tab_part_info->use_default_num_partitions)
+ ((table->s->db_type()->partition_flags() & HA_USE_AUTO_PARTITION) &&
+ !tab_part_info->use_default_num_partitions) ||
+ ((!(table->s->db_type()->partition_flags() & HA_USE_AUTO_PARTITION)) &&
+ tab_part_info->use_default_num_partitions))
{
my_error(ER_REORG_NO_PARAM_ERROR, MYF(0));
goto err;
}
- new_part_no= new_table->file->get_default_no_partitions(create_info);
+ new_part_no= table->file->get_default_no_partitions(create_info);
curr_part_no= tab_part_info->num_parts;
if (new_part_no == curr_part_no)
{
@@ -4709,7 +4789,23 @@ uint prep_alter_part_table(THD *thd, TABLE *table, Alter_info *alter_info,
after the change as before. Thus we can reply ok immediately
without any changes at all.
*/
- *fast_alter_table= new_table;
+ flags= table->file->alter_table_flags(alter_info->flags);
+ if (flags & (HA_FAST_CHANGE_PARTITION | HA_PARTITION_ONE_PHASE))
+ {
+ *fast_alter_table= true;
+ /* Force table re-open for consistency with the main case. */
+ table->m_needs_reopen= true;
+ }
+ else
+ {
+ /*
+ Create copy of partition_info to avoid modifying original
+ TABLE::part_info, to keep it safe for later use.
+ */
+ if (!(tab_part_info= tab_part_info->get_clone()))
+ DBUG_RETURN(TRUE);
+ }
+
thd->work_part_info= tab_part_info;
DBUG_RETURN(FALSE);
}
@@ -4719,7 +4815,7 @@ uint prep_alter_part_table(THD *thd, TABLE *table, Alter_info *alter_info,
We will add more partitions, we use the ADD PARTITION without
setting the flag for no default number of partitions
*/
- alter_info->flags|= ALTER_ADD_PARTITION;
+ alter_info->flags|= Alter_info::ALTER_ADD_PARTITION;
thd->work_part_info->num_parts= new_part_no - curr_part_no;
}
else
@@ -4728,21 +4824,41 @@ uint prep_alter_part_table(THD *thd, TABLE *table, Alter_info *alter_info,
We will remove hash partitions, we use the COALESCE PARTITION
without setting the flag for no default number of partitions
*/
- alter_info->flags|= ALTER_COALESCE_PARTITION;
+ alter_info->flags|= Alter_info::ALTER_COALESCE_PARTITION;
alter_info->num_parts= curr_part_no - new_part_no;
}
}
- if (!(flags= new_table->file->alter_table_flags(alter_info->flags)))
+ if (!(flags= table->file->alter_table_flags(alter_info->flags)))
{
my_error(ER_PARTITION_FUNCTION_FAILURE, MYF(0));
goto err;
}
if ((flags & (HA_FAST_CHANGE_PARTITION | HA_PARTITION_ONE_PHASE)) != 0)
- *fast_alter_table= new_table;
- DBUG_PRINT("info", ("*fast_alter_table: %p flags: 0x%x",
- *fast_alter_table, flags));
- if ((alter_info->flags & ALTER_ADD_PARTITION) ||
- (alter_info->flags & ALTER_REORGANIZE_PARTITION))
+ {
+ /*
+ "Fast" change of partitioning is supported in this case.
+ We will change TABLE::part_info (as this is how we pass
+ information to storage engine in this case), so the table
+ must be reopened.
+ */
+ *fast_alter_table= true;
+ table->m_needs_reopen= true;
+ }
+ else
+ {
+ /*
+ "Fast" changing of partitioning is not supported. Create
+ a copy of TABLE::part_info object, so we can modify it safely.
+ Modifying original TABLE::part_info will cause problems when
+ we read data from old version of table using this TABLE object
+ while copying them to new version of table.
+ */
+ if (!(tab_part_info= tab_part_info->get_clone()))
+ DBUG_RETURN(TRUE);
+ }
+ DBUG_PRINT("info", ("*fast_alter_table flags: 0x%x", flags));
+ if ((alter_info->flags & Alter_info::ALTER_ADD_PARTITION) ||
+ (alter_info->flags & Alter_info::ALTER_REORGANIZE_PARTITION))
{
if (thd->work_part_info->part_type != tab_part_info->part_type)
{
@@ -4809,7 +4925,7 @@ uint prep_alter_part_table(THD *thd, TABLE *table, Alter_info *alter_info,
goto err;
}
}
- if (alter_info->flags & ALTER_ADD_PARTITION)
+ if (alter_info->flags & Alter_info::ALTER_ADD_PARTITION)
{
/*
We start by moving the new partitions to the list of temporary
@@ -4860,8 +4976,7 @@ uint prep_alter_part_table(THD *thd, TABLE *table, Alter_info *alter_info,
}
alt_part_info->part_type= tab_part_info->part_type;
alt_part_info->subpart_type= tab_part_info->subpart_type;
- if (alt_part_info->set_up_defaults_for_partitioning(new_table->file,
- ULL(0),
+ if (alt_part_info->set_up_defaults_for_partitioning(table->file, 0,
tab_part_info->num_parts))
{
goto err;
@@ -5049,7 +5164,7 @@ that are reorganised.
of partitions anymore. We use this code also for Table reorganisations
and here we don't set any default flags to FALSE.
*/
- if (!(alter_info->flags & ALTER_TABLE_REORG))
+ if (!(alter_info->flags & Alter_info::ALTER_TABLE_REORG))
{
if (!alt_part_info->use_default_partitions)
{
@@ -5060,7 +5175,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_info::ALTER_DROP_PARTITION)
{
/*
Drop a partition from a range partition and list partitioning is
@@ -5104,14 +5219,14 @@ that are reorganised.
my_error(ER_DROP_PARTITION_NON_EXISTENT, MYF(0), "DROP");
goto err;
}
- if (new_table->file->is_fk_defined_on_table_or_index(MAX_KEY))
+ if (table->file->is_fk_defined_on_table_or_index(MAX_KEY))
{
my_error(ER_ROW_IS_REFERENCED, MYF(0));
goto err;
}
tab_part_info->num_parts-= num_parts_dropped;
}
- else if (alter_info->flags & ALTER_REBUILD_PARTITION)
+ else if (alter_info->flags & Alter_info::ALTER_REBUILD_PARTITION)
{
set_engine_all_partitions(tab_part_info,
tab_part_info->default_engine_type);
@@ -5122,11 +5237,11 @@ that are reorganised.
}
if (!(*fast_alter_table))
{
- new_table->file->print_error(HA_ERR_WRONG_COMMAND, MYF(0));
+ table->file->print_error(HA_ERR_WRONG_COMMAND, MYF(0));
goto err;
}
}
- else if (alter_info->flags & ALTER_COALESCE_PARTITION)
+ else if (alter_info->flags & Alter_info::ALTER_COALESCE_PARTITION)
{
uint num_parts_coalesced= alter_info->num_parts;
uint num_parts_remain= tab_part_info->num_parts - num_parts_coalesced;
@@ -5224,13 +5339,13 @@ state of p1.
} while (part_count < tab_part_info->num_parts);
tab_part_info->num_parts= num_parts_remain;
}
- if (!(alter_info->flags & ALTER_TABLE_REORG))
+ if (!(alter_info->flags & Alter_info::ALTER_TABLE_REORG))
{
tab_part_info->use_default_num_partitions= FALSE;
tab_part_info->is_auto_partitioned= FALSE;
}
}
- else if (alter_info->flags & ALTER_REORGANIZE_PARTITION)
+ else if (alter_info->flags & Alter_info::ALTER_REORGANIZE_PARTITION)
{
/*
Reorganise partitions takes a number of partitions that are next
@@ -5278,9 +5393,9 @@ state of p1.
alt_part_info->subpart_type= tab_part_info->subpart_type;
alt_part_info->num_subparts= tab_part_info->num_subparts;
DBUG_ASSERT(!alt_part_info->use_default_partitions);
- if (alt_part_info->set_up_defaults_for_partitioning(new_table->file,
- ULL(0),
- 0))
+ /* We specified partitions explicitly so don't use defaults anymore. */
+ tab_part_info->use_default_partitions= FALSE;
+ if (alt_part_info->set_up_defaults_for_partitioning(table->file, 0, 0))
{
goto err;
}
@@ -5403,8 +5518,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_info::ALTER_ADD_PARTITION ||
+ alter_info->flags & Alter_info::ALTER_REORGANIZE_PARTITION)
{
if (tab_part_info->use_default_subpartitions &&
!alt_part_info->use_default_subpartitions)
@@ -5413,7 +5528,7 @@ the generated partition syntax in a correct manner.
tab_part_info->use_default_num_subpartitions= FALSE;
}
if (tab_part_info->check_partition_info(thd, (handlerton**)NULL,
- new_table->file, ULL(0), TRUE))
+ table->file, 0, TRUE))
{
goto err;
}
@@ -5422,7 +5537,7 @@ the generated partition syntax in a correct manner.
since this function "fixes" the item trees of the new partitions
to reorganize into
*/
- if (alter_info->flags == ALTER_REORGANIZE_PARTITION &&
+ if (alter_info->flags == Alter_info::ALTER_REORGANIZE_PARTITION &&
tab_part_info->part_type == RANGE_PARTITION &&
((is_last_partition_reorged &&
(tab_part_info->column_list ?
@@ -5505,7 +5620,7 @@ the generated partition syntax in a correct manner.
if (tab_part_info)
{
- if (alter_info->flags & ALTER_REMOVE_PARTITIONING)
+ if (alter_info->flags & Alter_info::ALTER_REMOVE_PARTITIONING)
{
DBUG_PRINT("info", ("Remove partitioning"));
if (!(create_info->used_fields & HA_CREATE_USED_ENGINE))
@@ -5576,8 +5691,10 @@ the generated partition syntax in a correct manner.
rebuild). This is to handle KEY (numeric_cols) partitioned tables
created in 5.1. For more info, see bug#14521864.
*/
- if (alter_info->flags != ALTER_PARTITION ||
+ if (alter_info->flags != Alter_info::ALTER_PARTITION ||
!table->part_info ||
+ alter_info->requested_algorithm !=
+ Alter_info::ALTER_TABLE_ALGORITHM_INPLACE ||
!table->part_info->has_same_partitioning(part_info))
{
DBUG_PRINT("info", ("partition changed"));
@@ -5613,15 +5730,7 @@ the generated partition syntax in a correct manner.
}
DBUG_RETURN(FALSE);
err:
- if (new_table)
- {
- /*
- Only remove the intermediate table object and its share object,
- do not remove the .frm file, since it is the original one.
- */
- close_temporary(new_table, 1, 0);
- }
- *fast_alter_table= NULL;
+ *fast_alter_table= false;
DBUG_RETURN(TRUE);
}
@@ -5662,12 +5771,7 @@ static bool mysql_change_partitions(ALTER_PARTITION_PARAM_TYPE *lpt)
build_table_filename(path, sizeof(path) - 1, lpt->db, lpt->table_name, "", 0);
- /* First lock the original tables */
- if (file->ha_external_lock(thd, F_WRLCK))
- DBUG_RETURN(TRUE);
-
- /* Disable transactions for all new tables */
- if (mysql_trans_prepare_alter_copy_data(thd))
+ if(mysql_trans_prepare_alter_copy_data(thd))
DBUG_RETURN(TRUE);
/* TODO: test if bulk_insert would increase the performance */
@@ -5682,10 +5786,7 @@ static bool mysql_change_partitions(ALTER_PARTITION_PARAM_TYPE *lpt)
if (mysql_trans_commit_alter_copy_data(thd))
error= 1; /* The error has been reported */
- if (file->ha_external_lock(thd, F_UNLCK))
- error= 1;
-
- DBUG_RETURN(test(error));
+ DBUG_RETURN(MY_TEST(error));
}
@@ -5755,6 +5856,11 @@ static bool mysql_drop_partitions(ALTER_PARTITION_PARAM_TYPE *lpt)
int error;
DBUG_ENTER("mysql_drop_partitions");
+ DBUG_ASSERT(lpt->thd->mdl_context.is_lock_owner(MDL_key::TABLE,
+ lpt->table->s->db.str,
+ lpt->table->s->table_name.str,
+ MDL_EXCLUSIVE));
+
build_table_filename(path, sizeof(path) - 1, lpt->db, lpt->table_name, "", 0);
if ((error= lpt->table->file->ha_drop_partitions(path)))
{
@@ -6336,7 +6442,8 @@ static bool write_log_final_change_partition(ALTER_PARTITION_PARAM_TYPE *lpt)
if (write_log_changed_partitions(lpt, &next_entry, (const char*)path))
goto error;
if (write_log_dropped_partitions(lpt, &next_entry, (const char*)path,
- lpt->alter_info->flags & ALTER_REORGANIZE_PARTITION))
+ lpt->alter_info->flags &
+ Alter_info::ALTER_REORGANIZE_PARTITION))
goto error;
if (write_log_replace_delete_frm(lpt, next_entry, shadow_path, path, TRUE))
goto error;
@@ -6433,47 +6540,54 @@ static void alter_partition_lock_handling(ALTER_PARTITION_PARAM_TYPE *lpt)
{
THD *thd= lpt->thd;
- if (lpt->old_table)
- close_all_tables_for_name(thd, lpt->old_table->s, HA_EXTRA_NOT_USED);
if (lpt->table)
{
/*
- Only remove the intermediate table object and its share object,
- do not remove the .frm file, since it is the original one.
+ Remove all instances of the table and its locks and other resources.
*/
- close_temporary(lpt->table, 1, 0);
+ close_all_tables_for_name(thd, lpt->table->s, HA_EXTRA_NOT_USED, NULL);
}
lpt->table= 0;
- lpt->old_table= 0;
lpt->table_list->table= 0;
- if (thd->locked_tables_list.reopen_tables(thd))
- sql_print_warning("We failed to reacquire LOCKs in ALTER TABLE");
+ if (thd->locked_tables_mode)
+ {
+ Diagnostics_area *stmt_da= NULL;
+ Diagnostics_area tmp_stmt_da(true);
+
+ if (thd->is_error())
+ {
+ /* reopen might fail if we have a previous error, use a temporary da. */
+ stmt_da= thd->get_stmt_da();
+ thd->set_stmt_da(&tmp_stmt_da);
+ }
+
+ if (thd->locked_tables_list.reopen_tables(thd))
+ sql_print_warning("We failed to reacquire LOCKs in ALTER TABLE");
+
+ if (stmt_da)
+ thd->set_stmt_da(stmt_da);
+ }
}
-/*
- Unlock and close table before renaming and dropping partitions
- SYNOPSIS
- alter_close_tables()
- lpt Struct carrying parameters
- close_old Close original table too
- RETURN VALUES
- 0
+/**
+ Unlock and close table before renaming and dropping partitions.
+
+ @param lpt Struct carrying parameters
+
+ @return Always 0.
*/
-static int alter_close_tables(ALTER_PARTITION_PARAM_TYPE *lpt, bool close_old)
+static int alter_close_table(ALTER_PARTITION_PARAM_TYPE *lpt)
{
- DBUG_ENTER("alter_close_tables");
+ DBUG_ENTER("alter_close_table");
+
if (lpt->table->db_stat)
{
+ mysql_lock_remove(lpt->thd, lpt->thd->lock, lpt->table);
lpt->table->file->ha_close();
lpt->table->db_stat= 0; // Mark file closed
}
- if (close_old && lpt->old_table)
- {
- close_all_tables_for_name(lpt->thd, lpt->old_table->s, HA_EXTRA_NOT_USED);
- lpt->old_table= 0;
- }
DBUG_RETURN(0);
}
@@ -6495,23 +6609,54 @@ void handle_alter_part_error(ALTER_PARTITION_PARAM_TYPE *lpt,
bool close_table)
{
partition_info *part_info= lpt->part_info;
+ THD *thd= lpt->thd;
+ TABLE *table= lpt->table;
DBUG_ENTER("handle_alter_part_error");
+ DBUG_ASSERT(table->m_needs_reopen);
if (close_table)
{
/*
- Since the error handling (ddl_log) needs to drop newly created
- partitions they must be closed first to not issue errors.
- But we still need some information from the part_info object,
- so we clone it first to have a copy.
+ All instances of this table needs to be closed.
+ Better to do that here, than leave the cleaning up to others.
+ Aquire EXCLUSIVE mdl lock if not already aquired.
+ */
+ if (!thd->mdl_context.is_lock_owner(MDL_key::TABLE, lpt->db,
+ lpt->table_name,
+ MDL_EXCLUSIVE))
+ {
+ if (wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN))
+ {
+ /* At least remove this instance on failure */
+ goto err_exclusive_lock;
+ }
+ }
+ /* Ensure the share is destroyed and reopened. */
+ part_info= lpt->part_info->get_clone();
+ close_all_tables_for_name(thd, table->s, HA_EXTRA_NOT_USED, NULL);
+ }
+ else
+ {
+err_exclusive_lock:
+ /*
+ Temporarily remove it from the locked table list, so that it will get
+ reopened.
*/
+ thd->locked_tables_list.unlink_from_list(thd,
+ table->pos_in_locked_tables,
+ false);
+ /*
+ Make sure that the table is unlocked, closed and removed from
+ the table cache.
+ */
+ mysql_lock_remove(thd, thd->lock, table);
part_info= lpt->part_info->get_clone();
- alter_close_tables(lpt, action_completed);
+ close_thread_table(thd, &thd->open_tables);
+ lpt->table_list->table= NULL;
}
if (part_info->first_log_entry &&
- execute_ddl_log_entry(lpt->thd,
- part_info->first_log_entry->entry_pos))
+ execute_ddl_log_entry(thd, part_info->first_log_entry->entry_pos))
{
/*
We couldn't recover from error, most likely manual interaction
@@ -6524,14 +6669,14 @@ void handle_alter_part_error(ALTER_PARTITION_PARAM_TYPE *lpt,
if (drop_partition)
{
/* Table is still ok, but we left a shadow frm file behind. */
- push_warning_printf(lpt->thd, MYSQL_ERROR::WARN_LEVEL_WARN, 1,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, 1,
"%s %s",
"Operation was unsuccessful, table is still intact,",
"but it is possible that a shadow frm file was left behind");
}
else
{
- push_warning_printf(lpt->thd, MYSQL_ERROR::WARN_LEVEL_WARN, 1,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, 1,
"%s %s %s %s",
"Operation was unsuccessful, table is still intact,",
"but it is possible that a shadow frm file was left behind.",
@@ -6547,7 +6692,7 @@ void handle_alter_part_error(ALTER_PARTITION_PARAM_TYPE *lpt,
Failed during install of shadow frm file, table isn't intact
and dropped partitions are still there
*/
- push_warning_printf(lpt->thd, MYSQL_ERROR::WARN_LEVEL_WARN, 1,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, 1,
"%s %s %s",
"Failed during alter of partitions, table is no longer intact.",
"The frm file is in an unknown state, and a backup",
@@ -6561,7 +6706,7 @@ void handle_alter_part_error(ALTER_PARTITION_PARAM_TYPE *lpt,
ask the user to perform the action manually. We remove the log
records and ask the user to perform the action manually.
*/
- push_warning_printf(lpt->thd, MYSQL_ERROR::WARN_LEVEL_WARN, 1,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, 1,
"%s %s",
"Failed during drop of partitions, table is intact.",
"Manual drop of remaining partitions is required");
@@ -6573,7 +6718,7 @@ void handle_alter_part_error(ALTER_PARTITION_PARAM_TYPE *lpt,
certainly in a very bad state so we give user warning and disable
the table by writing an ancient frm version into it.
*/
- push_warning_printf(lpt->thd, MYSQL_ERROR::WARN_LEVEL_WARN, 1,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, 1,
"%s %s %s",
"Failed during renaming of partitions. We are now in a position",
"where table is not reusable",
@@ -6602,11 +6747,31 @@ void handle_alter_part_error(ALTER_PARTITION_PARAM_TYPE *lpt,
even though we reported an error the operation was successfully
completed.
*/
- push_warning_printf(lpt->thd, MYSQL_ERROR::WARN_LEVEL_WARN, 1,"%s %s",
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, 1,"%s %s",
"Operation was successfully completed by failure handling,",
"after failure of normal operation");
}
}
+
+ if (thd->locked_tables_mode)
+ {
+ Diagnostics_area *stmt_da= NULL;
+ Diagnostics_area tmp_stmt_da(true);
+
+ if (thd->is_error())
+ {
+ /* reopen might fail if we have a previous error, use a temporary da. */
+ stmt_da= thd->get_stmt_da();
+ thd->set_stmt_da(&tmp_stmt_da);
+ }
+
+ if (thd->locked_tables_list.reopen_tables(thd))
+ sql_print_warning("We failed to reacquire LOCKs in ALTER TABLE");
+
+ if (stmt_da)
+ thd->set_stmt_da(stmt_da);
+ }
+
DBUG_VOID_RETURN;
}
@@ -6623,7 +6788,7 @@ static void downgrade_mdl_if_lock_tables_mode(THD *thd, MDL_ticket *ticket,
enum_mdl_type type)
{
if (thd->locked_tables_mode)
- ticket->downgrade_exclusive_lock(type);
+ ticket->downgrade_lock(type);
}
@@ -6632,13 +6797,12 @@ static void downgrade_mdl_if_lock_tables_mode(THD *thd, MDL_ticket *ticket,
previously prepared.
@param thd Thread object
- @param table Original table object
+ @param table Original table object with new part_info
@param alter_info ALTER TABLE info
@param create_info Create info for CREATE TABLE
@param table_list List of the table involved
@param db Database name of new table
@param table_name Table name of new table
- @param fast_alter_table Prepared table object
@return Operation status
@retval TRUE Error
@@ -6654,8 +6818,7 @@ uint fast_alter_partition_table(THD *thd, TABLE *table,
HA_CREATE_INFO *create_info,
TABLE_LIST *table_list,
char *db,
- const char *table_name,
- TABLE *fast_alter_table)
+ const char *table_name)
{
/* Set-up struct used to write frm files */
partition_info *part_info;
@@ -6665,10 +6828,10 @@ uint fast_alter_partition_table(THD *thd, TABLE *table,
bool close_table_on_failure= FALSE;
bool frm_install= FALSE;
MDL_ticket *mdl_ticket= table->mdl_ticket;
- DBUG_ASSERT(fast_alter_table);
DBUG_ENTER("fast_alter_partition_table");
+ DBUG_ASSERT(table->m_needs_reopen);
- part_info= fast_alter_table->part_info;
+ part_info= table->part_info;
lpt->thd= thd;
lpt->table_list= table_list;
lpt->part_info= part_info;
@@ -6677,8 +6840,7 @@ uint fast_alter_partition_table(THD *thd, TABLE *table,
lpt->db_options= create_info->table_options;
if (create_info->row_type == ROW_TYPE_DYNAMIC)
lpt->db_options|= HA_OPTION_PACK_RECORD;
- lpt->table= fast_alter_table;
- lpt->old_table= table;
+ lpt->table= table;
lpt->key_info_buffer= 0;
lpt->key_count= 0;
lpt->db= db;
@@ -6688,9 +6850,6 @@ uint fast_alter_partition_table(THD *thd, TABLE *table,
lpt->pack_frm_data= NULL;
lpt->pack_frm_len= 0;
- /* Never update timestamp columns when alter */
- lpt->table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
-
if (table->file->alter_table_flags(alter_info->flags) &
HA_PARTITION_ONE_PHASE)
{
@@ -6739,7 +6898,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_info::ALTER_DROP_PARTITION)
{
/*
Now after all checks and setting state on dropped partitions we can
@@ -6774,9 +6933,9 @@ uint fast_alter_partition_table(THD *thd, TABLE *table,
3) Write the ddl log to ensure that the operation is completed
even in the presence of a MySQL Server crash (the log is executed
before any other threads are started, so there are no locking issues).
- 4) Close all tables that have already been opened but didn't stumble on
+ 4) Close the table that have already been opened but didn't stumble on
the abort locked previously. This is done as part of the
- alter_close_tables call.
+ alter_close_table call.
5) Write the bin log
Unfortunately the writing of the binlog is not synchronised with
other logging activities. So no matter in which order the binlog
@@ -6812,7 +6971,7 @@ uint fast_alter_partition_table(THD *thd, TABLE *table,
(action_completed= TRUE, FALSE) ||
ERROR_INJECT_CRASH("crash_drop_partition_4") ||
ERROR_INJECT_ERROR("fail_drop_partition_4") ||
- alter_close_tables(lpt, action_completed) ||
+ alter_close_table(lpt) ||
(close_table_on_failure= FALSE, FALSE) ||
ERROR_INJECT_CRASH("crash_drop_partition_5") ||
ERROR_INJECT_ERROR("fail_drop_partition_5") ||
@@ -6839,7 +6998,7 @@ uint fast_alter_partition_table(THD *thd, TABLE *table,
goto err;
}
}
- else if ((alter_info->flags & ALTER_ADD_PARTITION) &&
+ else if ((alter_info->flags & Alter_info::ALTER_ADD_PARTITION) &&
(part_info->part_type == RANGE_PARTITION ||
part_info->part_type == LIST_PARTITION))
{
@@ -6889,7 +7048,7 @@ uint fast_alter_partition_table(THD *thd, TABLE *table,
ERROR_INJECT_CRASH("crash_add_partition_5") ||
ERROR_INJECT_ERROR("fail_add_partition_5") ||
(close_table_on_failure= FALSE, FALSE) ||
- alter_close_tables(lpt, action_completed) ||
+ alter_close_table(lpt) ||
ERROR_INJECT_CRASH("crash_add_partition_6") ||
ERROR_INJECT_ERROR("fail_add_partition_6") ||
((!thd->lex->no_write_to_binlog) &&
@@ -6949,27 +7108,27 @@ uint fast_alter_partition_table(THD *thd, TABLE *table,
use a lower lock level. This can be handled inside store_lock in the
respective handler.
- 0) Write an entry that removes the shadow frm file if crash occurs
- 1) Write the shadow frm file of new partitioning
+ 0) Write an entry that removes the shadow frm file if crash occurs.
+ 1) Write the shadow frm file of new partitioning.
2) Log such that temporary partitions added in change phase are
- removed in a crash situation
- 3) Add the new partitions
- Copy from the reorganised partitions to the new partitions
+ removed in a crash situation.
+ 3) Add the new partitions.
+ Copy from the reorganised partitions to the new partitions.
4) Get an exclusive metadata lock on the table (waits for all active
transactions using this table). This ensures that we
can release all other locks on the table and since no one can open
the table, there can be no new threads accessing the table. They
will be hanging on this exclusive lock.
- 5) Log that operation is completed and log all complete actions
- needed to complete operation from here
- 6) Write bin log
- 7) Close all instances of the table and remove them from the table cache.
- 8) Prepare handlers for rename and delete of partitions
+ 5) Close the table.
+ 6) Log that operation is completed and log all complete actions
+ needed to complete operation from here.
+ 7) Write bin log.
+ 8) Prepare handlers for rename and delete of partitions.
9) Rename and drop the reorged partitions such that they are no
longer used and rename those added to their real new names.
- 10) Install the shadow frm file
- 11) Reopen the table if under lock tables
- 12) Complete query
+ 10) Install the shadow frm file.
+ 11) Reopen the table if under lock tables.
+ 12) Complete query.
*/
if (write_log_drop_shadow_frm(lpt) ||
ERROR_INJECT_CRASH("crash_change_partition_1") ||
@@ -6987,22 +7146,22 @@ uint fast_alter_partition_table(THD *thd, TABLE *table,
wait_while_table_is_used(thd, table, HA_EXTRA_NOT_USED) ||
ERROR_INJECT_CRASH("crash_change_partition_5") ||
ERROR_INJECT_ERROR("fail_change_partition_5") ||
- write_log_final_change_partition(lpt) ||
- (action_completed= TRUE, FALSE) ||
+ alter_close_table(lpt) ||
+ (close_table_on_failure= FALSE, FALSE) ||
ERROR_INJECT_CRASH("crash_change_partition_6") ||
ERROR_INJECT_ERROR("fail_change_partition_6") ||
+ write_log_final_change_partition(lpt) ||
+ (action_completed= TRUE, FALSE) ||
+ ERROR_INJECT_CRASH("crash_change_partition_7") ||
+ ERROR_INJECT_ERROR("fail_change_partition_7") ||
((!thd->lex->no_write_to_binlog) &&
(write_bin_log(thd, FALSE,
thd->query(), thd->query_length()), FALSE)) ||
- ERROR_INJECT_CRASH("crash_change_partition_7") ||
- ERROR_INJECT_ERROR("fail_change_partition_7") ||
+ ERROR_INJECT_CRASH("crash_change_partition_8") ||
+ ERROR_INJECT_ERROR("fail_change_partition_8") ||
((frm_install= TRUE), FALSE) ||
mysql_write_frm(lpt, WFRM_INSTALL_SHADOW) ||
(frm_install= FALSE, FALSE) ||
- ERROR_INJECT_CRASH("crash_change_partition_8") ||
- ERROR_INJECT_ERROR("fail_change_partition_8") ||
- alter_close_tables(lpt, action_completed) ||
- (close_table_on_failure= FALSE, FALSE) ||
ERROR_INJECT_CRASH("crash_change_partition_9") ||
ERROR_INJECT_ERROR("fail_change_partition_9") ||
mysql_drop_partitions(lpt) ||
@@ -7028,22 +7187,6 @@ uint fast_alter_partition_table(THD *thd, TABLE *table,
*/
DBUG_RETURN(fast_end_partition(thd, lpt->copied, lpt->deleted, table_list));
err:
- if (action_completed)
- {
- /*
- Although error occurred, the action was forced to retry for completion.
- Therefore we must close+reopen all instances of the table.
- */
- (void) alter_partition_lock_handling(lpt);
- }
- else
- {
- /*
- The failed action was reverted, leave the original table as is and
- close/destroy the intermediate table object and its share.
- */
- close_temporary(lpt->table, 1, 0);
- }
downgrade_mdl_if_lock_tables_mode(thd, mdl_ticket, MDL_SHARED_NO_READ_WRITE);
DBUG_RETURN(TRUE);
}
@@ -7106,7 +7249,7 @@ void set_key_field_ptr(KEY *key_info, const uchar *new_buf,
const uchar *old_buf)
{
KEY_PART_INFO *key_part= key_info->key_part;
- uint key_parts= key_info->key_parts;
+ uint key_parts= key_info->user_defined_key_parts;
uint i= 0;
my_ptrdiff_t diff= (new_buf - old_buf);
DBUG_ENTER("set_key_field_ptr");
@@ -7142,20 +7285,19 @@ void mem_alloc_error(size_t size)
}
#ifdef WITH_PARTITION_STORAGE_ENGINE
-/*
- Return comma-separated list of used partitions in the provided given string
+/**
+ Return comma-separated list of used partitions in the provided given string.
- SYNOPSIS
- make_used_partitions_str()
- part_info IN Partitioning info
- parts_str OUT The string to fill
+ @param part_info Partitioning info
+ @param[out] parts The resulting list of string to fill
- DESCRIPTION
- Generate a list of used partitions (from bits in part_info->used_partitions
- bitmap), asd store it into the provided String object.
+ Generate a list of used partitions (from bits in part_info->read_partitions
+ bitmap), and store it into the provided String object.
- NOTE
+ @note
The produced string must not be longer then MAX_PARTITIONS * (1 + FN_LEN).
+ In case of UPDATE, only the partitions read is given, not the partitions
+ that was written or locked.
*/
void make_used_partitions_str(partition_info *part_info, String *parts_str)
@@ -7173,7 +7315,7 @@ void make_used_partitions_str(partition_info *part_info, String *parts_str)
List_iterator<partition_element> it2(head_pe->subpartitions);
while ((pe= it2++))
{
- if (bitmap_is_set(&part_info->used_partitions, partition_id))
+ if (bitmap_is_set(&part_info->read_partitions, partition_id))
{
if (parts_str->length())
parts_str->append(',');
@@ -7193,7 +7335,7 @@ void make_used_partitions_str(partition_info *part_info, String *parts_str)
{
while ((pe= it++))
{
- if (bitmap_is_set(&part_info->used_partitions, partition_id))
+ if (bitmap_is_set(&part_info->read_partitions, partition_id))
{
if (parts_str->length())
parts_str->append(',');
@@ -7751,7 +7893,7 @@ int get_part_iter_for_interval_via_mapping(partition_info *part_info,
index-in-ordered-array-of-list-constants (for LIST) space.
*/
store_key_image_to_rec(field, min_value, field_len);
- bool include_endp= !test(flags & NEAR_MIN);
+ bool include_endp= !MY_TEST(flags & NEAR_MIN);
part_iter->part_nums.start= get_endpoint(part_info, 1, include_endp);
if (!can_match_multiple_values && part_info->part_expr->null_value)
{
@@ -7786,7 +7928,7 @@ int get_part_iter_for_interval_via_mapping(partition_info *part_info,
else
{
store_key_image_to_rec(field, max_value, field_len);
- bool include_endp= !test(flags & NEAR_MAX);
+ bool include_endp= !MY_TEST(flags & NEAR_MAX);
part_iter->part_nums.end= get_endpoint(part_info, 0, include_endp);
if (check_zero_dates &&
!zero_in_start_date &&
@@ -7953,8 +8095,8 @@ int get_part_iter_for_interval_via_walking(partition_info *part_info,
if ((ulonglong)b - (ulonglong)a == ~0ULL)
DBUG_RETURN(-1);
- a += test(flags & NEAR_MIN);
- b += test(!(flags & NEAR_MAX));
+ a+= MY_TEST(flags & NEAR_MIN);
+ b+= MY_TEST(!(flags & NEAR_MAX));
ulonglong n_values= b - a;
/*
@@ -8090,8 +8232,7 @@ static uint32 get_next_partition_via_walking(PARTITION_ITERATOR *part_iter)
while (part_iter->field_vals.cur != part_iter->field_vals.end)
{
longlong dummy;
- field->store(part_iter->field_vals.cur++,
- ((Field_num*)field)->unsigned_flag);
+ field->store(part_iter->field_vals.cur++, field->flags & UNSIGNED_FLAG);
if ((part_iter->part_info->is_sub_partitioned() &&
!part_iter->part_info->get_part_partition_id(part_iter->part_info,
&part_id, &dummy)) ||
@@ -8115,12 +8256,11 @@ static uint32 get_next_subpartition_via_walking(PARTITION_ITERATOR *part_iter)
part_iter->field_vals.cur= part_iter->field_vals.start;
return NOT_A_PARTITION_ID;
}
- field->store(part_iter->field_vals.cur++, FALSE);
+ field->store(part_iter->field_vals.cur++, field->flags & UNSIGNED_FLAG);
if (part_iter->part_info->get_subpartition_id(part_iter->part_info,
&res))
return NOT_A_PARTITION_ID;
return res;
-
}
diff --git a/sql/sql_partition.h b/sql/sql_partition.h
index 951d58a655b..5da132661c9 100644
--- a/sql/sql_partition.h
+++ b/sql/sql_partition.h
@@ -24,6 +24,7 @@
#include "table.h" /* TABLE_LIST */
class Alter_info;
+class Alter_table_ctx;
class Field;
class String;
class handler;
@@ -31,7 +32,6 @@ class partition_info;
struct TABLE;
struct TABLE_LIST;
typedef struct st_bitmap MY_BITMAP;
-typedef struct st_ha_create_information HA_CREATE_INFO;
typedef struct st_key KEY;
typedef struct st_key_range key_range;
@@ -54,7 +54,6 @@ typedef struct st_lock_param_type
HA_CREATE_INFO *create_info;
Alter_info *alter_info;
TABLE *table;
- TABLE *old_table;
KEY *key_info_buffer;
const char *db;
const char *table_name;
@@ -76,7 +75,7 @@ typedef struct {
} part_id_range;
struct st_partition_iter;
-#define NOT_A_PARTITION_ID ((uint32)-1)
+#define NOT_A_PARTITION_ID UINT_MAX32
bool is_partition_in_list(char *part_name, List<char> list_part_names);
char *are_partitions_in_table(partition_info *new_part_info,
@@ -126,6 +125,7 @@ bool check_part_func_fields(Field **ptr, bool ok_with_charsets);
bool field_is_partition_charset(Field *field);
Item* convert_charset_partition_constant(Item *item, CHARSET_INFO *cs);
void mem_alloc_error(size_t size);
+void truncate_partition_filename(char *path);
/*
A "Get next" function for partition iterator.
@@ -251,24 +251,24 @@ uint fast_alter_partition_table(THD *thd, TABLE *table,
HA_CREATE_INFO *create_info,
TABLE_LIST *table_list,
char *db,
- const char *table_name,
- TABLE *fast_alter_table);
+ const char *table_name);
bool set_part_state(Alter_info *alter_info, partition_info *tab_part_info,
enum partition_state part_state);
uint prep_alter_part_table(THD *thd, TABLE *table, Alter_info *alter_info,
HA_CREATE_INFO *create_info,
- handlerton *old_db_type,
+ Alter_table_ctx *alter_ctx,
bool *partition_changed,
- char *db,
- const char *table_name,
- const char *path,
- TABLE **fast_alter_table);
+ bool *fast_alter_table);
char *generate_partition_syntax(partition_info *part_info,
uint *buf_length, bool use_sql_alloc,
bool show_partition_options,
HA_CREATE_INFO *create_info,
Alter_info *alter_info,
const char *current_comment_start);
+bool verify_data_with_partition(TABLE *table, TABLE *part_table,
+ uint32 part_id);
+bool compare_partition_options(HA_CREATE_INFO *table_create_info,
+ partition_element *part_elem);
bool partition_key_modified(TABLE *table, const MY_BITMAP *fields);
#else
#define partition_key_modified(X,Y) 0
diff --git a/sql/sql_partition_admin.cc b/sql/sql_partition_admin.cc
index 809a2b651d6..f520281135a 100644
--- a/sql/sql_partition_admin.cc
+++ b/sql/sql_partition_admin.cc
@@ -14,8 +14,15 @@
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
#include "sql_parse.h" // check_one_table_access
+ // check_merge_table_access
+ // check_one_table_access
#include "sql_table.h" // mysql_alter_table, etc.
-#include "sql_lex.h" // Sql_statement
+#include "sql_cmd.h" // Sql_cmd
+#include "sql_alter.h" // Sql_cmd_alter_table
+#include "sql_partition.h" // struct partition_info, etc.
+#include "debug_sync.h" // DEBUG_SYNC
+#include "sql_truncate.h" // mysql_truncate_table,
+ // Sql_cmd_truncate_table
#include "sql_admin.h" // Analyze/Check/.._table_statement
#include "sql_partition_admin.h" // Alter_table_*_partition
#ifdef WITH_PARTITION_STORAGE_ENGINE
@@ -25,9 +32,9 @@
#ifndef WITH_PARTITION_STORAGE_ENGINE
-bool Partition_statement_unsupported::execute(THD *)
+bool Sql_cmd_partition_unsupported::execute(THD *)
{
- DBUG_ENTER("Partition_statement_unsupported::execute");
+ DBUG_ENTER("Sql_cmd_partition_unsupported::execute");
/* error, partitioning support not compiled in... */
my_error(ER_FEATURE_DISABLED, MYF(0), "partitioning",
"--with-plugin-partition");
@@ -36,41 +43,675 @@ bool Partition_statement_unsupported::execute(THD *)
#else
-bool Alter_table_analyze_partition_statement::execute(THD *thd)
+bool Sql_cmd_alter_table_exchange_partition::execute(THD *thd)
+{
+ /* Moved from mysql_execute_command */
+ LEX *lex= thd->lex;
+ /* first SELECT_LEX (have special meaning for many of non-SELECTcommands) */
+ SELECT_LEX *select_lex= &lex->select_lex;
+ /* first table of first SELECT_LEX */
+ TABLE_LIST *first_table= (TABLE_LIST*) select_lex->table_list.first;
+ /*
+ Code in mysql_alter_table() may modify its HA_CREATE_INFO argument,
+ so we have to use a copy of this structure to make execution
+ prepared statement- safe. A shallow copy is enough as no memory
+ referenced from this structure will be modified.
+ @todo move these into constructor...
+ */
+ HA_CREATE_INFO create_info(lex->create_info);
+ Alter_info alter_info(lex->alter_info, thd->mem_root);
+ ulong priv_needed= ALTER_ACL | DROP_ACL | INSERT_ACL | CREATE_ACL;
+
+ DBUG_ENTER("Sql_cmd_alter_table_exchange_partition::execute");
+
+ if (thd->is_fatal_error) /* out of memory creating a copy of alter_info */
+ DBUG_RETURN(TRUE);
+
+ /* Must be set in the parser */
+ DBUG_ASSERT(select_lex->db);
+ /* also check the table to be exchanged with the partition */
+ DBUG_ASSERT(alter_info.flags & Alter_info::ALTER_EXCHANGE_PARTITION);
+
+ if (check_access(thd, priv_needed, first_table->db,
+ &first_table->grant.privilege,
+ &first_table->grant.m_internal,
+ 0, 0) ||
+ check_access(thd, priv_needed, first_table->next_local->db,
+ &first_table->next_local->grant.privilege,
+ &first_table->next_local->grant.m_internal,
+ 0, 0))
+ DBUG_RETURN(TRUE);
+
+ if (check_grant(thd, priv_needed, first_table, FALSE, UINT_MAX, FALSE))
+ DBUG_RETURN(TRUE);
+
+ /* Not allowed with EXCHANGE PARTITION */
+ DBUG_ASSERT(!create_info.data_file_name && !create_info.index_file_name);
+
+ thd->enable_slow_log= opt_log_slow_admin_statements;
+ DBUG_RETURN(exchange_partition(thd, first_table, &alter_info));
+}
+
+
+/**
+ @brief Checks that the tables will be able to be used for EXCHANGE PARTITION.
+ @param table Non partitioned table.
+ @param part_table Partitioned table.
+
+ @retval FALSE if OK, otherwise error is reported and TRUE is returned.
+*/
+static bool check_exchange_partition(TABLE *table, TABLE *part_table)
+{
+ DBUG_ENTER("check_exchange_partition");
+
+ /* Both tables must exist */
+ if (!part_table || !table)
+ {
+ my_error(ER_CHECK_NO_SUCH_TABLE, MYF(0));
+ DBUG_RETURN(TRUE);
+ }
+
+ /* The first table must be partitioned, and the second must not */
+ if (!part_table->part_info)
+ {
+ my_error(ER_PARTITION_MGMT_ON_NONPARTITIONED, MYF(0));
+ DBUG_RETURN(TRUE);
+ }
+ if (table->part_info)
+ {
+ my_error(ER_PARTITION_EXCHANGE_PART_TABLE, MYF(0),
+ table->s->table_name.str);
+ DBUG_RETURN(TRUE);
+ }
+
+ if (part_table->file->ht != partition_hton)
+ {
+ /*
+ Only allowed on partitioned tables throught the generic ha_partition
+ handler, i.e not yet for native partitioning (NDB).
+ */
+ my_error(ER_PARTITION_MGMT_ON_NONPARTITIONED, MYF(0));
+ DBUG_RETURN(TRUE);
+ }
+
+ if (table->file->ht != part_table->part_info->default_engine_type)
+ {
+ my_error(ER_MIX_HANDLER_ERROR, MYF(0));
+ DBUG_RETURN(TRUE);
+ }
+
+ /* Verify that table is not tmp table, partitioned tables cannot be tmp. */
+ if (table->s->tmp_table != NO_TMP_TABLE)
+ {
+ my_error(ER_PARTITION_EXCHANGE_TEMP_TABLE, MYF(0),
+ table->s->table_name.str);
+ DBUG_RETURN(TRUE);
+ }
+
+ /* The table cannot have foreign keys constraints or be referenced */
+ if(!table->file->can_switch_engines())
+ {
+ my_error(ER_PARTITION_EXCHANGE_FOREIGN_KEY, MYF(0),
+ table->s->table_name.str);
+ DBUG_RETURN(TRUE);
+ }
+ DBUG_RETURN(FALSE);
+}
+
+
+/**
+ @brief Compare table structure/options between a non partitioned table
+ and a specific partition of a partitioned table.
+
+ @param thd Thread object.
+ @param table Non partitioned table.
+ @param part_table Partitioned table.
+ @param part_elem Partition element to use for partition specific compare.
+*/
+static bool compare_table_with_partition(THD *thd, TABLE *table,
+ TABLE *part_table,
+ partition_element *part_elem)
+{
+ HA_CREATE_INFO table_create_info, part_create_info;
+ Alter_info part_alter_info;
+ Alter_table_ctx part_alter_ctx; // Not used
+ DBUG_ENTER("compare_table_with_partition");
+
+ bool metadata_equal= false;
+ memset(&part_create_info, 0, sizeof(HA_CREATE_INFO));
+ memset(&table_create_info, 0, sizeof(HA_CREATE_INFO));
+
+ update_create_info_from_table(&table_create_info, table);
+ /* get the current auto_increment value */
+ table->file->update_create_info(&table_create_info);
+ /* mark all columns used, since they are used when preparing the new table */
+ part_table->use_all_columns();
+ table->use_all_columns();
+ if (mysql_prepare_alter_table(thd, part_table, &part_create_info,
+ &part_alter_info, &part_alter_ctx))
+ {
+ my_error(ER_TABLES_DIFFERENT_METADATA, MYF(0));
+ DBUG_RETURN(TRUE);
+ }
+ /* db_type is not set in prepare_alter_table */
+ part_create_info.db_type= part_table->part_info->default_engine_type;
+ /*
+ Since we exchange the partition with the table, allow exchanging
+ auto_increment value as well.
+ */
+ part_create_info.auto_increment_value=
+ table_create_info.auto_increment_value;
+
+ /* Check compatible row_types and set create_info accordingly. */
+ {
+ enum row_type part_row_type= part_table->file->get_row_type();
+ enum row_type table_row_type= table->file->get_row_type();
+ if (part_row_type != table_row_type)
+ {
+ my_error(ER_PARTITION_EXCHANGE_DIFFERENT_OPTION, MYF(0),
+ "ROW_FORMAT");
+ DBUG_RETURN(true);
+ }
+ part_create_info.row_type= table->s->row_type;
+ }
+
+ /*
+ NOTE: ha_blackhole does not support check_if_compatible_data,
+ so this always fail for blackhole tables.
+ ha_myisam compares pointers to verify that DATA/INDEX DIRECTORY is
+ the same, so any table using data/index_file_name will fail.
+ */
+ if (mysql_compare_tables(table, &part_alter_info, &part_create_info,
+ &metadata_equal))
+ {
+ my_error(ER_TABLES_DIFFERENT_METADATA, MYF(0));
+ DBUG_RETURN(TRUE);
+ }
+
+ DEBUG_SYNC(thd, "swap_partition_after_compare_tables");
+ if (!metadata_equal)
+ {
+ my_error(ER_TABLES_DIFFERENT_METADATA, MYF(0));
+ DBUG_RETURN(TRUE);
+ }
+ DBUG_ASSERT(table->s->db_create_options ==
+ part_table->s->db_create_options);
+ DBUG_ASSERT(table->s->db_options_in_use ==
+ part_table->s->db_options_in_use);
+
+ if (table_create_info.avg_row_length != part_create_info.avg_row_length)
+ {
+ my_error(ER_PARTITION_EXCHANGE_DIFFERENT_OPTION, MYF(0),
+ "AVG_ROW_LENGTH");
+ DBUG_RETURN(TRUE);
+ }
+
+ if (table_create_info.table_options != part_create_info.table_options)
+ {
+ my_error(ER_PARTITION_EXCHANGE_DIFFERENT_OPTION, MYF(0),
+ "TABLE OPTION");
+ DBUG_RETURN(TRUE);
+ }
+
+ if (table->s->table_charset != part_table->s->table_charset)
+ {
+ my_error(ER_PARTITION_EXCHANGE_DIFFERENT_OPTION, MYF(0),
+ "CHARACTER SET");
+ DBUG_RETURN(TRUE);
+ }
+
+ /*
+ NOTE: We do not support update of frm-file, i.e. change
+ max/min_rows, data/index_file_name etc.
+ The workaround is to use REORGANIZE PARTITION to rewrite
+ the frm file and then use EXCHANGE PARTITION when they are the same.
+ */
+ if (compare_partition_options(&table_create_info, part_elem))
+ DBUG_RETURN(TRUE);
+
+ DBUG_RETURN(FALSE);
+}
+
+
+/**
+ @brief Exchange partition/table with ddl log.
+
+ @details How to handle a crash in the middle of the rename (break on error):
+ 1) register in ddl_log that we are going to exchange swap_table with part.
+ 2) do the first rename (swap_table -> tmp-name) and sync the ddl_log.
+ 3) do the second rename (part -> swap_table) and sync the ddl_log.
+ 4) do the last rename (tmp-name -> part).
+ 5) mark the entry done.
+
+ Recover by:
+ 5) is done, All completed. Nothing to recover.
+ 4) is done see 3). (No mark or sync in the ddl_log...)
+ 3) is done -> try rename part -> tmp-name (ignore failure) goto 2).
+ 2) is done -> try rename swap_table -> part (ignore failure) goto 1).
+ 1) is done -> try rename tmp-name -> swap_table (ignore failure).
+ before 1) Nothing to recover...
+
+ @param thd Thread handle
+ @param name name of table/partition 1 (to be exchanged with 2)
+ @param from_name name of table/partition 2 (to be exchanged with 1)
+ @param tmp_name temporary name to use while exchaning
+ @param ht handlerton of the table/partitions
+
+ @return Operation status
+ @retval TRUE Error
+ @retval FALSE Success
+
+ @note ha_heap always succeeds in rename (since it is created upon usage).
+ This is OK when to recover from a crash since all heap are empty and the
+ recover is done early in the startup of the server (right before
+ read_init_file which can populate the tables).
+
+ And if no crash we can trust the syncs in the ddl_log.
+
+ What about if the rename is put into a background thread? That will cause
+ corruption and is avoided by the exlusive metadata lock.
+*/
+static bool exchange_name_with_ddl_log(THD *thd,
+ const char *name,
+ const char *from_name,
+ const char *tmp_name,
+ handlerton *ht)
+{
+ DDL_LOG_ENTRY exchange_entry;
+ DDL_LOG_MEMORY_ENTRY *log_entry= NULL;
+ DDL_LOG_MEMORY_ENTRY *exec_log_entry= NULL;
+ bool error= TRUE;
+ bool error_set= FALSE;
+ handler *file= NULL;
+ DBUG_ENTER("exchange_name_with_ddl_log");
+
+ if (!(file= get_new_handler(NULL, thd->mem_root, ht)))
+ {
+ mem_alloc_error(sizeof(handler));
+ DBUG_RETURN(TRUE);
+ }
+
+ /* prepare the action entry */
+ exchange_entry.entry_type= DDL_LOG_ENTRY_CODE;
+ exchange_entry.action_type= DDL_LOG_EXCHANGE_ACTION;
+ exchange_entry.next_entry= 0;
+ exchange_entry.name= name;
+ exchange_entry.from_name= from_name;
+ exchange_entry.tmp_name= tmp_name;
+ exchange_entry.handler_name= ha_resolve_storage_engine_name(ht);
+ exchange_entry.phase= EXCH_PHASE_NAME_TO_TEMP;
+
+ mysql_mutex_lock(&LOCK_gdl);
+ /*
+ write to the ddl log what to do by:
+ 1) write the action entry (i.e. which names to be exchanged)
+ 2) write the execution entry with a link to the action entry
+ */
+ DBUG_EXECUTE_IF("exchange_partition_fail_1", goto err_no_action_written;);
+ DBUG_EXECUTE_IF("exchange_partition_abort_1", DBUG_SUICIDE(););
+ if (write_ddl_log_entry(&exchange_entry, &log_entry))
+ goto err_no_action_written;
+
+ DBUG_EXECUTE_IF("exchange_partition_fail_2", goto err_no_execute_written;);
+ DBUG_EXECUTE_IF("exchange_partition_abort_2", DBUG_SUICIDE(););
+ if (write_execute_ddl_log_entry(log_entry->entry_pos, FALSE, &exec_log_entry))
+ goto err_no_execute_written;
+ /* ddl_log is written and synced */
+
+ mysql_mutex_unlock(&LOCK_gdl);
+ /*
+ Execute the name exchange.
+ Do one rename, increase the phase, update the action entry and sync.
+ In case of errors in the ddl_log we must fail and let the ddl_log try
+ to revert the changes, since otherwise it could revert the command after
+ we sent OK to the client.
+ */
+ /* call rename table from table to tmp-name */
+ DBUG_EXECUTE_IF("exchange_partition_fail_3",
+ my_error(ER_ERROR_ON_RENAME, MYF(0),
+ name, tmp_name, 0, "n/a");
+ error_set= TRUE;
+ goto err_rename;);
+ DBUG_EXECUTE_IF("exchange_partition_abort_3", DBUG_SUICIDE(););
+ if (file->ha_rename_table(name, tmp_name))
+ {
+ char errbuf[MYSYS_STRERROR_SIZE];
+ my_strerror(errbuf, sizeof(errbuf), my_errno);
+ my_error(ER_ERROR_ON_RENAME, MYF(0), name, tmp_name,
+ my_errno, errbuf);
+ error_set= TRUE;
+ goto err_rename;
+ }
+ DBUG_EXECUTE_IF("exchange_partition_fail_4", goto err_rename;);
+ DBUG_EXECUTE_IF("exchange_partition_abort_4", DBUG_SUICIDE(););
+ if (deactivate_ddl_log_entry(log_entry->entry_pos))
+ goto err_rename;
+
+ /* call rename table from partition to table */
+ DBUG_EXECUTE_IF("exchange_partition_fail_5",
+ my_error(ER_ERROR_ON_RENAME, MYF(0),
+ from_name, name, 0, "n/a");
+ error_set= TRUE;
+ goto err_rename;);
+ DBUG_EXECUTE_IF("exchange_partition_abort_5", DBUG_SUICIDE(););
+ if (file->ha_rename_table(from_name, name))
+ {
+ char errbuf[MYSYS_STRERROR_SIZE];
+ my_strerror(errbuf, sizeof(errbuf), my_errno);
+ my_error(ER_ERROR_ON_RENAME, MYF(0), from_name, name,
+ my_errno, errbuf);
+ error_set= TRUE;
+ goto err_rename;
+ }
+ DBUG_EXECUTE_IF("exchange_partition_fail_6", goto err_rename;);
+ DBUG_EXECUTE_IF("exchange_partition_abort_6", DBUG_SUICIDE(););
+ if (deactivate_ddl_log_entry(log_entry->entry_pos))
+ goto err_rename;
+
+ /* call rename table from tmp-nam to partition */
+ DBUG_EXECUTE_IF("exchange_partition_fail_7",
+ my_error(ER_ERROR_ON_RENAME, MYF(0),
+ tmp_name, from_name, 0, "n/a");
+ error_set= TRUE;
+ goto err_rename;);
+ DBUG_EXECUTE_IF("exchange_partition_abort_7", DBUG_SUICIDE(););
+ if (file->ha_rename_table(tmp_name, from_name))
+ {
+ char errbuf[MYSYS_STRERROR_SIZE];
+ my_strerror(errbuf, sizeof(errbuf), my_errno);
+ my_error(ER_ERROR_ON_RENAME, MYF(0), tmp_name, from_name,
+ my_errno, errbuf);
+ error_set= TRUE;
+ goto err_rename;
+ }
+ DBUG_EXECUTE_IF("exchange_partition_fail_8", goto err_rename;);
+ DBUG_EXECUTE_IF("exchange_partition_abort_8", DBUG_SUICIDE(););
+ if (deactivate_ddl_log_entry(log_entry->entry_pos))
+ goto err_rename;
+
+ /* The exchange is complete and ddl_log is deactivated */
+ DBUG_EXECUTE_IF("exchange_partition_fail_9", goto err_rename;);
+ DBUG_EXECUTE_IF("exchange_partition_abort_9", DBUG_SUICIDE(););
+ /* all OK */
+ error= FALSE;
+ delete file;
+ DBUG_RETURN(error);
+err_rename:
+ /*
+ Nothing to do if any of these commands fails :( the commands itselfs
+ will log to the error log about the failures...
+ */
+ /* execute the ddl log entry to revert the renames */
+ (void) execute_ddl_log_entry(current_thd, log_entry->entry_pos);
+ mysql_mutex_lock(&LOCK_gdl);
+ /* mark the execute log entry done */
+ (void) write_execute_ddl_log_entry(0, TRUE, &exec_log_entry);
+ /* release the execute log entry */
+ (void) release_ddl_log_memory_entry(exec_log_entry);
+err_no_execute_written:
+ /* release the action log entry */
+ (void) release_ddl_log_memory_entry(log_entry);
+err_no_action_written:
+ mysql_mutex_unlock(&LOCK_gdl);
+ delete file;
+ if (!error_set)
+ my_error(ER_DDL_LOG_ERROR, MYF(0));
+ DBUG_RETURN(error);
+}
+
+
+/**
+ @brief Swap places between a partition and a table.
+
+ @details Verify that the tables are compatible (same engine, definition etc),
+ verify that all rows in the table will fit in the partition,
+ if all OK, rename table to tmp name, rename partition to table
+ and finally rename tmp name to partition.
+
+ 1) Take upgradable mdl, open tables and then lock them (inited in parse)
+ 2) Verify that metadata matches
+ 3) verify data
+ 4) Upgrade to exclusive mdl for both tables
+ 5) Rename table <-> partition
+ 6) Rely on close_thread_tables to release mdl and table locks
+
+ @param thd Thread handle
+ @param table_list Table where the partition exists as first table,
+ Table to swap with the partition as second table
+ @param alter_info Contains partition name to swap
+
+ @note This is a DDL operation so triggers will not be used.
+*/
+bool Sql_cmd_alter_table_exchange_partition::
+ exchange_partition(THD *thd, TABLE_LIST *table_list, Alter_info *alter_info)
+{
+ TABLE *part_table, *swap_table;
+ TABLE_LIST *swap_table_list;
+ handlerton *table_hton;
+ partition_element *part_elem;
+ char *partition_name;
+ char temp_name[FN_REFLEN+1];
+ char part_file_name[FN_REFLEN+1];
+ char swap_file_name[FN_REFLEN+1];
+ char temp_file_name[FN_REFLEN+1];
+ uint swap_part_id;
+ uint part_file_name_len;
+ Alter_table_prelocking_strategy alter_prelocking_strategy;
+ MDL_ticket *swap_table_mdl_ticket= NULL;
+ MDL_ticket *part_table_mdl_ticket= NULL;
+ uint table_counter;
+ bool error= TRUE;
+ DBUG_ENTER("mysql_exchange_partition");
+ DBUG_ASSERT(alter_info->flags & Alter_info::ALTER_EXCHANGE_PARTITION);
+
+ /* Don't allow to exchange with log table */
+ swap_table_list= table_list->next_local;
+ if (check_if_log_table(swap_table_list, FALSE, "ALTER PARTITION"))
+ DBUG_RETURN(TRUE);
+
+ /*
+ Currently no MDL lock that allows both read and write and is upgradeable
+ to exclusive, so leave the lock type to TL_WRITE_ALLOW_READ also on the
+ partitioned table.
+
+ TODO: add MDL lock that allows both read and write and is upgradable to
+ exclusive lock. This would allow to continue using the partitioned table
+ also with update/insert/delete while the verification of the swap table
+ is running.
+ */
+
+ /*
+ NOTE: It is not possible to exchange a crashed partition/table since
+ we need some info from the engine, which we can only access after open,
+ to be able to verify the structure/metadata.
+ */
+ table_list->mdl_request.set_type(MDL_SHARED_NO_WRITE);
+ if (open_tables(thd, &table_list, &table_counter, 0,
+ &alter_prelocking_strategy))
+ DBUG_RETURN(true);
+
+#ifdef WITH_WSREP
+ /* Forward declaration */
+ TABLE *find_temporary_table(THD *thd, const TABLE_LIST *tl);
+
+ if ((!thd->is_current_stmt_binlog_format_row() ||
+ /* TODO: Do we really need to check for temp tables in this case? */
+ !find_temporary_table(thd, table_list)) &&
+ wsrep_to_isolation_begin(thd, table_list->db, table_list->table_name,
+ NULL))
+ {
+ WSREP_WARN("ALTER TABLE EXCHANGE PARTITION isolation failure");
+ DBUG_RETURN(TRUE);
+ }
+#endif /* WITH_WSREP */
+
+ part_table= table_list->table;
+ swap_table= swap_table_list->table;
+
+ if (check_exchange_partition(swap_table, part_table))
+ DBUG_RETURN(TRUE);
+
+ /* set lock pruning on first table */
+ partition_name= alter_info->partition_names.head();
+ if (table_list->table->part_info->
+ set_named_partition_bitmap(partition_name, strlen(partition_name)))
+ DBUG_RETURN(true);
+
+ if (lock_tables(thd, table_list, table_counter, 0))
+ DBUG_RETURN(true);
+
+
+ table_hton= swap_table->file->ht;
+
+ THD_STAGE_INFO(thd, stage_verifying_table);
+
+ /* Will append the partition name later in part_info->get_part_elem() */
+ part_file_name_len= build_table_filename(part_file_name,
+ sizeof(part_file_name),
+ table_list->db,
+ table_list->table_name,
+ "", 0);
+ build_table_filename(swap_file_name,
+ sizeof(swap_file_name),
+ swap_table_list->db,
+ swap_table_list->table_name,
+ "", 0);
+ /* create a unique temp name #sqlx-nnnn_nnnn, x for eXchange */
+ my_snprintf(temp_name, sizeof(temp_name), "%sx-%lx_%lx",
+ tmp_file_prefix, current_pid, thd->thread_id);
+ if (lower_case_table_names)
+ my_casedn_str(files_charset_info, temp_name);
+ build_table_filename(temp_file_name, sizeof(temp_file_name),
+ table_list->next_local->db,
+ temp_name, "", FN_IS_TMP);
+
+ if (!(part_elem= part_table->part_info->get_part_elem(partition_name,
+ part_file_name +
+ part_file_name_len,
+ &swap_part_id)))
+ {
+ // my_error(ER_UNKNOWN_PARTITION, MYF(0), partition_name,
+ // part_table->alias);
+ DBUG_RETURN(TRUE);
+ }
+
+ if (swap_part_id == NOT_A_PARTITION_ID)
+ {
+ DBUG_ASSERT(part_table->part_info->is_sub_partitioned());
+ my_error(ER_PARTITION_INSTEAD_OF_SUBPARTITION, MYF(0));
+ DBUG_RETURN(TRUE);
+ }
+
+ if (compare_table_with_partition(thd, swap_table, part_table, part_elem))
+ DBUG_RETURN(TRUE);
+
+ /* Table and partition has same structure/options, OK to exchange */
+
+ thd_proc_info(thd, "verifying data with partition");
+
+ if (verify_data_with_partition(swap_table, part_table, swap_part_id))
+ DBUG_RETURN(TRUE);
+
+ /*
+ Get exclusive mdl lock on both tables, alway the non partitioned table
+ first. Remember the tickets for downgrading locks later.
+ */
+ swap_table_mdl_ticket= swap_table->mdl_ticket;
+ part_table_mdl_ticket= part_table->mdl_ticket;
+
+ /*
+ No need to set used_partitions to only propagate
+ HA_EXTRA_PREPARE_FOR_RENAME to one part since no built in engine uses
+ that flag. And the action would probably be to force close all other
+ instances which is what we are doing any way.
+ */
+ if (wait_while_table_is_used(thd, swap_table, HA_EXTRA_PREPARE_FOR_RENAME) ||
+ wait_while_table_is_used(thd, part_table, HA_EXTRA_PREPARE_FOR_RENAME))
+ goto err;
+
+ DEBUG_SYNC(thd, "swap_partition_after_wait");
+
+ close_all_tables_for_name(thd, swap_table->s, HA_EXTRA_NOT_USED, NULL);
+ close_all_tables_for_name(thd, part_table->s, HA_EXTRA_NOT_USED, NULL);
+
+ DEBUG_SYNC(thd, "swap_partition_before_rename");
+
+ if (exchange_name_with_ddl_log(thd, swap_file_name, part_file_name,
+ temp_file_name, table_hton))
+ goto err;
+
+ /*
+ Reopen tables under LOCK TABLES. Ignore the return value for now. It's
+ better to keep master/slave in consistent state. Alternative would be to
+ try to revert the exchange operation and issue error.
+ */
+ (void) thd->locked_tables_list.reopen_tables(thd);
+
+ if ((error= write_bin_log(thd, TRUE, thd->query(), thd->query_length())))
+ {
+ /*
+ The error is reported in write_bin_log().
+ We try to revert to make it easier to keep the master/slave in sync.
+ */
+ (void) exchange_name_with_ddl_log(thd, part_file_name, swap_file_name,
+ temp_file_name, table_hton);
+ }
+
+err:
+ if (thd->locked_tables_mode)
+ {
+ if (swap_table_mdl_ticket)
+ swap_table_mdl_ticket->downgrade_lock(MDL_SHARED_NO_READ_WRITE);
+ if (part_table_mdl_ticket)
+ part_table_mdl_ticket->downgrade_lock(MDL_SHARED_NO_READ_WRITE);
+ }
+
+ if (!error)
+ my_ok(thd);
+
+ // For query cache
+ table_list->table= NULL;
+ table_list->next_local->table= NULL;
+ query_cache_invalidate3(thd, table_list, FALSE);
+
+ DBUG_RETURN(error);
+}
+
+bool Sql_cmd_alter_table_analyze_partition::execute(THD *thd)
{
bool res;
- DBUG_ENTER("Alter_table_analyze_partition_statement::execute");
+ DBUG_ENTER("Sql_cmd_alter_table_analyze_partition::execute");
/*
Flag that it is an ALTER command which administrates partitions, used
by ha_partition
*/
- m_lex->alter_info.flags|= ALTER_ADMIN_PARTITION;
-
- res= Analyze_table_statement::execute(thd);
+ thd->lex->alter_info.flags|= Alter_info::ALTER_ADMIN_PARTITION;
+ res= Sql_cmd_analyze_table::execute(thd);
+
DBUG_RETURN(res);
}
-bool Alter_table_check_partition_statement::execute(THD *thd)
+bool Sql_cmd_alter_table_check_partition::execute(THD *thd)
{
bool res;
- DBUG_ENTER("Alter_table_check_partition_statement::execute");
+ DBUG_ENTER("Sql_cmd_alter_table_check_partition::execute");
/*
Flag that it is an ALTER command which administrates partitions, used
by ha_partition
*/
- m_lex->alter_info.flags|= ALTER_ADMIN_PARTITION;
+ thd->lex->alter_info.flags|= Alter_info::ALTER_ADMIN_PARTITION;
- res= Check_table_statement::execute(thd);
+ res= Sql_cmd_check_table::execute(thd);
DBUG_RETURN(res);
}
-bool Alter_table_optimize_partition_statement::execute(THD *thd)
+bool Sql_cmd_alter_table_optimize_partition::execute(THD *thd)
{
bool res;
DBUG_ENTER("Alter_table_optimize_partition_statement::execute");
@@ -79,46 +720,49 @@ bool Alter_table_optimize_partition_statement::execute(THD *thd)
Flag that it is an ALTER command which administrates partitions, used
by ha_partition
*/
- m_lex->alter_info.flags|= ALTER_ADMIN_PARTITION;
+ thd->lex->alter_info.flags|= Alter_info::ALTER_ADMIN_PARTITION;
- res= Optimize_table_statement::execute(thd);
+ res= Sql_cmd_optimize_table::execute(thd);
DBUG_RETURN(res);
}
-bool Alter_table_repair_partition_statement::execute(THD *thd)
+bool Sql_cmd_alter_table_repair_partition::execute(THD *thd)
{
bool res;
- DBUG_ENTER("Alter_table_repair_partition_statement::execute");
+ DBUG_ENTER("Sql_cmd_alter_table_repair_partition::execute");
/*
Flag that it is an ALTER command which administrates partitions, used
by ha_partition
*/
- m_lex->alter_info.flags|= ALTER_ADMIN_PARTITION;
+ thd->lex->alter_info.flags|= Alter_info::ALTER_ADMIN_PARTITION;
- res= Repair_table_statement::execute(thd);
+ res= Sql_cmd_repair_table::execute(thd);
DBUG_RETURN(res);
}
-bool Alter_table_truncate_partition_statement::execute(THD *thd)
+bool Sql_cmd_alter_table_truncate_partition::execute(THD *thd)
{
int error;
ha_partition *partition;
ulong timeout= thd->variables.lock_wait_timeout;
TABLE_LIST *first_table= thd->lex->select_lex.table_list.first;
+ Alter_info *alter_info= &thd->lex->alter_info;
+ uint table_counter, i;
+ List<String> partition_names_list;
bool binlog_stmt;
- DBUG_ENTER("Alter_table_truncate_partition_statement::execute");
+ DBUG_ENTER("Sql_cmd_alter_table_truncate_partition::execute");
/*
Flag that it is an ALTER command which administrates partitions, used
by ha_partition.
*/
- m_lex->alter_info.flags|= ALTER_ADMIN_PARTITION |
- ALTER_TRUNCATE_PARTITION;
+ thd->lex->alter_info.flags|= Alter_info::ALTER_ADMIN_PARTITION |
+ Alter_info::ALTER_TRUNCATE_PARTITION;
/* Fix the lock types (not the same as ordinary ALTER TABLE). */
first_table->lock_type= TL_WRITE;
@@ -135,20 +779,21 @@ bool Alter_table_truncate_partition_statement::execute(THD *thd)
DBUG_RETURN(TRUE);
#ifdef WITH_WSREP
+ /* Forward declaration */
TABLE *find_temporary_table(THD *thd, const TABLE_LIST *tl);
- if ((!thd->is_current_stmt_binlog_format_row() ||
+ if (WSREP(thd) && (!thd->is_current_stmt_binlog_format_row() ||
!find_temporary_table(thd, first_table)) &&
wsrep_to_isolation_begin(
thd, first_table->db, first_table->table_name, NULL)
)
{
- WSREP_WARN("ALTER TABLE isolation failure");
+ WSREP_WARN("ALTER TABLE TRUNCATE PARTITION isolation failure");
DBUG_RETURN(TRUE);
}
#endif /* WITH_WSREP */
- if (open_and_lock_tables(thd, first_table, FALSE, 0))
- DBUG_RETURN(TRUE);
+ if (open_tables(thd, &first_table, &table_counter, 0))
+ DBUG_RETURN(true);
/*
TODO: Add support for TRUNCATE PARTITION for NDB and other
@@ -162,24 +807,45 @@ bool Alter_table_truncate_partition_statement::execute(THD *thd)
DBUG_RETURN(TRUE);
}
+
+ /*
+ Prune all, but named partitions,
+ to avoid excessive calls to external_lock().
+ */
+ List_iterator<char> partition_names_it(alter_info->partition_names);
+ uint num_names= alter_info->partition_names.elements;
+ for (i= 0; i < num_names; i++)
+ {
+ char *partition_name= partition_names_it++;
+ String *str_partition_name= new (thd->mem_root)
+ String(partition_name, system_charset_info);
+ if (!str_partition_name)
+ DBUG_RETURN(true);
+ partition_names_list.push_back(str_partition_name);
+ }
+ first_table->partition_names= &partition_names_list;
+ if (first_table->table->part_info->set_partition_bitmaps(first_table))
+ DBUG_RETURN(true);
+
+ if (lock_tables(thd, first_table, table_counter, 0))
+ DBUG_RETURN(true);
+
/*
Under locked table modes this might still not be an exclusive
lock. Hence, upgrade the lock since the handler truncate method
mandates an exclusive metadata lock.
*/
MDL_ticket *ticket= first_table->table->mdl_ticket;
- if (thd->mdl_context.upgrade_shared_lock_to_exclusive(ticket, timeout))
+ if (thd->mdl_context.upgrade_shared_lock(ticket, MDL_EXCLUSIVE, timeout))
DBUG_RETURN(TRUE);
tdc_remove_table(thd, TDC_RT_REMOVE_NOT_OWN, first_table->db,
first_table->table_name, FALSE);
- partition= (ha_partition *) first_table->table->file;
-
+ partition= (ha_partition*) first_table->table->file;
/* Invoke the handler method responsible for truncating the partition. */
- if ((error= partition->truncate_partition(&thd->lex->alter_info,
- &binlog_stmt)))
- first_table->table->file->print_error(error, MYF(0));
+ if ((error= partition->truncate_partition(alter_info, &binlog_stmt)))
+ partition->print_error(error, MYF(0));
/*
All effects of a truncate operation are committed even if the
@@ -204,11 +870,15 @@ bool Alter_table_truncate_partition_statement::execute(THD *thd)
to a shared one.
*/
if (thd->locked_tables_mode)
- ticket->downgrade_exclusive_lock(MDL_SHARED_NO_READ_WRITE);
+ ticket->downgrade_lock(MDL_SHARED_NO_READ_WRITE);
if (! error)
my_ok(thd);
+ // Invalidate query cache
+ DBUG_ASSERT(!first_table->next_local);
+ query_cache_invalidate3(thd, first_table, FALSE);
+
DBUG_RETURN(error);
}
diff --git a/sql/sql_partition_admin.h b/sql/sql_partition_admin.h
index 479371c3b4d..9c53744d9bc 100644
--- a/sql/sql_partition_admin.h
+++ b/sql/sql_partition_admin.h
@@ -22,214 +22,247 @@
Stub class that returns a error if the partition storage engine is
not supported.
*/
-class Partition_statement_unsupported : public Sql_statement
+class Sql_cmd_partition_unsupported : public Sql_cmd
{
public:
- Partition_statement_unsupported(LEX *lex)
- : Sql_statement(lex)
+ Sql_cmd_partition_unsupported()
{}
- ~Partition_statement_unsupported()
+ ~Sql_cmd_partition_unsupported()
{}
+ /* Override SQLCOM_*, since it is an ALTER command */
+ virtual enum_sql_command sql_command_code() const
+ {
+ return SQLCOM_ALTER_TABLE;
+ }
+
bool execute(THD *thd);
};
-class Alter_table_analyze_partition_statement :
- public Partition_statement_unsupported
+class Sql_cmd_alter_table_exchange_partition :
+ public Sql_cmd_partition_unsupported
{
public:
- Alter_table_analyze_partition_statement(LEX *lex)
- : Partition_statement_unsupported(lex)
+ Sql_cmd_alter_table_exchange_partition()
{}
- ~Alter_table_analyze_partition_statement()
+ ~Sql_cmd_alter_table_exchange_partition()
{}
};
-class Alter_table_check_partition_statement :
- public Partition_statement_unsupported
+class Sql_cmd_alter_table_analyze_partition :
+ public Sql_cmd_partition_unsupported
{
public:
- Alter_table_check_partition_statement(LEX *lex)
- : Partition_statement_unsupported(lex)
+ Sql_cmd_alter_table_analyze_partition()
{}
- ~Alter_table_check_partition_statement()
+ ~Sql_cmd_alter_table_analyze_partition()
{}
};
-class Alter_table_optimize_partition_statement :
- public Partition_statement_unsupported
+class Sql_cmd_alter_table_check_partition :
+ public Sql_cmd_partition_unsupported
{
public:
- Alter_table_optimize_partition_statement(LEX *lex)
- : Partition_statement_unsupported(lex)
+ Sql_cmd_alter_table_check_partition()
{}
- ~Alter_table_optimize_partition_statement()
+ ~Sql_cmd_alter_table_check_partition()
{}
};
-class Alter_table_repair_partition_statement :
- public Partition_statement_unsupported
+class Sql_cmd_alter_table_optimize_partition :
+ public Sql_cmd_partition_unsupported
{
public:
- Alter_table_repair_partition_statement(LEX *lex)
- : Partition_statement_unsupported(lex)
+ Sql_cmd_alter_table_optimize_partition()
{}
- ~Alter_table_repair_partition_statement()
+ ~Sql_cmd_alter_table_optimize_partition()
{}
};
-class Alter_table_truncate_partition_statement :
- public Partition_statement_unsupported
+class Sql_cmd_alter_table_repair_partition :
+ public Sql_cmd_partition_unsupported
{
public:
- Alter_table_truncate_partition_statement(LEX *lex)
- : Partition_statement_unsupported(lex)
+ Sql_cmd_alter_table_repair_partition()
{}
- ~Alter_table_truncate_partition_statement()
+ ~Sql_cmd_alter_table_repair_partition()
{}
};
+class Sql_cmd_alter_table_truncate_partition :
+ public Sql_cmd_partition_unsupported
+{
+public:
+ Sql_cmd_alter_table_truncate_partition()
+ {}
+
+ ~Sql_cmd_alter_table_truncate_partition()
+ {}
+};
+
#else
/**
Class that represents the ALTER TABLE t1 ANALYZE PARTITION p statement.
*/
-class Alter_table_analyze_partition_statement : public Analyze_table_statement
+class Sql_cmd_alter_table_exchange_partition : public Sql_cmd_common_alter_table
{
public:
/**
- Constructor, used to represent a ALTER TABLE ANALYZE PARTITION statement.
- @param lex the LEX structure for this statement.
+ Constructor, used to represent a ALTER TABLE EXCHANGE PARTITION statement.
*/
- Alter_table_analyze_partition_statement(LEX *lex)
- : Analyze_table_statement(lex)
+ Sql_cmd_alter_table_exchange_partition()
+ : Sql_cmd_common_alter_table()
{}
- ~Alter_table_analyze_partition_statement()
+ ~Sql_cmd_alter_table_exchange_partition()
{}
+ bool execute(THD *thd);
+
+private:
+ bool exchange_partition(THD *thd, TABLE_LIST *, Alter_info *);
+};
+
+
+/**
+ Class that represents the ALTER TABLE t1 ANALYZE PARTITION p statement.
+*/
+class Sql_cmd_alter_table_analyze_partition : public Sql_cmd_analyze_table
+{
+public:
/**
- Execute a ALTER TABLE ANALYZE PARTITION statement at runtime.
- @param thd the current thread.
- @return false on success.
+ Constructor, used to represent a ALTER TABLE ANALYZE PARTITION statement.
*/
+ Sql_cmd_alter_table_analyze_partition()
+ : Sql_cmd_analyze_table()
+ {}
+
+ ~Sql_cmd_alter_table_analyze_partition()
+ {}
+
bool execute(THD *thd);
+
+ /* Override SQLCOM_ANALYZE, since it is an ALTER command */
+ virtual enum_sql_command sql_command_code() const
+ {
+ return SQLCOM_ALTER_TABLE;
+ }
};
/**
Class that represents the ALTER TABLE t1 CHECK PARTITION p statement.
*/
-class Alter_table_check_partition_statement : public Check_table_statement
+class Sql_cmd_alter_table_check_partition : public Sql_cmd_check_table
{
public:
/**
Constructor, used to represent a ALTER TABLE CHECK PARTITION statement.
- @param lex the LEX structure for this statement.
*/
- Alter_table_check_partition_statement(LEX *lex)
- : Check_table_statement(lex)
+ Sql_cmd_alter_table_check_partition()
+ : Sql_cmd_check_table()
{}
- ~Alter_table_check_partition_statement()
+ ~Sql_cmd_alter_table_check_partition()
{}
- /**
- Execute a ALTER TABLE CHECK PARTITION statement at runtime.
- @param thd the current thread.
- @return false on success.
- */
bool execute(THD *thd);
+
+ /* Override SQLCOM_CHECK, since it is an ALTER command */
+ virtual enum_sql_command sql_command_code() const
+ {
+ return SQLCOM_ALTER_TABLE;
+ }
};
/**
Class that represents the ALTER TABLE t1 OPTIMIZE PARTITION p statement.
*/
-class Alter_table_optimize_partition_statement : public Optimize_table_statement
+class Sql_cmd_alter_table_optimize_partition : public Sql_cmd_optimize_table
{
public:
/**
Constructor, used to represent a ALTER TABLE OPTIMIZE PARTITION statement.
- @param lex the LEX structure for this statement.
*/
- Alter_table_optimize_partition_statement(LEX *lex)
- : Optimize_table_statement(lex)
+ Sql_cmd_alter_table_optimize_partition()
+ : Sql_cmd_optimize_table()
{}
- ~Alter_table_optimize_partition_statement()
+ ~Sql_cmd_alter_table_optimize_partition()
{}
- /**
- Execute a ALTER TABLE OPTIMIZE PARTITION statement at runtime.
- @param thd the current thread.
- @return false on success.
- */
bool execute(THD *thd);
+
+ /* Override SQLCOM_OPTIMIZE, since it is an ALTER command */
+ virtual enum_sql_command sql_command_code() const
+ {
+ return SQLCOM_ALTER_TABLE;
+ }
};
/**
Class that represents the ALTER TABLE t1 REPAIR PARTITION p statement.
*/
-class Alter_table_repair_partition_statement : public Repair_table_statement
+class Sql_cmd_alter_table_repair_partition : public Sql_cmd_repair_table
{
public:
/**
Constructor, used to represent a ALTER TABLE REPAIR PARTITION statement.
- @param lex the LEX structure for this statement.
*/
- Alter_table_repair_partition_statement(LEX *lex)
- : Repair_table_statement(lex)
+ Sql_cmd_alter_table_repair_partition()
+ : Sql_cmd_repair_table()
{}
- ~Alter_table_repair_partition_statement()
+ ~Sql_cmd_alter_table_repair_partition()
{}
- /**
- Execute a ALTER TABLE REPAIR PARTITION statement at runtime.
- @param thd the current thread.
- @return false on success.
- */
bool execute(THD *thd);
+
+ /* Override SQLCOM_REPAIR, since it is an ALTER command */
+ virtual enum_sql_command sql_command_code() const
+ {
+ return SQLCOM_ALTER_TABLE;
+ }
};
/**
Class that represents the ALTER TABLE t1 TRUNCATE PARTITION p statement.
*/
-class Alter_table_truncate_partition_statement : public Sql_statement
+class Sql_cmd_alter_table_truncate_partition : public Sql_cmd_truncate_table
{
public:
/**
Constructor, used to represent a ALTER TABLE TRUNCATE PARTITION statement.
- @param lex the LEX structure for this statement.
*/
- Alter_table_truncate_partition_statement(LEX *lex)
- : Sql_statement(lex)
+ Sql_cmd_alter_table_truncate_partition()
{}
- virtual ~Alter_table_truncate_partition_statement()
+ virtual ~Sql_cmd_alter_table_truncate_partition()
{}
- /**
- Execute a ALTER TABLE TRUNCATE PARTITION statement at runtime.
- @param thd the current thread.
- @return false on success.
- */
bool execute(THD *thd);
+
+ /* Override SQLCOM_TRUNCATE, since it is an ALTER command */
+ virtual enum_sql_command sql_command_code() const
+ {
+ return SQLCOM_ALTER_TABLE;
+ }
};
#endif /* WITH_PARTITION_STORAGE_ENGINE */
diff --git a/sql/sql_plist.h b/sql/sql_plist.h
index e703b4c0f62..df50cccc874 100644
--- a/sql/sql_plist.h
+++ b/sql/sql_plist.h
@@ -18,7 +18,7 @@
#include <my_global.h>
-template <typename T, typename B, typename C, typename I>
+template <typename T, typename L>
class I_P_List_iterator;
class I_P_List_null_counter;
template <typename T> class I_P_List_no_push_back;
@@ -156,10 +156,14 @@ public:
I::set_last(&rhs.m_first);
C::swap(rhs);
}
+ typedef B Adapter;
+ typedef I_P_List<T, B, C, I> Base;
+ typedef I_P_List_iterator<T, Base> Iterator;
+ typedef I_P_List_iterator<const T, Base> Const_Iterator;
#ifndef _lint
- friend class I_P_List_iterator<T, B, C, I>;
+ friend class I_P_List_iterator<T, Base>;
+ friend class I_P_List_iterator<const T, Base>;
#endif
- typedef I_P_List_iterator<T, B, C, I> Iterator;
};
@@ -167,33 +171,33 @@ public:
Iterator for I_P_List.
*/
-template <typename T, typename B,
- typename C = I_P_List_null_counter,
- typename I = I_P_List_no_push_back<T> >
+template <typename T, typename L>
class I_P_List_iterator
{
- const I_P_List<T, B, C, I> *list;
+ const L *list;
T *current;
public:
- I_P_List_iterator(const I_P_List<T, B, C, I> &a)
+ I_P_List_iterator(const L &a)
: list(&a), current(a.m_first) {}
- I_P_List_iterator(const I_P_List<T, B, C, I> &a, T* current_arg)
+ I_P_List_iterator(const L &a, T* current_arg)
: list(&a), current(current_arg) {}
- inline void init(const I_P_List<T, B, C, I> &a)
+ inline void init(const L &a)
{
list= &a;
current= a.m_first;
}
+ /* Operator for it++ */
inline T* operator++(int)
{
T *result= current;
if (result)
- current= *B::next_ptr(current);
+ current= *L::Adapter::next_ptr(current);
return result;
}
+ /* Operator for ++it */
inline T* operator++()
{
- current= *B::next_ptr(current);
+ current= *L::Adapter::next_ptr(current);
return current;
}
inline void rewind()
@@ -212,7 +216,7 @@ template <typename T, T* T::*next, T** T::*prev>
struct I_P_List_adapter
{
static inline T **next_ptr(T *el) { return &(el->*next); }
-
+ static inline const T* const* next_ptr(const T *el) { return &(el->*next); }
static inline T ***prev_ptr(T *el) { return &(el->*prev); }
};
diff --git a/sql/sql_plugin.cc b/sql/sql_plugin.cc
index 204fe5a9de3..6d48ebbfaa8 100644
--- a/sql/sql_plugin.cc
+++ b/sql/sql_plugin.cc
@@ -15,9 +15,9 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+#include "sql_plugin.h" // Includes my_global.h
#include "sql_priv.h" // SHOW_MY_BOOL
#include "unireg.h"
-#include "my_global.h" // REQUIRED by m_string.h
#include "sql_class.h" // set_var.h: THD
#include "sys_vars_shared.h"
#include "sql_locale.h"
@@ -53,8 +53,8 @@ static TYPELIB global_plugin_typelib=
{ array_elements(global_plugin_typelib_names)-1,
"", global_plugin_typelib_names, NULL };
-
-char *opt_plugin_load= NULL;
+static I_List<i_string> opt_plugin_load_list;
+I_List<i_string> *opt_plugin_load_list_ptr= &opt_plugin_load_list;
char *opt_plugin_dir_ptr;
char opt_plugin_dir[FN_REFLEN];
ulong plugin_maturity;
@@ -197,6 +197,8 @@ static bool reap_needed= false;
static int plugin_array_version=0;
static bool initialized= 0;
+ulong dlopen_count;
+
/*
write-lock on LOCK_system_variables_hash is required before modifying
@@ -256,15 +258,6 @@ class sys_var_pluginvar: public sys_var
public:
struct st_plugin_int *plugin;
struct st_mysql_sys_var *plugin_var;
- /**
- variable name from whatever is hard-coded in the plugin source
- and doesn't have pluginname- prefix is replaced by an allocated name
- with a plugin prefix. When plugin is uninstalled we need to restore the
- pointer to point to the hard-coded value, because plugin may be
- installed/uninstalled many times without reloading the shared object.
- */
- const char *orig_pluginvar_name;
-
static void *operator new(size_t size, MEM_ROOT *mem_root)
{ return (void*) alloc_root(mem_root, size); }
static void operator delete(void *ptr_arg,size_t size)
@@ -278,8 +271,7 @@ public:
(plugin_var_arg->flags & PLUGIN_VAR_READONLY ? READONLY : 0),
0, -1, NO_ARG, pluginvar_show_type(plugin_var_arg), 0, 0,
VARIABLE_NOT_IN_BINLOG, NULL, NULL, NULL),
- plugin(plugin_arg), plugin_var(plugin_var_arg),
- orig_pluginvar_name(plugin_var_arg->name)
+ plugin(plugin_arg), plugin_var(plugin_var_arg)
{ plugin_var->name= name_arg; }
sys_var_pluginvar *cast_pluginvar() { return this; }
bool check_update_type(Item_result type);
@@ -309,9 +301,7 @@ static bool register_builtin(struct st_maria_plugin *, struct st_plugin_int *,
static void unlock_variables(THD *thd, struct system_variables *vars);
static void cleanup_variables(THD *thd, struct system_variables *vars);
static void plugin_vars_free_values(sys_var *vars);
-static void restore_pluginvar_names(sys_var *first);
-static void plugin_opt_set_limits(struct my_option *,
- const struct st_mysql_sys_var *);
+static void restore_ptr_backup(uint n, st_ptr_backup *backup);
static plugin_ref intern_plugin_lock(LEX *lex, plugin_ref plugin);
static void intern_plugin_unlock(LEX *lex, plugin_ref plugin);
static void reap_plugins(void);
@@ -476,9 +466,16 @@ static st_plugin_dl *plugin_dl_insert_or_reuse(struct st_plugin_dl *plugin_dl)
#endif /* HAVE_DLOPEN */
-static inline void free_plugin_mem(struct st_plugin_dl *p)
+static void free_plugin_mem(struct st_plugin_dl *p)
{
#ifdef HAVE_DLOPEN
+ if (p->ptr_backup)
+ {
+ DBUG_ASSERT(p->nbackups);
+ DBUG_ASSERT(p->handle);
+ restore_ptr_backup(p->nbackups, p->ptr_backup);
+ my_free(p->ptr_backup);
+ }
if (p->handle)
dlclose(p->handle);
#endif
@@ -509,7 +506,6 @@ static my_bool read_mysql_plugin_info(struct st_plugin_dl *plugin_dl,
/* Determine interface version */
if (!sym)
{
- free_plugin_mem(plugin_dl);
report_error(report, ER_CANT_FIND_DL_ENTRY, plugin_interface_version_sym);
DBUG_RETURN(TRUE);
}
@@ -519,15 +515,13 @@ static my_bool read_mysql_plugin_info(struct st_plugin_dl *plugin_dl,
if (plugin_dl->mysqlversion < min_plugin_interface_version ||
(plugin_dl->mysqlversion >> 8) > (MYSQL_PLUGIN_INTERFACE_VERSION >> 8))
{
- free_plugin_mem(plugin_dl);
- report_error(report, ER_CANT_OPEN_LIBRARY, dlpath, 0,
+ report_error(report, ER_CANT_OPEN_LIBRARY, dlpath, ENOEXEC,
"plugin interface version mismatch");
DBUG_RETURN(TRUE);
}
/* Find plugin declarations */
if (!(sym= dlsym(plugin_dl->handle, plugin_declarations_sym)))
{
- free_plugin_mem(plugin_dl);
report_error(report, ER_CANT_FIND_DL_ENTRY, plugin_declarations_sym);
DBUG_RETURN(TRUE);
}
@@ -558,7 +552,6 @@ static my_bool read_mysql_plugin_info(struct st_plugin_dl *plugin_dl,
MYF(MY_ZEROFILL|MY_WME));
if (!cur)
{
- free_plugin_mem(plugin_dl);
report_error(report, ER_OUTOFMEMORY,
static_cast<int>(plugin_dl->dl.length));
DBUG_RETURN(TRUE);
@@ -633,7 +626,6 @@ static my_bool read_maria_plugin_info(struct st_plugin_dl *plugin_dl,
Actually this branch impossible because in case of absence of maria
version we try mysql version.
*/
- free_plugin_mem(plugin_dl);
report_error(report, ER_CANT_FIND_DL_ENTRY,
maria_plugin_interface_version_sym);
DBUG_RETURN(TRUE);
@@ -644,7 +636,6 @@ static my_bool read_maria_plugin_info(struct st_plugin_dl *plugin_dl,
if (plugin_dl->mariaversion < min_maria_plugin_interface_version ||
(plugin_dl->mariaversion >> 8) > (MARIA_PLUGIN_INTERFACE_VERSION >> 8))
{
- free_plugin_mem(plugin_dl);
report_error(report, ER_CANT_OPEN_LIBRARY, dlpath, ENOEXEC,
"plugin interface version mismatch");
DBUG_RETURN(TRUE);
@@ -652,7 +643,6 @@ static my_bool read_maria_plugin_info(struct st_plugin_dl *plugin_dl,
/* Find plugin declarations */
if (!(sym= dlsym(plugin_dl->handle, maria_plugin_declarations_sym)))
{
- free_plugin_mem(plugin_dl);
report_error(report, ER_CANT_FIND_DL_ENTRY, maria_plugin_declarations_sym);
DBUG_RETURN(TRUE);
}
@@ -666,7 +656,6 @@ static my_bool read_maria_plugin_info(struct st_plugin_dl *plugin_dl,
sizeof_st_plugin= *(int *)sym;
else
{
- free_plugin_mem(plugin_dl);
report_error(report, ER_CANT_FIND_DL_ENTRY, maria_sizeof_st_plugin_sym);
DBUG_RETURN(TRUE);
}
@@ -684,7 +673,6 @@ static my_bool read_maria_plugin_info(struct st_plugin_dl *plugin_dl,
MYF(MY_ZEROFILL|MY_WME));
if (!cur)
{
- free_plugin_mem(plugin_dl);
report_error(report, ER_OUTOFMEMORY,
static_cast<int>(plugin_dl->dl.length));
DBUG_RETURN(TRUE);
@@ -697,7 +685,7 @@ static my_bool read_maria_plugin_info(struct st_plugin_dl *plugin_dl,
for (i=0;
(old= (struct st_maria_plugin *)(ptr + i * sizeof_st_plugin))->info;
i++)
- memcpy(cur + i, old, min(sizeof(cur[i]), sizeof_st_plugin));
+ memcpy(cur + i, old, MY_MIN(sizeof(cur[i]), sizeof_st_plugin));
sym= cur;
plugin_dl->allocated= true;
@@ -716,11 +704,13 @@ static st_plugin_dl *plugin_dl_add(const LEX_STRING *dl, int report)
#ifdef HAVE_DLOPEN
char dlpath[FN_REFLEN];
uint plugin_dir_len, dummy_errors, dlpathlen, i;
- struct st_plugin_dl *tmp, plugin_dl;
+ struct st_plugin_dl *tmp= 0, plugin_dl;
void *sym;
+ st_ptr_backup tmp_backup[array_elements(list_of_services)];
DBUG_ENTER("plugin_dl_add");
DBUG_PRINT("enter", ("dl->str: '%s', dl->length: %d",
dl->str, (int) dl->length));
+ mysql_mutex_assert_owner(&LOCK_plugin);
plugin_dir_len= strlen(opt_plugin_dir);
/*
Ensure that the dll doesn't have a path.
@@ -758,8 +748,9 @@ static st_plugin_dl *plugin_dl_add(const LEX_STRING *dl, int report)
if (*errmsg == ' ') errmsg++;
}
report_error(report, ER_CANT_OPEN_LIBRARY, dlpath, errno, errmsg);
- DBUG_RETURN(0);
+ goto ret;
}
+ dlopen_count++;
/* Checks which plugin interface present and reads info */
if (!(sym= dlsym(plugin_dl.handle, maria_plugin_interface_version_sym)))
@@ -769,12 +760,12 @@ static st_plugin_dl *plugin_dl_add(const LEX_STRING *dl, int report)
plugin_interface_version_sym),
dlpath,
report))
- DBUG_RETURN(0);
+ goto ret;
}
else
{
if (read_maria_plugin_info(&plugin_dl, sym, dlpath, report))
- DBUG_RETURN(0);
+ goto ret;
}
/* link the services in */
@@ -782,7 +773,8 @@ static st_plugin_dl *plugin_dl_add(const LEX_STRING *dl, int report)
{
if ((sym= dlsym(plugin_dl.handle, list_of_services[i].name)))
{
- uint ver= (uint)(intptr)*(void**)sym;
+ void **ptr= (void **)sym;
+ uint ver= (uint)(intptr)*ptr;
if (ver > list_of_services[i].version ||
(ver >> 8) < (list_of_services[i].version >> 8))
{
@@ -791,20 +783,33 @@ static st_plugin_dl *plugin_dl_add(const LEX_STRING *dl, int report)
"service '%s' interface version mismatch",
list_of_services[i].name);
report_error(report, ER_CANT_OPEN_LIBRARY, dlpath, ENOEXEC, buf);
- DBUG_RETURN(0);
+ goto ret;
}
- *(void**)sym= list_of_services[i].service;
+ tmp_backup[plugin_dl.nbackups++].save(ptr);
+ *ptr= list_of_services[i].service;
+ }
+ }
+
+ if (plugin_dl.nbackups)
+ {
+ size_t bytes= plugin_dl.nbackups * sizeof(plugin_dl.ptr_backup[0]);
+ plugin_dl.ptr_backup= (st_ptr_backup *)my_malloc(bytes, MYF(0));
+ if (!plugin_dl.ptr_backup)
+ {
+ restore_ptr_backup(plugin_dl.nbackups, tmp_backup);
+ report_error(report, ER_OUTOFMEMORY, bytes);
+ goto ret;
}
+ memcpy(plugin_dl.ptr_backup, tmp_backup, bytes);
}
/* Duplicate and convert dll name */
plugin_dl.dl.length= dl->length * files_charset_info->mbmaxlen + 1;
if (! (plugin_dl.dl.str= (char*) my_malloc(plugin_dl.dl.length, MYF(0))))
{
- free_plugin_mem(&plugin_dl);
report_error(report, ER_OUTOFMEMORY,
static_cast<int>(plugin_dl.dl.length));
- DBUG_RETURN(0);
+ goto ret;
}
plugin_dl.dl.length= copy_and_convert(plugin_dl.dl.str, plugin_dl.dl.length,
files_charset_info, dl->str, dl->length, system_charset_info,
@@ -813,12 +818,17 @@ static st_plugin_dl *plugin_dl_add(const LEX_STRING *dl, int report)
/* Add this dll to array */
if (! (tmp= plugin_dl_insert_or_reuse(&plugin_dl)))
{
- free_plugin_mem(&plugin_dl);
report_error(report, ER_OUTOFMEMORY,
static_cast<int>(sizeof(struct st_plugin_dl)));
- DBUG_RETURN(0);
+ goto ret;
}
+
+ret:
+ if (!tmp)
+ free_plugin_mem(&plugin_dl);
+
DBUG_RETURN(tmp);
+
#else
DBUG_ENTER("plugin_dl_add");
report_error(report, ER_FEATURE_DISABLED, "plugin", "HAVE_DLOPEN");
@@ -827,34 +837,23 @@ static st_plugin_dl *plugin_dl_add(const LEX_STRING *dl, int report)
}
-static void plugin_dl_del(const LEX_STRING *dl)
+static void plugin_dl_del(struct st_plugin_dl *plugin_dl)
{
-#ifdef HAVE_DLOPEN
- uint i;
DBUG_ENTER("plugin_dl_del");
+ if (!plugin_dl)
+ DBUG_VOID_RETURN;
+
mysql_mutex_assert_owner(&LOCK_plugin);
- for (i= 0; i < plugin_dl_array.elements; i++)
+ /* Do not remove this element, unless no other plugin uses this dll. */
+ if (! --plugin_dl->ref_count)
{
- struct st_plugin_dl *tmp= *dynamic_element(&plugin_dl_array, i,
- struct st_plugin_dl **);
- if (tmp->ref_count &&
- ! my_strnncoll(files_charset_info,
- (const uchar *)dl->str, dl->length,
- (const uchar *)tmp->dl.str, tmp->dl.length))
- {
- /* Do not remove this element, unless no other plugin uses this dll. */
- if (! --tmp->ref_count)
- {
- free_plugin_mem(tmp);
- bzero(tmp, sizeof(struct st_plugin_dl));
- }
- break;
- }
+ free_plugin_mem(plugin_dl);
+ bzero(plugin_dl, sizeof(struct st_plugin_dl));
}
+
DBUG_VOID_RETURN;
-#endif
}
@@ -925,7 +924,8 @@ static plugin_ref intern_plugin_lock(LEX *lex, plugin_ref rc)
mysql_mutex_assert_owner(&LOCK_plugin);
- if (pi->state & (PLUGIN_IS_READY | PLUGIN_IS_UNINITIALIZED))
+ if (pi->state & (PLUGIN_IS_READY | PLUGIN_IS_UNINITIALIZED |
+ PLUGIN_IS_DELETED))
{
plugin_ref plugin;
#ifdef DBUG_OFF
@@ -1041,13 +1041,15 @@ static st_plugin_int *plugin_insert_or_reuse(struct st_plugin_int *plugin)
static bool plugin_add(MEM_ROOT *tmp_root,
const LEX_STRING *name, LEX_STRING *dl, int report)
{
- struct st_plugin_int tmp;
+ struct st_plugin_int tmp, *maybe_dupe;
struct st_maria_plugin *plugin;
- uint oks= 0, errs= 0;
+ uint oks= 0, errs= 0, dupes= 0;
DBUG_ENTER("plugin_add");
+ DBUG_PRINT("enter", ("name: %s dl: %s", name->str, dl->str));
+
if (name->str && plugin_find_internal(name, MYSQL_ANY_PLUGIN))
{
- report_error(report, ER_UDF_EXISTS, name->str);
+ report_error(report, ER_PLUGIN_INSTALLED, name->str);
DBUG_RETURN(TRUE);
}
/* Clear the whole struct to catch future extensions. */
@@ -1069,46 +1071,54 @@ static bool plugin_add(MEM_ROOT *tmp_root,
(const uchar *)tmp.name.str, tmp.name.length))
continue; // plugin name doesn't match
- if (!name->str && plugin_find_internal(&tmp.name, MYSQL_ANY_PLUGIN))
- continue; // already installed
-
- struct st_plugin_int *tmp_plugin_ptr;
- if (*(int*)plugin->info <
- min_plugin_info_interface_version[plugin->type] ||
- ((*(int*)plugin->info) >> 8) >
- (cur_plugin_info_interface_version[plugin->type] >> 8))
- {
- char buf[256];
- strxnmov(buf, sizeof(buf) - 1, "API version for ",
- plugin_type_names[plugin->type].str,
- " plugin ", tmp.name.str,
- " not supported by this version of the server", NullS);
- report_error(report, ER_CANT_OPEN_LIBRARY, dl->str, ENOEXEC, buf);
- goto err;
- }
- if (plugin_maturity_map[plugin->maturity] < plugin_maturity)
+ if (!name->str &&
+ (maybe_dupe= plugin_find_internal(&tmp.name, MYSQL_ANY_PLUGIN)))
+ {
+ if (plugin->name != maybe_dupe->plugin->name)
{
- char buf[256];
- strxnmov(buf, sizeof(buf) - 1, "Loading of ",
- plugin_maturity_names[plugin->maturity],
- " plugin ", tmp.name.str,
- " is prohibited by --plugin-maturity=",
- plugin_maturity_names[plugin_maturity],
- NullS);
- report_error(report, ER_CANT_OPEN_LIBRARY, dl->str, EPERM, buf);
- goto err;
+ report_error(report, ER_UDF_EXISTS, plugin->name);
+ DBUG_RETURN(TRUE);
}
- tmp.plugin= plugin;
- tmp.ref_count= 0;
- tmp.state= PLUGIN_IS_UNINITIALIZED;
- tmp.load_option= PLUGIN_ON;
+ dupes++;
+ continue; // already installed
+ }
+ struct st_plugin_int *tmp_plugin_ptr;
+ if (*(int*)plugin->info <
+ min_plugin_info_interface_version[plugin->type] ||
+ ((*(int*)plugin->info) >> 8) >
+ (cur_plugin_info_interface_version[plugin->type] >> 8))
+ {
+ char buf[256];
+ strxnmov(buf, sizeof(buf) - 1, "API version for ",
+ plugin_type_names[plugin->type].str,
+ " plugin ", tmp.name.str,
+ " not supported by this version of the server", NullS);
+ report_error(report, ER_CANT_OPEN_LIBRARY, dl->str, ENOEXEC, buf);
+ goto err;
+ }
+ if (plugin_maturity_map[plugin->maturity] < plugin_maturity)
+ {
+ char buf[256];
+ strxnmov(buf, sizeof(buf) - 1, "Loading of ",
+ plugin_maturity_names[plugin->maturity],
+ " plugin ", tmp.name.str,
+ " is prohibited by --plugin-maturity=",
+ plugin_maturity_names[plugin_maturity],
+ NullS);
+ report_error(report, ER_CANT_OPEN_LIBRARY, dl->str, EPERM, buf);
+ goto err;
+ }
+ tmp.plugin= plugin;
+ tmp.ref_count= 0;
+ tmp.state= PLUGIN_IS_UNINITIALIZED;
+ tmp.load_option= PLUGIN_ON;
- if (!(tmp_plugin_ptr= plugin_insert_or_reuse(&tmp)))
- goto err;
- plugin_array_version++;
- if (my_hash_insert(&plugin_hash[plugin->type], (uchar*)tmp_plugin_ptr))
- tmp_plugin_ptr->state= PLUGIN_IS_FREED;
- init_alloc_root(&tmp_plugin_ptr->mem_root, 4096, 4096);
+ if (!(tmp_plugin_ptr= plugin_insert_or_reuse(&tmp)))
+ goto err;
+ plugin_array_version++;
+ if (my_hash_insert(&plugin_hash[plugin->type], (uchar*)tmp_plugin_ptr))
+ tmp_plugin_ptr->state= PLUGIN_IS_FREED;
+ init_alloc_root(&tmp_plugin_ptr->mem_root, 4096, 4096, MYF(0));
if (name->str)
DBUG_RETURN(FALSE); // all done
@@ -1123,11 +1133,13 @@ err:
break;
}
- if (errs == 0 && oks == 0) // no plugin was found
+ DBUG_ASSERT(!name->str || !dupes); // dupes is ONLY for name->str == 0
+
+ if (errs == 0 && oks == 0 && !dupes) // no plugin was found
report_error(report, ER_CANT_FIND_DL_ENTRY, name->str);
- plugin_dl_del(dl);
- DBUG_RETURN(errs > 0 || oks == 0);
+ plugin_dl_del(tmp.plugin_dl);
+ DBUG_RETURN(errs > 0 || oks + dupes == 0);
}
@@ -1142,22 +1154,21 @@ static void plugin_deinitialize(struct st_plugin_int *plugin, bool ref_check)
if (plugin->plugin->status_vars)
{
-#ifdef FIX_LATER
- /**
- @todo
- unfortunately, status variables were introduced without a
- pluginname_ namespace, that is pluginname_ was not added automatically
- to status variable names. It should be fixed together with the next
- incompatible API change.
+ /*
+ historical ndb behavior caused MySQL plugins to specify
+ status var names in full, with the plugin name prefix.
+ this was never fixed in MySQL.
+ MariaDB fixes that but support MySQL style too.
*/
- SHOW_VAR array[2]= {
+ SHOW_VAR *show_vars= plugin->plugin->status_vars;
+ SHOW_VAR tmp_array[2]= {
{plugin->plugin->name, (char*)plugin->plugin->status_vars, SHOW_ARRAY},
{0, 0, SHOW_UNDEF}
};
- remove_status_vars(array);
-#else
- remove_status_vars(plugin->plugin->status_vars);
-#endif /* FIX_LATER */
+ if (strncasecmp(show_vars->name, plugin->name.str, plugin->name.length))
+ show_vars= tmp_array;
+
+ remove_status_vars(show_vars);
}
if (plugin_type_deinitialize[plugin->plugin->type])
@@ -1179,10 +1190,6 @@ static void plugin_deinitialize(struct st_plugin_int *plugin, bool ref_check)
}
plugin->state= PLUGIN_IS_UNINITIALIZED;
- /* maintain the obsolete @@have_innodb variable */
- if (!my_strcasecmp(&my_charset_latin1, plugin->name.str, "InnoDB"))
- have_innodb= SHOW_OPTION_DISABLED;
-
/*
We do the check here because NDB has a worker THD which doesn't
exit until NDB is shut down.
@@ -1191,7 +1198,7 @@ static void plugin_deinitialize(struct st_plugin_int *plugin, bool ref_check)
sql_print_error("Plugin '%s' has ref_count=%d after deinitialization.",
plugin->name.str, plugin->ref_count);
- restore_pluginvar_names(plugin->system_vars);
+ mysql_del_sys_var_chain(plugin->system_vars);
}
static void plugin_del(struct st_plugin_int *plugin)
@@ -1200,9 +1207,9 @@ static void plugin_del(struct st_plugin_int *plugin)
mysql_mutex_assert_owner(&LOCK_plugin);
/* Free allocated strings before deleting the plugin. */
plugin_vars_free_values(plugin->system_vars);
+ restore_ptr_backup(plugin->nbackups, plugin->ptr_backup);
my_hash_delete(&plugin_hash[plugin->plugin->type], (uchar*)plugin);
- if (plugin->plugin_dl)
- plugin_dl_del(&plugin->plugin_dl->dl);
+ plugin_dl_del(plugin->plugin_dl);
plugin->state= PLUGIN_IS_FREED;
plugin_array_version++;
free_root(&plugin->mem_root, MYF(0));
@@ -1389,40 +1396,33 @@ static int plugin_initialize(MEM_ROOT *tmp_root, struct st_plugin_int *plugin,
if (plugin->plugin->status_vars)
{
-#ifdef FIX_LATER
/*
- We have a problem right now where we can not prepend without
- breaking backwards compatibility. We will fix this shortly so
- that engines have "use names" and we wil use those for
- CREATE TABLE, and use the plugin name then for adding automatic
- variable names.
+ historical ndb behavior caused MySQL plugins to specify
+ status var names in full, with the plugin name prefix.
+ this was never fixed in MySQL.
+ MariaDB fixes that, but supports MySQL style too.
*/
- SHOW_VAR array[2]= {
+ SHOW_VAR *show_vars= plugin->plugin->status_vars;
+ SHOW_VAR tmp_array[2]= {
{plugin->plugin->name, (char*)plugin->plugin->status_vars, SHOW_ARRAY},
{0, 0, SHOW_UNDEF}
};
- if (add_status_vars(array)) // add_status_vars makes a copy
- goto err;
-#else
- if (add_status_vars(plugin->plugin->status_vars))
+ if (strncasecmp(show_vars->name, plugin->name.str, plugin->name.length))
+ show_vars= tmp_array;
+
+ if (add_status_vars(show_vars))
goto err;
-#endif /* FIX_LATER */
}
ret= 0;
err:
if (ret)
- restore_pluginvar_names(plugin->system_vars);
+ mysql_del_sys_var_chain(plugin->system_vars);
mysql_mutex_lock(&LOCK_plugin);
plugin->state= state;
- /* maintain the obsolete @@have_innodb variable */
- if (!my_strcasecmp(&my_charset_latin1, plugin->name.str, "InnoDB"))
- have_innodb= state & PLUGIN_IS_READY ? SHOW_OPTION_YES
- : SHOW_OPTION_DISABLED;
-
DBUG_RETURN(ret);
}
@@ -1505,13 +1505,15 @@ int plugin_init(int *argc, char **argv, int flags)
if (initialized)
DBUG_RETURN(0);
+ dlopen_count =0;
+
#ifdef HAVE_PSI_INTERFACE
init_plugin_psi_keys();
#endif
- init_alloc_root(&plugin_mem_root, 4096, 4096);
- init_alloc_root(&plugin_vars_mem_root, 4096, 4096);
- init_alloc_root(&tmp_root, 4096, 4096);
+ init_alloc_root(&plugin_mem_root, 4096, 4096, MYF(0));
+ init_alloc_root(&plugin_vars_mem_root, 4096, 4096, MYF(0));
+ init_alloc_root(&tmp_root, 4096, 4096, MYF(0));
if (my_hash_init(&bookmark_hash, &my_charset_bin, 16, 0, 0,
get_bookmark_hash_key, NULL, HASH_UNIQUE))
@@ -1521,9 +1523,9 @@ int plugin_init(int *argc, char **argv, int flags)
mysql_mutex_init(key_LOCK_plugin, &LOCK_plugin, MY_MUTEX_INIT_FAST);
if (my_init_dynamic_array(&plugin_dl_array,
- sizeof(struct st_plugin_dl *),16,16) ||
+ sizeof(struct st_plugin_dl *), 16, 16, MYF(0)) ||
my_init_dynamic_array(&plugin_array,
- sizeof(struct st_plugin_int *),16,16))
+ sizeof(struct st_plugin_int *), 16, 16, MYF(0)))
goto err;
for (i= 0; i < MYSQL_MAX_PLUGIN_TYPE_NUM; i++)
@@ -1534,8 +1536,8 @@ int plugin_init(int *argc, char **argv, int flags)
}
/* prepare debug_sync service */
- DBUG_ASSERT(strcmp(list_of_services[5].name, "debug_sync_service") == 0);
- list_of_services[5].service= *(void**)&debug_sync_C_callback_ptr;
+ DBUG_ASSERT(strcmp(list_of_services[4].name, "debug_sync_service") == 0);
+ list_of_services[4].service= *(void**)&debug_sync_C_callback_ptr;
mysql_mutex_lock(&LOCK_plugin);
@@ -1544,6 +1546,9 @@ int plugin_init(int *argc, char **argv, int flags)
/*
First we register builtin plugins
*/
+ if (global_system_variables.log_warnings >= 9)
+ sql_print_information("Initializing built-in plugins");
+
for (builtins= mysql_mandatory_plugins; *builtins || mandatory; builtins++)
{
if (!*builtins)
@@ -1623,10 +1628,19 @@ 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, opt_plugin_load);
+ I_List_iterator<i_string> iter(opt_plugin_load_list);
+ i_string *item;
+ if (global_system_variables.log_warnings >= 9)
+ sql_print_information("Initializing plugins specified on the command line");
+ while (NULL != (item= iter++))
+ plugin_load_list(&tmp_root, item->ptr);
+
if (!(flags & PLUGIN_INIT_SKIP_PLUGIN_TABLE))
+ {
+ if (global_system_variables.log_warnings >= 9)
+ sql_print_information("Initializing installed plugins");
plugin_load(&tmp_root);
+ }
}
/*
@@ -1710,40 +1724,27 @@ static bool register_builtin(struct st_maria_plugin *plugin,
*/
static void plugin_load(MEM_ROOT *tmp_root)
{
- THD thd;
TABLE_LIST tables;
TABLE *table;
READ_RECORD read_record_info;
int error;
- THD *new_thd= &thd;
+ THD *new_thd= new THD;
bool result;
-#ifdef EMBEDDED_LIBRARY
- No_such_table_error_handler error_handler;
-#endif /* EMBEDDED_LIBRARY */
DBUG_ENTER("plugin_load");
new_thd->thread_stack= (char*) &tables;
new_thd->store_globals();
new_thd->db= my_strdup("mysql", MYF(0));
new_thd->db_length= 5;
- bzero((char*) &thd.net, sizeof(thd.net));
+ bzero((char*) &new_thd->net, sizeof(new_thd->net));
tables.init_one_table("mysql", 5, "plugin", 6, "plugin", TL_READ);
-
-#ifdef EMBEDDED_LIBRARY
- /*
- When building an embedded library, if the mysql.plugin table
- does not exist, we silently ignore the missing table
- */
- new_thd->push_internal_handler(&error_handler);
-#endif /* EMBEDDED_LIBRARY */
+ tables.open_strategy= TABLE_LIST:: IF_EMBEDDED(OPEN_IF_EXISTS, OPEN_NORMAL);
result= open_and_lock_tables(new_thd, &tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT);
-#ifdef EMBEDDED_LIBRARY
- new_thd->pop_internal_handler();
- if (error_handler.safely_trapped_errors())
+ table= tables.table;
+ if (IF_EMBEDDED(!table, false))
goto end;
-#endif /* EMBEDDED_LIBRARY */
if (result)
{
@@ -1755,7 +1756,7 @@ static void plugin_load(MEM_ROOT *tmp_root)
sql_print_warning("Could not open mysql.plugin table. Some options may be missing from the help text");
goto end;
}
- table= tables.table;
+
if (init_read_record(&read_record_info, new_thd, table, NULL, 1, 0, FALSE))
{
sql_print_error("Could not initialize init_read_record; Plugins not "
@@ -1780,20 +1781,17 @@ static void plugin_load(MEM_ROOT *tmp_root)
the mutex here to satisfy the assert
*/
mysql_mutex_lock(&LOCK_plugin);
- if (plugin_add(tmp_root, &name, &dl, REPORT_TO_LOG))
- sql_print_warning("Couldn't load plugin named '%s' with soname '%s'.",
- str_name.c_ptr(), str_dl.c_ptr());
+ plugin_add(tmp_root, &name, &dl, REPORT_TO_LOG);
free_root(tmp_root, MYF(MY_MARK_BLOCKS_FREE));
mysql_mutex_unlock(&LOCK_plugin);
}
if (error > 0)
- sql_print_error(ER(ER_GET_ERRNO), my_errno);
+ sql_print_error(ER(ER_GET_ERRNO), my_errno, table->file->table_type());
end_read_record(&read_record_info);
table->m_needs_reopen= TRUE; // Force close to free memory
close_mysql_tables(new_thd);
end:
- /* Remember that we don't have a THD */
- my_pthread_setspecific_ptr(THR_THD, 0);
+ delete new_thd;
DBUG_VOID_RETURN;
}
@@ -2039,7 +2037,7 @@ static bool finalize_install(THD *thd, TABLE *table, const LEX_STRING *name,
if (tmp->state == PLUGIN_IS_DISABLED)
{
if (global_system_variables.log_warnings)
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_CANT_INITIALIZE_UDF, ER(ER_CANT_INITIALIZE_UDF),
name->str, "Plugin is disabled");
}
@@ -2077,14 +2075,8 @@ bool mysql_install_plugin(THD *thd, const LEX_STRING *name,
char **argv=orig_argv;
DBUG_ENTER("mysql_install_plugin");
- if (opt_noacl)
- {
- my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
- DBUG_RETURN(TRUE);
- }
-
tables.init_one_table("mysql", 5, "plugin", 6, "plugin", TL_WRITE);
- if (check_table_access(thd, INSERT_ACL, &tables, FALSE, 1, FALSE))
+ if (!opt_noacl && check_table_access(thd, INSERT_ACL, &tables, FALSE, 1, FALSE))
DBUG_RETURN(TRUE);
/* need to open before acquiring LOCK_plugin or it will deadlock */
@@ -2178,7 +2170,7 @@ static bool do_uninstall(THD *thd, TABLE *table, const LEX_STRING *name)
plugin->state= PLUGIN_IS_DELETED;
if (plugin->ref_count)
- push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning(thd, Sql_condition::WARN_LEVEL_WARN,
WARN_PLUGIN_BUSY, ER(WARN_PLUGIN_BUSY));
else
reap_needed= true;
@@ -2219,15 +2211,9 @@ bool mysql_uninstall_plugin(THD *thd, const LEX_STRING *name,
bool error= false;
DBUG_ENTER("mysql_uninstall_plugin");
- if (opt_noacl)
- {
- my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
- DBUG_RETURN(TRUE);
- }
-
tables.init_one_table("mysql", 5, "plugin", 6, "plugin", TL_WRITE);
- if (check_table_access(thd, DELETE_ACL, &tables, FALSE, 1, FALSE))
+ if (!opt_noacl && check_table_access(thd, DELETE_ACL, &tables, FALSE, 1, FALSE))
DBUG_RETURN(TRUE);
/* need to open before acquiring LOCK_plugin or it will deadlock */
@@ -2353,6 +2339,74 @@ err:
}
+static bool plugin_dl_foreach_internal(THD *thd, st_plugin_dl *plugin_dl,
+ st_maria_plugin *plug,
+ plugin_foreach_func *func, void *arg)
+{
+ for (; plug->name; plug++)
+ {
+ st_plugin_int tmp, *plugin;
+
+ tmp.name.str= const_cast<char*>(plug->name);
+ tmp.name.length= strlen(plug->name);
+ tmp.plugin= plug;
+ tmp.plugin_dl= plugin_dl;
+
+ mysql_mutex_lock(&LOCK_plugin);
+ if ((plugin= plugin_find_internal(&tmp.name, MYSQL_ANY_PLUGIN)) &&
+ plugin->plugin == plug)
+
+ {
+ tmp.state= plugin->state;
+ tmp.load_option= plugin->load_option;
+ }
+ else
+ {
+ tmp.state= PLUGIN_IS_FREED;
+ tmp.load_option= PLUGIN_OFF;
+ }
+ mysql_mutex_unlock(&LOCK_plugin);
+
+ plugin= &tmp;
+ if (func(thd, plugin_int_to_ref(plugin), arg))
+ return 1;
+ }
+ return 0;
+}
+
+bool plugin_dl_foreach(THD *thd, const LEX_STRING *dl,
+ plugin_foreach_func *func, void *arg)
+{
+ bool err= 0;
+
+ if (dl)
+ {
+ mysql_mutex_lock(&LOCK_plugin);
+ st_plugin_dl *plugin_dl= plugin_dl_add(dl, REPORT_TO_USER);
+ mysql_mutex_unlock(&LOCK_plugin);
+
+ if (!plugin_dl)
+ return 1;
+
+ err= plugin_dl_foreach_internal(thd, plugin_dl, plugin_dl->plugins,
+ func, arg);
+
+ mysql_mutex_lock(&LOCK_plugin);
+ plugin_dl_del(plugin_dl);
+ mysql_mutex_unlock(&LOCK_plugin);
+ }
+ else
+ {
+ struct st_maria_plugin **builtins;
+ for (builtins= mysql_mandatory_plugins; !err && *builtins; builtins++)
+ err= plugin_dl_foreach_internal(thd, 0, *builtins, func, arg);
+ for (builtins= mysql_optional_plugins; !err && *builtins; builtins++)
+ err= plugin_dl_foreach_internal(thd, 0, *builtins, func, arg);
+ }
+ return err;
+}
+
+
/****************************************************************************
Internal type declarations for variables support
****************************************************************************/
@@ -2862,17 +2916,6 @@ static st_bookmark *register_var(const char *plugin, const char *name,
return result;
}
-static void restore_pluginvar_names(sys_var *first)
-{
- mysql_del_sys_var_chain(first);
- for (sys_var *var= first; var; var= var->next)
- {
- sys_var_pluginvar *pv= var->cast_pluginvar();
- pv->plugin_var->name= pv->orig_pluginvar_name;
- }
-}
-
-
/*
returns a pointer to the memory which holds the thd-local variable or
a pointer to the global variable if thd==null.
@@ -2888,8 +2931,6 @@ static uchar *intern_sys_var_ptr(THD* thd, int offset, bool global_lock)
if (!thd)
DBUG_RETURN((uchar*) global_system_variables.dynamic_variables_ptr + offset);
- mysql_mutex_assert_not_owner(&LOCK_open);
-
/*
dynamic_variables_head points to the largest valid offset
*/
@@ -3016,10 +3057,10 @@ void plugin_thdvar_init(THD *thd)
{
plugin_ref old_table_plugin= thd->variables.table_plugin;
DBUG_ENTER("plugin_thdvar_init");
-
+
thd->variables.table_plugin= NULL;
cleanup_variables(thd, &thd->variables);
-
+
thd->variables= global_system_variables;
thd->variables.table_plugin= NULL;
@@ -3282,7 +3323,7 @@ bool sys_var_pluginvar::session_update(THD *thd, set_var *var)
mysql_mutex_unlock(&LOCK_global_system_variables);
plugin_var->update(thd, plugin_var, tgt, src);
-
+
return false;
}
@@ -3370,8 +3411,8 @@ bool sys_var_pluginvar::global_update(THD *thd, set_var *var)
options->block_size= (long) (opt)->blk_sz;
-static void plugin_opt_set_limits(struct my_option *options,
- const struct st_mysql_sys_var *opt)
+void plugin_opt_set_limits(struct my_option *options,
+ const struct st_mysql_sys_var *opt)
{
options->sub_size= 0;
@@ -3477,17 +3518,6 @@ static void plugin_opt_set_limits(struct my_option *options,
options->arg_type= OPT_ARG;
}
-extern "C" my_bool get_one_plugin_option(int optid, const struct my_option *,
- char *);
-
-my_bool get_one_plugin_option(int optid __attribute__((unused)),
- const struct my_option *opt,
- char *argument)
-{
- return 0;
-}
-
-
/**
Creates a set of my_option objects associated with a specified plugin-
handle.
@@ -3717,7 +3747,7 @@ static int construct_options(MEM_ROOT *mem_root, struct st_plugin_int *tmp,
if (opt->flags & PLUGIN_VAR_NOCMDOPT)
continue;
- optname= (char*) memdup_root(mem_root, v->key + 1,
+ optname= (char*) memdup_root(mem_root, v->key + 1,
(optnamelen= v->name_len) + 1);
}
@@ -3773,7 +3803,7 @@ static my_option *construct_help_options(MEM_ROOT *mem_root,
to get the correct (not double-prefixed) help text.
We won't need @@sysvars anymore and don't care about their proper names.
*/
- restore_pluginvar_names(p->system_vars);
+ restore_ptr_backup(p->nbackups, p->ptr_backup);
if (construct_options(mem_root, p, opts))
DBUG_RETURN(NULL);
@@ -3818,6 +3848,7 @@ static int test_plugin_options(MEM_ROOT *tmp_root, struct st_plugin_int *tmp,
sys_var *v __attribute__((unused));
struct st_bookmark *var;
uint len, count= EXTRA_OPTIONS;
+ st_ptr_backup *tmp_backup= 0;
DBUG_ENTER("test_plugin_options");
DBUG_ASSERT(tmp->plugin && tmp->name.str);
@@ -3890,59 +3921,86 @@ static int test_plugin_options(MEM_ROOT *tmp_root, struct st_plugin_int *tmp,
plugin_name= tmp->name;
error= 1;
- for (opt= tmp->plugin->system_vars; opt && *opt; opt++)
+
+ if (tmp->plugin->system_vars)
{
- st_mysql_sys_var *o= *opt;
+ for (len=0, opt= tmp->plugin->system_vars; *opt; len++, opt++) /* no-op */;
+ tmp_backup= (st_ptr_backup *)my_alloca(len * sizeof(tmp_backup[0]));
+ DBUG_ASSERT(tmp->nbackups == 0);
+ DBUG_ASSERT(tmp->ptr_backup == 0);
- /*
- PLUGIN_VAR_STR command-line options without PLUGIN_VAR_MEMALLOC, point
- directly to values in the argv[] array. For plugins started at the
- server startup, argv[] array is allocated with load_defaults(), and
- freed when the server is shut down. But for plugins loaded with
- INSTALL PLUGIN, the memory allocated with load_defaults() is freed with
- freed() at the end of mysql_install_plugin(). Which means we cannot
- allow any pointers into that area.
- Thus, for all plugins loaded after the server was started,
- we copy string values to a plugin's memroot.
- */
- if (mysqld_server_started &&
- ((o->flags & (PLUGIN_VAR_TYPEMASK | PLUGIN_VAR_NOCMDOPT |
- PLUGIN_VAR_MEMALLOC)) == PLUGIN_VAR_STR))
+ for (opt= tmp->plugin->system_vars; *opt; opt++)
{
- sysvar_str_t* str= (sysvar_str_t *)o;
- if (*str->value)
- *str->value= strdup_root(mem_root, *str->value);
- }
+ st_mysql_sys_var *o= *opt;
- if (o->flags & PLUGIN_VAR_NOSYSVAR)
- continue;
- if ((var= find_bookmark(plugin_name.str, o->name, o->flags)))
- v= new (mem_root) sys_var_pluginvar(&chain, var->key + 1, o, tmp);
- else
+ /*
+ PLUGIN_VAR_STR command-line options without PLUGIN_VAR_MEMALLOC, point
+ directly to values in the argv[] array. For plugins started at the
+ server startup, argv[] array is allocated with load_defaults(), and
+ freed when the server is shut down. But for plugins loaded with
+ INSTALL PLUGIN, the memory allocated with load_defaults() is freed with
+ freed() at the end of mysql_install_plugin(). Which means we cannot
+ allow any pointers into that area.
+ Thus, for all plugins loaded after the server was started,
+ we copy string values to a plugin's memroot.
+ */
+ if (mysqld_server_started &&
+ ((o->flags & (PLUGIN_VAR_TYPEMASK | PLUGIN_VAR_NOCMDOPT |
+ PLUGIN_VAR_MEMALLOC)) == PLUGIN_VAR_STR))
+ {
+ sysvar_str_t* str= (sysvar_str_t *)o;
+ if (*str->value)
+ *str->value= strdup_root(mem_root, *str->value);
+ }
+
+ if (o->flags & PLUGIN_VAR_NOSYSVAR)
+ continue;
+ tmp_backup[tmp->nbackups++].save(&o->name);
+ if ((var= find_bookmark(plugin_name.str, o->name, o->flags)))
+ v= new (mem_root) sys_var_pluginvar(&chain, var->key + 1, o, tmp);
+ else
+ {
+ len= plugin_name.length + strlen(o->name) + 2;
+ varname= (char*) alloc_root(mem_root, len);
+ strxmov(varname, plugin_name.str, "-", o->name, NullS);
+ my_casedn_str(&my_charset_latin1, varname);
+ convert_dash_to_underscore(varname, len-1);
+ v= new (mem_root) sys_var_pluginvar(&chain, varname, o, tmp);
+ }
+ DBUG_ASSERT(v); /* check that an object was actually constructed */
+ } /* end for */
+
+ if (tmp->nbackups)
{
- len= plugin_name.length + strlen(o->name) + 2;
- varname= (char*) alloc_root(mem_root, len);
- strxmov(varname, plugin_name.str, "-", o->name, NullS);
- my_casedn_str(&my_charset_latin1, varname);
- convert_dash_to_underscore(varname, len-1);
- v= new (mem_root) sys_var_pluginvar(&chain, varname, o, tmp);
+ size_t bytes= tmp->nbackups * sizeof(tmp->ptr_backup[0]);
+ tmp->ptr_backup= (st_ptr_backup *)alloc_root(mem_root, bytes);
+ if (!tmp->ptr_backup)
+ {
+ restore_ptr_backup(tmp->nbackups, tmp_backup);
+ goto err;
+ }
+ memcpy(tmp->ptr_backup, tmp_backup, bytes);
}
- DBUG_ASSERT(v); /* check that an object was actually constructed */
- } /* end for */
- if (chain.first)
- {
- chain.last->next = NULL;
- if (mysql_add_sys_var_chain(chain.first))
+
+ if (chain.first)
{
- sql_print_error("Plugin '%s' has conflicting system variables",
- tmp->name.str);
- goto err;
+ chain.last->next = NULL;
+ if (mysql_add_sys_var_chain(chain.first))
+ {
+ sql_print_error("Plugin '%s' has conflicting system variables",
+ tmp->name.str);
+ goto err;
+ }
+ tmp->system_vars= chain.first;
}
- tmp->system_vars= chain.first;
+ my_afree(tmp_backup);
}
+
DBUG_RETURN(0);
-
+
err:
+ if (tmp_backup)
+ my_afree(tmp_backup);
if (opts)
my_cleanup_options(opts);
DBUG_RETURN(error);
@@ -3976,3 +4034,53 @@ void add_plugin_options(DYNAMIC_ARRAY *options, MEM_ROOT *mem_root)
}
}
+
+/**
+ Returns a sys_var corresponding to a particular MYSQL_SYSVAR(...)
+*/
+sys_var *find_plugin_sysvar(st_plugin_int *plugin, st_mysql_sys_var *plugin_var)
+{
+ for (sys_var *var= plugin->system_vars; var; var= var->next)
+ {
+ sys_var_pluginvar *pvar=var->cast_pluginvar();
+ if (pvar->plugin_var == plugin_var)
+ return var;
+ }
+ return 0;
+}
+
+/*
+ On dlclose() we need to restore values of all symbols that we've modified in
+ the DSO. The reason is - the DSO might not actually be unloaded, so on the
+ next dlopen() these symbols will have old values, they won't be
+ reinitialized.
+
+ Perhaps, there can be many reason, why a DSO won't be unloaded. Strictly
+ speaking, it's implementation defined whether to unload an unused DSO or to
+ keep it in memory.
+
+ In particular, this happens for some plugins: In 2009 a new ELF stub was
+ introduced, see Ulrich Drepper's email "Unique symbols for C++"
+ http://www.redhat.com/archives/posix-c++-wg/2009-August/msg00002.html
+
+ DSO that has objects with this stub (STB_GNU_UNIQUE) cannot be unloaded
+ (this is mentioned in the email, see the url above).
+
+ These "unique" objects are, for example, static variables in templates,
+ in inline functions, in classes. So any DSO that uses them can
+ only be loaded once. And because Boost has them, any DSO that uses Boost
+ almost certainly cannot be unloaded.
+
+ To know whether a particular DSO has these objects, one can use
+
+ readelf -s /path/to/plugin.so|grep UNIQUE
+
+ There's nothing we can do about it, but to reset the DSO to its initial
+ state before dlclose().
+*/
+static void restore_ptr_backup(uint n, st_ptr_backup *backup)
+{
+ while (n--)
+ (backup++)->restore();
+}
+
diff --git a/sql/sql_plugin.h b/sql/sql_plugin.h
index be1cfcdcc4f..a0225f4a071 100644
--- a/sql/sql_plugin.h
+++ b/sql/sql_plugin.h
@@ -17,7 +17,6 @@
#ifndef _sql_plugin_h
#define _sql_plugin_h
-#include <my_global.h>
/*
the following #define adds server-only members to enum_mysql_show_type,
@@ -27,7 +26,7 @@
SHOW_LONG_STATUS, SHOW_DOUBLE_STATUS, \
SHOW_HAVE, SHOW_MY_BOOL, SHOW_HA_ROWS, SHOW_SYS, \
SHOW_LONG_NOFLUSH, SHOW_LONGLONG_STATUS, SHOW_LEX_STRING
-#include <mysql/plugin.h>
+#include <my_global.h>
#undef SHOW_always_last
#include "m_string.h" /* LEX_STRING */
@@ -39,7 +38,10 @@ enum enum_plugin_load_option { PLUGIN_OFF, PLUGIN_ON, PLUGIN_FORCE,
PLUGIN_FORCE_PLUS_PERMANENT };
extern const char *global_plugin_typelib_names[];
+extern ulong dlopen_count;
+
#include <my_sys.h>
+#include "sql_list.h"
#ifdef DBUG_OFF
#define plugin_ref_to_int(A) A
@@ -79,15 +81,25 @@ typedef struct st_mysql_show_var SHOW_VAR;
/* A handle for the dynamic library containing a plugin or plugins. */
+struct st_ptr_backup {
+ void **ptr;
+ void *value;
+ void save(void **p) { ptr= p; value= *p; }
+ void save(const char **p) { save((void**)p); }
+ void restore() { *ptr= value; }
+};
+
struct st_plugin_dl
{
LEX_STRING dl;
void *handle;
struct st_maria_plugin *plugins;
+ st_ptr_backup *ptr_backup;
+ uint nbackups;
+ uint ref_count; /* number of plugins loaded from the library */
int mysqlversion;
int mariaversion;
bool allocated;
- uint ref_count; /* number of plugins loaded from the library */
};
/* A handle of a plugin */
@@ -97,6 +109,8 @@ struct st_plugin_int
LEX_STRING name;
struct st_maria_plugin *plugin;
struct st_plugin_dl *plugin_dl;
+ st_ptr_backup *ptr_backup;
+ uint nbackups;
uint state;
uint ref_count; /* number of threads using the plugin */
uint locks_total; /* how many times the plugin was locked */
@@ -137,7 +151,7 @@ typedef struct st_plugin_int **plugin_ref;
typedef int (*plugin_type_init)(struct st_plugin_int *);
-extern char *opt_plugin_load;
+extern I_List<i_string> *opt_plugin_load_list_ptr;
extern char *opt_plugin_dir_ptr;
extern char opt_plugin_dir[FN_REFLEN];
extern const LEX_STRING plugin_type_names[];
@@ -150,9 +164,7 @@ extern void plugin_shutdown(void);
void add_plugin_options(DYNAMIC_ARRAY *options, MEM_ROOT *mem_root);
extern bool plugin_is_ready(const LEX_STRING *name, int type);
#define my_plugin_lock_by_name(A,B,C) plugin_lock_by_name(A,B,C)
-#define my_plugin_lock_by_name_ci(A,B,C) plugin_lock_by_name(A,B,C)
#define my_plugin_lock(A,B) plugin_lock(A,B)
-#define my_plugin_lock_ci(A,B) plugin_lock(A,B)
extern plugin_ref plugin_lock(THD *thd, plugin_ref ptr);
extern plugin_ref plugin_lock_by_name(THD *thd, const LEX_STRING *name,
int type);
@@ -165,6 +177,8 @@ extern bool mysql_uninstall_plugin(THD *thd, const LEX_STRING *name,
extern bool plugin_register_builtin(struct st_mysql_plugin *plugin);
extern void plugin_thdvar_init(THD *thd);
extern void plugin_thdvar_cleanup(THD *thd);
+sys_var *find_plugin_sysvar(st_plugin_int *plugin, st_mysql_sys_var *var);
+void plugin_opt_set_limits(struct my_option *, const struct st_mysql_sys_var *);
extern SHOW_COMP_OPTION plugin_status(const char *name, size_t len, int type);
extern bool check_valid_path(const char *path, size_t length);
@@ -174,4 +188,6 @@ typedef my_bool (plugin_foreach_func)(THD *thd,
#define plugin_foreach(A,B,C,D) plugin_foreach_with_mask(A,B,C,PLUGIN_IS_READY,D)
extern bool plugin_foreach_with_mask(THD *thd, plugin_foreach_func *func,
int type, uint state_mask, void *arg);
+extern bool plugin_dl_foreach(THD *thd, const LEX_STRING *dl,
+ plugin_foreach_func *func, void *arg);
#endif
diff --git a/sql/sql_plugin_services.h b/sql/sql_plugin_services.h
index ede8d9a675e..38b4c4074be 100644
--- a/sql/sql_plugin_services.h
+++ b/sql/sql_plugin_services.h
@@ -1,4 +1,5 @@
-/* Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
+/* Copyright (c) 2009, 2010, Oracle and/or its affiliates.
+ Copyright (c) 2012, 2013, Monty Program Ab
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -41,11 +42,6 @@ static struct thd_wait_service_st thd_wait_handler= {
thd_wait_end
};
-static struct my_thread_scheduler_service my_thread_scheduler_handler= {
- my_thread_scheduler_set,
- my_thread_scheduler_reset,
-};
-
static struct progress_report_service_st progress_report_handler= {
thd_progress_init,
thd_progress_report,
@@ -58,6 +54,16 @@ static struct kill_statement_service_st thd_kill_statement_handler= {
thd_kill_level
};
+static struct thd_timezone_service_st thd_timezone_handler= {
+ thd_TIME_to_gmt_sec,
+ thd_gmt_sec_to_TIME
+};
+
+static struct my_sha1_service_st my_sha1_handler = {
+ my_sha1,
+ my_sha1_multi
+};
+
static struct logger_service_st logger_service_handler= {
logger_init_mutexes,
logger_open,
@@ -68,15 +74,30 @@ static struct logger_service_st logger_service_handler= {
logger_rotate
};
+static struct thd_autoinc_service_st thd_autoinc_handler= {
+ thd_get_autoinc
+};
+
+static struct thd_error_context_service_st thd_error_conext_handler= {
+ thd_get_error_message,
+ thd_get_error_number,
+ thd_get_error_row,
+ thd_inc_error_row,
+ thd_get_error_context_description
+};
+
static struct st_service_ref list_of_services[]=
{
{ "my_snprintf_service", VERSION_my_snprintf, &my_snprintf_handler },
{ "thd_alloc_service", VERSION_thd_alloc, &thd_alloc_handler },
{ "thd_wait_service", VERSION_thd_wait, &thd_wait_handler },
- { "my_thread_scheduler_service", VERSION_my_thread_scheduler, &my_thread_scheduler_handler },
{ "progress_report_service", VERSION_progress_report, &progress_report_handler },
{ "debug_sync_service", VERSION_debug_sync, 0 }, // updated in plugin_init()
{ "thd_kill_statement_service", VERSION_kill_statement, &thd_kill_statement_handler },
+ { "thd_timezone_service", VERSION_thd_timezone, &thd_timezone_handler },
+ { "my_sha1_service", VERSION_my_sha1, &my_sha1_handler},
{ "logger_service", VERSION_logger, &logger_service_handler },
+ { "thd_autoinc_service", VERSION_thd_autoinc, &thd_autoinc_handler },
+ { "thd_error_context_service", VERSION_thd_error_context, &thd_error_conext_handler },
};
diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc
index 9318dae2e31..3df9ebcd6bc 100644
--- a/sql/sql_prepare.cc
+++ b/sql/sql_prepare.cc
@@ -74,7 +74,7 @@ When one supplies long data for a placeholder:
- Server gets the long data in pieces with command type
'COM_STMT_SEND_LONG_DATA'.
- - The packet recieved will have the format as:
+ - The packet received will have the format as:
[COM_STMT_SEND_LONG_DATA:1][STMT_ID:4][parameter_number:2][data]
- data from the packet is appended to the long data value buffer for this
placeholder.
@@ -84,7 +84,7 @@ When one supplies long data for a placeholder:
at statement execute.
*/
-#include "my_global.h" /* NO_EMBEDDED_ACCESS_CHECKS */
+#include <my_global.h> /* NO_EMBEDDED_ACCESS_CHECKS */
#include "sql_priv.h"
#include "unireg.h"
#include "sql_class.h" // set_var.h: THD
@@ -106,6 +106,7 @@ When one supplies long data for a placeholder:
#include "sp_head.h"
#include "sp.h"
#include "sp_cache.h"
+#include "sql_handler.h" // mysql_ha_rm_tables
#include "probes_mysql.h"
#ifdef EMBEDDED_LIBRARY
/* include MYSQL_BIND headers */
@@ -344,7 +345,7 @@ static bool send_prep_stmt(Prepared_statement *stmt, uint columns)
int2store(buff+5, columns);
int2store(buff+7, stmt->param_count);
buff[9]= 0; // Guard against a 4.1 client
- tmp= min(stmt->thd->warning_info->statement_warn_count(), 65535);
+ tmp= MY_MIN(stmt->thd->get_stmt_da()->current_statement_warn_count(), 65535);
int2store(buff+10, tmp);
/*
@@ -361,7 +362,7 @@ static bool send_prep_stmt(Prepared_statement *stmt, uint columns)
if (!error)
/* Flag that a response has already been sent */
- thd->stmt_da->disable_status();
+ thd->get_stmt_da()->disable_status();
DBUG_RETURN(error);
}
@@ -374,7 +375,7 @@ static bool send_prep_stmt(Prepared_statement *stmt,
thd->client_stmt_id= stmt->id;
thd->client_param_count= stmt->param_count;
thd->clear_error();
- thd->stmt_da->disable_status();
+ thd->get_stmt_da()->disable_status();
return 0;
}
@@ -977,7 +978,7 @@ static bool setup_conversion_functions(Prepared_statement *stmt,
typecode= sint2korr(read_pos);
read_pos+= 2;
- (**it).unsigned_flag= test(typecode & signed_bit);
+ (**it).unsigned_flag= MY_TEST(typecode & signed_bit);
setup_one_conversion_function(thd, *it, (uchar) (typecode & ~signed_bit));
}
}
@@ -1254,6 +1255,17 @@ static bool mysql_test_insert(Prepared_statement *stmt,
List_item *values;
DBUG_ENTER("mysql_test_insert");
+ /*
+ Since INSERT DELAYED doesn't support temporary tables, we could
+ not pre-open temporary tables for SQLCOM_INSERT / SQLCOM_REPLACE.
+ Open them here instead.
+ */
+ if (table_list->lock_type != TL_WRITE_DELAYED)
+ {
+ if (open_temporary_tables(thd, table_list))
+ goto error;
+ }
+
if (insert_precheck(thd, table_list))
goto error;
@@ -1461,7 +1473,10 @@ static bool mysql_test_delete(Prepared_statement *stmt,
goto error;
}
- DBUG_RETURN(mysql_prepare_delete(thd, table_list, &lex->select_lex.where));
+ DBUG_RETURN(mysql_prepare_delete(thd, table_list,
+ lex->select_lex.with_wild,
+ lex->select_lex.item_list,
+ &lex->select_lex.where));
error:
DBUG_RETURN(TRUE);
}
@@ -1763,7 +1778,7 @@ static bool mysql_test_create_table(Prepared_statement *stmt)
if (select_lex->item_list.elements)
{
/* Base table and temporary table are not in the same name space. */
- if (!(lex->create_info.options & HA_LEX_CREATE_TMP_TABLE))
+ if (!lex->create_info.tmp_table())
create_table->open_type= OT_BASE_ONLY;
if (open_normal_and_derived_tables(stmt->thd, lex->query_tables,
@@ -1822,6 +1837,13 @@ static bool mysql_test_create_view(Prepared_statement *stmt)
if (create_view_precheck(thd, tables, view, lex->create_view_mode))
goto err;
+ /*
+ Since we can't pre-open temporary tables for SQLCOM_CREATE_VIEW,
+ (see mysql_create_view) we have to do it here instead.
+ */
+ if (open_temporary_tables(thd, tables))
+ goto err;
+
if (open_normal_and_derived_tables(thd, tables, MYSQL_OPEN_FORCE_SHARED_MDL,
DT_PREPARE))
goto err;
@@ -2057,7 +2079,20 @@ static bool check_prepared_statement(Prepared_statement *stmt)
/* Reset warning count for each query that uses tables */
if (tables)
- thd->warning_info->opt_clear_warning_info(thd->query_id);
+ thd->get_stmt_da()->opt_clear_warning_info(thd->query_id);
+
+ if (sql_command_flags[sql_command] & CF_HA_CLOSE)
+ mysql_ha_rm_tables(thd, tables);
+
+ /*
+ Open temporary tables that are known now. Temporary tables added by
+ prelocking will be opened afterwards (during open_tables()).
+ */
+ if (sql_command_flags[sql_command] & CF_PREOPEN_TMP_TABLES)
+ {
+ if (open_temporary_tables(thd, tables))
+ goto error;
+ }
switch (sql_command) {
case SQLCOM_REPLACE:
@@ -2145,6 +2180,7 @@ static bool check_prepared_statement(Prepared_statement *stmt)
Note that we don't need to have cases in this list if they are
marked with CF_STATUS_COMMAND in sql_command_flags
*/
+ case SQLCOM_SHOW_EXPLAIN:
case SQLCOM_DROP_TABLE:
case SQLCOM_RENAME_TABLE:
case SQLCOM_ALTER_TABLE:
@@ -2162,6 +2198,8 @@ static bool check_prepared_statement(Prepared_statement *stmt)
case SQLCOM_FLUSH:
case SQLCOM_SLAVE_START:
case SQLCOM_SLAVE_STOP:
+ case SQLCOM_SLAVE_ALL_START:
+ case SQLCOM_SLAVE_ALL_STOP:
case SQLCOM_INSTALL_PLUGIN:
case SQLCOM_UNINSTALL_PLUGIN:
case SQLCOM_CREATE_DB:
@@ -2176,6 +2214,7 @@ static bool check_prepared_statement(Prepared_statement *stmt)
case SQLCOM_GRANT:
case SQLCOM_REVOKE:
case SQLCOM_KILL:
+ case SQLCOM_SHUTDOWN:
break;
case SQLCOM_PREPARE:
@@ -2486,6 +2525,7 @@ void reinit_stmt_before_use(THD *thd, LEX *lex)
object and because of this can be used in different threads.
*/
lex->thd= thd;
+ DBUG_ASSERT(!lex->explain);
if (lex->empty_field_list_on_rset)
{
@@ -2677,7 +2717,7 @@ void mysqld_stmt_execute(THD *thd, char *packet_arg, uint packet_length)
DBUG_PRINT("exec_query", ("%s", stmt->query()));
DBUG_PRINT("info",("stmt: 0x%lx", (long) stmt));
- open_cursor= test(flags & (ulong) CURSOR_TYPE_READ_ONLY);
+ open_cursor= MY_TEST(flags & (ulong) CURSOR_TYPE_READ_ONLY);
thd->protocol= &thd->protocol_binary;
stmt->execute_loop(&expanded_query, open_cursor, packet, packet_end);
@@ -2841,7 +2881,7 @@ void mysqld_stmt_reset(THD *thd, char *packet)
stmt->state= Query_arena::STMT_PREPARED;
- general_log_print(thd, thd->command, NullS);
+ general_log_print(thd, thd->get_command(), NullS);
my_ok(thd);
@@ -2863,7 +2903,7 @@ void mysqld_stmt_close(THD *thd, char *packet)
Prepared_statement *stmt;
DBUG_ENTER("mysqld_stmt_close");
- thd->stmt_da->disable_status();
+ thd->get_stmt_da()->disable_status();
if (!(stmt= find_prepared_statement(thd, stmt_id)))
DBUG_VOID_RETURN;
@@ -2874,7 +2914,7 @@ void mysqld_stmt_close(THD *thd, char *packet)
*/
DBUG_ASSERT(! stmt->is_in_use());
stmt->deallocate();
- general_log_print(thd, thd->command, NullS);
+ general_log_print(thd, thd->get_command(), NullS);
DBUG_VOID_RETURN;
}
@@ -2939,7 +2979,7 @@ void mysql_stmt_get_longdata(THD *thd, char *packet, ulong packet_length)
status_var_increment(thd->status_var.com_stmt_send_long_data);
- thd->stmt_da->disable_status();
+ thd->get_stmt_da()->disable_status();
#ifndef EMBEDDED_LIBRARY
/* Minimal size of long data packet is 6 bytes */
if (packet_length < MYSQL_LONG_DATA_HEADER)
@@ -2968,28 +3008,25 @@ void mysql_stmt_get_longdata(THD *thd, char *packet, ulong packet_length)
param= stmt->param_array[param_number];
- Diagnostics_area new_stmt_da, *save_stmt_da= thd->stmt_da;
- Warning_info new_warnning_info(thd->query_id, false);
- Warning_info *save_warinig_info= thd->warning_info;
+ Diagnostics_area new_stmt_da(thd->query_id, false, true);
+ Diagnostics_area *save_stmt_da= thd->get_stmt_da();
- thd->stmt_da= &new_stmt_da;
- thd->warning_info= &new_warnning_info;
+ thd->set_stmt_da(&new_stmt_da);
#ifndef EMBEDDED_LIBRARY
param->set_longdata(packet, (ulong) (packet_end - packet));
#else
param->set_longdata(thd->extra_data, thd->extra_length);
#endif
- if (thd->stmt_da->is_error())
+ if (thd->get_stmt_da()->is_error())
{
stmt->state= Query_arena::STMT_ERROR;
- stmt->last_errno= thd->stmt_da->sql_errno();
- strncpy(stmt->last_error, thd->stmt_da->message(), MYSQL_ERRMSG_SIZE);
+ stmt->last_errno= thd->get_stmt_da()->sql_errno();
+ strncpy(stmt->last_error, thd->get_stmt_da()->message(), MYSQL_ERRMSG_SIZE);
}
- thd->stmt_da= save_stmt_da;
- thd->warning_info= save_warinig_info;
+ thd->set_stmt_da(save_stmt_da);
- general_log_print(thd, thd->command, NullS);
+ general_log_print(thd, thd->get_command(), NullS);
DBUG_VOID_RETURN;
}
@@ -3063,8 +3100,7 @@ Reprepare_observer::report_error(THD *thd)
that this thread execution stops and returns to the caller,
backtracking all the way to Prepared_statement::execute_loop().
*/
- thd->stmt_da->set_error_status(thd, ER_NEED_REPREPARE,
- ER(ER_NEED_REPREPARE), "HY000");
+ thd->get_stmt_da()->set_error_status(ER_NEED_REPREPARE);
m_invalidated= TRUE;
return TRUE;
@@ -3099,6 +3135,7 @@ Execute_sql_statement(LEX_STRING sql_text)
bool
Execute_sql_statement::execute_server_code(THD *thd)
{
+ PSI_statement_locker *parent_locker;
bool error;
if (alloc_query(thd, m_sql_text.str, m_sql_text.length))
@@ -3118,7 +3155,10 @@ Execute_sql_statement::execute_server_code(THD *thd)
thd->lex->set_trg_event_type_for_tables();
+ parent_locker= thd->m_statement_psi;
+ thd->m_statement_psi= NULL;
error= mysql_execute_command(thd);
+ thd->m_statement_psi= parent_locker;
/* report error issued during command execution */
if (error == 0 && thd->spcont == NULL)
@@ -3147,7 +3187,7 @@ Prepared_statement::Prepared_statement(THD *thd_arg)
flags((uint) IS_IN_USE)
{
init_sql_alloc(&main_mem_root, thd_arg->variables.query_alloc_block_size,
- thd_arg->variables.query_prealloc_size);
+ thd_arg->variables.query_prealloc_size, MYF(MY_THREAD_SPECIFIC));
*last_error= '\0';
}
@@ -3510,8 +3550,10 @@ Prepared_statement::set_parameters(String *expanded_query,
}
#ifdef WITH_WSREP
+/* forward declaration */
void wsrep_replay_transaction(THD *thd);
#endif /* WITH_WSREP */
+
/**
Execute a prepared statement. Re-prepare it a limited number
of times if necessary.
@@ -3545,7 +3587,6 @@ Prepared_statement::execute_loop(String *expanded_query,
Reprepare_observer reprepare_observer;
bool error;
int reprepare_attempt= 0;
- bool need_set_parameters= true;
/* Check if we got an error when sending long data */
if (state == Query_arena::STMT_ERROR)
@@ -3554,20 +3595,19 @@ Prepared_statement::execute_loop(String *expanded_query,
return TRUE;
}
-reexecute:
- if (need_set_parameters &&
- set_parameters(expanded_query, packet, packet_end))
+ if (set_parameters(expanded_query, packet, packet_end))
return TRUE;
- /*
- if set_parameters() has generated warnings,
- we need to repeat it when reexecuting, to recreate these
- warnings.
- */
- need_set_parameters= thd->warning_info->statement_warn_count();
-
- reprepare_observer.reset_reprepare_observer();
+#ifdef NOT_YET_FROM_MYSQL_5_6
+ if (unlikely(thd->security_ctx->password_expired &&
+ !lex->is_change_password))
+ {
+ my_error(ER_MUST_CHANGE_PASSWORD, MYF(0));
+ return true;
+ }
+#endif
+reexecute:
/*
If the free_list is not empty, we'll wrongly free some externally
allocated items when cleaning up after validation of the prepared
@@ -3581,11 +3621,12 @@ reexecute:
the observer method will be invoked to push an error into
the error stack.
*/
- if (sql_command_flags[lex->sql_command] &
- CF_REEXECUTION_FRAGILE)
+
+ if (sql_command_flags[lex->sql_command] & CF_REEXECUTION_FRAGILE)
{
+ reprepare_observer.reset_reprepare_observer();
DBUG_ASSERT(thd->m_reprepare_observer == NULL);
- thd->m_reprepare_observer = &reprepare_observer;
+ thd->m_reprepare_observer= &reprepare_observer;
}
error= execute(expanded_query, open_cursor) || thd->is_error();
@@ -3597,22 +3638,25 @@ reexecute:
{
case CERT_FAILURE:
WSREP_DEBUG("PS execute fail for CERT_FAILURE: thd: %ld err: %d",
- thd->thread_id, thd->stmt_da->sql_errno() );
+ thd->thread_id, thd->get_stmt_da()->sql_errno() );
thd->wsrep_conflict_state = NO_CONFLICT;
break;
case MUST_REPLAY:
- (void)wsrep_replay_transaction(thd);
+ (void) wsrep_replay_transaction(thd);
+ /* fallthrough */
+
default: break;
}
mysql_mutex_unlock(&thd->LOCK_wsrep_thd);
#endif /* WITH_WSREP */
- if (error && !thd->is_fatal_error && !thd->killed &&
+ if ((sql_command_flags[lex->sql_command] & CF_REEXECUTION_FRAGILE) &&
+ error && !thd->is_fatal_error && !thd->killed &&
reprepare_observer.is_invalidated() &&
reprepare_attempt++ < MAX_REPREPARE_ATTEMPTS)
{
- DBUG_ASSERT(thd->stmt_da->sql_errno() == ER_NEED_REPREPARE);
+ DBUG_ASSERT(thd->get_stmt_da()->sql_errno() == ER_NEED_REPREPARE);
thd->clear_error();
error= reprepare();
@@ -3714,7 +3758,7 @@ Prepared_statement::reprepare()
Sic: we can't simply silence warnings during reprepare, because if
it's failed, we need to return all the warnings to the user.
*/
- thd->warning_info->clear_warning_info(thd->query_id);
+ thd->get_stmt_da()->clear_warning_info(thd->query_id);
}
return error;
}
@@ -3939,13 +3983,17 @@ bool Prepared_statement::execute(String *expanded_query, bool open_cursor)
if (query_cache_send_result_to_client(thd, thd->query(),
thd->query_length()) <= 0)
{
+ PSI_statement_locker *parent_locker;
MYSQL_QUERY_EXEC_START(thd->query(),
thd->thread_id,
(char *) (thd->db ? thd->db : ""),
&thd->security_ctx->priv_user[0],
(char *) thd->security_ctx->host_or_ip,
1);
+ parent_locker= thd->m_statement_psi;
+ thd->m_statement_psi= NULL;
error= mysql_execute_command(thd);
+ thd->m_statement_psi= parent_locker;
MYSQL_QUERY_EXEC_DONE(error);
}
else
@@ -3972,6 +4020,21 @@ bool Prepared_statement::execute(String *expanded_query, bool open_cursor)
if (! cursor)
cleanup_stmt();
+
+ /*
+ EXECUTE command has its own dummy "explain data". We don't need it,
+ instead, we want to keep the query plan of the statement that was
+ executed.
+ */
+ if (!stmt_backup.lex->explain ||
+ !stmt_backup.lex->explain->have_query_plan())
+ {
+ delete_explain_query(stmt_backup.lex);
+ stmt_backup.lex->explain = thd->lex->explain;
+ thd->lex->explain= NULL;
+ }
+ else
+ delete_explain_query(thd->lex);
thd->set_statement(&stmt_backup);
thd->stmt_arena= old_stmt_arena;
@@ -4082,7 +4145,7 @@ Ed_result_set::Ed_result_set(List<Ed_row> *rows_arg,
*/
Ed_connection::Ed_connection(THD *thd)
- :m_warning_info(thd->query_id, false),
+ :m_diagnostics_area(thd->query_id, false, true),
m_thd(thd),
m_rsets(0),
m_current_rset(0)
@@ -4108,7 +4171,7 @@ Ed_connection::free_old_result()
}
m_current_rset= m_rsets;
m_diagnostics_area.reset_diagnostics_area();
- m_warning_info.clear_warning_info(m_thd->query_id);
+ m_diagnostics_area.clear_warning_info(m_thd->query_id);
}
@@ -4145,23 +4208,20 @@ bool Ed_connection::execute_direct(Server_runnable *server_runnable)
Protocol_local protocol_local(m_thd, this);
Prepared_statement stmt(m_thd);
Protocol *save_protocol= m_thd->protocol;
- Diagnostics_area *save_diagnostics_area= m_thd->stmt_da;
- Warning_info *save_warning_info= m_thd->warning_info;
+ Diagnostics_area *save_diagnostics_area= m_thd->get_stmt_da();
DBUG_ENTER("Ed_connection::execute_direct");
free_old_result(); /* Delete all data from previous execution, if any */
m_thd->protocol= &protocol_local;
- m_thd->stmt_da= &m_diagnostics_area;
- m_thd->warning_info= &m_warning_info;
+ m_thd->set_stmt_da(&m_diagnostics_area);
rc= stmt.execute_server_runnable(server_runnable);
m_thd->protocol->end_statement();
m_thd->protocol= save_protocol;
- m_thd->stmt_da= save_diagnostics_area;
- m_thd->warning_info= save_warning_info;
+ m_thd->set_stmt_da(save_diagnostics_area);
/*
Protocol_local makes use of m_current_rset to keep
track of the last result set, while adding result sets to the end.
@@ -4451,7 +4511,7 @@ bool Protocol_local::store(const char *str, size_t length,
bool Protocol_local::store(MYSQL_TIME *time, int decimals)
{
if (decimals != AUTO_SEC_PART_DIGITS)
- time->second_part= sec_part_truncate(time->second_part, decimals);
+ my_time_trunc(time, decimals);
return store_column(time, sizeof(MYSQL_TIME));
}
@@ -4469,7 +4529,7 @@ bool Protocol_local::store_date(MYSQL_TIME *time)
bool Protocol_local::store_time(MYSQL_TIME *time, int decimals)
{
if (decimals != AUTO_SEC_PART_DIGITS)
- time->second_part= sec_part_truncate(time->second_part, decimals);
+ my_time_trunc(time, decimals);
return store_column(time, sizeof(MYSQL_TIME));
}
@@ -4506,7 +4566,7 @@ bool Protocol_local::send_result_set_metadata(List<Item> *columns, uint)
{
DBUG_ASSERT(m_rset == 0 && !alloc_root_inited(&m_rset_root));
- init_sql_alloc(&m_rset_root, MEM_ROOT_BLOCK_SIZE, 0);
+ init_sql_alloc(&m_rset_root, MEM_ROOT_BLOCK_SIZE, 0, MYF(MY_THREAD_SPECIFIC));
if (! (m_rset= new (&m_rset_root) List<Ed_row>))
return TRUE;
diff --git a/sql/sql_prepare.h b/sql/sql_prepare.h
index e0891bbd188..b468ac1bf9b 100644
--- a/sql/sql_prepare.h
+++ b/sql/sql_prepare.h
@@ -253,16 +253,9 @@ public:
*/
ulong get_warn_count() const
{
- return m_warning_info.warn_count();
+ return m_diagnostics_area.warn_count();
}
- /**
- Get the server warnings as a result set.
- The result set has fixed metadata:
- The first column is the level.
- The second is a numeric code.
- The third is warning text.
- */
- List<MYSQL_ERROR> *get_warn_list() { return &m_warning_info.warn_list(); }
+
/**
The following members are only valid if execute_direct()
or move_to_next_result() returned an error.
@@ -297,7 +290,7 @@ public:
one.
Never fails.
*/
- bool has_next_result() const { return test(m_current_rset->m_next_rset); }
+ bool has_next_result() const { return MY_TEST(m_current_rset->m_next_rset); }
/**
Only valid to call if has_next_result() returned true.
Otherwise the result is undefined.
@@ -305,13 +298,12 @@ public:
bool move_to_next_result()
{
m_current_rset= m_current_rset->m_next_rset;
- return test(m_current_rset);
+ return MY_TEST(m_current_rset);
}
~Ed_connection() { free_old_result(); }
private:
Diagnostics_area m_diagnostics_area;
- Warning_info m_warning_info;
/**
Execute direct interface does not support multi-statements, only
multi-results. So we never have a situation when we have
diff --git a/sql/sql_priv.h b/sql/sql_priv.h
index 749ee245aa7..09a22ba0444 100644
--- a/sql/sql_priv.h
+++ b/sql/sql_priv.h
@@ -50,7 +50,7 @@
do { \
compile_time_assert(MYSQL_VERSION_ID < VerHi * 10000 + VerLo * 100); \
if (((THD *) Thd) != NULL) \
- push_warning_printf(((THD *) Thd), MYSQL_ERROR::WARN_LEVEL_WARN, \
+ push_warning_printf(((THD *) Thd), Sql_condition::WARN_LEVEL_WARN, \
ER_WARN_DEPRECATED_SYNTAX, \
ER(ER_WARN_DEPRECATED_SYNTAX), \
(Old), (New)); \
@@ -78,7 +78,7 @@
#define WARN_DEPRECATED_NO_REPLACEMENT(Thd,Old) \
do { \
if (((THD *) Thd) != NULL) \
- push_warning_printf(((THD *) Thd), MYSQL_ERROR::WARN_LEVEL_WARN, \
+ push_warning_printf(((THD *) Thd), Sql_condition::WARN_LEVEL_WARN, \
ER_WARN_DEPRECATED_SYNTAX_NO_REPLACEMENT, \
ER(ER_WARN_DEPRECATED_SYNTAX_NO_REPLACEMENT), \
(Old)); \
@@ -137,6 +137,8 @@
/* The following is used to detect a conflict with DISTINCT */
#define SELECT_ALL (1ULL << 24) // SELECT, user, parser
+#define OPTION_GTID_BEGIN (1ULL << 25) // GTID BEGIN found in log
+
/** The following can be set when importing tables in a 'wrong order'
to suppress foreign key checks */
#define OPTION_NO_FOREIGN_KEY_CHECKS (1ULL << 26) // THD, user, binlog
@@ -170,50 +172,15 @@
however, needs to rollback the effects of the
succeeded statement to keep replication consistent.
*/
-#define OPTION_MASTER_SQL_ERROR (1ULL << 35)
+#define OPTION_MASTER_SQL_ERROR (1ULL << 35)
/*
Dont report errors for individual rows,
But just report error on commit (or read ofcourse)
Note! Reserved for use in MySQL Cluster
*/
-#define OPTION_ALLOW_BATCH (ULL(1) << 36) // THD, intern (slave)
-#define OPTION_SKIP_REPLICATION (ULL(1) << 37) // THD, user
-
-/*
- Check how many bytes are available on buffer.
-
- @param buf_start Pointer to buffer start.
- @param buf_current Pointer to the current position on buffer.
- @param buf_len Buffer length.
-
- @return Number of bytes available on event buffer.
-*/
-template <class T> T available_buffer(const char* buf_start,
- const char* buf_current,
- T buf_len)
-{
- return buf_len - (buf_current - buf_start);
-}
-
-/*
- Check if jump value is within buffer limits.
-
- @param jump Number of positions we want to advance.
- @param buf_start Pointer to buffer start
- @param buf_current Pointer to the current position on buffer.
- @param buf_len Buffer length.
-
- @return True If jump value is within buffer limits.
- False Otherwise.
-*/
-template <class T> bool valid_buffer_range(T jump,
- const char* buf_start,
- const char* buf_current,
- T buf_len)
-{
- return (jump <= available_buffer(buf_start, buf_current, buf_len));
-}
+#define OPTION_ALLOW_BATCH (1ULL << 36) // THD, intern (slave)
+#define OPTION_SKIP_REPLICATION (1ULL << 37) // THD, user
/* The rest of the file is included in the server only */
#ifndef MYSQL_CLIENT
@@ -254,7 +221,8 @@ template <class T> bool valid_buffer_range(T jump,
#define OPTIMIZER_SWITCH_OPTIMIZE_JOIN_BUFFER_SIZE (1ULL << 25)
#define OPTIMIZER_SWITCH_TABLE_ELIMINATION (1ULL << 26)
#define OPTIMIZER_SWITCH_EXTENDED_KEYS (1ULL << 27)
-#define OPTIMIZER_SWITCH_LAST (1ULL << 27)
+#define OPTIMIZER_SWITCH_EXISTS_TO_IN (1ULL << 28)
+#define OPTIMIZER_SWITCH_USE_CONDITION_SELECTIVITY (1ULL << 29)
#define OPTIMIZER_SWITCH_DEFAULT (OPTIMIZER_SWITCH_INDEX_MERGE | \
OPTIMIZER_SWITCH_INDEX_MERGE_UNION | \
@@ -264,6 +232,7 @@ template <class T> bool valid_buffer_range(T jump,
OPTIMIZER_SWITCH_DERIVED_MERGE | \
OPTIMIZER_SWITCH_DERIVED_WITH_KEYS | \
OPTIMIZER_SWITCH_TABLE_ELIMINATION | \
+ OPTIMIZER_SWITCH_EXTENDED_KEYS | \
OPTIMIZER_SWITCH_IN_TO_EXISTS | \
OPTIMIZER_SWITCH_MATERIALIZATION | \
OPTIMIZER_SWITCH_PARTIAL_MATCH_ROWID_MERGE|\
@@ -276,7 +245,8 @@ template <class T> bool valid_buffer_range(T jump,
OPTIMIZER_SWITCH_SUBQUERY_CACHE | \
OPTIMIZER_SWITCH_SEMIJOIN | \
OPTIMIZER_SWITCH_FIRSTMATCH | \
- OPTIMIZER_SWITCH_LOOSE_SCAN )
+ OPTIMIZER_SWITCH_LOOSE_SCAN | \
+ OPTIMIZER_SWITCH_EXISTS_TO_IN)
/*
Replication uses 8 bytes to store SQL_MODE in the binary log. The day you
use strictly more than 64 bits by adding one more define above, you should
@@ -389,10 +359,12 @@ enum enum_yes_no_unknown
};
#ifdef MYSQL_SERVER
+
/*
External variables
*/
+
/* sql_yacc.cc */
#ifndef DBUG_OFF
extern void turn_parser_debug_on();
diff --git a/sql/sql_profile.cc b/sql/sql_profile.cc
index feb7810fa28..6b30dd28a51 100644
--- a/sql/sql_profile.cc
+++ b/sql/sql_profile.cc
@@ -29,11 +29,10 @@
- "profiling_history_size", integer, session + global, "Num queries stored?"
*/
-
+#include <my_global.h>
#include "sql_priv.h"
-#include "unireg.h" // REQUIRED: for other includes
#include "sql_profile.h"
-#include "my_sys.h"
+#include <my_sys.h>
#include "sql_show.h" // schema_table_store_record
#include "sql_class.h" // THD
@@ -288,7 +287,7 @@ void QUERY_PROFILE::set_query_source(char *query_source_arg,
uint query_length_arg)
{
/* Truncate to avoid DoS attacks. */
- uint length= min(MAX_QUERY_LENGTH, query_length_arg);
+ uint length= MY_MIN(MAX_QUERY_LENGTH, query_length_arg);
DBUG_ASSERT(query_source == NULL); /* we don't leak memory */
if (query_source_arg != NULL)
diff --git a/sql/sql_reload.cc b/sql/sql_reload.cc
index dfeb544c8ed..4629154de7d 100644
--- a/sql/sql_reload.cc
+++ b/sql/sql_reload.cc
@@ -13,6 +13,7 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+#include <my_global.h>
#include "sql_reload.h"
#include "sql_priv.h"
#include "mysqld.h" // select_errors
@@ -28,6 +29,7 @@
#include "debug_sync.h"
#include "des_key_file.h"
+
static void disable_checkpoints(THD *thd);
/**
@@ -53,7 +55,7 @@ static void disable_checkpoints(THD *thd);
@retval !=0 Error; thd->killed is set or thd->is_error() is true
*/
-bool reload_acl_and_cache(THD *thd, unsigned long options,
+bool reload_acl_and_cache(THD *thd, unsigned long long options,
TABLE_LIST *tables, int *write_to_binlog)
{
bool result=0;
@@ -92,12 +94,11 @@ bool reload_acl_and_cache(THD *thd, unsigned long options,
my_error(ER_UNKNOWN_ERROR, MYF(0));
}
}
+ opt_noacl= 0;
if (tmp_thd)
{
delete tmp_thd;
- /* Remember that we don't have a THD */
- my_pthread_setspecific_ptr(THR_THD, 0);
thd= 0;
}
reset_mqh((LEX_USER *)NULL, TRUE);
@@ -158,10 +159,39 @@ bool reload_acl_and_cache(THD *thd, unsigned long options,
if (options & REFRESH_RELAY_LOG)
{
#ifdef HAVE_REPLICATION
- mysql_mutex_lock(&active_mi->data_lock);
- if (rotate_relay_log(active_mi))
- *write_to_binlog= -1;
- mysql_mutex_unlock(&active_mi->data_lock);
+ LEX_STRING connection_name;
+ Master_info *mi;
+ if (thd)
+ connection_name= thd->lex->relay_log_connection_name;
+ else
+ {
+ connection_name.str= (char*) "";
+ connection_name.length= 0;
+ }
+
+ /*
+ Writing this command to the binlog may cause problems as the
+ slave is not likely to have the same connection names.
+ */
+ tmp_write_to_binlog= 0;
+ mysql_mutex_lock(&LOCK_active_mi);
+ if (master_info_index)
+ {
+ if (!(mi= (master_info_index->
+ get_master_info(&connection_name,
+ Sql_condition::WARN_LEVEL_ERROR))))
+ {
+ result= 1;
+ }
+ else
+ {
+ mysql_mutex_lock(&mi->data_lock);
+ if (rotate_relay_log(mi))
+ *write_to_binlog= -1;
+ mysql_mutex_unlock(&mi->data_lock);
+ }
+ }
+ mysql_mutex_unlock(&LOCK_active_mi);
#endif
}
#ifdef HAVE_QUERY_CACHE
@@ -179,6 +209,7 @@ bool reload_acl_and_cache(THD *thd, unsigned long options,
DBUG_ASSERT(!thd || thd->locked_tables_mode ||
!thd->mdl_context.has_locks() ||
thd->handler_tables_hash.records ||
+ thd->ull_hash.records ||
thd->global_read_lock.is_acquired());
/*
@@ -281,6 +312,16 @@ bool reload_acl_and_cache(THD *thd, unsigned long options,
}
}
+#ifdef WITH_WSREP
+ if (thd && thd->wsrep_applier)
+ {
+ /*
+ In case of applier thread, do not wait for table share(s) to be
+ removed from table definition cache.
+ */
+ options|= REFRESH_FAST;
+ }
+#endif
if (close_cached_tables(thd, tables,
((options & REFRESH_FAST) ? FALSE : TRUE),
(thd ? thd->variables.lock_wait_timeout :
@@ -306,7 +347,7 @@ bool reload_acl_and_cache(THD *thd, unsigned long options,
{
DBUG_ASSERT(thd);
tmp_write_to_binlog= 0;
- if (reset_master(thd))
+ if (reset_master(thd, NULL, 0))
{
/* NOTE: my_error() has been already called by reset_master(). */
result= 1;
@@ -326,12 +367,28 @@ bool reload_acl_and_cache(THD *thd, unsigned long options,
#ifdef HAVE_REPLICATION
if (options & REFRESH_SLAVE)
{
+ LEX_MASTER_INFO* lex_mi= &thd->lex->mi;
+ Master_info *mi;
tmp_write_to_binlog= 0;
mysql_mutex_lock(&LOCK_active_mi);
- if (reset_slave(thd, active_mi))
+ if (master_info_index)
{
- /* NOTE: my_error() has been already called by reset_slave(). */
- result= 1;
+ if (!(mi= (master_info_index->
+ get_master_info(&lex_mi->connection_name,
+ Sql_condition::WARN_LEVEL_ERROR))))
+ {
+ result= 1;
+ }
+ else if (reset_slave(thd, mi))
+ {
+ /* NOTE: my_error() has been already called by reset_slave(). */
+ result= 1;
+ }
+ else if (mi->connection_name.length && thd->lex->reset_slave_info.all)
+ {
+ /* If not default connection and 'all' is used */
+ master_info_index->remove_master_info(&mi->connection_name);
+ }
}
mysql_mutex_unlock(&LOCK_active_mi);
}
@@ -377,7 +434,8 @@ bool reload_acl_and_cache(THD *thd, unsigned long options,
/**
- Implementation of FLUSH TABLES <table_list> WITH READ LOCK.
+ Implementation of FLUSH TABLES <table_list> WITH READ LOCK
+ and FLUSH TABLES <table_list> FOR EXPORT
In brief: take exclusive locks, expel tables from the table
cache, reopen the tables, enter the 'LOCKED TABLES' mode,
@@ -466,33 +524,36 @@ bool flush_tables_with_read_lock(THD *thd, TABLE_LIST *all_tables)
goto error;
}
- /*
- Acquire SNW locks on tables to be flushed. Don't acquire global
- IX and database-scope IX locks on the tables as this will make
- this statement incompatible with FLUSH TABLES WITH READ LOCK.
- */
- if (lock_table_names(thd, all_tables, NULL,
- thd->variables.lock_wait_timeout,
- MYSQL_OPEN_SKIP_SCOPED_MDL_LOCK))
- goto error;
+ if (thd->lex->type & REFRESH_READ_LOCK)
+ {
+ /*
+ Acquire SNW locks on tables to be flushed. Don't acquire global
+ IX and database-scope IX locks on the tables as this will make
+ this statement incompatible with FLUSH TABLES WITH READ LOCK.
+ */
+ if (lock_table_names(thd, all_tables, NULL,
+ thd->variables.lock_wait_timeout,
+ MYSQL_OPEN_SKIP_SCOPED_MDL_LOCK))
+ goto error;
- DEBUG_SYNC(thd,"flush_tables_with_read_lock_after_acquire_locks");
+ DEBUG_SYNC(thd,"flush_tables_with_read_lock_after_acquire_locks");
- for (table_list= all_tables; table_list;
- table_list= table_list->next_global)
- {
- /* Request removal of table from cache. */
- tdc_remove_table(thd, TDC_RT_REMOVE_UNUSED,
- table_list->db,
- table_list->table_name, FALSE);
- /* Reset ticket to satisfy asserts in open_tables(). */
- table_list->mdl_request.ticket= NULL;
+ for (table_list= all_tables; table_list;
+ table_list= table_list->next_global)
+ {
+ /* Request removal of table from cache. */
+ tdc_remove_table(thd, TDC_RT_REMOVE_UNUSED,
+ table_list->db,
+ table_list->table_name, FALSE);
+ /* Reset ticket to satisfy asserts in open_tables(). */
+ table_list->mdl_request.ticket= NULL;
+ }
}
/*
Before opening and locking tables the below call also waits
for old shares to go away, so the fact that we don't pass
- MYSQL_LOCK_IGNORE_FLUSH flag to it is important.
+ MYSQL_OPEN_IGNORE_FLUSH flag to it is important.
Also we don't pass MYSQL_OPEN_HAS_MDL_LOCK flag as we want
to open underlying tables if merge table is flushed.
For underlying tables of the merge the below call has to
@@ -501,11 +562,27 @@ bool flush_tables_with_read_lock(THD *thd, TABLE_LIST *all_tables)
*/
if (open_and_lock_tables(thd, all_tables, FALSE,
MYSQL_OPEN_SKIP_SCOPED_MDL_LOCK,
- &lock_tables_prelocking_strategy) ||
- thd->locked_tables_list.init_locked_tables(thd))
- {
+ &lock_tables_prelocking_strategy))
goto error;
+
+ if (thd->lex->type & REFRESH_FOR_EXPORT)
+ {
+ // Check if all storage engines support FOR EXPORT.
+ for (TABLE_LIST *table_list= all_tables; table_list;
+ table_list= table_list->next_global)
+ {
+ if (!(table_list->table->file->ha_table_flags() & HA_CAN_EXPORT))
+ {
+ my_error(ER_ILLEGAL_HA, MYF(0),table_list->table->file->table_type(),
+ table_list->db, table_list->table_name);
+ return true;
+ }
+ }
}
+
+ if (thd->locked_tables_list.init_locked_tables(thd))
+ goto error;
+
thd->variables.option_bits|= OPTION_TABLE_LOCK;
/*
diff --git a/sql/sql_reload.h b/sql/sql_reload.h
index ebb3d78c003..33ca022dc14 100644
--- a/sql/sql_reload.h
+++ b/sql/sql_reload.h
@@ -18,7 +18,7 @@
class THD;
struct TABLE_LIST;
-bool reload_acl_and_cache(THD *thd, unsigned long options,
+bool reload_acl_and_cache(THD *thd, unsigned long long options,
TABLE_LIST *tables, int *write_to_binlog);
bool flush_tables_with_read_lock(THD *thd, TABLE_LIST *all_tables);
diff --git a/sql/sql_rename.cc b/sql/sql_rename.cc
index ee7c0fd2f73..e0fd7005cd5 100644
--- a/sql/sql_rename.cc
+++ b/sql/sql_rename.cc
@@ -19,6 +19,7 @@
Atomic rename of table; RENAME TABLE t1 to t2, tmp to t1 [,...]
*/
+#include <my_global.h>
#include "sql_priv.h"
#include "unireg.h"
#include "sql_rename.h"
@@ -29,16 +30,19 @@
#include "lock.h" // MYSQL_OPEN_SKIP_TEMPORARY
#include "sql_base.h" // tdc_remove_table, lock_table_names,
#include "sql_handler.h" // mysql_ha_rm_tables
-#include "datadict.h"
+#include "sql_statistics.h"
static TABLE_LIST *rename_tables(THD *thd, TABLE_LIST *table_list,
bool skip_error);
+static bool do_rename(THD *thd, TABLE_LIST *ren_table, char *new_db,
+ char *new_table_name, char *new_table_alias,
+ bool skip_error);
static TABLE_LIST *reverse_table_list(TABLE_LIST *table_list);
/*
- Every second entry in the table_list is the original name and every
- second entry is the new name.
+ Every two entries in the table_list form a pair of original name and
+ the new name.
*/
bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list, bool silent)
@@ -81,12 +85,8 @@ bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list, bool silent)
for (to_table= 0, ren_table= table_list; ren_table;
to_table= 1 - to_table, ren_table= ren_table->next_local)
{
- int log_table_rename= 0;
-
- if ((log_table_rename=
- check_if_log_table(ren_table->db_length, ren_table->db,
- ren_table->table_name_length,
- ren_table->table_name, 1)))
+ int log_table_rename;
+ if ((log_table_rename= check_if_log_table(ren_table, TRUE, NullS)))
{
/*
as we use log_table_rename as an array index, we need it to start
@@ -142,13 +142,9 @@ bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list, bool silent)
}
if (lock_table_names(thd, table_list, 0, thd->variables.lock_wait_timeout,
- MYSQL_OPEN_SKIP_TEMPORARY))
+ 0))
goto err;
- for (ren_table= table_list; ren_table; ren_table= ren_table->next_local)
- tdc_remove_table(thd, TDC_RT_REMOVE_ALL, ren_table->db,
- ren_table->table_name, FALSE);
-
error=0;
/*
An exclusive lock on table names is satisfactory to ensure
@@ -236,16 +232,13 @@ static TABLE_LIST *reverse_table_list(TABLE_LIST *table_list)
true rename failed
*/
-bool
+static bool
do_rename(THD *thd, TABLE_LIST *ren_table, char *new_db, char *new_table_name,
char *new_table_alias, bool skip_error)
{
int rc= 1;
- char new_name[FN_REFLEN + 1], old_name[FN_REFLEN + 1];
+ handlerton *hton;
const char *new_alias, *old_alias;
- frm_type_enum frm_type;
- enum legacy_db_type table_type;
-
DBUG_ENTER("do_rename");
if (lower_case_table_names == 2)
@@ -260,47 +253,49 @@ do_rename(THD *thd, TABLE_LIST *ren_table, char *new_db, char *new_table_name,
}
DBUG_ASSERT(new_alias);
- build_table_filename(new_name, sizeof(new_name) - 1,
- new_db, new_alias, reg_ext, 0);
- build_table_filename(old_name, sizeof(old_name) - 1,
- ren_table->db, old_alias, reg_ext, 0);
- if (check_table_file_presence(old_name,
- new_name, new_db, new_alias, new_alias, TRUE))
+ if (ha_table_exists(thd, new_db, new_alias))
{
- DBUG_RETURN(1); // This can't be skipped
+ my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_alias);
+ DBUG_RETURN(1); // This can't be skipped
}
- frm_type= dd_frm_type(thd, old_name, &table_type);
- switch (frm_type)
+ if (ha_table_exists(thd, ren_table->db, old_alias, &hton) && hton)
{
- case FRMTYPE_TABLE:
+ DBUG_ASSERT(!thd->locked_tables_mode);
+ tdc_remove_table(thd, TDC_RT_REMOVE_ALL,
+ ren_table->db, ren_table->table_name, false);
+
+ if (hton != view_pseudo_hton)
+ {
+ if (!(rc= mysql_rename_table(hton, ren_table->db, old_alias,
+ new_db, new_alias, 0)))
{
- if (!(rc= mysql_rename_table(ha_resolve_by_legacy_type(thd,
- table_type),
- ren_table->db, old_alias,
- new_db, new_alias, 0)))
+ LEX_STRING db_name= { ren_table->db, ren_table->db_length };
+ LEX_STRING table_name= { ren_table->table_name,
+ ren_table->table_name_length };
+ LEX_STRING new_table= { (char *) new_alias, strlen(new_alias) };
+ LEX_STRING new_db_name= { (char*)new_db, strlen(new_db)};
+ (void) rename_table_in_stat_tables(thd, &db_name, &table_name,
+ &new_db_name, &new_table);
+ if ((rc= Table_triggers_list::change_table_name(thd, ren_table->db,
+ old_alias,
+ ren_table->table_name,
+ new_db,
+ new_alias)))
{
- if ((rc= Table_triggers_list::change_table_name(thd, ren_table->db,
- old_alias,
- ren_table->table_name,
- new_db,
- new_alias)))
- {
- /*
- We've succeeded in renaming table's .frm and in updating
- corresponding handler data, but have failed to update table's
- triggers appropriately. So let us revert operations on .frm
- and handler's data and report about failure to rename table.
- */
- (void) mysql_rename_table(ha_resolve_by_legacy_type(thd,
- table_type),
- new_db, new_alias,
- ren_table->db, old_alias, NO_FK_CHECKS);
- }
+ /*
+ We've succeeded in renaming table's .frm and in updating
+ corresponding handler data, but have failed to update table's
+ triggers appropriately. So let us revert operations on .frm
+ and handler's data and report about failure to rename table.
+ */
+ (void) mysql_rename_table(hton, new_db, new_alias,
+ ren_table->db, old_alias, NO_FK_CHECKS);
}
}
- break;
- case FRMTYPE_VIEW:
+ }
+ else
+ {
/*
change of schema is not allowed
except of ALTER ...UPGRADE DATA DIRECTORY NAME command
@@ -308,22 +303,19 @@ do_rename(THD *thd, TABLE_LIST *ren_table, char *new_db, char *new_table_name,
*/
if (thd->lex->sql_command != SQLCOM_ALTER_DB_UPGRADE &&
strcmp(ren_table->db, new_db))
- my_error(ER_FORBID_SCHEMA_CHANGE, MYF(0), ren_table->db,
- new_db);
+ my_error(ER_FORBID_SCHEMA_CHANGE, MYF(0), ren_table->db, new_db);
else
rc= mysql_rename_view(thd, new_db, new_alias, ren_table);
- break;
- default:
- DBUG_ASSERT(0); // should never happen
- case FRMTYPE_ERROR:
- my_error(ER_FILE_NOT_FOUND, MYF(0), old_name, my_errno);
- break;
+ }
+ }
+ else
+ {
+ my_error(ER_NO_SUCH_TABLE, MYF(0), ren_table->db, old_alias);
}
if (rc && !skip_error)
DBUG_RETURN(1);
DBUG_RETURN(0);
-
}
/*
Rename all tables in list; Return pointer to wrong entry if something goes
diff --git a/sql/sql_rename.h b/sql/sql_rename.h
index 039a3b8b4a1..aaf09a8d030 100644
--- a/sql/sql_rename.h
+++ b/sql/sql_rename.h
@@ -20,8 +20,5 @@ class THD;
struct TABLE_LIST;
bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list, bool silent);
-bool do_rename(THD *thd, TABLE_LIST *ren_table, char *new_db,
- char *new_table_name, char *new_table_alias,
- bool skip_error);
#endif /* SQL_RENAME_INCLUDED */
diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc
index dca29ec3273..b7f1528e42c 100644
--- a/sql/sql_repl.cc
+++ b/sql/sql_repl.cc
@@ -14,12 +14,15 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+#include <my_global.h>
#include "sql_priv.h"
#include "unireg.h"
+#include "sql_base.h"
#include "sql_parse.h" // check_access
#ifdef HAVE_REPLICATION
#include "rpl_mi.h"
+#include "rpl_rli.h"
#include "sql_repl.h"
#include "sql_acl.h" // SUPER_ACL
#include "log_event.h"
@@ -28,22 +31,122 @@
#include "rpl_handler.h"
#include "debug_sync.h"
+
+enum enum_gtid_until_state {
+ GTID_UNTIL_NOT_DONE,
+ GTID_UNTIL_STOP_AFTER_STANDALONE,
+ GTID_UNTIL_STOP_AFTER_TRANSACTION
+};
+
+
int max_binlog_dump_events = 0; // unlimited
my_bool opt_sporadic_binlog_dump_fail = 0;
#ifndef DBUG_OFF
static int binlog_dump_count = 0;
#endif
-/**
- a copy of active_mi->rli->slave_skip_counter, for showing in SHOW VARIABLES,
- INFORMATION_SCHEMA.GLOBAL_VARIABLES and @@sql_slave_skip_counter without
- taking all the mutexes needed to access active_mi->rli->slave_skip_counter
- properly.
-*/
-uint sql_slave_skip_counter;
-
extern TYPELIB binlog_checksum_typelib;
+
+static int
+fake_event_header(String* packet, Log_event_type event_type, ulong extra_len,
+ my_bool *do_checksum, ha_checksum *crc, const char** errmsg,
+ uint8 checksum_alg_arg, uint32 end_pos)
+{
+ char header[LOG_EVENT_HEADER_LEN];
+ ulong event_len;
+
+ *do_checksum= checksum_alg_arg != BINLOG_CHECKSUM_ALG_OFF &&
+ checksum_alg_arg != BINLOG_CHECKSUM_ALG_UNDEF;
+
+ /*
+ 'when' (the timestamp) is set to 0 so that slave could distinguish between
+ real and fake Rotate events (if necessary)
+ */
+ memset(header, 0, 4);
+ header[EVENT_TYPE_OFFSET] = (uchar)event_type;
+ event_len= LOG_EVENT_HEADER_LEN + extra_len +
+ (*do_checksum ? BINLOG_CHECKSUM_LEN : 0);
+ int4store(header + SERVER_ID_OFFSET, global_system_variables.server_id);
+ int4store(header + EVENT_LEN_OFFSET, event_len);
+ int2store(header + FLAGS_OFFSET, LOG_EVENT_ARTIFICIAL_F);
+ // TODO: check what problems this may cause and fix them
+ int4store(header + LOG_POS_OFFSET, end_pos);
+ if (packet->append(header, sizeof(header)))
+ {
+ *errmsg= "Failed due to out-of-memory writing event";
+ return -1;
+ }
+ if (*do_checksum)
+ {
+ *crc= my_checksum(0, (uchar*)header, sizeof(header));
+ }
+ return 0;
+}
+
+
+static int
+fake_event_footer(String *packet, my_bool do_checksum, ha_checksum crc, const char **errmsg)
+{
+ if (do_checksum)
+ {
+ char b[BINLOG_CHECKSUM_LEN];
+ int4store(b, crc);
+ if (packet->append(b, sizeof(b)))
+ {
+ *errmsg= "Failed due to out-of-memory writing event checksum";
+ return -1;
+ }
+ }
+ return 0;
+}
+
+
+static int
+fake_event_write(NET *net, String *packet, const char **errmsg)
+{
+ if (my_net_write(net, (uchar*) packet->ptr(), packet->length()))
+ {
+ *errmsg = "failed on my_net_write()";
+ return -1;
+ }
+ return 0;
+}
+
+
+/*
+ Helper structure, used to pass miscellaneous info from mysql_binlog_send()
+ into the helper functions that it calls.
+*/
+struct binlog_send_info {
+ rpl_binlog_state until_binlog_state;
+ slave_connection_state gtid_state;
+ THD *thd;
+ NET *net;
+ String *packet;
+ char *log_file_name;
+ slave_connection_state *until_gtid_state;
+ Format_description_log_event *fdev;
+ int mariadb_slave_capability;
+ enum_gtid_skip_type gtid_skip_group;
+ enum_gtid_until_state gtid_until_group;
+ ushort flags;
+ uint8 current_checksum_alg;
+ bool slave_gtid_strict_mode;
+ bool send_fake_gtid_list;
+ bool slave_gtid_ignore_duplicates;
+ bool using_gtid_state;
+
+ binlog_send_info(THD *thd_arg, String *packet_arg, ushort flags_arg, char *lfn)
+ : thd(thd_arg), net(&thd_arg->net), packet(packet_arg),
+ log_file_name(lfn), until_gtid_state(NULL), fdev(NULL),
+ gtid_skip_group(GTID_SKIP_NOT), gtid_until_group(GTID_UNTIL_NOT_DONE),
+ flags(flags_arg), current_checksum_alg(BINLOG_CHECKSUM_ALG_UNDEF),
+ slave_gtid_strict_mode(false), send_fake_gtid_list(false),
+ slave_gtid_ignore_duplicates(false)
+ { }
+};
+
/*
fake_rotate_event() builds a fake (=which does not exist physically in any
binlog) Rotate event, which contains the name of the binlog we are going to
@@ -62,64 +165,77 @@ extern TYPELIB binlog_checksum_typelib;
part.
*/
-static int fake_rotate_event(NET* net, String* packet, char* log_file_name,
- ulonglong position, const char** errmsg,
- uint8 checksum_alg_arg)
+static int fake_rotate_event(binlog_send_info *info, ulonglong position,
+ const char** errmsg, uint8 checksum_alg_arg)
{
DBUG_ENTER("fake_rotate_event");
- char header[LOG_EVENT_HEADER_LEN], buf[ROTATE_HEADER_LEN+100];
-
- /*
- this Rotate is to be sent with checksum if and only if
- slave's get_master_version_and_clock time handshake value
- of master's @@global.binlog_checksum was TRUE
- */
-
- my_bool do_checksum= checksum_alg_arg != BINLOG_CHECKSUM_ALG_OFF &&
- checksum_alg_arg != BINLOG_CHECKSUM_ALG_UNDEF;
-
- /*
- 'when' (the timestamp) is set to 0 so that slave could distinguish between
- real and fake Rotate events (if necessary)
- */
- memset(header, 0, 4);
- header[EVENT_TYPE_OFFSET] = ROTATE_EVENT;
-
- char* p = log_file_name+dirname_length(log_file_name);
+ char buf[ROTATE_HEADER_LEN+100];
+ my_bool do_checksum;
+ int err;
+ char* p = info->log_file_name+dirname_length(info->log_file_name);
uint ident_len = (uint) strlen(p);
- ulong event_len = ident_len + LOG_EVENT_HEADER_LEN + ROTATE_HEADER_LEN +
- (do_checksum ? BINLOG_CHECKSUM_LEN : 0);
- int4store(header + SERVER_ID_OFFSET, server_id);
- int4store(header + EVENT_LEN_OFFSET, event_len);
- int2store(header + FLAGS_OFFSET, LOG_EVENT_ARTIFICIAL_F);
+ String *packet= info->packet;
+ ha_checksum crc;
- // TODO: check what problems this may cause and fix them
- int4store(header + LOG_POS_OFFSET, 0);
+ if ((err= fake_event_header(packet, ROTATE_EVENT,
+ ident_len + ROTATE_HEADER_LEN, &do_checksum, &crc,
+ errmsg, checksum_alg_arg, 0)))
+ DBUG_RETURN(err);
- packet->append(header, sizeof(header));
int8store(buf+R_POS_OFFSET,position);
packet->append(buf, ROTATE_HEADER_LEN);
packet->append(p, ident_len);
if (do_checksum)
{
- char b[BINLOG_CHECKSUM_LEN];
- ha_checksum crc= my_checksum(0L, NULL, 0);
- crc= my_checksum(crc, (uchar*)header, sizeof(header));
crc= my_checksum(crc, (uchar*)buf, ROTATE_HEADER_LEN);
crc= my_checksum(crc, (uchar*)p, ident_len);
- int4store(b, crc);
- packet->append(b, sizeof(b));
}
- if (my_net_write(net, (uchar*) packet->ptr(), packet->length()))
+ if ((err= fake_event_footer(packet, do_checksum, crc, errmsg)) ||
+ (err= fake_event_write(info->net, packet, errmsg)))
+ DBUG_RETURN(err);
+
+ DBUG_RETURN(0);
+}
+
+
+static int fake_gtid_list_event(binlog_send_info *info,
+ Gtid_list_log_event *glev, const char** errmsg,
+ uint32 current_pos)
+{
+ my_bool do_checksum;
+ int err;
+ ha_checksum crc;
+ char buf[128];
+ String str(buf, sizeof(buf), system_charset_info);
+ String* packet= info->packet;
+
+ str.length(0);
+ if (glev->to_packet(&str))
{
- *errmsg = "failed on my_net_write()";
- DBUG_RETURN(-1);
+ *errmsg= "Failed due to out-of-memory writing Gtid_list event";
+ return -1;
}
- DBUG_RETURN(0);
+ if ((err= fake_event_header(packet, GTID_LIST_EVENT,
+ str.length(), &do_checksum, &crc,
+ errmsg, info->current_checksum_alg, current_pos)))
+ return err;
+
+ packet->append(str);
+ if (do_checksum)
+ {
+ crc= my_checksum(crc, (uchar*)str.ptr(), str.length());
+ }
+
+ if ((err= fake_event_footer(packet, do_checksum, crc, errmsg)) ||
+ (err= fake_event_write(info->net, packet, errmsg)))
+ return err;
+
+ return 0;
}
+
/*
Reset thread transmit packet buffer for event sending
@@ -492,6 +608,94 @@ static ulonglong get_heartbeat_period(THD * thd)
}
/*
+ Lookup the capabilities of the slave, which it announces by setting a value
+ MARIA_SLAVE_CAPABILITY_XXX in @mariadb_slave_capability.
+
+ Older MariaDB slaves, and other MySQL slaves, do not set
+ @mariadb_slave_capability, corresponding to a capability of
+ MARIA_SLAVE_CAPABILITY_UNKNOWN (0).
+*/
+static int
+get_mariadb_slave_capability(THD *thd)
+{
+ bool null_value;
+ const LEX_STRING name= { C_STRING_WITH_LEN("mariadb_slave_capability") };
+ const user_var_entry *entry=
+ (user_var_entry*) my_hash_search(&thd->user_vars, (uchar*) name.str,
+ name.length);
+ return entry ?
+ (int)(entry->val_int(&null_value)) : MARIA_SLAVE_CAPABILITY_UNKNOWN;
+}
+
+
+/*
+ Get the value of the @slave_connect_state user variable into the supplied
+ String (this is the GTID connect state requested by the connecting slave).
+
+ Returns false if error (ie. slave did not set the variable and does not
+ want to use GTID to set start position), true if success.
+*/
+static bool
+get_slave_connect_state(THD *thd, String *out_str)
+{
+ bool null_value;
+
+ const LEX_STRING name= { C_STRING_WITH_LEN("slave_connect_state") };
+ user_var_entry *entry=
+ (user_var_entry*) my_hash_search(&thd->user_vars, (uchar*) name.str,
+ name.length);
+ return entry && entry->val_str(&null_value, out_str, 0) && !null_value;
+}
+
+
+static bool
+get_slave_gtid_strict_mode(THD *thd)
+{
+ bool null_value;
+
+ const LEX_STRING name= { C_STRING_WITH_LEN("slave_gtid_strict_mode") };
+ user_var_entry *entry=
+ (user_var_entry*) my_hash_search(&thd->user_vars, (uchar*) name.str,
+ name.length);
+ return entry && entry->val_int(&null_value) && !null_value;
+}
+
+
+static bool
+get_slave_gtid_ignore_duplicates(THD *thd)
+{
+ bool null_value;
+
+ const LEX_STRING name= { C_STRING_WITH_LEN("slave_gtid_ignore_duplicates") };
+ user_var_entry *entry=
+ (user_var_entry*) my_hash_search(&thd->user_vars, (uchar*) name.str,
+ name.length);
+ return entry && entry->val_int(&null_value) && !null_value;
+}
+
+
+/*
+ Get the value of the @slave_until_gtid user variable into the supplied
+ String (this is the GTID position specified for START SLAVE UNTIL
+ master_gtid_pos='xxx').
+
+ Returns false if error (ie. slave did not set the variable and is not doing
+ START SLAVE UNTIL mater_gtid_pos='xxx'), true if success.
+*/
+static bool
+get_slave_until_gtid(THD *thd, String *out_str)
+{
+ bool null_value;
+
+ const LEX_STRING name= { C_STRING_WITH_LEN("slave_until_gtid") };
+ user_var_entry *entry=
+ (user_var_entry*) my_hash_search(&thd->user_vars, (uchar*) name.str,
+ name.length);
+ return entry && entry->val_str(&null_value, out_str, 0) && !null_value;
+}
+
+
+/*
Function prepares and sends repliation heartbeat event.
@param net net object of THD
@@ -526,7 +730,7 @@ static int send_heartbeat_event(NET* net, String* packet,
uint ident_len = strlen(p);
ulong event_len = ident_len + LOG_EVENT_HEADER_LEN +
(do_checksum ? BINLOG_CHECKSUM_LEN : 0);
- int4store(header + SERVER_ID_OFFSET, server_id);
+ int4store(header + SERVER_ID_OFFSET, global_system_variables.server_id);
int4store(header + EVENT_LEN_OFFSET, event_len);
int2store(header + FLAGS_OFFSET, 0);
@@ -538,8 +742,8 @@ static int send_heartbeat_event(NET* net, String* packet,
if (do_checksum)
{
char b[BINLOG_CHECKSUM_LEN];
- ha_checksum crc= my_checksum(0L, NULL, 0);
- crc= my_checksum(crc, (uchar*) header, sizeof(header));
+ ha_checksum crc;
+ crc= my_checksum(0, (uchar*) header, sizeof(header));
crc= my_checksum(crc, (uchar*) p, ident_len);
int4store(b, crc);
packet->append(b, sizeof(b));
@@ -554,6 +758,810 @@ static int send_heartbeat_event(NET* net, String* packet,
}
+struct binlog_file_entry
+{
+ binlog_file_entry *next;
+ char *name;
+};
+
+static binlog_file_entry *
+get_binlog_list(MEM_ROOT *memroot)
+{
+ IO_CACHE *index_file;
+ char fname[FN_REFLEN];
+ size_t length;
+ binlog_file_entry *current_list= NULL, *e;
+ DBUG_ENTER("get_binlog_list");
+
+ if (!mysql_bin_log.is_open())
+ {
+ my_error(ER_NO_BINARY_LOGGING, MYF(0));
+ DBUG_RETURN(NULL);
+ }
+
+ mysql_bin_log.lock_index();
+ index_file=mysql_bin_log.get_index_file();
+ reinit_io_cache(index_file, READ_CACHE, (my_off_t) 0, 0, 0);
+
+ /* The file ends with EOF or empty line */
+ while ((length=my_b_gets(index_file, fname, sizeof(fname))) > 1)
+ {
+ --length; /* Remove the newline */
+ if (!(e= (binlog_file_entry *)alloc_root(memroot, sizeof(*e))) ||
+ !(e->name= strmake_root(memroot, fname, length)))
+ {
+ mysql_bin_log.unlock_index();
+ my_error(ER_OUTOFMEMORY, MYF(0), length + 1 + sizeof(*e));
+ DBUG_RETURN(NULL);
+ }
+ e->next= current_list;
+ current_list= e;
+ }
+ mysql_bin_log.unlock_index();
+
+ DBUG_RETURN(current_list);
+}
+
+/*
+ Find the Gtid_list_log_event at the start of a binlog.
+
+ NULL for ok, non-NULL error message for error.
+
+ If ok, then the event is returned in *out_gtid_list. This can be NULL if we
+ get back to binlogs written by old server version without GTID support. If
+ so, it means we have reached the point to start from, as no GTID events can
+ exist in earlier binlogs.
+*/
+static const char *
+get_gtid_list_event(IO_CACHE *cache, Gtid_list_log_event **out_gtid_list)
+{
+ Format_description_log_event init_fdle(BINLOG_VERSION);
+ Format_description_log_event *fdle;
+ Log_event *ev;
+ const char *errormsg = NULL;
+
+ *out_gtid_list= NULL;
+
+ if (!(ev= Log_event::read_log_event(cache, 0, &init_fdle,
+ opt_master_verify_checksum)) ||
+ ev->get_type_code() != FORMAT_DESCRIPTION_EVENT)
+ {
+ if (ev)
+ delete ev;
+ return "Could not read format description log event while looking for "
+ "GTID position in binlog";
+ }
+
+ fdle= static_cast<Format_description_log_event *>(ev);
+
+ for (;;)
+ {
+ Log_event_type typ;
+
+ ev= Log_event::read_log_event(cache, 0, fdle, opt_master_verify_checksum);
+ if (!ev)
+ {
+ errormsg= "Could not read GTID list event while looking for GTID "
+ "position in binlog";
+ break;
+ }
+ typ= ev->get_type_code();
+ if (typ == GTID_LIST_EVENT)
+ break; /* Done, found it */
+ delete ev;
+ if (typ == ROTATE_EVENT || typ == STOP_EVENT ||
+ typ == FORMAT_DESCRIPTION_EVENT)
+ continue; /* Continue looking */
+
+ /* We did not find any Gtid_list_log_event, must be old binlog. */
+ ev= NULL;
+ break;
+ }
+
+ delete fdle;
+ *out_gtid_list= static_cast<Gtid_list_log_event *>(ev);
+ return errormsg;
+}
+
+
+/*
+ Check if every GTID requested by the slave is contained in this (or a later)
+ binlog file. Return true if so, false if not.
+
+ We do the check with a single scan of the list of GTIDs, avoiding the need
+ to build an in-memory hash or stuff like that.
+
+ We need to check that slave did not request GTID D-S-N1, when the
+ Gtid_list_log_event for this binlog file has D-S-N2 with N2 >= N1.
+ (Because this means that requested GTID is in an earlier binlog).
+ However, if the Gtid_list_log_event indicates that D-S-N1 is the very last
+ GTID for domain D in prior binlog files, then it is ok to start from the
+ very start of this binlog file. This special case is important, as it
+ allows to purge old logs even if some domain is unused for long.
+
+ In addition, we need to check that we do not have a GTID D-S-N3 in the
+ Gtid_list_log_event where D is not present in the requested slave state at
+ all. Since if D is not in requested slave state, it means that slave needs
+ to start at the very first GTID in domain D.
+*/
+static bool
+contains_all_slave_gtid(slave_connection_state *st, Gtid_list_log_event *glev)
+{
+ uint32 i;
+
+ for (i= 0; i < glev->count; ++i)
+ {
+ uint32 gl_domain_id= glev->list[i].domain_id;
+ const rpl_gtid *gtid= st->find(gl_domain_id);
+ if (!gtid)
+ {
+ /*
+ The slave needs to start from the very beginning of this domain, which
+ is in an earlier binlog file. So we need to search back further.
+ */
+ return false;
+ }
+ if (gtid->server_id == glev->list[i].server_id &&
+ gtid->seq_no <= glev->list[i].seq_no)
+ {
+ /*
+ The slave needs to start after gtid, but it is contained in an earlier
+ binlog file. So we need to search back further, unless it was the very
+ last gtid logged for the domain in earlier binlog files.
+ */
+ if (gtid->seq_no < glev->list[i].seq_no)
+ return false;
+
+ /*
+ The slave requested D-S-N1, which happens to be the last GTID logged
+ in prior binlog files with same domain id D and server id S.
+
+ The Gtid_list is kept sorted on domain_id, with the last GTID in each
+ domain_id group being the last one logged. So if this is the last GTID
+ within the domain_id group, then it is ok to start from the very
+ beginning of this group, per the special case explained in comment at
+ the start of this function. If not, then we need to search back further.
+ */
+ if (i+1 < glev->count && gl_domain_id == glev->list[i+1].domain_id)
+ return false;
+ }
+ }
+
+ return true;
+}
+
+
+static void
+give_error_start_pos_missing_in_binlog(int *err, const char **errormsg,
+ rpl_gtid *error_gtid)
+{
+ rpl_gtid binlog_gtid;
+
+ if (mysql_bin_log.lookup_domain_in_binlog_state(error_gtid->domain_id,
+ &binlog_gtid) &&
+ binlog_gtid.seq_no >= error_gtid->seq_no)
+ {
+ *errormsg= "Requested slave GTID state not found in binlog. The slave has "
+ "probably diverged due to executing erroneous transactions";
+ *err= ER_GTID_POSITION_NOT_FOUND_IN_BINLOG2;
+ }
+ else
+ {
+ *errormsg= "Requested slave GTID state not found in binlog";
+ *err= ER_GTID_POSITION_NOT_FOUND_IN_BINLOG;
+ }
+}
+
+
+/*
+ Check the start GTID state requested by the slave against our binlog state.
+
+ Give an error if the slave requests something that we do not have in our
+ binlog.
+*/
+
+static int
+check_slave_start_position(binlog_send_info *info, const char **errormsg,
+ rpl_gtid *error_gtid)
+{
+ uint32 i;
+ int err;
+ slave_connection_state::entry **delete_list= NULL;
+ uint32 delete_idx= 0;
+ slave_connection_state *st= &info->gtid_state;
+
+ if (rpl_load_gtid_slave_state(info->thd))
+ {
+ *errormsg= "Failed to load replication slave GTID state";
+ err= ER_CANNOT_LOAD_SLAVE_GTID_STATE;
+ goto end;
+ }
+
+ for (i= 0; i < st->hash.records; ++i)
+ {
+ slave_connection_state::entry *slave_gtid_entry=
+ (slave_connection_state::entry *)my_hash_element(&st->hash, i);
+ rpl_gtid *slave_gtid= &slave_gtid_entry->gtid;
+ rpl_gtid master_gtid;
+ rpl_gtid master_replication_gtid;
+ rpl_gtid start_gtid;
+ bool start_at_own_slave_pos=
+ rpl_global_gtid_slave_state->domain_to_gtid(slave_gtid->domain_id,
+ &master_replication_gtid) &&
+ slave_gtid->server_id == master_replication_gtid.server_id &&
+ slave_gtid->seq_no == master_replication_gtid.seq_no;
+
+ if (mysql_bin_log.find_in_binlog_state(slave_gtid->domain_id,
+ slave_gtid->server_id,
+ &master_gtid) &&
+ master_gtid.seq_no >= slave_gtid->seq_no)
+ {
+ /*
+ If connecting slave requests to start at the GTID we last applied when
+ we were ourselves a slave, then this GTID may not exist in our binlog
+ (in case of --log-slave-updates=0). So set the flag to disable the
+ error about missing GTID in the binlog in this case.
+ */
+ if (start_at_own_slave_pos)
+ slave_gtid_entry->flags|= slave_connection_state::START_OWN_SLAVE_POS;
+ continue;
+ }
+
+ if (!start_at_own_slave_pos)
+ {
+ rpl_gtid domain_gtid;
+ slave_connection_state *until_gtid_state= info->until_gtid_state;
+ rpl_gtid *until_gtid;
+
+ if (!mysql_bin_log.lookup_domain_in_binlog_state(slave_gtid->domain_id,
+ &domain_gtid))
+ {
+ /*
+ We do not have anything in this domain, neither in the binlog nor
+ in the slave state. So we are probably one master in a multi-master
+ setup, and this domain is served by a different master.
+
+ But set a flag so that if we then ever _do_ happen to encounter
+ anything in this domain, then we will re-check that the requested
+ slave position exists, and give the error at that time if not.
+ */
+ slave_gtid_entry->flags|= slave_connection_state::START_ON_EMPTY_DOMAIN;
+ continue;
+ }
+
+ if (info->slave_gtid_ignore_duplicates &&
+ domain_gtid.seq_no < slave_gtid->seq_no)
+ {
+ /*
+ When --gtid-ignore-duplicates, it is ok for the slave to request
+ something that we do not have (yet) - they might already have gotten
+ it through another path in a multi-path replication hierarchy.
+ */
+ continue;
+ }
+
+ if (until_gtid_state &&
+ ( !(until_gtid= until_gtid_state->find(slave_gtid->domain_id)) ||
+ (mysql_bin_log.find_in_binlog_state(until_gtid->domain_id,
+ until_gtid->server_id,
+ &master_gtid) &&
+ master_gtid.seq_no >= until_gtid->seq_no)))
+ {
+ /*
+ The slave requested to start from a position that is not (yet) in
+ our binlog, but it also specified an UNTIL condition that _is_ in
+ our binlog (or a missing UNTIL, which means stop at the very
+ beginning). So the stop position is before the start position, and
+ we just delete the entry from the UNTIL hash to mark that this
+ domain has already reached the UNTIL condition.
+ */
+ if(until_gtid)
+ until_gtid_state->remove(until_gtid);
+ continue;
+ }
+
+ *error_gtid= *slave_gtid;
+ give_error_start_pos_missing_in_binlog(&err, errormsg, error_gtid);
+ goto end;
+ }
+
+ /*
+ Ok, so connecting slave asked to start at a GTID that we do not have in
+ our binlog, but it was in fact the last GTID we applied earlier, when we
+ were acting as a replication slave.
+
+ So this means that we were running as a replication slave without
+ --log-slave-updates, but now we switched to be a master. It is worth it
+ to handle this special case, as it allows users to run a simple
+ master -> slave without --log-slave-updates, and then exchange slave and
+ master, as long as they make sure the slave is caught up before switching.
+ */
+
+ /*
+ First check if we logged something ourselves as a master after being a
+ slave. This will be seen as a GTID with our own server_id and bigger
+ seq_no than what is in the slave state.
+
+ If we did not log anything ourselves, then start the connecting slave
+ replicating from the current binlog end position, which in this case
+ corresponds to our replication slave state and hence what the connecting
+ slave is requesting.
+ */
+ if (mysql_bin_log.find_in_binlog_state(slave_gtid->domain_id,
+ global_system_variables.server_id,
+ &start_gtid) &&
+ start_gtid.seq_no > slave_gtid->seq_no)
+ {
+ /*
+ Start replication within this domain at the first GTID that we logged
+ ourselves after becoming a master.
+
+ Remember that this starting point is in fact a "fake" GTID which may
+ not exists in the binlog, so that we do not complain about it in
+ --gtid-strict-mode.
+ */
+ slave_gtid->server_id= global_system_variables.server_id;
+ slave_gtid_entry->flags|= slave_connection_state::START_OWN_SLAVE_POS;
+ }
+ else if (mysql_bin_log.lookup_domain_in_binlog_state(slave_gtid->domain_id,
+ &start_gtid))
+ {
+ slave_gtid->server_id= start_gtid.server_id;
+ slave_gtid->seq_no= start_gtid.seq_no;
+ }
+ else
+ {
+ /*
+ We do not have _anything_ in our own binlog for this domain. Just
+ delete the entry in the slave connection state, then it will pick up
+ anything new that arrives.
+
+ We just queue up the deletion and do it later, after the loop, so that
+ we do not mess up the iteration over the hash.
+ */
+ if (!delete_list)
+ {
+ if (!(delete_list= (slave_connection_state::entry **)
+ my_malloc(sizeof(*delete_list) * st->hash.records, MYF(MY_WME))))
+ {
+ *errormsg= "Out of memory while checking slave start position";
+ err= ER_OUT_OF_RESOURCES;
+ goto end;
+ }
+ }
+ delete_list[delete_idx++]= slave_gtid_entry;
+ }
+ }
+
+ /* Do any delayed deletes from the hash. */
+ if (delete_list)
+ {
+ for (i= 0; i < delete_idx; ++i)
+ st->remove(&(delete_list[i]->gtid));
+ }
+ err= 0;
+
+end:
+ if (delete_list)
+ my_free(delete_list);
+ return err;
+}
+
+/*
+ Find the name of the binlog file to start reading for a slave that connects
+ using GTID state.
+
+ Returns the file name in out_name, which must be of size at least FN_REFLEN.
+
+ Returns NULL on ok, error message on error.
+
+ In case of non-error return, the returned binlog file is guaranteed to
+ contain the first event to be transmitted to the slave for every domain
+ present in our binlogs. It is still necessary to skip all GTIDs up to
+ and including the GTID requested by slave within each domain.
+
+ However, as a special case, if the event to be sent to the slave is the very
+ first event (within that domain) in the returned binlog, then nothing should
+ be skipped, so that domain is deleted from the passed in slave connection
+ state.
+
+ This is necessary in case the slave requests a GTID within a replication
+ domain that has long been inactive. The binlog file containing that GTID may
+ have been long since purged. However, as long as no GTIDs after that have
+ been purged, we have the GTID requested by slave in the Gtid_list_log_event
+ of the latest binlog. So we can start from there, as long as we delete the
+ corresponding entry in the slave state so we do not wrongly skip any events
+ that might turn up if that domain becomes active again, vainly looking for
+ the requested GTID that was already purged.
+*/
+static const char *
+gtid_find_binlog_file(slave_connection_state *state, char *out_name,
+ slave_connection_state *until_gtid_state)
+{
+ MEM_ROOT memroot;
+ binlog_file_entry *list;
+ Gtid_list_log_event *glev= NULL;
+ const char *errormsg= NULL;
+ char buf[FN_REFLEN];
+
+ init_alloc_root(&memroot, 10*(FN_REFLEN+sizeof(binlog_file_entry)), 0,
+ MYF(MY_THREAD_SPECIFIC));
+ if (!(list= get_binlog_list(&memroot)))
+ {
+ errormsg= "Out of memory while looking for GTID position in binlog";
+ goto end;
+ }
+
+ while (list)
+ {
+ File file;
+ IO_CACHE cache;
+
+ if (!list->next)
+ {
+ /*
+ It should be safe to read the currently used binlog, as we will only
+ read the header part that is already written.
+
+ But if that does not work on windows, then we will need to cache the
+ event somewhere in memory I suppose - that could work too.
+ */
+ }
+ /*
+ Read the Gtid_list_log_event at the start of the binlog file to
+ get the binlog state.
+ */
+ if (normalize_binlog_name(buf, list->name, false))
+ {
+ errormsg= "Failed to determine binlog file name while looking for "
+ "GTID position in binlog";
+ goto end;
+ }
+ bzero((char*) &cache, sizeof(cache));
+ if ((file= open_binlog(&cache, buf, &errormsg)) == (File)-1)
+ goto end;
+ errormsg= get_gtid_list_event(&cache, &glev);
+ end_io_cache(&cache);
+ mysql_file_close(file, MYF(MY_WME));
+ if (errormsg)
+ goto end;
+
+ if (!glev || contains_all_slave_gtid(state, glev))
+ {
+ strmake(out_name, buf, FN_REFLEN);
+
+ if (glev)
+ {
+ uint32 i;
+
+ /*
+ As a special case, we allow to start from binlog file N if the
+ requested GTID is the last event (in the corresponding domain) in
+ binlog file (N-1), but then we need to remove that GTID from the slave
+ state, rather than skipping events waiting for it to turn up.
+
+ If slave is doing START SLAVE UNTIL, check for any UNTIL conditions
+ that are already included in a previous binlog file. Delete any such
+ from the UNTIL hash, to mark that such domains have already reached
+ their UNTIL condition.
+ */
+ for (i= 0; i < glev->count; ++i)
+ {
+ const rpl_gtid *gtid= state->find(glev->list[i].domain_id);
+ if (!gtid)
+ {
+ /*
+ Contains_all_slave_gtid() returns false if there is any domain in
+ Gtid_list_event which is not in the requested slave position.
+
+ We may delete a domain from the slave state inside this loop, but
+ we only do this when it is the very last GTID logged for that
+ domain in earlier binlogs, and then we can not encounter it in any
+ further GTIDs in the Gtid_list.
+ */
+ DBUG_ASSERT(0);
+ } else if (gtid->server_id == glev->list[i].server_id &&
+ gtid->seq_no == glev->list[i].seq_no)
+ {
+ /*
+ The slave requested to start from the very beginning of this
+ domain in this binlog file. So delete the entry from the state,
+ we do not need to skip anything.
+ */
+ state->remove(gtid);
+ }
+
+ if (until_gtid_state &&
+ (gtid= until_gtid_state->find(glev->list[i].domain_id)) &&
+ gtid->server_id == glev->list[i].server_id &&
+ gtid->seq_no <= glev->list[i].seq_no)
+ {
+ /*
+ We've already reached the stop position in UNTIL for this domain,
+ since it is before the start position.
+ */
+ until_gtid_state->remove(gtid);
+ }
+ }
+ }
+
+ goto end;
+ }
+ delete glev;
+ glev= NULL;
+ list= list->next;
+ }
+
+ /* We reached the end without finding anything. */
+ errormsg= "Could not find GTID state requested by slave in any binlog "
+ "files. Probably the slave state is too old and required binlog files "
+ "have been purged.";
+
+end:
+ if (glev)
+ delete glev;
+
+ free_root(&memroot, MYF(0));
+ return errormsg;
+}
+
+
+/*
+ Given an old-style binlog position with file name and file offset, find the
+ corresponding gtid position. If the offset is not at an event boundary, give
+ an error.
+
+ Return NULL on ok, error message string on error.
+
+ ToDo: Improve the performance of this by using binlog index files.
+*/
+static const char *
+gtid_state_from_pos(const char *name, uint32 offset,
+ slave_connection_state *gtid_state)
+{
+ IO_CACHE cache;
+ File file;
+ const char *errormsg= NULL;
+ bool found_gtid_list_event= false;
+ bool found_format_description_event= false;
+ bool valid_pos= false;
+ uint8 current_checksum_alg= BINLOG_CHECKSUM_ALG_UNDEF;
+ int err;
+ String packet;
+ Format_description_log_event *fdev= NULL;
+
+ if (gtid_state->load((const rpl_gtid *)NULL, 0))
+ {
+ errormsg= "Internal error (out of memory?) initializing slave state "
+ "while scanning binlog to find start position";
+ return errormsg;
+ }
+
+ if ((file= open_binlog(&cache, name, &errormsg)) == (File)-1)
+ return errormsg;
+
+ if (!(fdev= new Format_description_log_event(3)))
+ {
+ errormsg= "Out of memory initializing format_description event "
+ "while scanning binlog to find start position";
+ goto end;
+ }
+
+ /*
+ First we need to find the initial GTID_LIST_EVENT. We need this even
+ if the offset is at the very start of the binlog file.
+
+ But if we do not find any GTID_LIST_EVENT, then this is an old binlog
+ with no GTID information, so we return empty GTID state.
+ */
+ for (;;)
+ {
+ Log_event_type typ;
+ uint32 cur_pos;
+
+ cur_pos= (uint32)my_b_tell(&cache);
+ if (cur_pos == offset)
+ valid_pos= true;
+ if (found_format_description_event && found_gtid_list_event &&
+ cur_pos >= offset)
+ break;
+
+ packet.length(0);
+ err= Log_event::read_log_event(&cache, &packet, NULL,
+ current_checksum_alg);
+ if (err)
+ {
+ errormsg= "Could not read binlog while searching for slave start "
+ "position on master";
+ goto end;
+ }
+ /*
+ The cast to uchar is needed to avoid a signed char being converted to a
+ negative number.
+ */
+ typ= (Log_event_type)(uchar)packet[EVENT_TYPE_OFFSET];
+ if (typ == FORMAT_DESCRIPTION_EVENT)
+ {
+ Format_description_log_event *tmp;
+
+ if (found_format_description_event)
+ {
+ errormsg= "Duplicate format description log event found while "
+ "searching for old-style position in binlog";
+ goto end;
+ }
+
+ current_checksum_alg= get_checksum_alg(packet.ptr(), packet.length());
+ found_format_description_event= true;
+ if (!(tmp= new Format_description_log_event(packet.ptr(), packet.length(),
+ fdev)))
+ {
+ errormsg= "Corrupt Format_description event found or out-of-memory "
+ "while searching for old-style position in binlog";
+ goto end;
+ }
+ delete fdev;
+ fdev= tmp;
+ }
+ else if (typ != FORMAT_DESCRIPTION_EVENT && !found_format_description_event)
+ {
+ errormsg= "Did not find format description log event while searching "
+ "for old-style position in binlog";
+ goto end;
+ }
+ else if (typ == ROTATE_EVENT || typ == STOP_EVENT ||
+ typ == BINLOG_CHECKPOINT_EVENT)
+ continue; /* Continue looking */
+ else if (typ == GTID_LIST_EVENT)
+ {
+ rpl_gtid *gtid_list;
+ bool status;
+ uint32 list_len;
+
+ if (found_gtid_list_event)
+ {
+ errormsg= "Found duplicate Gtid_list_log_event while scanning binlog "
+ "to find slave start position";
+ goto end;
+ }
+ status= Gtid_list_log_event::peek(packet.ptr(), packet.length(),
+ current_checksum_alg,
+ &gtid_list, &list_len, fdev);
+ if (status)
+ {
+ errormsg= "Error reading Gtid_list_log_event while searching "
+ "for old-style position in binlog";
+ goto end;
+ }
+ err= gtid_state->load(gtid_list, list_len);
+ my_free(gtid_list);
+ if (err)
+ {
+ errormsg= "Internal error (out of memory?) initialising slave state "
+ "while scanning binlog to find start position";
+ goto end;
+ }
+ found_gtid_list_event= true;
+ }
+ else if (!found_gtid_list_event)
+ {
+ /* We did not find any Gtid_list_log_event, must be old binlog. */
+ goto end;
+ }
+ else if (typ == GTID_EVENT)
+ {
+ rpl_gtid gtid;
+ uchar flags2;
+ if (Gtid_log_event::peek(packet.ptr(), packet.length(),
+ current_checksum_alg, &gtid.domain_id,
+ &gtid.server_id, &gtid.seq_no, &flags2, fdev))
+ {
+ errormsg= "Corrupt gtid_log_event found while scanning binlog to find "
+ "initial slave position";
+ goto end;
+ }
+ if (gtid_state->update(&gtid))
+ {
+ errormsg= "Internal error (out of memory?) updating slave state while "
+ "scanning binlog to find start position";
+ goto end;
+ }
+ }
+ }
+
+ if (!valid_pos)
+ {
+ errormsg= "Slave requested incorrect position in master binlog. "
+ "Requested position %u in file '%s', but this position does not "
+ "correspond to the location of any binlog event.";
+ }
+
+end:
+ delete fdev;
+ end_io_cache(&cache);
+ mysql_file_close(file, MYF(MY_WME));
+
+ return errormsg;
+}
+
+
+int
+gtid_state_from_binlog_pos(const char *in_name, uint32 pos, String *out_str)
+{
+ slave_connection_state gtid_state;
+ const char *lookup_name;
+ char name_buf[FN_REFLEN];
+ LOG_INFO linfo;
+
+ if (!mysql_bin_log.is_open())
+ {
+ my_error(ER_NO_BINARY_LOGGING, MYF(0));
+ return 1;
+ }
+
+ if (in_name && in_name[0])
+ {
+ mysql_bin_log.make_log_name(name_buf, in_name);
+ lookup_name= name_buf;
+ }
+ else
+ lookup_name= NULL;
+ linfo.index_file_offset= 0;
+ if (mysql_bin_log.find_log_pos(&linfo, lookup_name, 1))
+ return 1;
+
+ if (pos < 4)
+ pos= 4;
+
+ if (gtid_state_from_pos(linfo.log_file_name, pos, &gtid_state) ||
+ gtid_state.to_string(out_str))
+ return 1;
+ return 0;
+}
+
+
+static bool
+is_until_reached(binlog_send_info *info, ulong *ev_offset,
+ Log_event_type event_type, const char **errmsg,
+ uint32 current_pos)
+{
+ switch (info->gtid_until_group)
+ {
+ case GTID_UNTIL_NOT_DONE:
+ return false;
+ case GTID_UNTIL_STOP_AFTER_STANDALONE:
+ if (Log_event::is_part_of_group(event_type))
+ return false;
+ break;
+ case GTID_UNTIL_STOP_AFTER_TRANSACTION:
+ if (event_type != XID_EVENT &&
+ (event_type != QUERY_EVENT ||
+ !Query_log_event::peek_is_commit_rollback
+ (info->packet->ptr()+*ev_offset,
+ info->packet->length()-*ev_offset,
+ info->current_checksum_alg)))
+ return false;
+ break;
+ }
+
+ /*
+ The last event group has been sent, now the START SLAVE UNTIL condition
+ has been reached.
+
+ Send a last fake Gtid_list_log_event with a flag set to mark that we
+ stop due to UNTIL condition.
+ */
+ if (reset_transmit_packet(info->thd, info->flags, ev_offset, errmsg))
+ return true;
+ Gtid_list_log_event glev(&info->until_binlog_state,
+ Gtid_list_log_event::FLAG_UNTIL_REACHED);
+ if (fake_gtid_list_event(info, &glev, errmsg, current_pos))
+ return true;
+ *errmsg= NULL;
+ return true;
+}
+
+
/*
Helper function for mysql_binlog_send() to write an event down the slave
connection.
@@ -561,22 +1569,313 @@ static int send_heartbeat_event(NET* net, String* packet,
Returns NULL on success, error message string on error.
*/
static const char *
-send_event_to_slave(THD *thd, NET *net, String* const packet, ushort flags,
- Log_event_type event_type, char *log_file_name,
- IO_CACHE *log)
+send_event_to_slave(binlog_send_info *info, Log_event_type event_type,
+ IO_CACHE *log, ulong ev_offset, rpl_gtid *error_gtid)
{
my_off_t pos;
+ String* const packet= info->packet;
+ size_t len= packet->length();
+ int mariadb_slave_capability= info->mariadb_slave_capability;
+ uint8 current_checksum_alg= info->current_checksum_alg;
+ slave_connection_state *gtid_state= &info->gtid_state;
+ slave_connection_state *until_gtid_state= info->until_gtid_state;
+
+ if (event_type == GTID_LIST_EVENT &&
+ info->using_gtid_state && until_gtid_state)
+ {
+ rpl_gtid *gtid_list;
+ uint32 list_len;
+ bool err;
+
+ if (ev_offset > len ||
+ Gtid_list_log_event::peek(packet->ptr()+ev_offset, len - ev_offset,
+ current_checksum_alg,
+ &gtid_list, &list_len, info->fdev))
+ {
+ my_errno= ER_MASTER_FATAL_ERROR_READING_BINLOG;
+ return "Failed to read Gtid_list_log_event: corrupt binlog";
+ }
+ err= info->until_binlog_state.load(gtid_list, list_len);
+ my_free(gtid_list);
+ if (err)
+ {
+ my_errno= ER_MASTER_FATAL_ERROR_READING_BINLOG;
+ return "Failed in internal GTID book-keeping: Out of memory";
+ }
+ }
+
+ /* Skip GTID event groups until we reach slave position within a domain_id. */
+ if (event_type == GTID_EVENT && info->using_gtid_state)
+ {
+ uchar flags2;
+ slave_connection_state::entry *gtid_entry;
+ rpl_gtid *gtid;
+
+ if (gtid_state->count() > 0 || until_gtid_state)
+ {
+ rpl_gtid event_gtid;
+
+ if (ev_offset > len ||
+ Gtid_log_event::peek(packet->ptr()+ev_offset, len - ev_offset,
+ current_checksum_alg,
+ &event_gtid.domain_id, &event_gtid.server_id,
+ &event_gtid.seq_no, &flags2, info->fdev))
+ {
+ my_errno= ER_MASTER_FATAL_ERROR_READING_BINLOG;
+ return "Failed to read Gtid_log_event: corrupt binlog";
+ }
+
+ DBUG_EXECUTE_IF("gtid_force_reconnect_at_10_1_100",
+ {
+ rpl_gtid *dbug_gtid;
+ if ((dbug_gtid= info->until_binlog_state.find_nolock(10,1)) &&
+ dbug_gtid->seq_no == 100)
+ {
+ DBUG_SET("-d,gtid_force_reconnect_at_10_1_100");
+ DBUG_SET_INITIAL("-d,gtid_force_reconnect_at_10_1_100");
+ my_errno= ER_UNKNOWN_ERROR;
+ return "DBUG-injected forced reconnect";
+ }
+ });
+
+ if (info->until_binlog_state.update_nolock(&event_gtid, false))
+ {
+ my_errno= ER_MASTER_FATAL_ERROR_READING_BINLOG;
+ return "Failed in internal GTID book-keeping: Out of memory";
+ }
+
+ if (gtid_state->count() > 0)
+ {
+ gtid_entry= gtid_state->find_entry(event_gtid.domain_id);
+ if (gtid_entry != NULL)
+ {
+ gtid= &gtid_entry->gtid;
+ if (gtid_entry->flags & slave_connection_state::START_ON_EMPTY_DOMAIN)
+ {
+ rpl_gtid master_gtid;
+ if (!mysql_bin_log.find_in_binlog_state(gtid->domain_id,
+ gtid->server_id,
+ &master_gtid) ||
+ master_gtid.seq_no < gtid->seq_no)
+ {
+ int err;
+ const char *errormsg;
+ *error_gtid= *gtid;
+ give_error_start_pos_missing_in_binlog(&err, &errormsg, error_gtid);
+ my_errno= err;
+ return errormsg;
+ }
+ gtid_entry->flags&= ~(uint32)slave_connection_state::START_ON_EMPTY_DOMAIN;
+ }
+
+ /* Skip this event group if we have not yet reached slave start pos. */
+ if (event_gtid.server_id != gtid->server_id ||
+ event_gtid.seq_no <= gtid->seq_no)
+ info->gtid_skip_group= (flags2 & Gtid_log_event::FL_STANDALONE ?
+ GTID_SKIP_STANDALONE : GTID_SKIP_TRANSACTION);
+ if (event_gtid.server_id == gtid->server_id &&
+ event_gtid.seq_no >= gtid->seq_no)
+ {
+ if (info->slave_gtid_strict_mode &&
+ event_gtid.seq_no > gtid->seq_no &&
+ !(gtid_entry->flags & slave_connection_state::START_OWN_SLAVE_POS))
+ {
+ /*
+ In strict mode, it is an error if the slave requests to start
+ in a "hole" in the master's binlog: a GTID that does not
+ exist, even though both the prior and subsequent seq_no exists
+ for same domain_id and server_id.
+ */
+ my_errno= ER_GTID_START_FROM_BINLOG_HOLE;
+ *error_gtid= *gtid;
+ return "The binlog on the master is missing the GTID requested "
+ "by the slave (even though both a prior and a subsequent "
+ "sequence number does exist), and GTID strict mode is enabled.";
+ }
+
+ /*
+ Send a fake Gtid_list event to the slave.
+ This allows the slave to update its current binlog position
+ so MASTER_POS_WAIT() and MASTER_GTID_WAIT() can work.
+ The fake event will be sent at the end of this event group.
+ */
+ info->send_fake_gtid_list= true;
+
+ /*
+ Delete this entry if we have reached slave start position (so we
+ will not skip subsequent events and won't have to look them up
+ and check).
+ */
+ gtid_state->remove(gtid);
+ }
+ }
+ }
+
+ if (until_gtid_state)
+ {
+ gtid= until_gtid_state->find(event_gtid.domain_id);
+ if (gtid == NULL)
+ {
+ /*
+ This domain already reached the START SLAVE UNTIL stop condition,
+ so skip this event group.
+ */
+ info->gtid_skip_group = (flags2 & Gtid_log_event::FL_STANDALONE ?
+ GTID_SKIP_STANDALONE : GTID_SKIP_TRANSACTION);
+ }
+ else if (event_gtid.server_id == gtid->server_id &&
+ event_gtid.seq_no >= gtid->seq_no)
+ {
+ /*
+ We have reached the stop condition.
+ Delete this domain_id from the hash, so we will skip all further
+ events in this domain and eventually stop when all domains are
+ done.
+ */
+ uint64 until_seq_no= gtid->seq_no;
+ until_gtid_state->remove(gtid);
+ if (until_gtid_state->count() == 0)
+ info->gtid_until_group= (flags2 & Gtid_log_event::FL_STANDALONE ?
+ GTID_UNTIL_STOP_AFTER_STANDALONE :
+ GTID_UNTIL_STOP_AFTER_TRANSACTION);
+ if (event_gtid.seq_no > until_seq_no)
+ {
+ /*
+ The GTID in START SLAVE UNTIL condition is missing in our binlog.
+ This should normally not happen (user error), but since we can be
+ sure that we are now beyond the position that the UNTIL condition
+ should be in, we can just stop now. And we also need to skip this
+ event group (as it is beyond the UNTIL condition).
+ */
+ info->gtid_skip_group = (flags2 & Gtid_log_event::FL_STANDALONE ?
+ GTID_SKIP_STANDALONE : GTID_SKIP_TRANSACTION);
+ }
+ }
+ }
+ }
+ }
+
+ /*
+ Skip event group if we have not yet reached the correct slave GTID position.
+
+ Note that slave that understands GTID can also tolerate holes, so there is
+ no need to supply dummy event.
+ */
+ switch (info->gtid_skip_group)
+ {
+ case GTID_SKIP_STANDALONE:
+ if (!Log_event::is_part_of_group(event_type))
+ info->gtid_skip_group= GTID_SKIP_NOT;
+ return NULL;
+ case GTID_SKIP_TRANSACTION:
+ if (event_type == XID_EVENT ||
+ (event_type == QUERY_EVENT &&
+ Query_log_event::peek_is_commit_rollback(packet->ptr() + ev_offset,
+ len - ev_offset,
+ current_checksum_alg)))
+ info->gtid_skip_group= GTID_SKIP_NOT;
+ return NULL;
+ case GTID_SKIP_NOT:
+ break;
+ }
/* Do not send annotate_rows events unless slave requested it. */
if (event_type == ANNOTATE_ROWS_EVENT &&
- !(flags & BINLOG_SEND_ANNOTATE_ROWS_EVENT))
- return NULL;
+ !(info->flags & BINLOG_SEND_ANNOTATE_ROWS_EVENT))
+ {
+ if (mariadb_slave_capability >= MARIA_SLAVE_CAPABILITY_TOLERATE_HOLES)
+ {
+ /* This slave can tolerate events omitted from the binlog stream. */
+ return NULL;
+ }
+ else if (mariadb_slave_capability >= MARIA_SLAVE_CAPABILITY_ANNOTATE)
+ {
+ /*
+ The slave did not request ANNOTATE_ROWS_EVENT (it does not need them as
+ it will not log them in its own binary log). However, it understands the
+ event and will just ignore it, and it would break if we omitted it,
+ leaving a hole in the binlog stream. So just send the event as-is.
+ */
+ }
+ else
+ {
+ /*
+ The slave does not understand ANNOTATE_ROWS_EVENT.
+
+ Older MariaDB slaves (and MySQL slaves) will break replication if there
+ are holes in the binlog stream (they will miscompute the binlog offset
+ and request the wrong position when reconnecting).
+
+ So replace the event with a dummy event of the same size that will be
+ a no-operation on the slave.
+ */
+ if (Query_log_event::dummy_event(packet, ev_offset, current_checksum_alg))
+ {
+ my_errno= ER_MASTER_FATAL_ERROR_READING_BINLOG;
+ return "Failed to replace row annotate event with dummy: too small event.";
+ }
+ }
+ }
+
+ /*
+ Replace GTID events with old-style BEGIN events for slaves that do not
+ understand global transaction IDs. For stand-alone events, where there is
+ no terminating COMMIT query event, omit the GTID event or replace it with
+ a dummy event, as appropriate.
+ */
+ if (event_type == GTID_EVENT &&
+ mariadb_slave_capability < MARIA_SLAVE_CAPABILITY_GTID)
+ {
+ bool need_dummy=
+ mariadb_slave_capability < MARIA_SLAVE_CAPABILITY_TOLERATE_HOLES;
+ bool err= Gtid_log_event::make_compatible_event(packet, &need_dummy,
+ ev_offset,
+ current_checksum_alg);
+ if (err)
+ {
+ my_errno= ER_MASTER_FATAL_ERROR_READING_BINLOG;
+ return "Failed to replace GTID event with backwards-compatible event: "
+ "currupt event.";
+ }
+ if (!need_dummy)
+ return NULL;
+ }
+
+ /*
+ Do not send binlog checkpoint or gtid list events to a slave that does not
+ understand it.
+ */
+ if ((unlikely(event_type == BINLOG_CHECKPOINT_EVENT) &&
+ mariadb_slave_capability < MARIA_SLAVE_CAPABILITY_BINLOG_CHECKPOINT) ||
+ (unlikely(event_type == GTID_LIST_EVENT) &&
+ mariadb_slave_capability < MARIA_SLAVE_CAPABILITY_GTID))
+ {
+ if (mariadb_slave_capability >= MARIA_SLAVE_CAPABILITY_TOLERATE_HOLES)
+ {
+ /* This slave can tolerate events omitted from the binlog stream. */
+ return NULL;
+ }
+ else
+ {
+ /*
+ The slave does not understand BINLOG_CHECKPOINT_EVENT. Send a dummy
+ event instead, with same length so slave does not get confused about
+ binlog positions.
+ */
+ if (Query_log_event::dummy_event(packet, ev_offset, current_checksum_alg))
+ {
+ my_errno= ER_MASTER_FATAL_ERROR_READING_BINLOG;
+ return "Failed to replace binlog checkpoint or gtid list event with "
+ "dummy: too small event.";
+ }
+ }
+ }
/*
Skip events with the @@skip_replication flag set, if slave requested
skipping of such events.
*/
- if (thd->variables.option_bits & OPTION_SKIP_REPLICATION)
+ if (info->thd->variables.option_bits & OPTION_SKIP_REPLICATION)
{
/*
The first byte of the packet is a '\0' to distinguish it from an error
@@ -587,29 +1886,43 @@ send_event_to_slave(THD *thd, NET *net, String* const packet, ushort flags,
return NULL;
}
- thd_proc_info(thd, "Sending binlog event to slave");
+ THD_STAGE_INFO(info->thd, stage_sending_binlog_event_to_slave);
pos= my_b_tell(log);
if (RUN_HOOK(binlog_transmit, before_send_event,
- (thd, flags, packet, log_file_name, pos)))
+ (info->thd, info->flags, packet, info->log_file_name, pos)))
+ {
+ my_errno= ER_UNKNOWN_ERROR;
return "run 'before_send_event' hook failed";
+ }
- if (my_net_write(net, (uchar*) packet->ptr(), packet->length()))
+ if (my_net_write(info->net, (uchar*) packet->ptr(), len))
+ {
+ my_errno= ER_UNKNOWN_ERROR;
return "Failed on my_net_write()";
+ }
DBUG_PRINT("info", ("log event code %d", (*packet)[LOG_EVENT_OFFSET+1] ));
if (event_type == LOAD_EVENT)
{
- if (send_file(thd))
+ if (send_file(info->thd))
+ {
+ my_errno= ER_UNKNOWN_ERROR;
return "failed in send_file()";
+ }
}
- if (RUN_HOOK(binlog_transmit, after_send_event, (thd, flags, packet)))
+ if (RUN_HOOK(binlog_transmit, after_send_event,
+ (info->thd, info->flags, packet)))
+ {
+ my_errno= ER_UNKNOWN_ERROR;
return "Failed to run hook 'after_send_event'";
+ }
return NULL; /* Success */
}
+
void mysql_binlog_send(THD* thd, char* log_ident, my_off_t pos,
ushort flags)
{
@@ -621,24 +1934,32 @@ void mysql_binlog_send(THD* thd, char* log_ident, my_off_t pos,
IO_CACHE log;
File file = -1;
- String* const packet = &thd->packet;
+ String* const packet= &thd->packet;
int error;
const char *errmsg = "Unknown error", *tmp_msg;
char error_text[MAX_SLAVE_ERRMSG]; // to be send to slave via my_message()
- NET* net = &thd->net;
mysql_mutex_t *log_lock;
mysql_cond_t *log_cond;
+ char str_buf[128];
+ String connect_gtid_state(str_buf, sizeof(str_buf), system_charset_info);
+ char str_buf2[128];
+ String slave_until_gtid_str(str_buf2, sizeof(str_buf2), system_charset_info);
+ slave_connection_state until_gtid_state_obj;
+ rpl_gtid error_gtid;
+ binlog_send_info info(thd, packet, flags, log_file_name);
+ bool has_transmit_started= false;
- uint8 current_checksum_alg= BINLOG_CHECKSUM_ALG_UNDEF;
int old_max_allowed_packet= thd->variables.max_allowed_packet;
#ifndef DBUG_OFF
int left_events = max_binlog_dump_events;
+ uint dbug_reconnect_counter= 0;
#endif
DBUG_ENTER("mysql_binlog_send");
DBUG_PRINT("enter",("log_ident: '%s' pos: %ld", log_ident, (long) pos));
bzero((char*) &log,sizeof(log));
+ bzero(&error_gtid, sizeof(error_gtid));
/*
heartbeat_period from @master_heartbeat_period user variable
*/
@@ -649,21 +1970,43 @@ void mysql_binlog_send(THD* thd, char* log_ident, my_off_t pos,
*p_start_coord= &start_coord;
LOG_POS_COORD coord_buf= { log_file_name, BIN_LOG_HEADER_SIZE },
*p_coord= &coord_buf;
- if (heartbeat_period != LL(0))
+ if (heartbeat_period != 0)
{
heartbeat_ts= &heartbeat_buf;
set_timespec_nsec(*heartbeat_ts, 0);
}
- if (global_system_variables.log_warnings > 1)
- sql_print_information("Start binlog_dump to slave_server(%u), pos(%s, %lu)",
- thd->server_id, log_ident, (ulong)pos);
- if (RUN_HOOK(binlog_transmit, transmit_start, (thd, flags, log_ident, pos)))
+ info.mariadb_slave_capability= get_mariadb_slave_capability(thd);
+
+ connect_gtid_state.length(0);
+ info.using_gtid_state= get_slave_connect_state(thd, &connect_gtid_state);
+ DBUG_EXECUTE_IF("simulate_non_gtid_aware_master", info.using_gtid_state= false;);
+ if (info.using_gtid_state)
{
- errmsg= "Failed to run hook 'transmit_start'";
- my_errno= ER_UNKNOWN_ERROR;
- goto err;
+ info.slave_gtid_strict_mode= get_slave_gtid_strict_mode(thd);
+ info.slave_gtid_ignore_duplicates= get_slave_gtid_ignore_duplicates(thd);
+ if(get_slave_until_gtid(thd, &slave_until_gtid_str))
+ info.until_gtid_state= &until_gtid_state_obj;
}
+ DBUG_EXECUTE_IF("binlog_force_reconnect_after_22_events",
+ {
+ DBUG_SET("-d,binlog_force_reconnect_after_22_events");
+ DBUG_SET_INITIAL("-d,binlog_force_reconnect_after_22_events");
+ dbug_reconnect_counter= 22;
+ });
+
+ /*
+ We want to corrupt the first event, in Log_event::read_log_event().
+ But we do not want the corruption to happen early, eg. when client does
+ BINLOG_GTID_POS(). So test case sets a DBUG trigger which causes us to
+ set the real DBUG injection here.
+ */
+ DBUG_EXECUTE_IF("corrupt_read_log_event2_set",
+ {
+ DBUG_SET("-d,corrupt_read_log_event2_set");
+ DBUG_SET("+d,corrupt_read_log_event2");
+ });
+
#ifndef DBUG_OFF
if (opt_sporadic_binlog_dump_fail && (binlog_dump_count++ % 2))
{
@@ -673,6 +2016,13 @@ void mysql_binlog_send(THD* thd, char* log_ident, my_off_t pos,
}
#endif
+ if (!(info.fdev= new Format_description_log_event(3)))
+ {
+ errmsg= "Out of memory initializing format_description event";
+ my_errno= ER_MASTER_FATAL_ERROR_READING_BINLOG;
+ goto err;
+ }
+
if (!mysql_bin_log.is_open())
{
errmsg = "Binary log is not open";
@@ -687,10 +2037,45 @@ void mysql_binlog_send(THD* thd, char* log_ident, my_off_t pos,
}
name=search_file_name;
- if (log_ident[0])
- mysql_bin_log.make_log_name(search_file_name, log_ident);
+ if (info.using_gtid_state)
+ {
+ if (info.gtid_state.load(connect_gtid_state.c_ptr_quick(),
+ connect_gtid_state.length()))
+ {
+ errmsg= "Out of memory or malformed slave request when obtaining start "
+ "position from GTID state";
+ my_errno= ER_UNKNOWN_ERROR;
+ goto err;
+ }
+ if (info.until_gtid_state &&
+ info.until_gtid_state->load(slave_until_gtid_str.c_ptr_quick(),
+ slave_until_gtid_str.length()))
+ {
+ errmsg= "Out of memory or malformed slave request when obtaining UNTIL "
+ "position sent from slave";
+ my_errno= ER_UNKNOWN_ERROR;
+ goto err;
+ }
+ if ((error= check_slave_start_position(&info, &errmsg, &error_gtid)))
+ {
+ my_errno= error;
+ goto err;
+ }
+ if ((errmsg= gtid_find_binlog_file(&info.gtid_state, search_file_name,
+ info.until_gtid_state)))
+ {
+ my_errno= ER_MASTER_FATAL_ERROR_READING_BINLOG;
+ goto err;
+ }
+ pos= 4;
+ }
else
- name=0; // Find first log
+ {
+ if (log_ident[0])
+ mysql_bin_log.make_log_name(search_file_name, log_ident);
+ else
+ name=0; // Find first log
+ }
linfo.index_file_offset = 0;
@@ -718,6 +2103,17 @@ impossible position";
goto err;
}
+ if (global_system_variables.log_warnings > 1)
+ sql_print_information("Start binlog_dump to slave_server(%lu), pos(%s, %lu)",
+ thd->variables.server_id, log_ident, (ulong)pos);
+ if (RUN_HOOK(binlog_transmit, transmit_start, (thd, flags, log_ident, pos)))
+ {
+ errmsg= "Failed to run hook 'transmit_start'";
+ my_errno= ER_UNKNOWN_ERROR;
+ goto err;
+ }
+ has_transmit_started= true;
+
/* reset transmit packet for the fake rotate event below */
if (reset_transmit_packet(thd, flags, &ev_offset, &errmsg))
goto err;
@@ -750,7 +2146,7 @@ impossible position";
given that we want minimum modification of 4.0, we send the normal
and fake Rotates.
*/
- if (fake_rotate_event(net, packet, log_file_name, pos, &errmsg,
+ if (fake_rotate_event(&info, pos, &errmsg,
get_binlog_checksum_value_at_connect(thd)))
{
/*
@@ -800,14 +2196,16 @@ impossible position";
(*packet)[EVENT_TYPE_OFFSET+ev_offset]));
if ((*packet)[EVENT_TYPE_OFFSET+ev_offset] == FORMAT_DESCRIPTION_EVENT)
{
- current_checksum_alg= get_checksum_alg(packet->ptr() + ev_offset,
- packet->length() - ev_offset);
- DBUG_ASSERT(current_checksum_alg == BINLOG_CHECKSUM_ALG_OFF ||
- current_checksum_alg == BINLOG_CHECKSUM_ALG_UNDEF ||
- current_checksum_alg == BINLOG_CHECKSUM_ALG_CRC32);
+ Format_description_log_event *tmp;
+
+ info.current_checksum_alg= get_checksum_alg(packet->ptr() + ev_offset,
+ packet->length() - ev_offset);
+ DBUG_ASSERT(info.current_checksum_alg == BINLOG_CHECKSUM_ALG_OFF ||
+ info.current_checksum_alg == BINLOG_CHECKSUM_ALG_UNDEF ||
+ info.current_checksum_alg == BINLOG_CHECKSUM_ALG_CRC32);
if (!is_slave_checksum_aware(thd) &&
- current_checksum_alg != BINLOG_CHECKSUM_ALG_OFF &&
- current_checksum_alg != BINLOG_CHECKSUM_ALG_UNDEF)
+ info.current_checksum_alg != BINLOG_CHECKSUM_ALG_OFF &&
+ info.current_checksum_alg != BINLOG_CHECKSUM_ALG_UNDEF)
{
my_errno= ER_MASTER_FATAL_ERROR_READING_BINLOG;
errmsg= "Slave can not handle replication events with the checksum "
@@ -817,6 +2215,18 @@ impossible position";
"slaves that cannot process them");
goto err;
}
+
+ if (!(tmp= new Format_description_log_event(packet->ptr()+ev_offset,
+ packet->length()-ev_offset,
+ info.fdev)))
+ {
+ my_errno= ER_MASTER_FATAL_ERROR_READING_BINLOG;
+ errmsg= "Corrupt Format_description event found or out-of-memory";
+ goto err;
+ }
+ delete info.fdev;
+ info.fdev= tmp;
+
(*packet)[FLAGS_OFFSET+ev_offset] &= ~LOG_EVENT_BINLOG_IN_USE_F;
/*
mark that this event with "log_pos=0", so the slave
@@ -832,12 +2242,12 @@ impossible position";
ST_CREATED_OFFSET+ev_offset, (ulong) 0);
/* fix the checksum due to latest changes in header */
- if (current_checksum_alg != BINLOG_CHECKSUM_ALG_OFF &&
- current_checksum_alg != BINLOG_CHECKSUM_ALG_UNDEF)
+ if (info.current_checksum_alg != BINLOG_CHECKSUM_ALG_OFF &&
+ info.current_checksum_alg != BINLOG_CHECKSUM_ALG_UNDEF)
fix_checksum(packet, ev_offset);
/* send it */
- if (my_net_write(net, (uchar*) packet->ptr(), packet->length()))
+ if (my_net_write(info.net, (uchar*) packet->ptr(), packet->length()))
{
errmsg = "Failed on my_net_write()";
my_errno= ER_UNKNOWN_ERROR;
@@ -867,12 +2277,22 @@ impossible position";
/* The Format_description_log_event event will be found naturally. */
}
+ /*
+ Handle the case of START SLAVE UNTIL with an UNTIL condition already
+ fulfilled at the start position.
+
+ We will send one event, the format_description, and then stop.
+ */
+ if (info.until_gtid_state && info.until_gtid_state->count() == 0)
+ info.gtid_until_group= GTID_UNTIL_STOP_AFTER_STANDALONE;
+
/* seek to the requested position, to start the requested dump */
my_b_seek(&log, pos); // Seek will done on next read
- while (!net->error && net->vio != 0 && !thd->killed)
+ while (!info.net->error && info.net->vio != 0 && !thd->killed)
{
Log_event_type event_type= UNKNOWN_EVENT;
+ killed_state killed;
/* reset the transmit packet for the event read from binary log
file */
@@ -880,15 +2300,16 @@ impossible position";
goto err;
bool is_active_binlog= false;
- while (!(error= Log_event::read_log_event(&log, packet, log_lock,
- current_checksum_alg,
+ while (!(killed= thd->killed) &&
+ !(error = Log_event::read_log_event(&log, packet, log_lock,
+ info.current_checksum_alg,
log_file_name,
&is_active_binlog)))
{
#ifndef DBUG_OFF
if (max_binlog_dump_events && !left_events--)
{
- net_flush(net);
+ net_flush(info.net);
errmsg = "Debugging binlog dump abort";
my_errno= ER_UNKNOWN_ERROR;
goto err;
@@ -906,7 +2327,7 @@ impossible position";
{
if (event_type == XID_EVENT)
{
- net_flush(net);
+ net_flush(info.net);
const char act[]=
"now "
"wait_for signal.continue";
@@ -923,14 +2344,16 @@ impossible position";
#endif
if (event_type == FORMAT_DESCRIPTION_EVENT)
{
- current_checksum_alg= get_checksum_alg(packet->ptr() + ev_offset,
+ Format_description_log_event *tmp;
+
+ info.current_checksum_alg= get_checksum_alg(packet->ptr() + ev_offset,
packet->length() - ev_offset);
- DBUG_ASSERT(current_checksum_alg == BINLOG_CHECKSUM_ALG_OFF ||
- current_checksum_alg == BINLOG_CHECKSUM_ALG_UNDEF ||
- current_checksum_alg == BINLOG_CHECKSUM_ALG_CRC32);
+ DBUG_ASSERT(info.current_checksum_alg == BINLOG_CHECKSUM_ALG_OFF ||
+ info.current_checksum_alg == BINLOG_CHECKSUM_ALG_UNDEF ||
+ info.current_checksum_alg == BINLOG_CHECKSUM_ALG_CRC32);
if (!is_slave_checksum_aware(thd) &&
- current_checksum_alg != BINLOG_CHECKSUM_ALG_OFF &&
- current_checksum_alg != BINLOG_CHECKSUM_ALG_UNDEF)
+ info.current_checksum_alg != BINLOG_CHECKSUM_ALG_OFF &&
+ info.current_checksum_alg != BINLOG_CHECKSUM_ALG_UNDEF)
{
my_errno= ER_MASTER_FATAL_ERROR_READING_BINLOG;
errmsg= "Slave can not handle replication events with the checksum "
@@ -941,22 +2364,94 @@ impossible position";
goto err;
}
+ if (!(tmp= new Format_description_log_event(packet->ptr()+ev_offset,
+ packet->length()-ev_offset,
+ info.fdev)))
+ {
+ my_errno= ER_MASTER_FATAL_ERROR_READING_BINLOG;
+ errmsg= "Corrupt Format_description event found or out-of-memory";
+ goto err;
+ }
+ delete info.fdev;
+ info.fdev= tmp;
+
(*packet)[FLAGS_OFFSET+ev_offset] &= ~LOG_EVENT_BINLOG_IN_USE_F;
+
+ if (info.using_gtid_state)
+ {
+ /*
+ If this event has the field `created' set, then it will cause the
+ slave to delete all active temporary tables. This must not happen
+ if the slave received any later GTIDs in a previous connect, as
+ those GTIDs might have created new temporary tables that are still
+ needed.
+
+ So here, we check if the starting GTID position was already
+ reached before this format description event. If not, we clear the
+ `created' flag to preserve temporary tables on the slave. (If the
+ slave connects at a position past this event, it means that it
+ already received and handled it in a previous connect).
+ */
+ if (!info.gtid_state.is_pos_reached())
+ {
+ int4store((char*) packet->ptr()+LOG_EVENT_MINIMAL_HEADER_LEN+
+ ST_CREATED_OFFSET+ev_offset, (ulong) 0);
+ if (info.current_checksum_alg != BINLOG_CHECKSUM_ALG_OFF &&
+ info.current_checksum_alg != BINLOG_CHECKSUM_ALG_UNDEF)
+ fix_checksum(packet, ev_offset);
+ }
+ }
}
- if ((tmp_msg= send_event_to_slave(thd, net, packet, flags, event_type,
- log_file_name, &log)))
+#ifndef DBUG_OFF
+ if (dbug_reconnect_counter > 0)
+ {
+ --dbug_reconnect_counter;
+ if (dbug_reconnect_counter == 0)
+ {
+ errmsg= "DBUG-injected forced reconnect";
+ my_errno= ER_UNKNOWN_ERROR;
+ goto err;
+ }
+ }
+#endif
+
+ if ((tmp_msg= send_event_to_slave(&info, event_type, &log,
+ ev_offset, &error_gtid)))
{
errmsg= tmp_msg;
- my_errno= ER_UNKNOWN_ERROR;
goto err;
}
+ if (unlikely(info.send_fake_gtid_list) &&
+ info.gtid_skip_group == GTID_SKIP_NOT)
+ {
+ Gtid_list_log_event glev(&info.until_binlog_state, 0);
+
+ if (reset_transmit_packet(thd, flags, &ev_offset, &errmsg) ||
+ fake_gtid_list_event(&info, &glev, &errmsg, my_b_tell(&log)))
+ {
+ my_errno= ER_UNKNOWN_ERROR;
+ goto err;
+ }
+ info.send_fake_gtid_list= false;
+ }
+ if (info.until_gtid_state &&
+ is_until_reached(&info, &ev_offset, event_type, &errmsg,
+ my_b_tell(&log)))
+ {
+ if (errmsg)
+ {
+ my_errno= ER_UNKNOWN_ERROR;
+ goto err;
+ }
+ goto end;
+ }
DBUG_EXECUTE_IF("dump_thread_wait_before_send_xid",
{
if (event_type == XID_EVENT)
{
- net_flush(net);
+ net_flush(info.net);
}
});
@@ -964,6 +2459,8 @@ impossible position";
if (reset_transmit_packet(thd, flags, &ev_offset, &errmsg))
goto err;
}
+ if (killed)
+ goto end;
DBUG_EXECUTE_IF("wait_after_binlog_EOF",
{
@@ -991,7 +2488,7 @@ impossible position";
/*
Block until there is more data in the log
*/
- if (net_flush(net))
+ if (net_flush(info.net))
{
errmsg = "failed on net_flush()";
my_errno= ER_UNKNOWN_ERROR;
@@ -1034,7 +2531,7 @@ impossible position";
mysql_mutex_lock(log_lock);
switch (error= Log_event::read_log_event(&log, packet, (mysql_mutex_t*) 0,
- current_checksum_alg)) {
+ info.current_checksum_alg)) {
case 0:
/* we read successfully, so we'll need to send it to the slave */
mysql_mutex_unlock(log_lock);
@@ -1049,7 +2546,8 @@ impossible position";
int ret;
ulong signal_cnt;
DBUG_PRINT("wait",("waiting for data in binary log"));
- if (thd->server_id==0) // for mysqlbinlog (mysqlbinlog.server_id==0)
+ /* For mysqlbinlog (mysqlbinlog.server_id==0). */
+ if (thd->variables.server_id==0)
{
mysql_mutex_unlock(log_lock);
goto end;
@@ -1058,7 +2556,7 @@ impossible position";
#ifndef DBUG_OFF
ulong hb_info_counter= 0;
#endif
- const char* old_msg= thd->proc_info;
+ PSI_stage_info old_stage;
signal_cnt= mysql_bin_log.signal_cnt;
do
{
@@ -1067,9 +2565,11 @@ impossible position";
DBUG_ASSERT(heartbeat_ts);
set_timespec_nsec(*heartbeat_ts, heartbeat_period);
}
- thd->enter_cond(log_cond, log_lock,
- "Master has sent all binlog to slave; "
- "waiting for binlog to be updated");
+ thd->ENTER_COND(log_cond, log_lock,
+ &stage_master_has_sent_all_binlog_to_slave,
+ &old_stage);
+ if (thd->killed)
+ break;
ret= mysql_bin_log.wait_for_update_bin_log(thd, heartbeat_ts);
DBUG_ASSERT(ret == 0 || (heartbeat_period != 0));
if (ret == ETIMEDOUT || ret == ETIME)
@@ -1086,14 +2586,15 @@ impossible position";
/* reset transmit packet for the heartbeat event */
if (reset_transmit_packet(thd, flags, &ev_offset, &errmsg))
{
- thd->exit_cond(old_msg);
+ thd->EXIT_COND(&old_stage);
goto err;
}
- if (send_heartbeat_event(net, packet, p_coord, current_checksum_alg))
+ if (send_heartbeat_event(info.net, packet, p_coord,
+ info.current_checksum_alg))
{
errmsg = "Failed on my_net_write()";
my_errno= ER_UNKNOWN_ERROR;
- thd->exit_cond(old_msg);
+ thd->EXIT_COND(&old_stage);
goto err;
}
}
@@ -1101,8 +2602,8 @@ impossible position";
{
DBUG_PRINT("wait",("binary log received update or a broadcast signal caught"));
}
- } while (signal_cnt == mysql_bin_log.signal_cnt && !thd->killed);
- thd->exit_cond(old_msg);
+ } while (signal_cnt == mysql_bin_log.signal_cnt);
+ thd->EXIT_COND(&old_stage);
}
break;
@@ -1112,14 +2613,39 @@ impossible position";
goto err;
}
- if (read_packet &&
- (tmp_msg= send_event_to_slave(thd, net, packet, flags, event_type,
- log_file_name, &log)))
+ if (read_packet)
{
- errmsg= tmp_msg;
- my_errno= ER_UNKNOWN_ERROR;
- goto err;
- }
+ if ((tmp_msg= send_event_to_slave(&info, event_type, &log,
+ ev_offset, &error_gtid)))
+ {
+ errmsg= tmp_msg;
+ goto err;
+ }
+ if (unlikely(info.send_fake_gtid_list)
+ && info.gtid_skip_group == GTID_SKIP_NOT)
+ {
+ Gtid_list_log_event glev(&info.until_binlog_state, 0);
+
+ if (reset_transmit_packet(thd, flags, &ev_offset, &errmsg) ||
+ fake_gtid_list_event(&info, &glev, &errmsg, my_b_tell(&log)))
+ {
+ my_errno= ER_UNKNOWN_ERROR;
+ goto err;
+ }
+ info.send_fake_gtid_list= false;
+ }
+ if (info.until_gtid_state &&
+ is_until_reached(&info, &ev_offset, event_type, &errmsg,
+ my_b_tell(&log)))
+ {
+ if (errmsg)
+ {
+ my_errno= ER_UNKNOWN_ERROR;
+ goto err;
+ }
+ goto end;
+ }
+ }
log.error=0;
}
@@ -1129,7 +2655,7 @@ impossible position";
bool loop_breaker = 0;
/* need this to break out of the for loop from switch */
- thd_proc_info(thd, "Finished reading one binlog; switching to next binlog");
+ THD_STAGE_INFO(thd, stage_finished_reading_one_binlog_switching_to_next_binlog);
switch (mysql_bin_log.find_next_log(&linfo, 1)) {
case 0:
break;
@@ -1165,8 +2691,8 @@ impossible position";
read and send is Format_description_log_event.
*/
if ((file=open_binlog(&log, log_file_name, &errmsg)) < 0 ||
- fake_rotate_event(net, packet, log_file_name, BIN_LOG_HEADER_SIZE,
- &errmsg, current_checksum_alg))
+ fake_rotate_event(&info, BIN_LOG_HEADER_SIZE, &errmsg,
+ info.current_checksum_alg))
{
my_errno= ER_MASTER_FATAL_ERROR_READING_BINLOG;
goto err;
@@ -1180,17 +2706,19 @@ end:
end_io_cache(&log);
mysql_file_close(file, MYF(MY_WME));
- RUN_HOOK(binlog_transmit, transmit_stop, (thd, flags));
+ if (has_transmit_started)
+ RUN_HOOK(binlog_transmit, transmit_stop, (thd, flags));
my_eof(thd);
- thd_proc_info(thd, "Waiting to finalize termination");
+ THD_STAGE_INFO(thd, stage_waiting_to_finalize_termination);
mysql_mutex_lock(&LOCK_thread_count);
thd->current_linfo = 0;
mysql_mutex_unlock(&LOCK_thread_count);
thd->variables.max_allowed_packet= old_max_allowed_packet;
+ delete info.fdev;
DBUG_VOID_RETURN;
err:
- thd_proc_info(thd, "Waiting to finalize termination");
+ THD_STAGE_INFO(thd, stage_waiting_to_finalize_termination);
if (my_errno == ER_MASTER_FATAL_ERROR_READING_BINLOG && my_b_inited(&log))
{
/*
@@ -1206,10 +2734,50 @@ err:
my_basename(p_coord->file_name), p_coord->pos,
my_basename(log_file_name), my_b_tell(&log));
}
+ else if (my_errno == ER_GTID_POSITION_NOT_FOUND_IN_BINLOG)
+ {
+ my_snprintf(error_text, sizeof(error_text),
+ "Error: connecting slave requested to start from GTID "
+ "%u-%u-%llu, which is not in the master's binlog",
+ error_gtid.domain_id, error_gtid.server_id, error_gtid.seq_no);
+ /* Use this error code so slave will know not to try reconnect. */
+ my_errno = ER_MASTER_FATAL_ERROR_READING_BINLOG;
+ }
+ else if (my_errno == ER_GTID_POSITION_NOT_FOUND_IN_BINLOG2)
+ {
+ my_snprintf(error_text, sizeof(error_text),
+ "Error: connecting slave requested to start from GTID "
+ "%u-%u-%llu, which is not in the master's binlog. Since the "
+ "master's binlog contains GTIDs with higher sequence numbers, "
+ "it probably means that the slave has diverged due to "
+ "executing extra erroneous transactions",
+ error_gtid.domain_id, error_gtid.server_id, error_gtid.seq_no);
+ /* Use this error code so slave will know not to try reconnect. */
+ my_errno = ER_MASTER_FATAL_ERROR_READING_BINLOG;
+ }
+ else if (my_errno == ER_GTID_START_FROM_BINLOG_HOLE)
+ {
+ my_snprintf(error_text, sizeof(error_text),
+ "The binlog on the master is missing the GTID %u-%u-%llu "
+ "requested by the slave (even though both a prior and a "
+ "subsequent sequence number does exist), and GTID strict mode "
+ "is enabled",
+ error_gtid.domain_id, error_gtid.server_id, error_gtid.seq_no);
+ /* Use this error code so slave will know not to try reconnect. */
+ my_errno = ER_MASTER_FATAL_ERROR_READING_BINLOG;
+ }
+ else if (my_errno == ER_CANNOT_LOAD_SLAVE_GTID_STATE)
+ {
+ my_snprintf(error_text, sizeof(error_text),
+ "Failed to load replication slave GTID state from table %s.%s",
+ "mysql", rpl_gtid_slave_state_table_name.str);
+ my_errno = ER_MASTER_FATAL_ERROR_READING_BINLOG;
+ }
else
strcpy(error_text, errmsg);
end_io_cache(&log);
- RUN_HOOK(binlog_transmit, transmit_stop, (thd, flags));
+ if (has_transmit_started)
+ RUN_HOOK(binlog_transmit, transmit_stop, (thd, flags));
/*
Exclude iteration through thread list
this is needed for purge_logs() - it will iterate through
@@ -1223,6 +2791,7 @@ err:
if (file >= 0)
mysql_file_close(file, MYF(MY_WME));
thd->variables.max_allowed_packet= old_max_allowed_packet;
+ delete info.fdev;
my_message(my_errno, error_text, MYF(0));
DBUG_VOID_RETURN;
@@ -1241,18 +2810,52 @@ err:
@retval 0 success
@retval 1 error
+ @retval -1 fatal error
*/
+
int start_slave(THD* thd , Master_info* mi, bool net_report)
{
int slave_errno= 0;
int thread_mask;
+ char master_info_file_tmp[FN_REFLEN];
+ char relay_log_info_file_tmp[FN_REFLEN];
DBUG_ENTER("start_slave");
if (check_access(thd, SUPER_ACL, any_db, NULL, NULL, 0, 0))
- DBUG_RETURN(1);
+ DBUG_RETURN(-1);
+
+ create_logfile_name_with_suffix(master_info_file_tmp,
+ sizeof(master_info_file_tmp),
+ master_info_file, 0,
+ &mi->cmp_connection_name);
+ create_logfile_name_with_suffix(relay_log_info_file_tmp,
+ sizeof(relay_log_info_file_tmp),
+ relay_log_info_file, 0,
+ &mi->cmp_connection_name);
+
lock_slave_threads(mi); // this allows us to cleanly read slave_running
// Get a mask of _stopped_ threads
init_thread_mask(&thread_mask,mi,1 /* inverse */);
+
+ if (thd->lex->mi.gtid_pos_str.str)
+ {
+ if (thread_mask != (SLAVE_IO|SLAVE_SQL))
+ {
+ slave_errno= ER_SLAVE_WAS_RUNNING;
+ goto err;
+ }
+ if (thd->lex->slave_thd_opt)
+ {
+ slave_errno= ER_BAD_SLAVE_UNTIL_COND;
+ goto err;
+ }
+ if (mi->using_gtid == Master_info::USE_GTID_NO)
+ {
+ slave_errno= ER_UNTIL_REQUIRES_USING_GTID;
+ goto err;
+ }
+ }
+
/*
Below we will start all stopped threads. But if the user wants to
start only one thread, do as if the other thread was running (as we
@@ -1263,10 +2866,22 @@ int start_slave(THD* thd , Master_info* mi, bool net_report)
thread_mask&= thd->lex->slave_thd_opt;
if (thread_mask) //some threads are stopped, start them
{
- if (init_master_info(mi,master_info_file,relay_log_info_file, 0,
+ if (init_master_info(mi,master_info_file_tmp,relay_log_info_file_tmp, 0,
thread_mask))
slave_errno=ER_MASTER_INFO;
- else if (server_id_supplied && *mi->host)
+ else if (!server_id_supplied)
+ {
+ slave_errno= ER_BAD_SLAVE; net_report= 0;
+ my_message(slave_errno, "Misconfigured slave: server_id was not set; Fix in config file",
+ MYF(0));
+ }
+ else if (!*mi->host)
+ {
+ slave_errno= ER_BAD_SLAVE; net_report= 0;
+ my_message(slave_errno, "Misconfigured slave: MASTER_HOST was not set; Fix in config file or with CHANGE MASTER TO",
+ MYF(0));
+ }
+ else
{
/*
If we will start SQL thread we will care about UNTIL options If
@@ -1297,10 +2912,22 @@ int start_slave(THD* thd , Master_info* mi, bool net_report)
mi->rli.until_log_pos= thd->lex->mi.relay_log_pos;
strmake_buf(mi->rli.until_log_name, thd->lex->mi.relay_log_name);
}
+ else if (thd->lex->mi.gtid_pos_str.str)
+ {
+ if (mi->rli.until_gtid_pos.load(thd->lex->mi.gtid_pos_str.str,
+ thd->lex->mi.gtid_pos_str.length))
+ {
+ slave_errno= ER_INCORRECT_GTID_STATE;
+ mysql_mutex_unlock(&mi->rli.data_lock);
+ goto err;
+ }
+ mi->rli.until_condition= Relay_log_info::UNTIL_GTID;
+ }
else
mi->rli.clear_until_condition();
- if (mi->rli.until_condition != Relay_log_info::UNTIL_NONE)
+ if (mi->rli.until_condition == Relay_log_info::UNTIL_MASTER_POS ||
+ mi->rli.until_condition == Relay_log_info::UNTIL_RELAY_POS)
{
/* Preparing members for effective until condition checking */
const char *p= fn_ext(mi->rli.until_log_name);
@@ -1323,10 +2950,13 @@ int start_slave(THD* thd , Master_info* mi, bool net_report)
/* mark the cached result of the UNTIL comparison as "undefined" */
mi->rli.until_log_names_cmp_result=
Relay_log_info::UNTIL_LOG_NAMES_CMP_UNKNOWN;
+ }
+ if (mi->rli.until_condition != Relay_log_info::UNTIL_NONE)
+ {
/* Issuing warning then started without --skip-slave-start */
if (!opt_skip_slave_start)
- push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
+ push_warning(thd, Sql_condition::WARN_LEVEL_NOTE,
ER_MISSING_SKIP_SLAVE,
ER(ER_MISSING_SKIP_SLAVE));
}
@@ -1334,36 +2964,45 @@ int start_slave(THD* thd , Master_info* mi, bool net_report)
mysql_mutex_unlock(&mi->rli.data_lock);
}
else if (thd->lex->mi.pos || thd->lex->mi.relay_log_pos)
- push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, ER_UNTIL_COND_IGNORED,
+ push_warning(thd, Sql_condition::WARN_LEVEL_NOTE, ER_UNTIL_COND_IGNORED,
ER(ER_UNTIL_COND_IGNORED));
if (!slave_errno)
slave_errno = start_slave_threads(0 /*no mutex */,
- 1 /* wait for start */,
- mi,
- master_info_file,relay_log_info_file,
- thread_mask);
+ 1 /* wait for start */,
+ mi,
+ master_info_file_tmp,
+ relay_log_info_file_tmp,
+ thread_mask);
}
- else
- slave_errno = ER_BAD_SLAVE;
}
else
{
/* no error if all threads are already started, only a warning */
- push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, ER_SLAVE_WAS_RUNNING,
+ push_warning(thd, Sql_condition::WARN_LEVEL_NOTE, ER_SLAVE_WAS_RUNNING,
ER(ER_SLAVE_WAS_RUNNING));
}
+err:
unlock_slave_threads(mi);
+#ifdef WITH_WSREP
+ if (WSREP(thd))
+ thd_proc_info(thd, "exit stop_slave()");
+ else
+ thd_proc_info(thd, 0);
+#else /* WITH_WSREP */
+ thd_proc_info(thd, 0);
+#endif /* WITH_WSREP */
+
if (slave_errno)
{
if (net_report)
- my_message(slave_errno, ER(slave_errno), MYF(0));
- DBUG_RETURN(1);
+ my_error(slave_errno, MYF(0),
+ (int) mi->connection_name.length,
+ mi->connection_name.str);
+ DBUG_RETURN(slave_errno == ER_BAD_SLAVE ? -1 : 1);
}
- else if (net_report)
- my_ok(thd);
DBUG_RETURN(0);
}
@@ -1381,18 +3020,18 @@ int start_slave(THD* thd , Master_info* mi, bool net_report)
@retval 0 success
@retval 1 error
+ @retval -1 error
*/
+
int stop_slave(THD* thd, Master_info* mi, bool net_report )
{
- DBUG_ENTER("stop_slave");
-
int slave_errno;
- if (!thd)
- thd = current_thd;
+ DBUG_ENTER("stop_slave");
+ DBUG_PRINT("enter",("Connection: %s", mi->connection_name.str));
if (check_access(thd, SUPER_ACL, any_db, NULL, NULL, 0, 0))
- DBUG_RETURN(1);
- thd_proc_info(thd, "Killing slave");
+ DBUG_RETURN(-1);
+ THD_STAGE_INFO(thd, stage_killing_slave);
int thread_mask;
lock_slave_threads(mi);
// Get a mask of _running_ threads
@@ -1415,18 +3054,10 @@ int stop_slave(THD* thd, Master_info* mi, bool net_report )
{
//no error if both threads are already stopped, only a warning
slave_errno= 0;
- push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, ER_SLAVE_WAS_NOT_RUNNING,
+ push_warning(thd, Sql_condition::WARN_LEVEL_NOTE, ER_SLAVE_WAS_NOT_RUNNING,
ER(ER_SLAVE_WAS_NOT_RUNNING));
}
unlock_slave_threads(mi);
-#ifdef WITH_WSREP
- if (WSREP(thd))
- thd_proc_info(thd, "exit stop_slave()");
- else
- thd_proc_info(thd, 0);
-#else /* WITH_WSREP */
- thd_proc_info(thd, 0);
-#endif /* WITH_WSREP */
if (slave_errno)
{
@@ -1434,8 +3065,6 @@ int stop_slave(THD* thd, Master_info* mi, bool net_report )
my_message(slave_errno, ER(slave_errno), MYF(0));
DBUG_RETURN(1);
}
- else if (net_report)
- my_ok(thd);
DBUG_RETURN(0);
}
@@ -1459,15 +3088,18 @@ int reset_slave(THD *thd, Master_info* mi)
int thread_mask= 0, error= 0;
uint sql_errno=ER_UNKNOWN_ERROR;
const char* errmsg= "Unknown error occured while reseting slave";
+ char master_info_file_tmp[FN_REFLEN];
+ char relay_log_info_file_tmp[FN_REFLEN];
DBUG_ENTER("reset_slave");
lock_slave_threads(mi);
init_thread_mask(&thread_mask,mi,0 /* not inverse */);
if (thread_mask) // We refuse if any slave thread is running
{
- sql_errno= ER_SLAVE_MUST_STOP;
- error=1;
- goto err;
+ unlock_slave_threads(mi);
+ my_error(ER_SLAVE_MUST_STOP, MYF(0), (int) mi->connection_name.length,
+ mi->connection_name.str);
+ DBUG_RETURN(ER_SLAVE_MUST_STOP);
}
ha_reset_slave(thd);
@@ -1491,25 +3123,41 @@ int reset_slave(THD *thd, Master_info* mi)
mi->clear_error();
mi->rli.clear_error();
mi->rli.clear_until_condition();
+ mi->rli.slave_skip_counter= 0;
// close master_info_file, relay_log_info_file, set mi->inited=rli->inited=0
end_master_info(mi);
+
// and delete these two files
- fn_format(fname, master_info_file, mysql_data_home, "", 4+32);
+ create_logfile_name_with_suffix(master_info_file_tmp,
+ sizeof(master_info_file_tmp),
+ master_info_file, 0,
+ &mi->cmp_connection_name);
+ create_logfile_name_with_suffix(relay_log_info_file_tmp,
+ sizeof(relay_log_info_file_tmp),
+ relay_log_info_file, 0,
+ &mi->cmp_connection_name);
+
+ fn_format(fname, master_info_file_tmp, mysql_data_home, "", 4+32);
if (mysql_file_stat(key_file_master_info, fname, &stat_area, MYF(0)) &&
mysql_file_delete(key_file_master_info, fname, MYF(MY_WME)))
{
error=1;
goto err;
}
+ else if (global_system_variables.log_warnings > 1)
+ sql_print_information("Deleted Master_info file '%s'.", fname);
+
// delete relay_log_info_file
- fn_format(fname, relay_log_info_file, mysql_data_home, "", 4+32);
+ fn_format(fname, relay_log_info_file_tmp, mysql_data_home, "", 4+32);
if (mysql_file_stat(key_file_relay_log_info, fname, &stat_area, MYF(0)) &&
mysql_file_delete(key_file_relay_log_info, fname, MYF(MY_WME)))
{
error=1;
goto err;
}
+ else if (global_system_variables.log_warnings > 1)
+ sql_print_information("Deleted Master_info file '%s'.", fname);
RUN_HOOK(binlog_relay_io, after_reset_slave, (thd, mi));
err:
@@ -1547,8 +3195,8 @@ void kill_zombie_dump_threads(uint32 slave_server_id)
while ((tmp=it++))
{
- if (tmp->command == COM_BINLOG_DUMP &&
- tmp->server_id == slave_server_id)
+ if (tmp->get_command() == COM_BINLOG_DUMP &&
+ tmp->variables.server_id == slave_server_id)
{
mysql_mutex_lock(&tmp->LOCK_thd_data); // Lock from delete
break;
@@ -1603,10 +3251,14 @@ static bool get_string_parameter(char *to, const char *from, size_t length,
@param mi Pointer to Master_info object belonging to the slave's IO
thread.
+ @param master_info_added Out parameter saying if the Master_info *mi was
+ added to the global list of masters. This is useful in error conditions
+ to know if caller should free Master_info *mi.
+
@retval FALSE success
@retval TRUE error
*/
-bool change_master(THD* thd, Master_info* mi)
+bool change_master(THD* thd, Master_info* mi, bool *master_info_added)
{
int thread_mask;
const char* errmsg= 0;
@@ -1615,20 +3267,17 @@ bool change_master(THD* thd, Master_info* mi)
char saved_host[HOSTNAME_LENGTH + 1];
uint saved_port;
char saved_log_name[FN_REFLEN];
+ Master_info::enum_using_gtid saved_using_gtid;
+ char master_info_file_tmp[FN_REFLEN];
+ char relay_log_info_file_tmp[FN_REFLEN];
my_off_t saved_log_pos;
+ LEX_MASTER_INFO* lex_mi= &thd->lex->mi;
DBUG_ENTER("change_master");
- lock_slave_threads(mi);
- init_thread_mask(&thread_mask,mi,0 /*not inverse*/);
- LEX_MASTER_INFO* lex_mi= &thd->lex->mi;
- if (thread_mask) // We refuse if any slave thread is running
- {
- my_message(ER_SLAVE_MUST_STOP, ER(ER_SLAVE_MUST_STOP), MYF(0));
- ret= TRUE;
- goto err;
- }
+ mysql_mutex_assert_owner(&LOCK_active_mi);
+ DBUG_ASSERT(master_info_index);
- thd_proc_info(thd, "Changing master");
+ *master_info_added= false;
/*
We need to check if there is an empty master_host. Otherwise
change master succeeds, a master.info file is created containing
@@ -1636,17 +3285,65 @@ bool change_master(THD* thd, Master_info* mi)
is thrown stating that the server is not configured as slave.
(See BUG#28796).
*/
- if(lex_mi->host && !*lex_mi->host)
+ if (lex_mi->host && !*lex_mi->host)
{
my_error(ER_WRONG_ARGUMENTS, MYF(0), "MASTER_HOST");
- unlock_slave_threads(mi);
DBUG_RETURN(TRUE);
}
- // TODO: see if needs re-write
- if (init_master_info(mi, master_info_file, relay_log_info_file, 0,
+ if (master_info_index->check_duplicate_master_info(&lex_mi->connection_name,
+ lex_mi->host,
+ lex_mi->port))
+ DBUG_RETURN(TRUE);
+
+ lock_slave_threads(mi);
+ init_thread_mask(&thread_mask,mi,0 /*not inverse*/);
+ if (thread_mask) // We refuse if any slave thread is running
+ {
+ my_error(ER_SLAVE_MUST_STOP, MYF(0), (int) mi->connection_name.length,
+ mi->connection_name.str);
+ ret= TRUE;
+ goto err;
+ }
+
+ THD_STAGE_INFO(thd, stage_changing_master);
+
+ create_logfile_name_with_suffix(master_info_file_tmp,
+ sizeof(master_info_file_tmp),
+ master_info_file, 0,
+ &mi->cmp_connection_name);
+ create_logfile_name_with_suffix(relay_log_info_file_tmp,
+ sizeof(relay_log_info_file_tmp),
+ relay_log_info_file, 0,
+ &mi->cmp_connection_name);
+
+ /* if new Master_info doesn't exists, add it */
+ if (!master_info_index->get_master_info(&mi->connection_name,
+ Sql_condition::WARN_LEVEL_NOTE))
+ {
+ if (master_info_index->add_master_info(mi, TRUE))
+ {
+ my_error(ER_MASTER_INFO, MYF(0),
+ (int) lex_mi->connection_name.length,
+ lex_mi->connection_name.str);
+ ret= TRUE;
+ goto err;
+ }
+ *master_info_added= true;
+ }
+ if (global_system_variables.log_warnings > 1)
+ sql_print_information("Master connection name: '%.*s' "
+ "Master_info_file: '%s' "
+ "Relay_info_file: '%s'",
+ (int) mi->connection_name.length,
+ mi->connection_name.str,
+ master_info_file_tmp, relay_log_info_file_tmp);
+
+ if (init_master_info(mi, master_info_file_tmp, relay_log_info_file_tmp, 0,
thread_mask))
{
- my_message(ER_MASTER_INFO, ER(ER_MASTER_INFO), MYF(0));
+ my_error(ER_MASTER_INFO, MYF(0),
+ (int) lex_mi->connection_name.length,
+ lex_mi->connection_name.str);
ret= TRUE;
goto err;
}
@@ -1664,6 +3361,7 @@ bool change_master(THD* thd, Master_info* mi)
saved_port= mi->port;
strmake_buf(saved_log_name, mi->master_log_name);
saved_log_pos= mi->master_log_pos;
+ saved_using_gtid= mi->using_gtid;
/*
If the user specified host or port without binlog or position,
@@ -1703,9 +3401,9 @@ bool change_master(THD* thd, Master_info* mi)
if (lex_mi->heartbeat_opt != LEX_MASTER_INFO::LEX_MI_UNCHANGED)
mi->heartbeat_period = lex_mi->heartbeat_period;
else
- mi->heartbeat_period= (float) min(SLAVE_MAX_HEARTBEAT_PERIOD,
+ mi->heartbeat_period= (float) MY_MIN(SLAVE_MAX_HEARTBEAT_PERIOD,
(slave_net_timeout/2.0));
- mi->received_heartbeats= LL(0); // counter lives until master is CHANGEd
+ mi->received_heartbeats= 0; // counter lives until master is CHANGEd
/*
reset the last time server_id list if the current CHANGE MASTER
is mentioning IGNORE_SERVER_IDS= (...)
@@ -1716,7 +3414,7 @@ bool change_master(THD* thd, Master_info* mi)
{
ulong s_id;
get_dynamic(&lex_mi->repl_ignore_server_ids, (uchar*) &s_id, i);
- if (s_id == ::server_id && replicate_same_server_id)
+ if (s_id == global_system_variables.server_id && replicate_same_server_id)
{
my_error(ER_SLAVE_IGNORE_SERVER_IDS, MYF(0), static_cast<int>(s_id));
ret= TRUE;
@@ -1751,11 +3449,16 @@ bool change_master(THD* thd, Master_info* mi)
strmake_buf(mi->ssl_cipher, lex_mi->ssl_cipher);
if (lex_mi->ssl_key)
strmake_buf(mi->ssl_key, lex_mi->ssl_key);
+ if (lex_mi->ssl_crl)
+ strmake_buf(mi->ssl_crl, lex_mi->ssl_crl);
+ if (lex_mi->ssl_crlpath)
+ strmake_buf(mi->ssl_crlpath, lex_mi->ssl_crlpath);
+
#ifndef HAVE_OPENSSL
if (lex_mi->ssl || lex_mi->ssl_ca || lex_mi->ssl_capath ||
lex_mi->ssl_cert || lex_mi->ssl_cipher || lex_mi->ssl_key ||
- lex_mi->ssl_verify_server_cert )
- push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
+ lex_mi->ssl_verify_server_cert || lex_mi->ssl_crl || lex_mi->ssl_crlpath)
+ push_warning(thd, Sql_condition::WARN_LEVEL_NOTE,
ER_SLAVE_IGNORED_SSL_PARAMS, ER(ER_SLAVE_IGNORED_SSL_PARAMS));
#endif
@@ -1774,6 +3477,15 @@ bool change_master(THD* thd, Master_info* mi)
mi->rli.group_relay_log_pos= mi->rli.event_relay_log_pos= lex_mi->relay_log_pos;
}
+ if (lex_mi->use_gtid_opt == LEX_MASTER_INFO::LEX_GTID_SLAVE_POS)
+ mi->using_gtid= Master_info::USE_GTID_SLAVE_POS;
+ else if (lex_mi->use_gtid_opt == LEX_MASTER_INFO::LEX_GTID_CURRENT_POS)
+ mi->using_gtid= Master_info::USE_GTID_CURRENT_POS;
+ else if (lex_mi->use_gtid_opt == LEX_MASTER_INFO::LEX_GTID_NO ||
+ lex_mi->log_file_name || lex_mi->pos ||
+ lex_mi->relay_log_name || lex_mi->relay_log_pos)
+ mi->using_gtid= Master_info::USE_GTID_NO;
+
/*
If user did specify neither host nor port nor any log name nor any log
pos, i.e. he specified only user/password/master_connect_retry, he probably
@@ -1794,15 +3506,16 @@ bool change_master(THD* thd, Master_info* mi)
{
/*
Sometimes mi->rli.master_log_pos == 0 (it happens when the SQL thread is
- not initialized), so we use a max().
+ not initialized), so we use a MY_MAX().
What happens to mi->rli.master_log_pos during the initialization stages
of replication is not 100% clear, so we guard against problems using
- max().
+ MY_MAX().
*/
- mi->master_log_pos = max(BIN_LOG_HEADER_SIZE,
+ mi->master_log_pos = MY_MAX(BIN_LOG_HEADER_SIZE,
mi->rli.group_master_log_pos);
strmake_buf(mi->master_log_name, mi->rli.group_master_log_name);
}
+
/*
Relay log's IO_CACHE may not be inited, if rli->inited==0 (server was never
a slave before).
@@ -1815,8 +3528,7 @@ bool change_master(THD* thd, Master_info* mi)
}
if (need_relay_log_purge)
{
- relay_log_purge= 1;
- thd_proc_info(thd, "Purging old relay logs");
+ THD_STAGE_INFO(thd, stage_purging_old_relay_logs);
if (purge_relay_logs(&mi->rli, thd,
0 /* not only reset, but also reinit */,
&errmsg))
@@ -1829,7 +3541,6 @@ bool change_master(THD* thd, Master_info* mi)
else
{
const char* msg;
- relay_log_purge= 0;
/* Relay log is already initialized */
if (init_relay_log_pos(&mi->rli,
mi->rli.group_relay_log_name,
@@ -1864,6 +3575,7 @@ bool change_master(THD* thd, Master_info* mi)
/* Clear the errors, for a clean start */
mi->rli.clear_error();
mi->rli.clear_until_condition();
+ mi->rli.slave_skip_counter= 0;
sql_print_information("'CHANGE MASTER TO executed'. "
"Previous state master_host='%s', master_port='%u', master_log_file='%s', "
@@ -1872,6 +3584,11 @@ bool change_master(THD* thd, Master_info* mi)
"master_log_pos='%ld'.", saved_host, saved_port, saved_log_name,
(ulong) saved_log_pos, mi->host, mi->port, mi->master_log_name,
(ulong) mi->master_log_pos);
+ if (saved_using_gtid != Master_info::USE_GTID_NO ||
+ mi->using_gtid != Master_info::USE_GTID_NO)
+ sql_print_information("Previous Using_Gtid=%s. New Using_Gtid=%s",
+ mi->using_gtid_astext(saved_using_gtid),
+ mi->using_gtid_astext(mi->using_gtid));
/*
If we don't write new coordinates to disk now, then old will remain in
@@ -1886,14 +3603,6 @@ bool change_master(THD* thd, Master_info* mi)
err:
unlock_slave_threads(mi);
-#ifdef WITH_WSREP
- if (WSREP(thd))
- thd_proc_info(thd, "exit change_master()");
- else
- thd_proc_info(thd, 0);
-#else /* WITH_WSREP */
- thd_proc_info(thd, 0);
-#endif /* WITH_WSREP */
if (ret == FALSE)
my_ok(thd);
DBUG_RETURN(ret);
@@ -1909,7 +3618,7 @@ err:
@retval 0 success
@retval 1 error
*/
-int reset_master(THD* thd)
+int reset_master(THD* thd, rpl_gtid *init_state, uint32 init_state_len)
{
if (!mysql_bin_log.is_open())
{
@@ -1918,7 +3627,7 @@ int reset_master(THD* thd)
return 1;
}
- if (mysql_bin_log.reset_logs(thd))
+ if (mysql_bin_log.reset_logs(thd, 1, init_state, init_state_len))
return 1;
RUN_HOOK(binlog_transmit, after_reset_master, (thd, 0 /* flags */));
return 0;
@@ -1944,6 +3653,7 @@ bool mysql_show_binlog_events(THD* thd)
File file = -1;
MYSQL_BIN_LOG *binary_log= NULL;
int old_max_allowed_packet= thd->variables.max_allowed_packet;
+ Master_info *mi= 0;
LOG_INFO linfo;
DBUG_ENTER("mysql_show_binlog_events");
@@ -1973,10 +3683,16 @@ bool mysql_show_binlog_events(THD* thd)
}
else /* showing relay log contents */
{
- if (!active_mi)
+ mysql_mutex_lock(&LOCK_active_mi);
+ if (!master_info_index ||
+ !(mi= master_info_index->
+ get_master_info(&thd->variables.default_master_connection,
+ Sql_condition::WARN_LEVEL_ERROR)))
+ {
+ mysql_mutex_unlock(&LOCK_active_mi);
DBUG_RETURN(TRUE);
-
- binary_log= &(active_mi->rli.relay_log);
+ }
+ binary_log= &(mi->rli.relay_log);
}
if (binary_log->is_open())
@@ -1984,12 +3700,19 @@ bool mysql_show_binlog_events(THD* thd)
LEX_MASTER_INFO *lex_mi= &thd->lex->mi;
SELECT_LEX_UNIT *unit= &thd->lex->unit;
ha_rows event_count, limit_start, limit_end;
- my_off_t pos = max(BIN_LOG_HEADER_SIZE, lex_mi->pos); // user-friendly
+ my_off_t pos = MY_MAX(BIN_LOG_HEADER_SIZE, lex_mi->pos); // user-friendly
char search_file_name[FN_REFLEN], *name;
const char *log_file_name = lex_mi->log_file_name;
mysql_mutex_t *log_lock = binary_log->get_log_lock();
Log_event* ev;
+ if (mi)
+ {
+ /* We can unlock the mutex as we have a lock on the file */
+ mysql_mutex_unlock(&LOCK_active_mi);
+ mi= 0;
+ }
+
unit->set_limit(thd->lex->current_select);
limit_start= unit->offset_limit_cnt;
limit_end= unit->select_limit_cnt;
@@ -2084,6 +3807,9 @@ bool mysql_show_binlog_events(THD* thd)
mysql_mutex_unlock(log_lock);
}
+ else if (mi)
+ mysql_mutex_unlock(&LOCK_active_mi);
+
// Check that linfo is still on the function scope.
DEBUG_SYNC(thd, "after_show_binlog_events");
@@ -2264,14 +3990,14 @@ int log_loaded_block(IO_CACHE* file)
DBUG_RETURN(0);
for (block_len= (uint) (my_b_get_bytes_in_buffer(file)); block_len > 0;
- buffer += min(block_len, max_event_size),
- block_len -= min(block_len, max_event_size))
+ buffer += MY_MIN(block_len, max_event_size),
+ block_len -= MY_MIN(block_len, max_event_size))
{
lf_info->last_pos_in_file= my_b_get_pos_in_file(file);
if (lf_info->wrote_create_file)
{
Append_block_log_event a(lf_info->thd, lf_info->thd->db, buffer,
- min(block_len, max_event_size),
+ MY_MIN(block_len, max_event_size),
lf_info->log_delayed);
if (mysql_bin_log.write(&a))
DBUG_RETURN(1);
@@ -2280,7 +4006,7 @@ int log_loaded_block(IO_CACHE* file)
{
Begin_load_query_log_event b(lf_info->thd, lf_info->thd->db,
buffer,
- min(block_len, max_event_size),
+ MY_MIN(block_len, max_event_size),
lf_info->log_delayed);
if (mysql_bin_log.write(&b))
DBUG_RETURN(1);
@@ -2290,4 +4016,204 @@ int log_loaded_block(IO_CACHE* file)
DBUG_RETURN(0);
}
+
+/**
+ Initialise the slave replication state from the mysql.gtid_slave_pos table.
+
+ This is called each time an SQL thread starts, but the data is only actually
+ loaded on the first call.
+
+ The slave state is the last GTID applied on the slave within each
+ replication domain.
+
+ To avoid row lock contention, there are multiple rows for each domain_id.
+ The one containing the current slave state is the one with the maximal
+ sub_id value, within each domain_id.
+
+ CREATE TABLE mysql.gtid_slave_pos (
+ domain_id INT UNSIGNED NOT NULL,
+ sub_id BIGINT UNSIGNED NOT NULL,
+ server_id INT UNSIGNED NOT NULL,
+ seq_no BIGINT UNSIGNED NOT NULL,
+ PRIMARY KEY (domain_id, sub_id))
+*/
+
+void
+rpl_init_gtid_slave_state()
+{
+ rpl_global_gtid_slave_state= new rpl_slave_state;
+}
+
+
+void
+rpl_deinit_gtid_slave_state()
+{
+ delete rpl_global_gtid_slave_state;
+}
+
+
+void
+rpl_init_gtid_waiting()
+{
+ rpl_global_gtid_waiting.init();
+}
+
+
+void
+rpl_deinit_gtid_waiting()
+{
+ rpl_global_gtid_waiting.destroy();
+}
+
+
+/*
+ Format the current GTID state as a string, for returning the value of
+ @@global.gtid_slave_pos.
+
+ If the flag use_binlog is true, then the contents of the binary log (if
+ enabled) is merged into the current GTID state (@@global.gtid_current_pos).
+*/
+int
+rpl_append_gtid_state(String *dest, bool use_binlog)
+{
+ int err;
+ rpl_gtid *gtid_list= NULL;
+ uint32 num_gtids= 0;
+
+ if (use_binlog && opt_bin_log &&
+ (err= mysql_bin_log.get_most_recent_gtid_list(&gtid_list, &num_gtids)))
+ return err;
+
+ err= rpl_global_gtid_slave_state->tostring(dest, gtid_list, num_gtids);
+ my_free(gtid_list);
+
+ return err;
+}
+
+
+/*
+ Load the current GTID position into a slave_connection_state, for use when
+ connecting to a master server with GTID.
+
+ If the flag use_binlog is true, then the contents of the binary log (if
+ enabled) is merged into the current GTID state (master_use_gtid=current_pos).
+*/
+int
+rpl_load_gtid_state(slave_connection_state *state, bool use_binlog)
+{
+ int err;
+ rpl_gtid *gtid_list= NULL;
+ uint32 num_gtids= 0;
+
+ if (use_binlog && opt_bin_log &&
+ (err= mysql_bin_log.get_most_recent_gtid_list(&gtid_list, &num_gtids)))
+ return err;
+
+ err= state->load(rpl_global_gtid_slave_state, gtid_list, num_gtids);
+ my_free(gtid_list);
+
+ return err;
+}
+
+
+bool
+rpl_gtid_pos_check(THD *thd, char *str, size_t len)
+{
+ slave_connection_state tmp_slave_state;
+ bool gave_conflict_warning= false, gave_missing_warning= false;
+
+ /* Check that we can parse the supplied string. */
+ if (tmp_slave_state.load(str, len))
+ return true;
+
+ /*
+ Check our own binlog for any of our own transactions that are newer
+ than the GTID state the user is requesting. Any such transactions would
+ result in an out-of-order binlog, which could break anyone replicating
+ with us as master.
+
+ So give an error if this is found, requesting the user to do a
+ RESET MASTER (to clean up the binlog) if they really want this.
+ */
+ if (mysql_bin_log.is_open())
+ {
+ rpl_gtid *binlog_gtid_list= NULL;
+ uint32 num_binlog_gtids= 0;
+ uint32 i;
+
+ if (mysql_bin_log.get_most_recent_gtid_list(&binlog_gtid_list,
+ &num_binlog_gtids))
+ {
+ my_error(ER_OUT_OF_RESOURCES, MYF(MY_WME));
+ return true;
+ }
+ for (i= 0; i < num_binlog_gtids; ++i)
+ {
+ rpl_gtid *binlog_gtid= &binlog_gtid_list[i];
+ rpl_gtid *slave_gtid;
+ if (binlog_gtid->server_id != global_system_variables.server_id)
+ continue;
+ if (!(slave_gtid= tmp_slave_state.find(binlog_gtid->domain_id)))
+ {
+ if (opt_gtid_strict_mode)
+ {
+ my_error(ER_MASTER_GTID_POS_MISSING_DOMAIN, MYF(0),
+ binlog_gtid->domain_id, binlog_gtid->domain_id,
+ binlog_gtid->server_id, binlog_gtid->seq_no);
+ break;
+ }
+ else if (!gave_missing_warning)
+ {
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
+ ER_MASTER_GTID_POS_MISSING_DOMAIN,
+ ER(ER_MASTER_GTID_POS_MISSING_DOMAIN),
+ binlog_gtid->domain_id, binlog_gtid->domain_id,
+ binlog_gtid->server_id, binlog_gtid->seq_no);
+ gave_missing_warning= true;
+ }
+ }
+ else if (slave_gtid->seq_no < binlog_gtid->seq_no)
+ {
+ if (opt_gtid_strict_mode)
+ {
+ my_error(ER_MASTER_GTID_POS_CONFLICTS_WITH_BINLOG, MYF(0),
+ slave_gtid->domain_id, slave_gtid->server_id,
+ slave_gtid->seq_no, binlog_gtid->domain_id,
+ binlog_gtid->server_id, binlog_gtid->seq_no);
+ break;
+ }
+ else if (!gave_conflict_warning)
+ {
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
+ ER_MASTER_GTID_POS_CONFLICTS_WITH_BINLOG,
+ ER(ER_MASTER_GTID_POS_CONFLICTS_WITH_BINLOG),
+ slave_gtid->domain_id, slave_gtid->server_id,
+ slave_gtid->seq_no, binlog_gtid->domain_id,
+ binlog_gtid->server_id, binlog_gtid->seq_no);
+ gave_conflict_warning= true;
+ }
+ }
+ }
+ my_free(binlog_gtid_list);
+ if (i != num_binlog_gtids)
+ return true;
+ }
+
+ return false;
+}
+
+
+bool
+rpl_gtid_pos_update(THD *thd, char *str, size_t len)
+{
+ if (rpl_global_gtid_slave_state->load(thd, str, len, true, true))
+ {
+ my_error(ER_FAILED_GTID_STATE_INIT, MYF(0));
+ return true;
+ }
+ else
+ return false;
+}
+
+
#endif /* HAVE_REPLICATION */
diff --git a/sql/sql_repl.h b/sql/sql_repl.h
index c5a0b31388e..7f7751b8f44 100644
--- a/sql/sql_repl.h
+++ b/sql/sql_repl.h
@@ -32,6 +32,8 @@ typedef struct st_slave_info
THD* thd;
} SLAVE_INFO;
+struct slave_connection_state;
+
extern my_bool opt_show_slave_auth_info;
extern char *master_host, *master_info_file;
extern bool server_id_supplied;
@@ -41,10 +43,10 @@ extern my_bool opt_sporadic_binlog_dump_fail;
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);
+bool change_master(THD* thd, Master_info* mi, bool *master_info_added);
bool mysql_show_binlog_events(THD* thd);
int reset_slave(THD *thd, Master_info* mi);
-int reset_master(THD* thd);
+int reset_master(THD* thd, rpl_gtid *init_state, uint32 init_state_len);
bool purge_master_logs(THD* thd, const char* to_log);
bool purge_master_logs_before_date(THD* thd, time_t purge_time);
bool log_in_use(const char* log_name);
@@ -65,6 +67,19 @@ int log_loaded_block(IO_CACHE* file);
int init_replication_sys_vars();
void mysql_binlog_send(THD* thd, char* log_ident, my_off_t pos, ushort flags);
+#ifdef HAVE_PSI_INTERFACE
+extern PSI_mutex_key key_LOCK_slave_state, key_LOCK_binlog_state;
+#endif
+void rpl_init_gtid_slave_state();
+void rpl_deinit_gtid_slave_state();
+void rpl_init_gtid_waiting();
+void rpl_deinit_gtid_waiting();
+int gtid_state_from_binlog_pos(const char *name, uint32 pos, String *out_str);
+int rpl_append_gtid_state(String *dest, bool use_binlog);
+int rpl_load_gtid_state(slave_connection_state *state, bool use_binlog);
+bool rpl_gtid_pos_check(THD *thd, char *str, size_t len);
+bool rpl_gtid_pos_update(THD *thd, char *str, size_t len);
+
#endif /* HAVE_REPLICATION */
#endif /* SQL_REPL_INCLUDED */
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index b1e22537b37..514ba947172 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -29,6 +29,7 @@
#pragma implementation // gcc: Class implementation
#endif
+#include <my_global.h>
#include "sql_priv.h"
#include "unireg.h"
#include "sql_select.h"
@@ -51,6 +52,7 @@
#include "opt_subselect.h"
#include "log_slow.h"
#include "sql_derived.h"
+#include "sql_statistics.h"
#include "debug_sync.h" // DEBUG_SYNC
#include <m_ctype.h>
@@ -64,8 +66,6 @@ const char *join_type_str[]={ "UNKNOWN","system","const","eq_ref","ref",
"index_merge", "hash_ALL", "hash_range",
"hash_index", "hash_index_merge" };
-const char *copy_to_tmp_table= "Copying to tmp table";
-
struct st_sargable_param;
static void optimize_keyuse(JOIN *join, DYNAMIC_ARRAY *keyuse_array);
@@ -88,12 +88,14 @@ void best_access_path(JOIN *join, JOIN_TAB *s,
POSITION *pos, POSITION *loose_scan_pos);
static void optimize_straight_join(JOIN *join, table_map join_tables);
static bool greedy_search(JOIN *join, table_map remaining_tables,
- uint depth, uint prune_level);
+ uint depth, uint prune_level,
+ uint use_cond_selectivity);
static bool best_extension_by_limited_search(JOIN *join,
table_map remaining_tables,
uint idx, double record_count,
double read_time, uint depth,
- uint prune_level);
+ uint prune_level,
+ uint use_cond_selectivity);
static uint determine_search_depth(JOIN* join);
C_MODE_START
static int join_tab_cmp(const void *dummy, const void* ptr1, const void* ptr2);
@@ -105,7 +107,7 @@ C_MODE_END
tested and approved.
*/
static bool find_best(JOIN *join,table_map rest_tables,uint index,
- double record_count,double read_time);
+ double record_count,double read_time, uint use_cond_selectivity);
static uint cache_record_length(JOIN *join,uint index);
bool get_best_combination(JOIN *join);
static store_key *get_store_key(THD *thd,
@@ -133,7 +135,8 @@ static COND *build_equal_items(JOIN *join, COND *cond,
COND_EQUAL *inherited,
List<TABLE_LIST> *join_list,
bool ignore_on_conds,
- COND_EQUAL **cond_equal_ref);
+ COND_EQUAL **cond_equal_ref,
+ bool link_equal_fields= FALSE);
static COND* substitute_for_best_equal_field(JOIN_TAB *context_tab,
COND *cond,
COND_EQUAL *cond_equal,
@@ -149,12 +152,10 @@ static uint build_bitmap_for_nested_joins(List<TABLE_LIST> *join_list,
static COND *optimize_cond(JOIN *join, COND *conds,
List<TABLE_LIST> *join_list,
bool ignore_on_conds,
- Item::cond_result *cond_value,
- COND_EQUAL **cond_equal);
+ Item::cond_result *cond_value,
+ COND_EQUAL **cond_equal,
+ int flags= 0);
bool const_expression_in_where(COND *conds,Item *item, Item **comp_item);
-static bool create_internal_tmp_table_from_heap2(THD *, TABLE *,
- ENGINE_COLUMNDEF *, ENGINE_COLUMNDEF **,
- int, bool, handlerton *, const char *, bool *);
static int do_select(JOIN *join,List<Item> *fields,TABLE *tmp_table,
Procedure *proc);
@@ -279,6 +280,59 @@ enum enum_exec_or_opt {WALK_OPTIMIZATION_TABS , WALK_EXECUTION_TABS};
JOIN_TAB *first_breadth_first_tab(JOIN *join, enum enum_exec_or_opt tabs_kind);
JOIN_TAB *next_breadth_first_tab(JOIN *join, enum enum_exec_or_opt tabs_kind,
JOIN_TAB *tab);
+static double table_cond_selectivity(JOIN *join, uint idx, JOIN_TAB *s,
+ table_map rem_tables);
+
+#ifndef DBUG_OFF
+
+/*
+ SHOW EXPLAIN testing: wait for, and serve n_calls APC requests.
+*/
+void dbug_serve_apcs(THD *thd, int n_calls)
+{
+ const char *save_proc_info= thd->proc_info;
+
+ /* Busy-wait for n_calls APC requests to arrive and be processed */
+ int n_apcs= thd->apc_target.n_calls_processed + n_calls;
+ while (thd->apc_target.n_calls_processed < n_apcs)
+ {
+ /* This is so that mysqltest knows we're ready to serve requests: */
+ thd_proc_info(thd, "show_explain_trap");
+ my_sleep(30000);
+ thd_proc_info(thd, save_proc_info);
+ if (thd->check_killed())
+ break;
+ }
+}
+
+
+/*
+ Debugging: check if @name=value, comparing as integer
+
+ Intended usage:
+
+ DBUG_EXECUTE_IF("show_explain_probe_2",
+ if (dbug_user_var_equals_int(thd, "select_id", select_id))
+ dbug_serve_apcs(thd, 1);
+ );
+
+*/
+
+bool dbug_user_var_equals_int(THD *thd, const char *name, int value)
+{
+ user_var_entry *var;
+ LEX_STRING varname= {(char*)name, strlen(name)};
+ if ((var= get_variable(&thd->user_vars, varname, FALSE)))
+ {
+ bool null_value;
+ longlong var_value= var->val_int(&null_value);
+ if (!null_value && var_value == value)
+ return TRUE;
+ }
+ return FALSE;
+}
+#endif
+
/**
This handles SELECT with and without UNION.
@@ -329,7 +383,7 @@ bool handle_select(THD *thd, LEX *lex, select_result *result,
If LIMIT ROWS EXAMINED interrupted query execution, issue a warning,
continue with normal processing and produce an incomplete query result.
*/
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_QUERY_EXCEEDED_ROWS_EXAMINED_LIMIT,
ER(ER_QUERY_EXCEEDED_ROWS_EXAMINED_LIMIT),
thd->accessed_rows_and_keys,
@@ -562,7 +616,9 @@ inline int setup_without_group(THD *thd, Item **ref_pointer_array,
List<Item> &all_fields,
COND **conds,
ORDER *order,
- ORDER *group, bool *hidden_group_fields)
+ ORDER *group,
+ bool *hidden_group_fields,
+ uint *reserved)
{
int res;
st_select_lex *const select= thd->lex->current_select;
@@ -576,6 +632,13 @@ inline int setup_without_group(THD *thd, Item **ref_pointer_array,
thd->lex->allow_sum_func&= ~((nesting_map)1 << select->nest_level);
res= setup_conds(thd, tables, leaves, conds);
+ if (thd->lex->current_select->first_cond_optimization)
+ {
+ if (!res && *conds && ! thd->lex->current_select->merged_into)
+ (*reserved)= (*conds)->exists2in_reserved_items();
+ else
+ (*reserved)= 0;
+ }
/* it's not wrong to have non-aggregated columns in a WHERE */
select->set_non_agg_field_used(saved_non_agg_field_used);
@@ -723,7 +786,7 @@ JOIN::prepare(Item ***rref_pointer_array,
setup_without_group(thd, (*rref_pointer_array), tables_list,
select_lex->leaf_tables, fields_list,
all_fields, &conds, order, group_list,
- &hidden_group_fields))
+ &hidden_group_fields, &select_lex->select_n_reserved))
DBUG_RETURN(-1); /* purecov: inspected */
ref_pointer_array= *rref_pointer_array;
@@ -955,6 +1018,34 @@ err:
DBUG_RETURN(res); /* purecov: inspected */
}
+int JOIN::optimize()
+{
+ bool was_optimized= optimized;
+ int res= optimize_inner();
+ /*
+ If we're inside a non-correlated subquery, this function may be
+ called for the second time after the subquery has been executed
+ and deleted. The second call will not produce a valid query plan, it will
+ short-circuit because optimized==TRUE.
+
+ "was_optimized != optimized" is here to handle this case:
+ - first optimization starts, gets an error (from a const. cheap
+ subquery), returns 1
+ - another JOIN::optimize() call made, and now join->optimize() will
+ return 0, even though we never had a query plan.
+ */
+ if (was_optimized != optimized && !res && have_query_plan != QEP_DELETED)
+ {
+ create_explain_query_if_not_exists(thd->lex, thd->mem_root);
+ have_query_plan= QEP_AVAILABLE;
+ save_explain_data(thd->lex->explain, false /* can overwrite */,
+ need_tmp,
+ !skip_sort_order && !no_order && (order || group_list),
+ select_distinct);
+ }
+ return res;
+}
+
/**
global select optimisation.
@@ -969,7 +1060,7 @@ err:
*/
int
-JOIN::optimize()
+JOIN::optimize_inner()
{
ulonglong select_opts_for_readinfo;
uint no_jbuf_after;
@@ -982,7 +1073,7 @@ JOIN::optimize()
optimized= 1;
DEBUG_SYNC(thd, "before_join_optimize");
- thd_proc_info(thd, "optimizing");
+ THD_STAGE_INFO(thd, stage_optimizing);
set_allowed_join_cache_types();
need_distinct= TRUE;
@@ -1002,6 +1093,24 @@ JOIN::optimize()
// Update used tables after all handling derived table procedures
select_lex->update_used_tables();
+ /*
+ In fact we transform underlying subqueries after their 'prepare' phase and
+ before 'optimize' from upper query 'optimize' to allow semijoin
+ conversion happened (which done in the same way.
+ */
+ if(select_lex->first_cond_optimization &&
+ conds && conds->walk(&Item::exists2in_processor, 0, (uchar *)thd))
+ DBUG_RETURN(1);
+ /*
+TODO: make view to decide if it is possible to write to WHERE directly or make Semi-Joins able to process ON condition if it is possible
+ for (TABLE_LIST *tbl= tables_list; tbl; tbl= tbl->next_local)
+ {
+ if (tbl->on_expr &&
+ tbl->on_expr->walk(&Item::exists2in_processor, 0, (uchar *)thd))
+ DBUG_RETURN(1);
+ }
+ */
+
if (transform_max_min_subquery())
DBUG_RETURN(1); /* purecov: inspected */
@@ -1085,7 +1194,7 @@ JOIN::optimize()
DBUG_RETURN(1);
conds= optimize_cond(this, conds, join_list, FALSE,
- &cond_value, &cond_equal);
+ &cond_value, &cond_equal, OPT_LINK_EQUAL_FIELDS);
if (thd->is_error())
{
@@ -1144,15 +1253,16 @@ JOIN::optimize()
(tbl->embedding && tbl->embedding->sj_on_expr))
{
Item *prune_cond= tbl->on_expr? tbl->on_expr : conds;
- tbl->table->no_partitions_used= prune_partitions(thd, tbl->table,
- prune_cond);
- }
+ tbl->table->all_partitions_pruned_away= prune_partitions(thd,
+ tbl->table,
+ prune_cond);
+ }
}
}
#endif
/*
- Try to optimize count(*), min() and max() to const fields if
+ Try to optimize count(*), MY_MIN() and MY_MAX() to const fields if
there is implicit grouping (aggregate functions but no
group_list). In this case, the result set shall only contain one
row.
@@ -1225,7 +1335,7 @@ JOIN::optimize()
MEM_UNDEFINED(&sort_by_table, sizeof(sort_by_table));
/* Calculate how to do the join */
- thd_proc_info(thd, "statistics");
+ THD_STAGE_INFO(thd, stage_statistics);
if (make_join_statistics(this, select_lex->leaf_tables, conds, &keyuse) ||
thd->is_fatal_error)
{
@@ -1250,7 +1360,7 @@ JOIN::optimize()
select_distinct= select_distinct && (const_tables != table_count);
}
- thd_proc_info(thd, "preparing");
+ THD_STAGE_INFO(thd, stage_preparing);
if (result->initialize_tables(this))
{
DBUG_PRINT("error",("Error: initialize_tables() failed"));
@@ -1422,6 +1532,9 @@ JOIN::optimize()
/* Cache constant expressions in WHERE, HAVING, ON clauses. */
cache_const_exprs();
+ if (setup_semijoin_loosescan(this))
+ DBUG_RETURN(1);
+
if (make_join_select(this, select, conds))
{
zero_result_cause=
@@ -1478,9 +1591,10 @@ JOIN::optimize()
We have found that grouping can be removed since groups correspond to
only one row anyway, but we still have to guarantee correct result
order. The line below effectively rewrites the query from GROUP BY
- <fields> to ORDER BY <fields>. There are two exceptions:
+ <fields> to ORDER BY <fields>. There are three exceptions:
- if skip_sort_order is set (see above), then we can simply skip
GROUP BY;
+ - if we are in a subquery, we don't have to maintain order
- we can only rewrite ORDER BY if the ORDER BY fields are 'compatible'
with the GROUP BY ones, i.e. either one is a prefix of another.
We only check if the ORDER BY is a prefix of GROUP BY. In this case
@@ -1490,7 +1604,13 @@ JOIN::optimize()
'order' as is.
*/
if (!order || test_if_subpart(group_list, order))
- order= skip_sort_order ? 0 : group_list;
+ {
+ if (skip_sort_order ||
+ select_lex->master_unit()->item) // This is a subquery
+ order= NULL;
+ else
+ order= group_list;
+ }
/*
If we have an IGNORE INDEX FOR GROUP BY(fields) clause, this must be
rewritten to IGNORE INDEX FOR ORDER BY(fields).
@@ -1534,8 +1654,10 @@ JOIN::optimize()
JOIN_TAB *tab= &join_tab[const_tables];
bool all_order_fields_used;
if (order)
+ {
skip_sort_order= test_if_skip_sort_order(tab, order, select_limit, 1,
&tab->table->keys_in_use_for_order_by);
+ }
if ((group_list=create_distinct_group(thd, select_lex->ref_pointer_array,
order, fields_list, all_fields,
&all_order_fields_used)))
@@ -1660,7 +1782,7 @@ JOIN::optimize()
/* Perform FULLTEXT search before all regular searches */
if (!(select_options & SELECT_DESCRIBE))
- init_ftfuncs(thd, select_lex, test(order));
+ init_ftfuncs(thd, select_lex, MY_TEST(order));
if (optimize_unflattened_subqueries())
DBUG_RETURN(1);
@@ -1835,7 +1957,7 @@ int JOIN::init_execution()
if (need_tmp)
{
DBUG_PRINT("info",("Creating tmp table"));
- thd_proc_info(thd, "Creating tmp table");
+ THD_STAGE_INFO(thd, stage_copying_to_tmp_table);
init_items_ref_array();
@@ -1880,7 +2002,7 @@ int JOIN::init_execution()
if (group_list && simple_group)
{
DBUG_PRINT("info",("Sorting for group"));
- thd_proc_info(thd, "Sorting for group");
+ THD_STAGE_INFO(thd, stage_sorting_for_group);
if (create_sort_index(thd, this, group_list,
HA_POS_ERROR, HA_POS_ERROR, FALSE) ||
alloc_group_fields(this, group_list) ||
@@ -1903,7 +2025,8 @@ int JOIN::init_execution()
if (!group_list && ! exec_tmp_table1->distinct && order && simple_order)
{
- thd_proc_info(thd, "Sorting for order");
+ DBUG_PRINT("info",("Sorting for order"));
+ THD_STAGE_INFO(thd, stage_sorting_for_order);
if (create_sort_index(thd, this, order,
HA_POS_ERROR, HA_POS_ERROR, TRUE))
{
@@ -2115,8 +2238,7 @@ JOIN::reinit()
DBUG_ENTER("JOIN::reinit");
unit->offset_limit_cnt= (ha_rows)(select_lex->offset_limit ?
- select_lex->offset_limit->val_uint() :
- ULL(0));
+ select_lex->offset_limit->val_uint() : 0);
first_record= 0;
cleaned= false;
@@ -2175,7 +2297,7 @@ JOIN::reinit()
}
if (!(select_options & SELECT_DESCRIBE))
- init_ftfuncs(thd, select_lex, test(order));
+ init_ftfuncs(thd, select_lex, MY_TEST(order));
DBUG_RETURN(0);
}
@@ -2215,6 +2337,59 @@ JOIN::save_join_tab()
}
+void JOIN::save_explain_data(Explain_query *output, bool can_overwrite,
+ bool need_tmp_table, bool need_order,
+ bool distinct)
+{
+ if (select_lex->select_number != UINT_MAX &&
+ select_lex->select_number != INT_MAX /* this is not a UNION's "fake select */ &&
+ have_query_plan != JOIN::QEP_NOT_PRESENT_YET &&
+ have_query_plan != JOIN::QEP_DELETED && // this happens when there was
+ // no QEP ever, but then
+ //cleanup() is called multiple times
+ output && // for "SET" command in SPs.
+ (can_overwrite? true: !output->get_select(select_lex->select_number)))
+ {
+ const char *message= NULL;
+ if (!table_count || !tables_list || zero_result_cause)
+ {
+ /* It's a degenerate join */
+ message= zero_result_cause ? zero_result_cause : "No tables used";
+ }
+ save_explain_data_intern(thd->lex->explain, need_tmp_table, need_order,
+ distinct, message);
+ }
+}
+
+
+void JOIN::exec()
+{
+ DBUG_EXECUTE_IF("show_explain_probe_join_exec_start",
+ if (dbug_user_var_equals_int(thd,
+ "show_explain_probe_select_id",
+ select_lex->select_number))
+ dbug_serve_apcs(thd, 1);
+ );
+ exec_inner();
+
+ if (!exec_saved_explain)
+ {
+ save_explain_data(thd->lex->explain, true /* can overwrite */,
+ need_tmp,
+ order != 0 && !skip_sort_order,
+ select_distinct);
+ exec_saved_explain= true;
+ }
+
+ DBUG_EXECUTE_IF("show_explain_probe_join_exec_end",
+ if (dbug_user_var_equals_int(thd,
+ "show_explain_probe_select_id",
+ select_lex->select_number))
+ dbug_serve_apcs(thd, 1);
+ );
+}
+
+
/**
Exec select.
@@ -2226,15 +2401,17 @@ JOIN::save_join_tab()
@todo
When can we have here thd->net.report_error not zero?
*/
-void
-JOIN::exec()
+
+void JOIN::exec_inner()
{
List<Item> *columns_list= &fields_list;
int tmp_error;
DBUG_ENTER("JOIN::exec");
- thd_proc_info(thd, "executing");
+ const bool has_group_by= this->group;
+
+ THD_STAGE_INFO(thd, stage_executing);
error= 0;
if (procedure)
{
@@ -2242,7 +2419,8 @@ JOIN::exec()
if (procedure->change_columns(procedure_fields_list) ||
result->prepare(procedure_fields_list, unit))
{
- thd->limit_found_rows= thd->examined_row_count= 0;
+ thd->set_examined_row_count(0);
+ thd->limit_found_rows= 0;
DBUG_VOID_RETURN;
}
columns_list= &procedure_fields_list;
@@ -2282,7 +2460,7 @@ JOIN::exec()
error= 1;
else
send_records= ((select_options & OPTION_FOUND_ROWS) ? 1 :
- thd->sent_row_count);
+ thd->get_sent_row_count());
}
else
send_records= 0;
@@ -2294,7 +2472,7 @@ JOIN::exec()
}
/* Single select (without union) always returns 0 or 1 row */
thd->limit_found_rows= send_records;
- thd->examined_row_count= 0;
+ thd->set_examined_row_count(0);
DBUG_VOID_RETURN;
}
/*
@@ -2347,7 +2525,7 @@ JOIN::exec()
Item *cur_const_item;
while ((cur_const_item= const_item_it++))
{
- cur_const_item->val_str(&cur_const_item->str_value);
+ cur_const_item->val_str(); // This caches val_str() to Item::str_value
if (thd->is_error())
{
error= thd->is_error();
@@ -2436,13 +2614,13 @@ JOIN::exec()
curr_tmp_table= exec_tmp_table1;
/* Copy data to the temporary table */
- thd_proc_info(thd, copy_to_tmp_table);
+ THD_STAGE_INFO(thd, stage_copying_to_tmp_table);
DBUG_PRINT("info", ("%s", thd->proc_info));
if (!curr_join->sort_and_group &&
curr_join->const_tables != curr_join->table_count)
{
JOIN_TAB *first_tab= curr_join->join_tab + curr_join->const_tables;
- first_tab->sorted= test(first_tab->loosescan_match_tab);
+ first_tab->sorted= MY_TEST(first_tab->loosescan_match_tab);
}
Procedure *save_proc= curr_join->procedure;
@@ -2537,6 +2715,10 @@ JOIN::exec()
DBUG_PRINT("info",("Creating group table"));
/* Free first data from old join */
+
+ /*
+ psergey-todo: this is the place of pre-mature JOIN::free call.
+ */
curr_join->join_free();
if (curr_join->make_simple_join(this, curr_tmp_table))
DBUG_VOID_RETURN;
@@ -2577,11 +2759,12 @@ JOIN::exec()
}
if (curr_join->group_list)
{
- thd_proc_info(thd, "Creating sort index");
if (curr_join->join_tab == join_tab && save_join_tab())
{
DBUG_VOID_RETURN;
}
+ DBUG_PRINT("info",("Sorting for index"));
+ THD_STAGE_INFO(thd, stage_creating_sort_index);
if (create_sort_index(thd, curr_join, curr_join->group_list,
HA_POS_ERROR, HA_POS_ERROR, FALSE) ||
make_group_fields(this, curr_join))
@@ -2591,7 +2774,7 @@ JOIN::exec()
sortorder= curr_join->sortorder;
}
- thd_proc_info(thd, "Copying to group table");
+ THD_STAGE_INFO(thd, stage_copying_to_group_table);
DBUG_PRINT("info", ("%s", thd->proc_info));
if (curr_join != this)
{
@@ -2617,7 +2800,7 @@ JOIN::exec()
curr_join->const_tables != curr_join->table_count)
{
JOIN_TAB *first_tab= curr_join->join_tab + curr_join->const_tables;
- first_tab->sorted= test(first_tab->loosescan_match_tab);
+ first_tab->sorted= MY_TEST(first_tab->loosescan_match_tab);
}
tmp_error= -1;
if (setup_sum_funcs(curr_join->thd, curr_join->sum_funcs) ||
@@ -2666,7 +2849,7 @@ JOIN::exec()
if (curr_join->select_distinct && ! curr_join->group_list)
{
- thd_proc_info(thd, "Removing duplicates");
+ THD_STAGE_INFO(thd, stage_removing_duplicates);
if (remove_duplicates(curr_join, curr_tmp_table,
*curr_fields_list, curr_join->tmp_having))
DBUG_VOID_RETURN;
@@ -2737,7 +2920,7 @@ JOIN::exec()
if (curr_join->group_list || curr_join->order)
{
DBUG_PRINT("info",("Sorting for send_result_set_metadata"));
- thd_proc_info(thd, "Sorting result");
+ THD_STAGE_INFO(thd, stage_sorting_result);
/* If we have already done the group, add HAVING to sorted table */
if (curr_join->tmp_having && ! curr_join->group_list &&
! curr_join->sort_and_group)
@@ -2745,6 +2928,7 @@ JOIN::exec()
JOIN_TAB *curr_table= &curr_join->join_tab[curr_join->const_tables];
table_map used_tables= (curr_join->const_table_map |
curr_table->table->map);
+ curr_join->tmp_having->update_used_tables();
Item* sort_table_cond= make_cond_for_table(thd, curr_join->tmp_having,
used_tables,
@@ -2834,13 +3018,40 @@ JOIN::exec()
the query. XXX: it's never shown in EXPLAIN!
OPTION_FOUND_ROWS supersedes LIMIT and is taken into account.
*/
- if (create_sort_index(thd, curr_join,
- curr_join->group_list ?
- curr_join->group_list : curr_join->order,
- curr_join->select_limit,
- (select_options & OPTION_FOUND_ROWS ?
- HA_POS_ERROR : unit->select_limit_cnt),
- curr_join->group_list ? TRUE : FALSE))
+ DBUG_PRINT("info",("Sorting for order by/group by"));
+ ORDER *order_arg=
+ curr_join->group_list ? curr_join->group_list : curr_join->order;
+ /*
+ filesort_limit: Return only this many rows from filesort().
+ We can use select_limit_cnt only if we have no group_by and 1 table.
+ This allows us to use Bounded_queue for queries like:
+ "select SQL_CALC_FOUND_ROWS * from t1 order by b desc limit 1;"
+ select_limit == HA_POS_ERROR (we need a full table scan)
+ unit->select_limit_cnt == 1 (we only need one row in the result set)
+ */
+ const ha_rows filesort_limit_arg=
+ (has_group_by || curr_join->table_count > 1)
+ ? curr_join->select_limit : unit->select_limit_cnt;
+ const ha_rows select_limit_arg=
+ select_options & OPTION_FOUND_ROWS
+ ? HA_POS_ERROR : unit->select_limit_cnt;
+ curr_join->filesort_found_rows= filesort_limit_arg != HA_POS_ERROR;
+
+ DBUG_PRINT("info", ("has_group_by %d "
+ "curr_join->table_count %d "
+ "curr_join->m_select_limit %d "
+ "unit->select_limit_cnt %d",
+ has_group_by,
+ curr_join->table_count,
+ (int) curr_join->select_limit,
+ (int) unit->select_limit_cnt));
+
+ if (create_sort_index(thd,
+ curr_join,
+ order_arg,
+ filesort_limit_arg,
+ select_limit_arg,
+ curr_join->group_list ? FALSE : TRUE))
DBUG_VOID_RETURN;
sortorder= curr_join->sortorder;
if (curr_join->const_tables != curr_join->table_count &&
@@ -2865,18 +3076,26 @@ JOIN::exec()
curr_join->fields= curr_fields_list;
curr_join->procedure= procedure;
- thd_proc_info(thd, "Sending data");
+ THD_STAGE_INFO(thd, stage_sending_data);
DBUG_PRINT("info", ("%s", thd->proc_info));
result->send_result_set_metadata((procedure ? curr_join->procedure_fields_list :
*curr_fields_list),
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF);
error= do_select(curr_join, curr_fields_list, NULL, procedure);
thd->limit_found_rows= curr_join->send_records;
+ if (curr_join->order && curr_join->sortorder &&
+ curr_join->filesort_found_rows)
+ {
+ /* Use info provided by filesort. */
+ DBUG_ASSERT(curr_join->table_count > curr_join->const_tables);
+ JOIN_TAB *tab= curr_join->join_tab + curr_join->const_tables;
+ thd->limit_found_rows= tab->records;
+ }
/* Accumulate the counts from all join iterations of all join parts. */
- thd->examined_row_count+= curr_join->examined_rows;
+ thd->inc_examined_row_count(curr_join->examined_rows);
DBUG_PRINT("counts", ("thd->examined_row_count: %lu",
- (ulong) thd->examined_row_count));
+ (ulong) thd->get_examined_row_count()));
/*
With EXPLAIN EXTENDED we have to restore original ref_array
@@ -3012,7 +3231,7 @@ mysql_select(THD *thd, Item ***rref_pointer_array,
select_result *result, SELECT_LEX_UNIT *unit,
SELECT_LEX *select_lex)
{
- bool err;
+ int err= 0;
bool free_join= 1;
DBUG_ENTER("mysql_select");
@@ -3030,11 +3249,6 @@ mysql_select(THD *thd, Item ***rref_pointer_array,
{
if (select_lex->linkage != GLOBAL_OPTIONS_TYPE)
{
- //here is EXPLAIN of subselect or derived table
- if (join->change_result(result))
- {
- DBUG_RETURN(TRUE);
- }
/*
Original join tabs might be overwritten at first
subselect execution. So we need to restore them.
@@ -3067,7 +3281,7 @@ mysql_select(THD *thd, Item ***rref_pointer_array,
if (!(join= new JOIN(thd, fields, select_options, result)))
DBUG_RETURN(TRUE);
- thd_proc_info(thd, "init");
+ THD_STAGE_INFO(thd, stage_init);
thd->lex->used_tables=0;
if ((err= join->prepare(rref_pointer_array, tables, wild_num,
conds, og_num, order, false, group, having, proc_param,
@@ -3102,11 +3316,11 @@ mysql_select(THD *thd, Item ***rref_pointer_array,
err:
if (free_join)
{
- thd_proc_info(thd, "end");
+ THD_STAGE_INFO(thd, stage_end);
err|= select_lex->cleanup();
DBUG_RETURN(err || thd->is_error());
}
- DBUG_RETURN(join->error);
+ DBUG_RETURN(join->error ? join->error: err);
}
@@ -3221,11 +3435,13 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list,
table_vector[i]=s->table=table=tables->table;
table->pos_in_table_list= tables;
error= tables->fetch_number_of_rows();
+ set_statistics_for_table(join->thd, table);
+ bitmap_clear_all(&table->cond_set);
#ifdef WITH_PARTITION_STORAGE_ENGINE
- const bool no_partitions_used= table->no_partitions_used;
+ const bool all_partitions_pruned_away= table->all_partitions_pruned_away;
#else
- const bool no_partitions_used= FALSE;
+ const bool all_partitions_pruned_away= FALSE;
#endif
DBUG_EXECUTE_IF("bug11747970_raise_error",
@@ -3246,8 +3462,8 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list,
s->dependent= tables->dep_tables;
if (tables->schema_table)
- table->file->stats.records= 2;
- table->quick_condition_rows= table->file->stats.records;
+ table->file->stats.records= table->used_stat_records= 2;
+ table->quick_condition_rows= table->stat_records();
s->on_expr_ref= &tables->on_expr;
if (*s->on_expr_ref)
@@ -3256,7 +3472,7 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list,
if (!table->is_filled_at_execution() &&
((!table->file->stats.records &&
(table->file->ha_table_flags() & HA_STATS_RECORDS_IS_EXACT)) ||
- no_partitions_used) && !embedding)
+ all_partitions_pruned_away) && !embedding)
{ // Empty table
s->dependent= 0; // Ignore LEFT JOIN depend.
no_rows_const_tables |= table->map;
@@ -3300,7 +3516,7 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list,
(table->s->system ||
(table->file->stats.records <= 1 &&
(table->file->ha_table_flags() & HA_STATS_RECORDS_IS_EXACT)) ||
- no_partitions_used) &&
+ all_partitions_pruned_away) &&
!s->dependent &&
!table->fulltext_searched && !join->no_const_tables)
{
@@ -3550,7 +3766,7 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list,
(!embedding || (embedding->sj_on_expr && !embedding->embedding)))
{
key_map base_part, base_const_ref, base_eq_part;
- base_part.set_prefix(keyinfo->key_parts);
+ base_part.set_prefix(keyinfo->user_defined_key_parts);
base_const_ref= const_ref;
base_const_ref.intersect(base_part);
base_eq_part= eq_part;
@@ -3693,7 +3909,7 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list,
This is can't be to high as otherwise we are likely to use
table scan.
*/
- s->worst_seeks= min((double) s->found_records / 10,
+ s->worst_seeks= MY_MIN((double) s->found_records / 10,
(double) s->read_time*3);
if (s->worst_seeks < 2.0) // Fix for small tables
s->worst_seeks=2.0;
@@ -3703,6 +3919,8 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list,
all select distinct fields participate in one index.
*/
add_group_and_distinct_keys(join, s);
+
+ s->table->cond_selectivity= 1.0;
/*
Perform range analysis if there are keys it could use (1).
@@ -3711,7 +3929,8 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list,
Don't do range analysis for materialized subqueries (4).
Don't do range analysis for materialized derived tables (5)
*/
- if (!s->const_keys.is_clear_all() && // (1)
+ if ((!s->const_keys.is_clear_all() ||
+ !bitmap_is_clear_all(&s->table->cond_set)) && // (1)
(!s->table->pos_in_table_list->embedding || // (2)
(s->table->pos_in_table_list->embedding && // (3)
s->table->pos_in_table_list->embedding->sj_on_expr)) && // (3)
@@ -3719,20 +3938,37 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list,
!(s->table->pos_in_table_list->derived && // (5)
s->table->pos_in_table_list->is_materialized_derived())) // (5)
{
- ha_rows records;
- SQL_SELECT *select;
- select= make_select(s->table, found_const_table_map,
- found_const_table_map,
- *s->on_expr_ref ? *s->on_expr_ref : conds,
- 1, &error);
- if (!select)
- goto error;
- records= get_quick_record_count(join->thd, select, s->table,
- &s->const_keys, join->row_limit);
- s->quick=select->quick;
- s->needed_reg=select->needed_reg;
- select->quick=0;
- if (records == 0 && s->table->reginfo.impossible_range)
+ bool impossible_range= FALSE;
+ ha_rows records= HA_POS_ERROR;
+ SQL_SELECT *select= 0;
+ if (!s->const_keys.is_clear_all())
+ {
+ select= make_select(s->table, found_const_table_map,
+ found_const_table_map,
+ *s->on_expr_ref ? *s->on_expr_ref : conds,
+ 1, &error);
+ if (!select)
+ goto error;
+ records= get_quick_record_count(join->thd, select, s->table,
+ &s->const_keys, join->row_limit);
+ s->quick=select->quick;
+ s->needed_reg=select->needed_reg;
+ select->quick=0;
+ impossible_range= records == 0 && s->table->reginfo.impossible_range;
+ }
+ if (!impossible_range)
+ {
+ if (join->thd->variables.optimizer_use_condition_selectivity > 1)
+ calculate_cond_selectivity_for_table(join->thd, s->table,
+ *s->on_expr_ref ?
+ *s->on_expr_ref : conds);
+ if (s->table->reginfo.impossible_range)
+ {
+ impossible_range= TRUE;
+ records= 0;
+ }
+ }
+ if (impossible_range)
{
/*
Impossible WHERE or ON expression
@@ -3746,7 +3982,7 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list,
if (*s->on_expr_ref)
{
/* Generate empty row */
- s->info= "Impossible ON condition";
+ s->info= ET_IMPOSSIBLE_ON_CONDITION;
found_const_table_map|= s->table->map;
s->type= JT_CONST;
mark_as_null_row(s->table); // All fields are NULL
@@ -3757,8 +3993,10 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list,
s->found_records=records;
s->read_time= s->quick ? s->quick->read_time : 0.0;
}
- delete select;
+ if (select)
+ delete select;
}
+
}
if (pull_out_semijoin_tables(join))
@@ -3829,7 +4067,7 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list,
DEBUG_SYNC(join->thd, "inside_make_join_statistics");
/* Generate an execution plan from the found optimal join order. */
- DBUG_RETURN(join->thd->killed || get_best_combination(join));
+ DBUG_RETURN(join->thd->check_killed() || get_best_combination(join));
error:
/*
@@ -4128,11 +4366,12 @@ add_key_field(JOIN *join,
else if (!(field->flags & PART_KEY_FLAG))
{
// Don't remove column IS NULL on a LEFT JOIN table
- if (!eq_func || (*value)->type() != Item::NULL_ITEM ||
- !field->table->maybe_null || field->null_ptr)
- return; // Not a key. Skip it
- optimize= KEY_OPTIMIZE_EXISTS;
- DBUG_ASSERT(num_values == 1);
+ if (eq_func && (*value)->type() == Item::NULL_ITEM &&
+ field->table->maybe_null && !field->null_ptr)
+ {
+ optimize= KEY_OPTIMIZE_EXISTS;
+ DBUG_ASSERT(num_values == 1);
+ }
}
if (optimize != KEY_OPTIMIZE_EXISTS)
{
@@ -4181,7 +4420,10 @@ add_key_field(JOIN *join,
break;
}
if (is_const)
+ {
stat[0].const_keys.merge(possible_keys);
+ bitmap_set_bit(&field->table->cond_set, field->field_index);
+ }
else if (!eq_func)
{
/*
@@ -4198,6 +4440,32 @@ add_key_field(JOIN *join,
if (!eq_func) // eq_func is NEVER true when num_values > 1
return;
+ if ((*value)->cmp_type() == TIME_RESULT &&
+ field->cmp_type() != TIME_RESULT)
+ return;
+
+ /*
+ Note, for ITEM/ENUM columns:
+ - field->cmp_type() returns INT_RESULT
+ - field->result_type() returns STRING_RESULT
+ - field->type() returns MYSQL_TYPE_STRING
+
+ Using field->real_type() to detect ENUM/SET,
+ as they need a special handling:
+ - Conditions between a ENUM/SET filter and a TIME expression
+ cannot be optimized. They were filtered out in the previous if block.
+ - It's Ok to use ref access for an ENUM/SET field compared to an
+ INT/REAL/DECIMAL expression.
+ - It's Ok to use ref for an ENUM/SET field compared to a STRING
+ expression if the collation of the field and the collation of
+ the condition match.
+ */
+ if ((field->real_type() == MYSQL_TYPE_ENUM ||
+ field->real_type() == MYSQL_TYPE_SET) &&
+ (*value)->cmp_type () == STRING_RESULT &&
+ field->charset() != cond->compare_collation())
+ return;
+
/*
We can't use indexes when comparing a string index to a
number or two strings if the effective collation
@@ -4208,7 +4476,7 @@ add_key_field(JOIN *join,
{
if ((*value)->cmp_type() != STRING_RESULT)
return;
- if (((Field_str*)field)->charset() != cond->compare_collation())
+ if (field->charset() != cond->compare_collation())
return;
}
}
@@ -4688,7 +4956,18 @@ add_key_part(DYNAMIC_ARRAY *keyuse_array, KEY_FIELD *key_field)
}
-#define FT_KEYPART (MAX_REF_PARTS+10)
+/*
+ A key part number that means we're using a fulltext scan.
+
+ In order not to confuse it with regular equalities, we need to pick
+ a number that's greater than MAX_REF_PARTS.
+
+ Hash Join code stores field->field_index in KEYUSE::keypart, so the
+ number needs to be bigger than MAX_FIELDS, also.
+
+ CAUTION: sql_test.cc has its own definition of FT_KEYPART.
+*/
+#define FT_KEYPART (MAX_FIELDS+10)
static bool
add_ft_keys(DYNAMIC_ARRAY *keyuse_array,
@@ -4769,8 +5048,8 @@ sort_keyuse(KEYUSE *a,KEYUSE *b)
if (a->keypart != b->keypart)
return (int) (a->keypart - b->keypart);
// Place const values before other ones
- if ((res= test((a->used_tables & ~OUTER_REF_TABLE_BIT)) -
- test((b->used_tables & ~OUTER_REF_TABLE_BIT))))
+ if ((res= MY_TEST((a->used_tables & ~OUTER_REF_TABLE_BIT)) -
+ MY_TEST((b->used_tables & ~OUTER_REF_TABLE_BIT))))
return res;
/* Place rows that are not 'OPTIMIZE_REF_OR_NULL' first */
return (int) ((a->optimize & KEY_OPTIMIZE_REF_OR_NULL) -
@@ -4905,7 +5184,7 @@ update_ref_and_keys(THD *thd, DYNAMIC_ARRAY *keyuse,JOIN_TAB *join_tab,
uint and_level,i;
KEY_FIELD *key_fields, *end, *field;
uint sz;
- uint m= max(select_lex->max_equal_elems,1);
+ uint m= MY_MAX(select_lex->max_equal_elems,1);
DBUG_ENTER("update_ref_and_keys");
DBUG_PRINT("enter", ("normal_tables: %llx", normal_tables));
@@ -4951,7 +5230,7 @@ update_ref_and_keys(THD *thd, DYNAMIC_ARRAY *keyuse,JOIN_TAB *join_tab,
can be not more than select_lex->max_equal_elems such
substitutions.
*/
- sz= max(sizeof(KEY_FIELD),sizeof(SARGABLE_PARAM))*
+ sz= MY_MAX(sizeof(KEY_FIELD),sizeof(SARGABLE_PARAM))*
((sel->cond_count*2 + sel->between_count)*m+1);
if (!(key_fields=(KEY_FIELD*) thd->alloc(sz)))
DBUG_RETURN(TRUE); /* purecov: inspected */
@@ -4962,7 +5241,8 @@ update_ref_and_keys(THD *thd, DYNAMIC_ARRAY *keyuse,JOIN_TAB *join_tab,
/* set a barrier for the array of SARGABLE_PARAM */
(*sargables)[0].field= 0;
- if (my_init_dynamic_array(keyuse,sizeof(KEYUSE),20,64))
+ if (my_init_dynamic_array(keyuse,sizeof(KEYUSE),20,64,
+ MYF(MY_THREAD_SPECIFIC)))
DBUG_RETURN(TRUE);
if (cond)
@@ -5062,7 +5342,8 @@ static bool sort_and_filter_keyuse(THD *thd, DYNAMIC_ARRAY *keyuse,
{
if (!use->is_for_hash_join())
{
- if (!use->used_tables && use->optimize != KEY_OPTIMIZE_REF_OR_NULL)
+ if (!(use->used_tables & ~OUTER_REF_TABLE_BIT) &&
+ use->optimize != KEY_OPTIMIZE_REF_OR_NULL)
use->table->const_key_parts[use->key]|= use->keypart_map;
if (use->keypart != FT_KEYPART)
{
@@ -5133,7 +5414,7 @@ static void optimize_keyuse(JOIN *join, DYNAMIC_ARRAY *keyuse_array)
DBUG_ASSERT(tablenr != Table_map_iterator::BITMAP_END);
TABLE *tmp_table=join->table[tablenr];
if (tmp_table) // already created
- keyuse->ref_table_rows= max(tmp_table->file->stats.records, 100);
+ keyuse->ref_table_rows= MY_MAX(tmp_table->file->stats.records, 100);
}
}
/*
@@ -5325,6 +5606,7 @@ void set_position(JOIN *join,uint idx,JOIN_TAB *table,KEYUSE *key)
join->positions[idx].table= table;
join->positions[idx].key=key;
join->positions[idx].records_read=1.0; /* This is a const table */
+ join->positions[idx].cond_selectivity= 1.0;
join->positions[idx].ref_depend_map= 0;
// join->positions[idx].loosescan_key= MAX_KEY; /* Not a LooseScan */
@@ -5344,12 +5626,39 @@ void set_position(JOIN *join,uint idx,JOIN_TAB *table,KEYUSE *key)
}
-/* Estimate of the number matching candidates in the joined table */
+/*
+ Estimate how many records we will get if we read just this table and apply
+ a part of WHERE that can be checked for it.
+
+ @detail
+ Estimate how many records we will get if we
+ - read the given table with its "independent" access method (either quick
+ select or full table/index scan),
+ - apply the part of WHERE that refers only to this table.
+
+ @seealso
+ table_cond_selectivity() produces selectivity of condition that is checked
+ after joining rows from this table to rows from preceding tables.
+*/
inline
-ha_rows matching_candidates_in_table(JOIN_TAB *s, bool with_found_constraint)
+double matching_candidates_in_table(JOIN_TAB *s, bool with_found_constraint,
+ uint use_cond_selectivity)
{
- ha_rows records= s->found_records;
+ ha_rows records;
+ double dbl_records;
+
+ if (use_cond_selectivity > 1)
+ {
+ TABLE *table= s->table;
+ double sel= table->cond_selectivity;
+ double table_records= table->stat_records();
+ dbl_records= table_records * sel;
+ return dbl_records;
+ }
+
+ records = s->found_records;
+
/*
If there is a filtering condition on the table (i.e. ref analyzer found
at least one "table.keyXpartY= exprZ", where exprZ refers only to tables
@@ -5369,7 +5678,8 @@ ha_rows matching_candidates_in_table(JOIN_TAB *s, bool with_found_constraint)
if (s->table->quick_condition_rows != s->found_records)
records= s->table->quick_condition_rows;
- return records;
+ dbl_records= records;
+ return dbl_records;
}
@@ -5412,6 +5722,7 @@ best_access_path(JOIN *join,
POSITION *loose_scan_pos)
{
THD *thd= join->thd;
+ uint use_cond_selectivity= thd->variables.optimizer_use_condition_selectivity;
KEYUSE *best_key= 0;
uint best_max_key_part= 0;
my_bool found_constraint= 0;
@@ -5553,7 +5864,7 @@ best_access_path(JOIN *join,
}
else
{
- found_constraint= test(found_part);
+ found_constraint= MY_TEST(found_part);
loose_scan_opt.check_ref_access_part1(s, key, start_key, found_part);
/* Check if we found full key */
@@ -5562,7 +5873,7 @@ best_access_path(JOIN *join,
{ /* use eq key */
max_key_part= (uint) ~0;
if ((key_flags & (HA_NOSAME | HA_NULL_PART_KEY)) == HA_NOSAME ||
- test(key_flags & HA_EXT_NOSAME))
+ MY_TEST(key_flags & HA_EXT_NOSAME))
{
tmp = prev_record_reads(join->positions, idx, found_ref);
records=1.0;
@@ -5599,7 +5910,7 @@ best_access_path(JOIN *join,
else
{
uint key_parts= table->actual_n_key_parts(keyinfo);
- if (!(records=keyinfo->rec_per_key[key_parts-1]))
+ if (!(records= keyinfo->actual_rec_per_key(key_parts-1)))
{ /* Prefer longer keys */
records=
((double) s->records / (double) rec *
@@ -5636,7 +5947,7 @@ best_access_path(JOIN *join,
tmp= table->file->keyread_time(key, 1, (ha_rows) tmp);
else
tmp= table->file->read_time(key, 1,
- (ha_rows) min(tmp,s->worst_seeks));
+ (ha_rows) MY_MIN(tmp,s->worst_seeks));
tmp*= record_count;
}
}
@@ -5649,7 +5960,7 @@ best_access_path(JOIN *join,
*/
if ((found_part & 1) &&
(!(table->file->index_flags(key, 0, 0) & HA_ONLY_WHOLE_INDEX) ||
- found_part == PREV_BITS(uint,keyinfo->key_parts)))
+ found_part == PREV_BITS(uint,keyinfo->user_defined_key_parts)))
{
max_key_part= max_part_bit(found_part);
/*
@@ -5693,14 +6004,14 @@ best_access_path(JOIN *join,
*/
if (table->quick_keys.is_set(key) && !found_ref && //(C1)
table->quick_key_parts[key] == max_key_part && //(C2)
- table->quick_n_ranges[key] == 1+test(ref_or_null_part)) //(C3)
+ table->quick_n_ranges[key] == 1 + MY_TEST(ref_or_null_part)) //(C3)
{
tmp= records= (double) table->quick_rows[key];
}
else
{
/* Check if we have statistic about the distribution */
- if ((records= keyinfo->rec_per_key[max_key_part-1]))
+ if ((records= keyinfo->actual_rec_per_key(max_key_part-1)))
{
/*
Fix for the case where the index statistics is too
@@ -5743,7 +6054,7 @@ best_access_path(JOIN *join,
*/
double rec_per_key;
if (!(rec_per_key=(double)
- keyinfo->rec_per_key[keyinfo->key_parts-1]))
+ keyinfo->rec_per_key[keyinfo->user_defined_key_parts-1]))
rec_per_key=(double) s->records/rec+1;
if (!s->records)
@@ -5753,10 +6064,10 @@ best_access_path(JOIN *join,
else
{
double a=s->records*0.01;
- if (keyinfo->key_parts > 1)
+ if (keyinfo->user_defined_key_parts > 1)
tmp= (max_key_part * (rec_per_key - a) +
- a*keyinfo->key_parts - rec_per_key)/
- (keyinfo->key_parts-1);
+ a*keyinfo->user_defined_key_parts - rec_per_key)/
+ (keyinfo->user_defined_key_parts-1);
else
tmp= a;
set_if_bigger(tmp,1.0);
@@ -5787,8 +6098,8 @@ best_access_path(JOIN *join,
table->quick_key_parts[key] <= max_key_part &&
const_part &
((key_part_map)1 << table->quick_key_parts[key]) &&
- table->quick_n_ranges[key] == 1 + test(ref_or_null_part &
- const_part) &&
+ table->quick_n_ranges[key] == 1 + MY_TEST(ref_or_null_part &
+ const_part) &&
records > (double) table->quick_rows[key])
{
tmp= records= (double) table->quick_rows[key];
@@ -5801,7 +6112,7 @@ best_access_path(JOIN *join,
tmp= table->file->keyread_time(key, 1, (ha_rows) tmp);
else
tmp= table->file->read_time(key, 1,
- (ha_rows) min(tmp,s->worst_seeks));
+ (ha_rows) MY_MIN(tmp,s->worst_seeks));
tmp*= record_count;
}
else
@@ -5843,7 +6154,8 @@ best_access_path(JOIN *join,
{
double join_sel= 0.1;
/* Estimate the cost of the hash join access to the table */
- ha_rows rnd_records= matching_candidates_in_table(s, found_constraint);
+ double rnd_records= matching_candidates_in_table(s, found_constraint,
+ use_cond_selectivity);
tmp= s->quick ? s->quick->read_time : s->scan_time();
tmp+= (s->records - rnd_records)/(double) TIME_FOR_COMPARE;
@@ -5855,7 +6167,7 @@ best_access_path(JOIN *join,
best_time= tmp +
(record_count*join_sel) / TIME_FOR_COMPARE * rnd_records;
best= tmp;
- records= rows2double(rnd_records);
+ records= rnd_records;
best_key= hj_start_key;
best_ref_depends_map= 0;
best_uses_jbuf= TRUE;
@@ -5902,7 +6214,8 @@ best_access_path(JOIN *join,
!(s->table->force_index && best_key && !s->quick) && // (4)
!(best_key && s->table->pos_in_table_list->jtbm_subselect)) // (5)
{ // Check full join
- ha_rows rnd_records= matching_candidates_in_table(s, found_constraint);
+ double rnd_records= matching_candidates_in_table(s, found_constraint,
+ use_cond_selectivity);
/*
Range optimizer never proposes a RANGE if it isn't better
@@ -5930,7 +6243,11 @@ best_access_path(JOIN *join,
else
{
/* Estimate cost of reading table. */
- tmp= s->scan_time();
+ if (s->table->force_index && !best_key) // index scan
+ tmp= s->table->file->read_time(s->ref.key, 1, s->records);
+ else // table scan
+ tmp= s->scan_time();
+
if ((s->table->map & join->outer_join) || disable_jbuf) // Can't use join cache
{
/*
@@ -5975,12 +6292,12 @@ best_access_path(JOIN *join,
will ensure that this will be used
*/
best= tmp;
- records= rows2double(rnd_records);
+ records= rnd_records;
best_key= 0;
/* range/index_merge/ALL/index access method are "independent", so: */
best_ref_depends_map= 0;
- best_uses_jbuf= test(!disable_jbuf && !((s->table->map &
- join->outer_join)));
+ best_uses_jbuf= MY_TEST(!disable_jbuf && !((s->table->map &
+ join->outer_join)));
}
}
@@ -6046,6 +6363,7 @@ static void choose_initial_table_order(JOIN *join)
TABLE_LIST *emb_subq;
JOIN_TAB **tab= join->best_ref + join->const_tables;
JOIN_TAB **tabs_end= tab + join->table_count - join->const_tables;
+ DBUG_ENTER("choose_initial_table_order");
/* Find where the top-level JOIN_TABs end and subquery JOIN_TABs start */
for (; tab != tabs_end; tab++)
{
@@ -6055,7 +6373,7 @@ static void choose_initial_table_order(JOIN *join)
uint n_subquery_tabs= tabs_end - tab;
if (!n_subquery_tabs)
- return;
+ DBUG_VOID_RETURN;
/* Copy the subquery JOIN_TABs to a separate array */
JOIN_TAB *subquery_tabs[MAX_TABLES];
@@ -6110,6 +6428,7 @@ static void choose_initial_table_order(JOIN *join)
subq_tab += n_subquery_tables - 1;
}
}
+ DBUG_VOID_RETURN;
}
@@ -6141,7 +6460,9 @@ choose_plan(JOIN *join, table_map join_tables)
{
uint search_depth= join->thd->variables.optimizer_search_depth;
uint prune_level= join->thd->variables.optimizer_prune_level;
- bool straight_join= test(join->select_options & SELECT_STRAIGHT_JOIN);
+ uint use_cond_selectivity=
+ join->thd->variables.optimizer_use_condition_selectivity;
+ bool straight_join= MY_TEST(join->select_options & SELECT_STRAIGHT_JOIN);
DBUG_ENTER("choose_plan");
join->cur_embedding_map= 0;
@@ -6196,15 +6517,19 @@ choose_plan(JOIN *join, table_map join_tables)
the greedy version. Will be removed when greedy_search is approved.
*/
join->best_read= DBL_MAX;
- if (find_best(join, join_tables, join->const_tables, 1.0, 0.0))
+ if (find_best(join, join_tables, join->const_tables, 1.0, 0.0,
+ use_cond_selectivity))
+ {
DBUG_RETURN(TRUE);
+ }
}
else
{
if (search_depth == 0)
/* Automatically determine a reasonable value for 'search_depth' */
search_depth= determine_search_depth(join);
- if (greedy_search(join, join_tables, search_depth, prune_level))
+ if (greedy_search(join, join_tables, search_depth, prune_level,
+ use_cond_selectivity))
DBUG_RETURN(TRUE);
}
}
@@ -6478,6 +6803,8 @@ optimize_straight_join(JOIN *join, table_map join_tables)
bool disable_jbuf= join->thd->variables.join_cache_level == 0;
double record_count= 1.0;
double read_time= 0.0;
+ uint use_cond_selectivity=
+ join->thd->variables.optimizer_use_condition_selectivity;
POSITION loose_scan_pos;
for (JOIN_TAB **pos= join->best_ref + idx ; (s= *pos) ; pos++)
@@ -6494,6 +6821,11 @@ optimize_straight_join(JOIN *join, table_map join_tables)
&loose_scan_pos);
join_tables&= ~(s->table->map);
+ double pushdown_cond_selectivity= 1.0;
+ if (use_cond_selectivity > 1)
+ pushdown_cond_selectivity= table_cond_selectivity(join, idx, s,
+ join_tables);
+ join->positions[idx].cond_selectivity= pushdown_cond_selectivity;
++idx;
}
@@ -6581,6 +6913,8 @@ optimize_straight_join(JOIN *join, table_map join_tables)
@param search_depth controlls the exhaustiveness of the search
@param prune_level the pruning heuristics that should be applied during
search
+ @param use_cond_selectivity specifies how the selectivity of the conditions
+ pushed to a table should be taken into account
@retval
FALSE ok
@@ -6592,7 +6926,8 @@ static bool
greedy_search(JOIN *join,
table_map remaining_tables,
uint search_depth,
- uint prune_level)
+ uint prune_level,
+ uint use_cond_selectivity)
{
double record_count= 1.0;
double read_time= 0.0;
@@ -6603,7 +6938,6 @@ greedy_search(JOIN *join,
JOIN_TAB *best_table; // the next plan node to be added to the curr QEP
// ==join->tables or # tables in the sj-mat nest we're optimizing
uint n_tables __attribute__((unused));
-
DBUG_ENTER("greedy_search");
/* number of tables that remain to be optimized */
@@ -6618,7 +6952,8 @@ greedy_search(JOIN *join,
/* Find the extension of the current QEP with the lowest cost */
join->best_read= DBL_MAX;
if (best_extension_by_limited_search(join, remaining_tables, idx, record_count,
- read_time, search_depth, prune_level))
+ read_time, search_depth, prune_level,
+ use_cond_selectivity))
DBUG_RETURN(TRUE);
/*
'best_read < DBL_MAX' means that optimizer managed to find
@@ -6840,7 +7175,7 @@ void JOIN::get_prefix_cost_and_fanout(uint n_tables,
double JOIN::get_examined_rows()
{
- ha_rows examined_rows;
+ double examined_rows;
double prev_fanout= 1;
JOIN_TAB *tab= first_breadth_first_tab(this, WALK_OPTIMIZATION_TABS);
JOIN_TAB *prev_tab= tab;
@@ -6850,7 +7185,7 @@ double JOIN::get_examined_rows()
while ((tab= next_breadth_first_tab(this, WALK_OPTIMIZATION_TABS, tab)))
{
prev_fanout *= prev_tab->records_read;
- examined_rows+= (ha_rows) (tab->get_examined_rows() * prev_fanout);
+ examined_rows+= tab->get_examined_rows() * prev_fanout;
prev_tab= tab;
}
return examined_rows;
@@ -6858,6 +7193,351 @@ double JOIN::get_examined_rows()
/**
+ @brief
+ Get the selectivity of equalities between columns when joining a table
+
+ @param join The optimized join
+ @param idx The number of tables in the evaluated partual join
+ @param s The table to be joined for evaluation
+ @param rem_tables The bitmap of tables to be joined later
+ @param keyparts The number of key parts to used when joining s
+ @param ref_keyuse_steps Array of references to keyuses employed to join s
+*/
+
+static
+double table_multi_eq_cond_selectivity(JOIN *join, uint idx, JOIN_TAB *s,
+ table_map rem_tables, uint keyparts,
+ uint16 *ref_keyuse_steps)
+{
+ double sel= 1.0;
+ COND_EQUAL *cond_equal= join->cond_equal;
+
+ if (!cond_equal || !cond_equal->current_level.elements)
+ return sel;
+
+ if (!s->keyuse)
+ return sel;
+
+ Item_equal *item_equal;
+ List_iterator_fast<Item_equal> it(cond_equal->current_level);
+ TABLE *table= s->table;
+ table_map table_bit= table->map;
+ POSITION *pos= &join->positions[idx];
+
+ while ((item_equal= it++))
+ {
+ /*
+ Check whether we need to take into account the selectivity of
+ multiple equality item_equal. If this is the case multiply
+ the current value of sel by this selectivity
+ */
+ table_map used_tables= item_equal->used_tables();
+ if (!(used_tables & table_bit))
+ continue;
+ if (item_equal->get_const())
+ continue;
+
+ Field *fld;
+ bool adjust_sel= FALSE;
+ Item_equal_fields_iterator fi(*item_equal);
+ while((fi++) && !adjust_sel)
+ {
+ Field *fld= fi.get_curr_field();
+ if (fld->table->map != table_bit)
+ continue;
+ if (pos->key == 0)
+ adjust_sel= TRUE;
+ else
+ {
+ uint i;
+ KEYUSE *keyuse= pos->key;
+ uint key= keyuse->key;
+ for (i= 0; i < keyparts; i++)
+ {
+ if (i > 0)
+ keyuse+= ref_keyuse_steps[i-1];
+ uint fldno;
+ if (is_hash_join_key_no(key))
+ fldno= keyuse->keypart;
+ else
+ fldno= table->key_info[key].key_part[i].fieldnr - 1;
+ if (fld->field_index == fldno)
+ break;
+ }
+ keyuse= pos->key;
+
+ if (i == keyparts)
+ {
+ /*
+ Field fld is included in multiple equality item_equal
+ and is not a part of the ref key.
+ The selectivity of the multiple equality must be taken
+ into account unless one of the ref arguments is
+ equal to fld.
+ */
+ adjust_sel= TRUE;
+ for (uint j= 0; j < keyparts && adjust_sel; j++)
+ {
+ if (j > 0)
+ keyuse+= ref_keyuse_steps[j-1];
+ Item *ref_item= keyuse->val;
+ if (ref_item->real_item()->type() == Item::FIELD_ITEM)
+ {
+ Item_field *field_item= (Item_field *) (ref_item->real_item());
+ if (item_equal->contains(field_item->field))
+ adjust_sel= FALSE;
+ }
+ }
+ }
+ }
+ }
+ if (adjust_sel)
+ {
+ /*
+ If ref == 0 and there are no fields in the multiple equality
+ item_equal that belong to the tables joined prior to s
+ then the selectivity of multiple equality will be set to 1.0.
+ */
+ double eq_fld_sel= 1.0;
+ fi.rewind();
+ while ((fi++))
+ {
+ double curr_eq_fld_sel;
+ fld= fi.get_curr_field();
+ if (!(fld->table->map & ~(table_bit | rem_tables)))
+ continue;
+ curr_eq_fld_sel= get_column_avg_frequency(fld) /
+ fld->table->stat_records();
+ if (curr_eq_fld_sel < 1.0)
+ set_if_bigger(eq_fld_sel, curr_eq_fld_sel);
+ }
+ sel*= eq_fld_sel;
+ }
+ }
+ return sel;
+}
+
+
+/**
+ @brief
+ Get the selectivity of conditions when joining a table
+
+ @param join The optimized join
+ @param s The table to be joined for evaluation
+ @param rem_tables The bitmap of tables to be joined later
+
+ @detail
+ Get selectivity of conditions that can be applied when joining this table
+ with previous tables.
+
+ For quick selects and full table scans, selectivity of COND(this_table)
+ is accounted for in matching_candidates_in_table(). Here, we only count
+ selectivity of COND(this_table, previous_tables).
+
+ For other access methods, we need to calculate selectivity of the whole
+ condition, "COND(this_table) AND COND(this_table, previous_tables)".
+
+ @retval
+ selectivity of the conditions imposed on the rows of s
+*/
+
+static
+double table_cond_selectivity(JOIN *join, uint idx, JOIN_TAB *s,
+ table_map rem_tables)
+{
+ uint16 ref_keyuse_steps[MAX_REF_PARTS - 1];
+ Field *field;
+ TABLE *table= s->table;
+ MY_BITMAP *read_set= table->read_set;
+ double sel= s->table->cond_selectivity;
+ POSITION *pos= &join->positions[idx];
+ uint keyparts= 0;
+ uint found_part_ref_or_null= 0;
+
+ if (pos->key != 0)
+ {
+ /*
+ A ref access or hash join is used for this table. ref access is created
+ from
+
+ tbl.keypart1=expr1 AND tbl.keypart2=expr2 AND ...
+
+ and it will only return rows for which this condition is satisified.
+ Suppose, certain expr{i} is a constant. Since ref access only returns
+ rows that satisfy
+
+ tbl.keypart{i}=const (*)
+
+ then selectivity of this equality should not be counted in return value
+ of this function. This function uses the value of
+
+ table->cond_selectivity=selectivity(COND(tbl)) (**)
+
+ as a starting point. This value includes selectivity of equality (*). We
+ should somehow discount it.
+
+ Looking at calculate_cond_selectivity_for_table(), one can see that that
+ the value is not necessarily a direct multiplicand in
+ table->cond_selectivity
+
+ There are three possible ways to discount
+ 1. There is a potential range access on t.keypart{i}=const.
+ (an important special case: the used ref access has a const prefix for
+ which a range estimate is available)
+
+ 2. The field has a histogram. field[x]->cond_selectivity has the data.
+
+ 3. Use index stats on this index:
+ rec_per_key[key_part+1]/rec_per_key[key_part]
+
+ (TODO: more details about the "t.key=othertable.col" case)
+ */
+ KEYUSE *keyuse= pos->key;
+ KEYUSE *prev_ref_keyuse= keyuse;
+ uint key= keyuse->key;
+
+ /*
+ Check if we have a prefix of key=const that matches a quick select.
+ */
+ if (!is_hash_join_key_no(key))
+ {
+ table_map quick_key_map= (table_map(1) << table->quick_key_parts[key]) - 1;
+ if (table->quick_rows[key] &&
+ !(quick_key_map & ~table->const_key_parts[key]))
+ {
+ /*
+ Ok, there is an equality for each of the key parts used by the
+ quick select. This means, quick select's estimate can be reused to
+ discount the selectivity of a prefix of a ref access.
+ */
+ for (; quick_key_map & 1 ; quick_key_map>>= 1)
+ {
+ while (keyuse->table == table && keyuse->key == key &&
+ keyuse->keypart == keyparts)
+ {
+ keyuse++;
+ }
+ keyparts++;
+ }
+ sel /= (double)table->quick_rows[key] / (double) table->stat_records();
+ }
+ }
+
+ /*
+ Go through the "keypart{N}=..." equalities and find those that were
+ already taken into account in table->cond_selectivity.
+ */
+ keyuse= pos->key;
+ keyparts=0;
+ while (keyuse->table == table && keyuse->key == key)
+ {
+ if (!(keyuse->used_tables & (rem_tables | table->map)))
+ {
+ if (are_tables_local(s, keyuse->val->used_tables()))
+ {
+ if (is_hash_join_key_no(key))
+ {
+ if (keyparts == keyuse->keypart)
+ keyparts++;
+ }
+ else
+ {
+ if (keyparts == keyuse->keypart &&
+ !((keyuse->val->used_tables()) & ~pos->ref_depend_map) &&
+ !(found_part_ref_or_null & keyuse->optimize))
+ {
+ /* Found a KEYUSE object that will be used by ref access */
+ keyparts++;
+ found_part_ref_or_null|= keyuse->optimize & ~KEY_OPTIMIZE_EQ;
+ }
+ }
+
+ if (keyparts > keyuse->keypart)
+ {
+ /* Ok this is the keyuse that will be used for ref access */
+ uint fldno;
+ if (is_hash_join_key_no(key))
+ fldno= keyuse->keypart;
+ else
+ fldno= table->key_info[key].key_part[keyparts-1].fieldnr - 1;
+ if (keyuse->val->const_item())
+ {
+ if (table->field[fldno]->cond_selectivity > 0)
+ {
+ sel /= table->field[fldno]->cond_selectivity;
+ set_if_smaller(sel, 1.0);
+ }
+ /*
+ TODO: we could do better here:
+ 1. cond_selectivity might be =1 (the default) because quick
+ select on some index prevented us from analyzing
+ histogram for this column.
+ 2. we could get an estimate through this?
+ rec_per_key[key_part-1] / rec_per_key[key_part]
+ */
+ }
+ if (keyparts > 1)
+ {
+ ref_keyuse_steps[keyparts-2]= keyuse - prev_ref_keyuse;
+ prev_ref_keyuse= keyuse;
+ }
+ }
+ }
+ }
+ keyuse++;
+ }
+ }
+ else
+ {
+ /*
+ The table is accessed with full table scan, or quick select.
+ Selectivity of COND(table) is already accounted for in
+ matching_candidates_in_table().
+ */
+ sel= 1;
+ }
+
+ /*
+ If the field f from the table is equal to a field from one the
+ earlier joined tables then the selectivity of the range conditions
+ over the field f must be discounted.
+
+ We need to discount selectivity only if we're using ref-based
+ access method (and have sel!=1).
+ If we use ALL/range/index_merge, then sel==1, and no need to discount.
+ */
+ if (pos->key != NULL)
+ {
+ for (Field **f_ptr=table->field ; (field= *f_ptr) ; f_ptr++)
+ {
+ if (!bitmap_is_set(read_set, field->field_index) ||
+ !field->next_equal_field)
+ continue;
+ for (Field *next_field= field->next_equal_field;
+ next_field != field;
+ next_field= next_field->next_equal_field)
+ {
+ if (!(next_field->table->map & rem_tables) && next_field->table != table)
+ {
+ if (field->cond_selectivity > 0)
+ {
+ sel/= field->cond_selectivity;
+ set_if_smaller(sel, 1.0);
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ sel*= table_multi_eq_cond_selectivity(join, idx, s, rem_tables,
+ keyparts, ref_keyuse_steps);
+
+ return sel;
+}
+
+
+/**
Find a good, possibly optimal, query execution plan (QEP) by a possibly
exhaustive search.
@@ -6967,6 +7647,8 @@ double JOIN::get_examined_rows()
@param prune_level pruning heuristics that should be applied during
optimization
(values: 0 = EXHAUSTIVE, 1 = PRUNE_BY_TIME_OR_ROWS)
+ @param use_cond_selectivity specifies how the selectivity of the conditions
+ pushed to a table should be taken into account
@retval
FALSE ok
@@ -6981,12 +7663,21 @@ best_extension_by_limited_search(JOIN *join,
double record_count,
double read_time,
uint search_depth,
- uint prune_level)
+ uint prune_level,
+ uint use_cond_selectivity)
{
DBUG_ENTER("best_extension_by_limited_search");
THD *thd= join->thd;
- if (thd->killed) // Abort
+
+ DBUG_EXECUTE_IF("show_explain_probe_best_ext_lim_search",
+ if (dbug_user_var_equals_int(thd,
+ "show_explain_probe_select_id",
+ join->select_lex->select_number))
+ dbug_serve_apcs(thd, 1);
+ );
+
+ if (thd->check_killed()) // Abort
DBUG_RETURN(TRUE);
DBUG_EXECUTE("opt", print_plan(join, idx, read_time, record_count, idx,
@@ -7084,16 +7775,25 @@ best_extension_by_limited_search(JOIN *join,
}
}
+ double pushdown_cond_selectivity= 1.0;
+ if (use_cond_selectivity > 1)
+ pushdown_cond_selectivity= table_cond_selectivity(join, idx, s,
+ remaining_tables &
+ ~real_table_bit);
+ join->positions[idx].cond_selectivity= pushdown_cond_selectivity;
+ double partial_join_cardinality= current_record_count *
+ pushdown_cond_selectivity;
if ( (search_depth > 1) && (remaining_tables & ~real_table_bit) & allowed_tables )
{ /* Recursively expand the current partial plan */
swap_variables(JOIN_TAB*, join->best_ref[idx], *pos);
if (best_extension_by_limited_search(join,
remaining_tables & ~real_table_bit,
idx + 1,
- current_record_count,
+ partial_join_cardinality,
current_read_time,
search_depth - 1,
- prune_level))
+ prune_level,
+ use_cond_selectivity))
DBUG_RETURN(TRUE);
swap_variables(JOIN_TAB*, join->best_ref[idx], *pos);
}
@@ -7105,13 +7805,17 @@ best_extension_by_limited_search(JOIN *join,
if (join->sort_by_table &&
join->sort_by_table !=
join->positions[join->const_tables].table->table)
- /* We have to make a temp table */
+ /*
+ We may have to make a temp table, note that this is only a
+ heuristic since we cannot know for sure at this point.
+ Hence it may be wrong.
+ */
current_read_time+= current_record_count;
if (current_read_time < join->best_read)
{
memcpy((uchar*) join->best_positions, (uchar*) join->positions,
sizeof(POSITION) * (idx + 1));
- join->record_count= current_record_count;
+ join->record_count= partial_join_cardinality;
join->best_read= current_read_time - 0.001;
}
DBUG_EXECUTE("opt", print_plan(join, idx+1,
@@ -7139,11 +7843,11 @@ best_extension_by_limited_search(JOIN *join,
*/
static bool
find_best(JOIN *join,table_map rest_tables,uint idx,double record_count,
- double read_time)
+ double read_time, uint use_cond_selectivity)
{
DBUG_ENTER("find_best");
THD *thd= join->thd;
- if (thd->killed)
+ if (thd->check_killed())
DBUG_RETURN(TRUE);
if (!rest_tables)
{
@@ -7190,20 +7894,30 @@ find_best(JOIN *join,table_map rest_tables,uint idx,double record_count,
advance_sj_state(join, rest_tables, idx, &current_record_count,
&current_read_time, &loose_scan_pos);
- if (best_record_count > current_record_count ||
+ double pushdown_cond_selectivity= 1.0;
+ if (use_cond_selectivity > 1)
+ pushdown_cond_selectivity= table_cond_selectivity(join, idx, s,
+ rest_tables &
+ ~real_table_bit);
+ join->positions[idx].cond_selectivity= pushdown_cond_selectivity;
+ double partial_join_cardinality= current_record_count *
+ pushdown_cond_selectivity;
+
+ if (best_record_count > partial_join_cardinality ||
best_read_time > current_read_time ||
(idx == join->const_tables && s->table == join->sort_by_table))
{
- if (best_record_count >= current_record_count &&
+ if (best_record_count >= partial_join_cardinality &&
best_read_time >= current_read_time &&
(!(s->key_dependent & rest_tables) || records < 2.0))
{
- best_record_count=current_record_count;
+ best_record_count= partial_join_cardinality;
best_read_time=current_read_time;
}
swap_variables(JOIN_TAB*, join->best_ref[idx], *pos);
if (find_best(join,rest_tables & ~real_table_bit,idx+1,
- current_record_count,current_read_time))
+ partial_join_cardinality,current_read_time,
+ use_cond_selectivity))
DBUG_RETURN(TRUE);
swap_variables(JOIN_TAB*, join->best_ref[idx], *pos);
}
@@ -7765,7 +8479,9 @@ get_best_combination(JOIN *join)
sub-order
*/
SJ_MATERIALIZATION_INFO *sjm= cur_pos->table->emb_sj_nest->sj_mat_info;
- j->records= j->records_read= (ha_rows)(sjm->is_sj_scan? sjm->rows : 1);
+ j->records_read= (sjm->is_sj_scan? sjm->rows : 1);
+ j->records= (ha_rows) j->records_read;
+ j->cond_selectivity= 1.0;
JOIN_TAB *jt;
JOIN_TAB_RANGE *jt_range;
if (!(jt= (JOIN_TAB*)join->thd->alloc(sizeof(JOIN_TAB)*sjm->tables)) ||
@@ -7828,7 +8544,8 @@ get_best_combination(JOIN *join)
Save records_read in JOIN_TAB so that select_describe()/etc don't have
to access join->best_positions[].
*/
- j->records_read= (ha_rows)join->best_positions[tablenr].records_read;
+ j->records_read= join->best_positions[tablenr].records_read;
+ j->cond_selectivity= join->best_positions[tablenr].cond_selectivity;
join->map2table[j->table->tablenr]= j;
/* If we've reached the end of sjm nest, switch back to main sequence */
@@ -7850,6 +8567,7 @@ get_best_combination(JOIN *join)
join->table_access_tabs= join->join_tab;
join->top_table_access_tabs_count= join->top_join_tab_count;
+
update_depend_map(join);
DBUG_RETURN(0);
}
@@ -7920,12 +8638,13 @@ static bool create_hj_key_for_table(JOIN *join, JOIN_TAB *join_tab,
!(key_part_info = (KEY_PART_INFO *) thd->alloc(sizeof(KEY_PART_INFO)*
key_parts)))
DBUG_RETURN(TRUE);
- keyinfo->usable_key_parts= keyinfo->key_parts = key_parts;
- keyinfo->ext_key_parts= keyinfo->key_parts;
+ keyinfo->usable_key_parts= keyinfo->user_defined_key_parts = key_parts;
+ keyinfo->ext_key_parts= keyinfo->user_defined_key_parts;
keyinfo->key_part= key_part_info;
keyinfo->key_length=0;
keyinfo->algorithm= HA_KEY_ALG_UNDEF;
keyinfo->flags= HA_GENERATED_KEY;
+ keyinfo->is_statistics_from_stat_tables= FALSE;
keyinfo->name= (char *) "$hj";
keyinfo->rec_per_key= (ulong*) thd->calloc(sizeof(ulong)*key_parts);
if (!keyinfo->rec_per_key)
@@ -7967,7 +8686,7 @@ static bool create_hj_key_for_table(JOIN *join, JOIN_TAB *join_tab,
keyuse++;
} while (keyuse->table == table && keyuse->is_for_hash_join());
- keyinfo->ext_key_parts= keyinfo->key_parts;
+ keyinfo->ext_key_parts= keyinfo->user_defined_key_parts;
keyinfo->ext_key_flags= keyinfo->flags;
keyinfo->ext_key_part_map= 0;
@@ -7991,7 +8710,7 @@ static bool are_tables_local(JOIN_TAB *jtab, table_map used_tables)
table_map local_tables= jtab->emb_sj_nest->nested_join->used_tables |
jtab->join->const_table_map |
OUTER_REF_TABLE_BIT;
- return !test(used_tables & ~local_tables);
+ return !MY_TEST(used_tables & ~local_tables);
}
/*
@@ -8096,6 +8815,7 @@ static bool create_ref_for_key(JOIN *join, JOIN_TAB *j,
j->ref.null_rejecting= 0;
j->ref.disable_cache= FALSE;
j->ref.null_ref_part= NO_REF_PART;
+ j->ref.const_ref_part_map= 0;
keyuse=org_keyuse;
store_key **ref_key= j->ref.key_copy;
@@ -8127,12 +8847,19 @@ static bool create_ref_for_key(JOIN *join, JOIN_TAB *j,
!are_tables_local(j, keyuse->val->used_tables()))
keyuse++; /* Skip other parts */
- uint maybe_null= test(keyinfo->key_part[i].null_bit);
+ uint maybe_null= MY_TEST(keyinfo->key_part[i].null_bit);
j->ref.items[i]=keyuse->val; // Save for cond removal
j->ref.cond_guards[i]= keyuse->cond_guard;
if (keyuse->null_rejecting)
j->ref.null_rejecting|= (key_part_map)1 << i;
keyuse_uses_no_tables= keyuse_uses_no_tables && !keyuse->used_tables;
+ /*
+ Todo: we should remove this check for thd->lex->describe on the next
+ line. With SHOW EXPLAIN code, EXPLAIN printout code no longer depends
+ on it. However, removing the check caused change in lots of query
+ plans! Does the optimizer depend on the contents of
+ table_ref->key_copy ? If yes, do we produce incorrect EXPLAINs?
+ */
if (!keyuse->val->used_tables() && !thd->lex->describe)
{ // Compare against constant
store_key_item tmp(thd,
@@ -8145,6 +8872,7 @@ static bool create_ref_for_key(JOIN *join, JOIN_TAB *j,
if (thd->is_fatal_error)
DBUG_RETURN(TRUE);
tmp.copy();
+ j->ref.const_ref_part_map |= key_part_map(1) << i ;
}
else
*ref_key++= get_store_key(thd,
@@ -8170,10 +8898,10 @@ static bool create_ref_for_key(JOIN *join, JOIN_TAB *j,
ulong key_flags= j->table->actual_key_flags(keyinfo);
if (j->type == JT_CONST)
j->table->const_table= 1;
- else if (!((keyparts == keyinfo->key_parts &&
+ else if (!((keyparts == keyinfo->user_defined_key_parts &&
((key_flags & (HA_NOSAME | HA_NULL_PART_KEY)) == HA_NOSAME)) ||
- (keyparts > keyinfo->key_parts && // true only for extended keys
- test(key_flags & HA_EXT_NOSAME) &&
+ (keyparts > keyinfo->user_defined_key_parts && // true only for extended keys
+ MY_TEST(key_flags & HA_EXT_NOSAME) &&
keyparts == keyinfo->ext_key_parts)) ||
null_ref_key)
{
@@ -8262,6 +8990,7 @@ JOIN::make_simple_join(JOIN *parent, TABLE *temp_table)
!(parent->join_tab_reexec= (JOIN_TAB*) thd->alloc(sizeof(JOIN_TAB))))
DBUG_RETURN(TRUE); /* purecov: inspected */
+ // psergey-todo: here, save the pointer for original join_tabs.
join_tab= parent->join_tab_reexec;
table= &parent->table_reexec[0]; parent->table_reexec[0]= temp_table;
table_count= top_join_tab_count= 1;
@@ -8298,7 +9027,7 @@ JOIN::make_simple_join(JOIN *parent, TABLE *temp_table)
functions are handled.
*/
// the temporary table was explicitly requested
- DBUG_ASSERT(test(select_options & OPTION_BUFFER_RESULT));
+ DBUG_ASSERT(MY_TEST(select_options & OPTION_BUFFER_RESULT));
// the temporary table does not have a grouping expression
DBUG_ASSERT(!temp_table->group);
}
@@ -8665,10 +9394,6 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
{ /* there may be a select without a cond. */
if (join->table_count > 1)
cond->update_used_tables(); // Tablenr may have changed
- if (join->const_tables == join->table_count &&
- thd->lex->current_select->master_unit() ==
- &thd->lex->unit) // not upper level SELECT
- join->const_table_map|=RAND_TABLE_BIT;
/*
Extract expressions that depend on constant tables
@@ -8888,11 +9613,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
if (tab->table)
{
tab->table->file->pushed_cond= NULL;
- if (((thd->variables.optimizer_switch &
- OPTIMIZER_SWITCH_ENGINE_CONDITION_PUSHDOWN) ||
- (tab->table->file->ha_table_flags() &
- HA_MUST_USE_TABLE_CONDITION_PUSHDOWN)) &&
- !first_inner_tab)
+ if (thd->use_cond_push(tab->table->file) && !first_inner_tab)
{
COND *push_cond=
make_cond_for_table(thd, tmp, current_map, current_map,
@@ -8955,9 +9676,14 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
Check again if we should use an index.
We could have used an column from a previous table in
the index if we are using limit and this is the first table
+
+ (1) - Don't switch the used index if we are using semi-join
+ LooseScan on this table. Using different index will not
+ produce the desired ordering and de-duplication.
*/
if (!tab->table->is_filled_at_execution() &&
+ !tab->loosescan_match_tab && // (1)
((cond && (!tab->keys.is_subset(tab->const_keys) && i > 0)) ||
(!tab->const_keys.is_clear_all() && i == join->const_tables &&
join->unit->select_limit_cnt <
@@ -9683,7 +10409,7 @@ end_sj_materialize(JOIN *join, JOIN_TAB *join_tab, bool end_of_records)
if (item->is_null())
DBUG_RETURN(NESTED_LOOP_OK);
}
- fill_record(thd, table->field, sjm->sjm_table_cols, TRUE, FALSE);
+ fill_record(thd, table, table->field, sjm->sjm_table_cols, TRUE, FALSE);
if (thd->is_error())
DBUG_RETURN(NESTED_LOOP_ERROR); /* purecov: inspected */
if ((error= table->file->ha_write_tmp_row(table->record[0])))
@@ -9843,7 +10569,7 @@ uint check_join_cache_usage(JOIN_TAB *tab,
uint table_index,
JOIN_TAB *prev_tab)
{
- COST_VECT cost;
+ Cost_estimate cost;
uint flags= 0;
ha_rows rows= 0;
uint bufsz= 4096;
@@ -9969,10 +10695,10 @@ uint check_join_cache_usage(JOIN_TAB *tab,
if (cache_level == 1)
prev_cache= 0;
if ((tab->cache= new JOIN_CACHE_BNL(join, tab, prev_cache)) &&
- ((options & SELECT_DESCRIBE) || !tab->cache->init()))
+ !tab->cache->init(options & SELECT_DESCRIBE))
{
tab->icp_other_tables_ok= FALSE;
- return (2-test(!prev_cache));
+ return (2 - MY_TEST(!prev_cache));
}
goto no_join_cache;
case JT_SYSTEM:
@@ -9983,7 +10709,7 @@ uint check_join_cache_usage(JOIN_TAB *tab,
goto no_join_cache;
if (tab->ref.is_access_triggered())
goto no_join_cache;
-
+
if (!tab->is_ref_for_hash_join() && !no_bka_cache)
{
flags= HA_MRR_NO_NULL_ENDPOINTS | HA_MRR_SINGLE_POINT;
@@ -10004,10 +10730,10 @@ uint check_join_cache_usage(JOIN_TAB *tab,
if (cache_level == 3)
prev_cache= 0;
if ((tab->cache= new JOIN_CACHE_BNLH(join, tab, prev_cache)) &&
- ((options & SELECT_DESCRIBE) || !tab->cache->init()))
+ !tab->cache->init(options & SELECT_DESCRIBE))
{
tab->icp_other_tables_ok= FALSE;
- return (4-test(!prev_cache));
+ return (4 - MY_TEST(!prev_cache));
}
goto no_join_cache;
}
@@ -10025,8 +10751,8 @@ uint check_join_cache_usage(JOIN_TAB *tab,
if (cache_level == 5)
prev_cache= 0;
if ((tab->cache= new JOIN_CACHE_BKA(join, tab, flags, prev_cache)) &&
- ((options & SELECT_DESCRIBE) || !tab->cache->init()))
- return (6-test(!prev_cache));
+ !tab->cache->init(options & SELECT_DESCRIBE))
+ return (6 - MY_TEST(!prev_cache));
goto no_join_cache;
}
else
@@ -10034,10 +10760,10 @@ uint check_join_cache_usage(JOIN_TAB *tab,
if (cache_level == 7)
prev_cache= 0;
if ((tab->cache= new JOIN_CACHE_BKAH(join, tab, flags, prev_cache)) &&
- ((options & SELECT_DESCRIBE) || !tab->cache->init()))
+ !tab->cache->init(options & SELECT_DESCRIBE))
{
tab->idx_cond_fact_out= FALSE;
- return (8-test(!prev_cache));
+ return (8 - MY_TEST(!prev_cache));
}
goto no_join_cache;
}
@@ -10132,7 +10858,7 @@ restart:
no_jbuf_after,
idx,
prev_tab);
- tab->use_join_cache= test(tab->used_join_cache_level);
+ tab->use_join_cache= MY_TEST(tab->used_join_cache_level);
/*
psergey-merge: todo: raise the question that this is really stupid that
we can first allocate a join buffer, then decide not to use it and free
@@ -10152,6 +10878,86 @@ restart:
}
}
+/**
+ Remove pushdown conditions that are already checked by the scan phase
+ of BNL/BNLH joins.
+
+ @note
+ If the single-table condition for this table will be used by a
+ blocked join to pre-filter this table's rows, there is no need
+ to re-check the same single-table condition for each joined record.
+
+ This method removes from JOIN_TAB::select_cond and JOIN_TAB::select::cond
+ all top-level conjuncts that also appear in in JOIN_TAB::cache_select::cond.
+*/
+
+void JOIN_TAB::remove_redundant_bnl_scan_conds()
+{
+ if (!(select_cond && cache_select && cache &&
+ (cache->get_join_alg() == JOIN_CACHE::BNL_JOIN_ALG ||
+ cache->get_join_alg() == JOIN_CACHE::BNLH_JOIN_ALG)))
+ return;
+
+ /*
+ select->cond is not processed separately. This method assumes it is always
+ the same as select_cond.
+ */
+ DBUG_ASSERT(!select || !select->cond ||
+ (select->cond == select_cond));
+
+ if (is_cond_and(select_cond))
+ {
+ List_iterator<Item> pushed_cond_li(*((Item_cond*) select_cond)->argument_list());
+ Item *pushed_item;
+ Item_cond_and *reduced_select_cond= new Item_cond_and;
+
+ if (is_cond_and(cache_select->cond))
+ {
+ List_iterator<Item> scan_cond_li(*((Item_cond*) cache_select->cond)->argument_list());
+ Item *scan_item;
+ while ((pushed_item= pushed_cond_li++))
+ {
+ bool found= false;
+ scan_cond_li.rewind();
+ while ((scan_item= scan_cond_li++))
+ {
+ if (pushed_item->eq(scan_item, 0))
+ {
+ found= true;
+ break;
+ }
+ }
+ if (!found)
+ reduced_select_cond->add(pushed_item);
+ }
+ }
+ else
+ {
+ while ((pushed_item= pushed_cond_li++))
+ {
+ if (!pushed_item->eq(cache_select->cond, 0))
+ reduced_select_cond->add(pushed_item);
+ }
+ }
+
+ /*
+ JOIN_CACHE::check_match uses JOIN_TAB::select->cond instead of
+ JOIN_TAB::select_cond. set_cond() sets both pointers.
+ */
+ if (reduced_select_cond->argument_list()->is_empty())
+ set_cond(NULL);
+ else if (reduced_select_cond->argument_list()->elements == 1)
+ set_cond(reduced_select_cond->argument_list()->head());
+ else
+ {
+ reduced_select_cond->quick_fix_field();
+ set_cond(reduced_select_cond);
+ }
+ }
+ else if (select_cond->eq(cache_select->cond, 0))
+ set_cond(NULL);
+}
+
/*
Plan refinement stage: do various setup things for the executor
@@ -10182,7 +10988,7 @@ make_join_readinfo(JOIN *join, ulonglong options, uint no_jbuf_after)
uint i;
DBUG_ENTER("make_join_readinfo");
- bool statistics= test(!(join->select_options & SELECT_DESCRIBE));
+ bool statistics= MY_TEST(!(join->select_options & SELECT_DESCRIBE));
bool sorted= 1;
join->complex_firstmatch_tables= table_map(0);
@@ -10315,10 +11121,10 @@ make_join_readinfo(JOIN *join, ulonglong options, uint no_jbuf_after)
/* These init changes read_record */
if (tab->use_quick == 2)
{
- join->thd->server_status|=SERVER_QUERY_NO_GOOD_INDEX_USED;
+ join->thd->set_status_no_good_index_used();
tab->read_first_record= join_init_quick_read_record;
if (statistics)
- status_var_increment(join->thd->status_var.select_range_check_count);
+ join->thd->inc_status_select_range_check();
}
else
{
@@ -10329,14 +11135,14 @@ make_join_readinfo(JOIN *join, ulonglong options, uint no_jbuf_after)
if (tab->select && tab->select->quick)
{
if (statistics)
- status_var_increment(join->thd->status_var.select_range_count);
+ join->thd->inc_status_select_range();
}
else
{
- join->thd->server_status|=SERVER_QUERY_NO_INDEX_USED;
+ join->thd->set_status_no_index_used();
if (statistics)
{
- status_var_increment(join->thd->status_var.select_scan_count);
+ join->thd->inc_status_select_scan();
join->thd->query_plan_flags|= QPLAN_FULL_SCAN;
}
}
@@ -10346,16 +11152,14 @@ make_join_readinfo(JOIN *join, ulonglong options, uint no_jbuf_after)
if (tab->select && tab->select->quick)
{
if (statistics)
- status_var_increment(join->thd->status_var.
- select_full_range_join_count);
+ join->thd->inc_status_select_full_range_join();
}
else
{
- join->thd->server_status|=SERVER_QUERY_NO_INDEX_USED;
+ join->thd->set_status_no_index_used();
if (statistics)
{
- status_var_increment(join->thd->status_var.
- select_full_join_count);
+ join->thd->inc_status_select_full_join();
join->thd->query_plan_flags|= QPLAN_FULL_JOIN;
}
}
@@ -10409,6 +11213,15 @@ make_join_readinfo(JOIN *join, ulonglong options, uint no_jbuf_after)
abort();
/* purecov: end */
}
+
+ tab->remove_redundant_bnl_scan_conds();
+ DBUG_EXECUTE("where",
+ char buff[256];
+ String str(buff,sizeof(buff),system_charset_info);
+ str.length(0);
+ str.append(tab->table? tab->table->alias.c_ptr() :"<no_table_name>");
+ str.append(" final_pushdown_cond");
+ print_where(tab->select_cond, str.c_ptr_safe(), QT_ORDINARY););
}
uint n_top_tables= join->join_tab_ranges.head()->end -
join->join_tab_ranges.head()->start;
@@ -10582,7 +11395,7 @@ double JOIN_TAB::scan_time()
}
else
{
- found_records= records= table->file->stats.records;
+ found_records= records= table->stat_records();
read_time= table->file->scan_time();
/*
table->quick_condition_rows has already been set to
@@ -10593,7 +11406,7 @@ double JOIN_TAB::scan_time()
}
else
{
- found_records= records=table->file->stats.records;
+ found_records= records=table->stat_records();
read_time= found_records ? (double)found_records: 10.0;// TODO:fix this stub
res= read_time;
}
@@ -10609,9 +11422,9 @@ double JOIN_TAB::scan_time()
ha_rows JOIN_TAB::get_examined_rows()
{
- ha_rows examined_rows;
+ double examined_rows;
- if (select && select->quick)
+ if (select && select->quick && use_quick != 2)
examined_rows= select->quick->records;
else if (type == JT_NEXT || type == JT_ALL ||
type == JT_HASH || type ==JT_HASH_NEXT)
@@ -10634,14 +11447,14 @@ ha_rows JOIN_TAB::get_examined_rows()
handler->info(HA_STATUS_VARIABLE) has been called in
make_join_statistics()
*/
- examined_rows= table->file->stats.records;
+ examined_rows= table->stat_records();
}
}
}
else
- examined_rows= (ha_rows) records_read;
+ examined_rows= records_read;
- return examined_rows;
+ return (ha_rows) examined_rows;
}
@@ -10665,13 +11478,21 @@ bool JOIN_TAB::preread_init()
mysql_handle_single_derived(join->thd->lex,
derived, DT_CREATE | DT_FILL))
return TRUE;
+
preread_init_done= TRUE;
if (select && select->quick)
select->quick->replace_handler(table->file);
+ DBUG_EXECUTE_IF("show_explain_probe_join_tab_preread",
+ if (dbug_user_var_equals_int(join->thd,
+ "show_explain_probe_select_id",
+ join->select_lex->select_number))
+ dbug_serve_apcs(join->thd, 1);
+ );
+
/* init ftfuns for just initialized derived table */
if (table->fulltext_searched)
- init_ftfuncs(join->thd, join->select_lex, test(join->order));
+ init_ftfuncs(join->thd, join->select_lex, MY_TEST(join->order));
return FALSE;
}
@@ -10699,7 +11520,7 @@ bool TABLE_REF::tmp_table_index_lookup_init(THD *thd,
bool value,
uint skip)
{
- uint tmp_key_parts= tmp_key->key_parts;
+ uint tmp_key_parts= tmp_key->user_defined_key_parts;
uint i;
DBUG_ENTER("TABLE_REF::tmp_table_index_lookup_init");
@@ -10727,12 +11548,12 @@ bool TABLE_REF::tmp_table_index_lookup_init(THD *thd,
Item *item= it.next();
DBUG_ASSERT(item);
items[i]= item;
- int null_count= test(cur_key_part->field->real_maybe_null());
+ int null_count= MY_TEST(cur_key_part->field->real_maybe_null());
*ref_key= new store_key_item(thd, cur_key_part->field,
/* TIMOUR:
the NULL byte is taken into account in
cur_key_part->store_length, so instead of
- cur_ref_buff + test(maybe_null), we could
+ cur_ref_buff + MY_TEST(maybe_null), we could
use that information instead.
*/
cur_ref_buff + null_count,
@@ -10796,7 +11617,7 @@ bool TABLE_REF::is_access_triggered()
a correlated subquery itself, but has subqueries, we can free it
fully and also free JOINs of all its subqueries. The exception
is a subquery in SELECT list, e.g: @n
- SELECT a, (select max(b) from t1) group by c @n
+ SELECT a, (select MY_MAX(b) from t1) group by c @n
This subquery will not be evaluated at first sweep and its value will
not be inserted into the temporary table. Instead, it's evaluated
when selecting from the temporary table. Therefore, it can't be freed
@@ -10879,6 +11700,9 @@ void JOIN::cleanup(bool full)
{
DBUG_ENTER("JOIN::cleanup");
DBUG_PRINT("enter", ("full %u", (uint) full));
+
+ if (full)
+ have_query_plan= QEP_DELETED;
if (table)
{
@@ -11387,7 +12211,8 @@ return_zero_rows(JOIN *join, select_result *result, List<TABLE_LIST> &tables,
result->send_eof(); // Should be safe
}
/* Update results for FOUND_ROWS */
- join->thd->limit_found_rows= join->thd->examined_row_count= 0;
+ join->thd->limit_found_rows= 0;
+ join->thd->set_examined_row_count(0);
DBUG_RETURN(0);
}
@@ -11427,18 +12252,10 @@ public:
{ TRASH(ptr, size); }
Item *and_level;
- Item_func *cmp_func;
- COND_CMP(Item *a,Item_func *b) :and_level(a),cmp_func(b) {}
+ Item_bool_func2 *cmp_func;
+ COND_CMP(Item *a,Item_bool_func2 *b) :and_level(a),cmp_func(b) {}
};
-#ifdef HAVE_EXPLICIT_TEMPLATE_INSTANTIATION
-template class I_List<COND_CMP>;
-template class I_List_iterator<COND_CMP>;
-template class List<Item_func_match>;
-template class List_iterator<Item_func_match>;
-#endif
-
-
/**
Find the multiple equality predicate containing a field.
@@ -11697,7 +12514,7 @@ static bool check_simple_equality(Item *left_item, Item *right_item,
if (field_item->cmp_type() == STRING_RESULT)
{
- CHARSET_INFO *cs= ((Field_str*) field_item->field)->charset();
+ CHARSET_INFO *cs= field_item->field->charset();
if (!item)
{
Item_func_eq *eq_item;
@@ -11923,7 +12740,8 @@ static bool check_equality(THD *thd, Item *item, COND_EQUAL *cond_equal,
*/
static COND *build_equal_items_for_cond(THD *thd, COND *cond,
- COND_EQUAL *inherited)
+ COND_EQUAL *inherited,
+ bool link_item_fields)
{
Item_equal *item_equal;
COND_EQUAL cond_equal;
@@ -11970,6 +12788,7 @@ static COND *build_equal_items_for_cond(THD *thd, COND *cond,
List_iterator_fast<Item_equal> it(cond_equal.current_level);
while ((item_equal= it++))
{
+ item_equal->set_link_equal_fields(link_item_fields);
item_equal->fix_fields(thd, NULL);
item_equal->update_used_tables();
set_if_bigger(thd->lex->current_select->max_equal_elems,
@@ -11989,7 +12808,8 @@ static COND *build_equal_items_for_cond(THD *thd, COND *cond,
while ((item= li++))
{
Item *new_item;
- if ((new_item= build_equal_items_for_cond(thd, item, inherited)) != item)
+ if ((new_item= build_equal_items_for_cond(thd, item, inherited, FALSE))
+ != item)
{
/* This replacement happens only for standalone equalities */
/*
@@ -12132,9 +12952,9 @@ static COND *build_equal_items_for_cond(THD *thd, COND *cond,
@endcode
Thus, applying equalities from the where condition we basically
can get more freedom in performing join operations.
- Althogh we don't use this property now, it probably makes sense to use
+ Although we don't use this property now, it probably makes sense to use
it in the future.
- @param thd Thread handler
+ @param thd Thread handler
@param cond condition to build the multiple equalities for
@param inherited path to all inherited multiple equality items
@param join_list list of join tables to which the condition
@@ -12143,6 +12963,7 @@ static COND *build_equal_items_for_cond(THD *thd, COND *cond,
for on expressions
@param[out] cond_equal_ref pointer to the structure to place built
equalities in
+ @param link_equal_items equal fields are to be linked
@return
pointer to the transformed condition containing multiple equalities
@@ -12152,14 +12973,15 @@ static COND *build_equal_items(JOIN *join, COND *cond,
COND_EQUAL *inherited,
List<TABLE_LIST> *join_list,
bool ignore_on_conds,
- COND_EQUAL **cond_equal_ref)
+ COND_EQUAL **cond_equal_ref,
+ bool link_equal_fields)
{
THD *thd= join->thd;
COND_EQUAL *cond_equal= 0;
if (cond)
{
- cond= build_equal_items_for_cond(thd, cond, inherited);
+ cond= build_equal_items_for_cond(thd, cond, inherited, link_equal_fields);
cond->update_used_tables();
if (cond->type() == Item::COND_ITEM &&
((Item_cond*) cond)->functype() == Item_func::COND_AND_FUNC)
@@ -12290,7 +13112,7 @@ static int compare_fields_by_table_order(Item *field1,
if (!cmp)
{
KEY *key_info= tab->table->key_info + keyno;
- for (uint i= 0; i < key_info->key_parts; i++)
+ for (uint i= 0; i < key_info->user_defined_key_parts; i++)
{
Field *fld= key_info->key_part[i].field;
if (fld->eq(f2->field))
@@ -12473,7 +13295,7 @@ Item *eliminate_item_equal(COND *cond, COND_EQUAL *upper_levels,
item= NULL; /* Don't produce equality */
}
- bool produce_equality= test(item == field_item);
+ bool produce_equality= MY_TEST(item == field_item);
if (!item_const && field_sjm && field_sjm != current_sjm)
{
/* Entering an SJM nest */
@@ -12820,6 +13642,75 @@ static void update_const_equal_items(COND *cond, JOIN_TAB *tab, bool const_key)
}
+/**
+ Check if
+ WHERE expr=value AND expr=const
+ can be rewritten as:
+ WHERE const=value AND expr=const
+
+ @param target - the target operator whose "expr" argument will be
+ replaced to "const".
+ @param target_expr - the target's "expr" which will be replaced to "const".
+ @param target_value - the target's second argument, it will remain unchanged.
+ @param source - the equality expression ("=" or "<=>") that
+ can be used to rewrite the "target" part
+ (under certain conditions, see the code).
+ @param source_expr - the source's "expr". It should be exactly equal to
+ the target's "expr" to make condition rewrite possible.
+ @param source_const - the source's "const" argument, it will be inserted
+ into "target" instead of "expr".
+*/
+static bool
+can_change_cond_ref_to_const(Item_bool_func2 *target,
+ Item *target_expr, Item *target_value,
+ Item_bool_func2 *source,
+ Item *source_expr, Item *source_const)
+{
+ if (!target_expr->eq(source_expr,0) ||
+ target_value == source_const ||
+ target_expr->cmp_context != source_expr->cmp_context)
+ return false;
+ if (target_expr->cmp_context == STRING_RESULT)
+ {
+ /*
+ In this example:
+ SET NAMES utf8 COLLATE utf8_german2_ci;
+ DROP TABLE IF EXISTS t1;
+ CREATE TABLE t1 (a CHAR(10) CHARACTER SET utf8);
+ INSERT INTO t1 VALUES ('o-umlaut'),('oe');
+ SELECT * FROM t1 WHERE a='oe' COLLATE utf8_german2_ci AND a='oe';
+
+ the query should return only the row with 'oe'.
+ It should not return 'o-umlaut', because 'o-umlaut' does not match
+ the right part of the condition: a='oe'
+ ('o-umlaut' is not equal to 'oe' in utf8_general_ci,
+ which is the collation of the field "a").
+
+ If we change the right part from:
+ ... AND a='oe'
+ to
+ ... AND 'oe' COLLATE utf8_german2_ci='oe'
+ it will be evalulated to TRUE and removed from the condition,
+ so the overall query will be simplified to:
+
+ SELECT * FROM t1 WHERE a='oe' COLLATE utf8_german2_ci;
+
+ which will erroneously start to return both 'oe' and 'o-umlaut'.
+ So changing "expr" to "const" is not possible if the effective
+ collations of "target" and "source" are not exactly the same.
+
+ Note, the code before the fix for MDEV-7152 only checked that
+ collations of "source_const" and "target_value" are the same.
+ This was not enough, as the bug report demonstrated.
+ */
+ return
+ target->compare_collation() == source->compare_collation() &&
+ target_value->collation.collation == source_const->collation.collation;
+ }
+ return true; // Non-string comparison
+}
+
+
/*
change field = field to field = const for each found field = const in the
and_level
@@ -12828,6 +13719,7 @@ static void update_const_equal_items(COND *cond, JOIN_TAB *tab, bool const_key)
static void
change_cond_ref_to_const(THD *thd, I_List<COND_CMP> *save_list,
Item *and_father, Item *cond,
+ Item_bool_func2 *field_value_owner,
Item *field, Item *value)
{
if (cond->type() == Item::COND_ITEM)
@@ -12838,7 +13730,7 @@ change_cond_ref_to_const(THD *thd, I_List<COND_CMP> *save_list,
Item *item;
while ((item=li++))
change_cond_ref_to_const(thd, save_list,and_level ? cond : item, item,
- field, value);
+ field_value_owner, field, value);
return;
}
if (cond->eq_cmp_result() == Item::COND_OK)
@@ -12850,11 +13742,8 @@ change_cond_ref_to_const(THD *thd, I_List<COND_CMP> *save_list,
Item *right_item= args[1];
Item_func::Functype functype= func->functype();
- if (right_item->eq(field,0) && left_item != value &&
- right_item->cmp_context == field->cmp_context &&
- (left_item->result_type() != STRING_RESULT ||
- value->result_type() != STRING_RESULT ||
- left_item->collation.collation == value->collation.collation))
+ if (can_change_cond_ref_to_const(func, right_item, left_item,
+ field_value_owner, field, value))
{
Item *tmp=value->clone_item();
if (tmp)
@@ -12873,11 +13762,8 @@ change_cond_ref_to_const(THD *thd, I_List<COND_CMP> *save_list,
func->set_cmp_func();
}
}
- else if (left_item->eq(field,0) && right_item != value &&
- left_item->cmp_context == field->cmp_context &&
- (right_item->result_type() != STRING_RESULT ||
- value->result_type() != STRING_RESULT ||
- right_item->collation.collation == value->collation.collation))
+ else if (can_change_cond_ref_to_const(func, left_item, right_item,
+ field_value_owner, field, value))
{
Item *tmp= value->clone_item();
if (tmp)
@@ -12926,7 +13812,8 @@ propagate_cond_constants(THD *thd, I_List<COND_CMP> *save_list,
Item **args= cond_cmp->cmp_func->arguments();
if (!args[0]->const_item())
change_cond_ref_to_const(thd, &save,cond_cmp->and_level,
- cond_cmp->and_level, args[0], args[1]);
+ cond_cmp->and_level,
+ cond_cmp->cmp_func, args[0], args[1]);
}
}
}
@@ -12948,14 +13835,14 @@ propagate_cond_constants(THD *thd, I_List<COND_CMP> *save_list,
resolve_const_item(thd, &args[1], args[0]);
func->update_used_tables();
change_cond_ref_to_const(thd, save_list, and_father, and_father,
- args[0], args[1]);
+ func, args[0], args[1]);
}
else if (left_const)
{
resolve_const_item(thd, &args[0], args[1]);
func->update_used_tables();
change_cond_ref_to_const(thd, save_list, and_father, and_father,
- args[1], args[0]);
+ func, args[1], args[0]);
}
}
}
@@ -13091,7 +13978,7 @@ simplify_joins(JOIN *join, List<TABLE_LIST> *join_list, COND *conds, bool top,
NESTED_JOIN *nested_join;
TABLE_LIST *prev_table= 0;
List_iterator<TABLE_LIST> li(*join_list);
- bool straight_join= test(join->select_options & SELECT_STRAIGHT_JOIN);
+ bool straight_join= MY_TEST(join->select_options & SELECT_STRAIGHT_JOIN);
DBUG_ENTER("simplify_joins");
/*
@@ -13686,9 +14573,10 @@ void optimize_wo_join_buffering(JOIN *join, uint first_tab, uint last_tab,
static COND *
-optimize_cond(JOIN *join, COND *conds,
+optimize_cond(JOIN *join, COND *conds,
List<TABLE_LIST> *join_list, bool ignore_on_conds,
- Item::cond_result *cond_value, COND_EQUAL **cond_equal)
+ Item::cond_result *cond_value, COND_EQUAL **cond_equal,
+ int flags)
{
THD *thd= join->thd;
DBUG_ENTER("optimize_cond");
@@ -13711,9 +14599,10 @@ optimize_cond(JOIN *join, COND *conds,
multiple equality contains a constant.
*/
DBUG_EXECUTE("where", print_where(conds, "original", QT_ORDINARY););
- conds= build_equal_items(join, conds, NULL, join_list, ignore_on_conds,
- cond_equal);
- DBUG_EXECUTE("where",print_where(conds,"after equal_items", QT_ORDINARY););
+ conds= build_equal_items(join, conds, NULL, join_list,
+ ignore_on_conds, cond_equal,
+ MY_TEST(flags & OPT_LINK_EQUAL_FIELDS));
+ 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);
@@ -14342,7 +15231,7 @@ remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value)
static bool
test_if_equality_guarantees_uniqueness(Item *l, Item *r)
{
- return r->const_item() &&
+ return (r->const_item() || !(r->used_tables() & ~OUTER_REF_TABLE_BIT)) &&
item_cmp_type(l->cmp_type(), r->cmp_type()) == l->cmp_type() &&
(l->cmp_type() != STRING_RESULT ||
l->collation.collation == r->collation.collation);
@@ -14508,6 +15397,10 @@ Field *create_tmp_field_from_field(THD *thd, Field *org_field,
((Field_double *) new_field)->not_fixed= TRUE;
new_field->vcol_info= 0;
new_field->stored_in_db= TRUE;
+ new_field->cond_selectivity= 1.0;
+ new_field->next_equal_field= NULL;
+ new_field->option_list= NULL;
+ new_field->option_struct= NULL;
}
return new_field;
}
@@ -14809,6 +15702,7 @@ Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type,
case Item::REAL_ITEM:
case Item::DECIMAL_ITEM:
case Item::STRING_ITEM:
+ case Item::DATE_ITEM:
case Item::REF_ITEM:
case Item::NULL_ITEM:
case Item::VARBIN_ITEM:
@@ -14842,17 +15736,20 @@ Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type,
void setup_tmp_table_column_bitmaps(TABLE *table, uchar *bitmaps)
{
uint field_count= table->s->fields;
- bitmap_init(&table->def_read_set, (my_bitmap_map*) bitmaps, field_count,
+ my_bitmap_init(&table->def_read_set, (my_bitmap_map*) bitmaps, field_count,
FALSE);
- bitmap_init(&table->def_vcol_set,
+ my_bitmap_init(&table->def_vcol_set,
(my_bitmap_map*) (bitmaps+ bitmap_buffer_size(field_count)),
field_count, FALSE);
- bitmap_init(&table->tmp_set,
+ my_bitmap_init(&table->tmp_set,
(my_bitmap_map*) (bitmaps+ 2*bitmap_buffer_size(field_count)),
field_count, FALSE);
- bitmap_init(&table->eq_join_set,
+ my_bitmap_init(&table->eq_join_set,
(my_bitmap_map*) (bitmaps+ 3*bitmap_buffer_size(field_count)),
field_count, FALSE);
+ my_bitmap_init(&table->cond_set,
+ (my_bitmap_map*) (bitmaps+ 4*bitmap_buffer_size(field_count)),
+ field_count, FALSE);
/* write_set and all_set are copies of read_set */
table->def_write_set= table->def_read_set;
table->s->all_set= table->def_read_set;
@@ -14916,7 +15813,7 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields,
KEY *keyinfo;
KEY_PART_INFO *key_part_info;
Item **copy_func;
- ENGINE_COLUMNDEF *recinfo;
+ TMP_ENGINE_COLUMNDEF *recinfo;
/*
total_uneven_bit_length is uneven bit length for visible fields
hidden_uneven_bit_length is uneven bit length for hidden fields
@@ -14930,9 +15827,8 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields,
("table_alias: '%s' distinct: %d save_sum_fields: %d "
"rows_limit: %lu group: %d", table_alias,
(int) distinct, (int) save_sum_fields,
- (ulong) rows_limit,test(group)));
+ (ulong) rows_limit, MY_TEST(group)));
- status_var_increment(thd->status_var.created_tmp_tables);
thd->query_plan_flags|= QPLAN_TMP_TABLE;
if (use_temp_pool && !(test_flags & TEST_KEEP_TMP_TABLES))
@@ -15000,7 +15896,7 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields,
if (param->precomputed_group_by)
copy_func_count+= param->sum_func_count;
- init_sql_alloc(&own_root, TABLE_ALLOC_BLOCK_SIZE, 0);
+ init_sql_alloc(&own_root, TABLE_ALLOC_BLOCK_SIZE, 0, MYF(MY_THREAD_SPECIFIC));
if (!multi_alloc_root(&own_root,
&table, sizeof(*table),
@@ -15018,7 +15914,7 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields,
&tmpname, (uint) strlen(path)+1,
&group_buff, (group && ! using_unique_constraint ?
param->group_length : 0),
- &bitmaps, bitmap_buffer_size(field_count)*4,
+ &bitmaps, bitmap_buffer_size(field_count)*5,
NullS))
{
if (temp_pool_slot != MY_BIT_NONE)
@@ -15064,7 +15960,6 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields,
table->s= share;
init_tmp_table_share(thd, share, "", 0, tmpname, tmpname);
share->blob_field= blob_field;
- share->blob_ptr_size= portable_sizeof_char_ptr;
share->table_charset= param->table_charset;
share->primary_key= MAX_KEY; // Indicate no primary key
share->keys_for_keyread.init();
@@ -15307,6 +16202,12 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields,
if (!table->file)
goto err;
+ if (table->file->set_ha_share_ref(&share->ha_share))
+ {
+ delete table->file;
+ goto err;
+ }
+
if (!using_unique_constraint)
reclength+= group_null_items; // null flag is stored separately
@@ -15453,7 +16354,7 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields,
share->max_rows= ~(ha_rows) 0;
else
share->max_rows= (ha_rows) (((share->db_type() == heap_hton) ?
- min(thd->variables.tmp_table_size,
+ MY_MIN(thd->variables.tmp_table_size,
thd->variables.max_heap_table_size) :
thd->variables.tmp_table_size) /
share->reclength);
@@ -15473,18 +16374,21 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields,
table->group=group; /* Table is grouped by key */
param->group_buff=group_buff;
share->keys=1;
- share->uniques= test(using_unique_constraint);
+ share->uniques= MY_TEST(using_unique_constraint);
table->key_info= table->s->key_info= keyinfo;
table->keys_in_use_for_query.set_bit(0);
share->keys_in_use.set_bit(0);
keyinfo->key_part=key_part_info;
keyinfo->flags=HA_NOSAME | HA_BINARY_PACK_KEY | HA_PACK_KEY;
keyinfo->ext_key_flags= keyinfo->flags;
- keyinfo->usable_key_parts=keyinfo->key_parts= param->group_parts;
- keyinfo->ext_key_parts= keyinfo->key_parts;
+ keyinfo->usable_key_parts=keyinfo->user_defined_key_parts= param->group_parts;
+ keyinfo->ext_key_parts= keyinfo->user_defined_key_parts;
keyinfo->key_length=0;
- keyinfo->rec_per_key=0;
+ keyinfo->rec_per_key=NULL;
+ keyinfo->read_stats= NULL;
+ keyinfo->collected_stats= NULL;
keyinfo->algorithm= HA_KEY_ALG_UNDEF;
+ keyinfo->is_statistics_from_stat_tables= FALSE;
keyinfo->name= (char*) "group_key";
ORDER *cur_group= group;
for (; cur_group ; cur_group= cur_group->next, key_part_info++)
@@ -15524,7 +16428,8 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields,
if (!(cur_group->field= field->new_key_field(thd->mem_root,table,
group_buff +
- test(maybe_null),
+ MY_TEST(maybe_null),
+ key_part_info->length,
field->null_ptr,
field->null_bit)))
goto err; /* purecov: inspected */
@@ -15578,16 +16483,17 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields,
share->uniques= 1;
}
null_pack_length-=hidden_null_pack_length;
- keyinfo->key_parts= ((field_count-param->hidden_field_count)+
- (share->uniques ? test(null_pack_length) : 0));
- keyinfo->ext_key_parts= keyinfo->key_parts;
+ keyinfo->user_defined_key_parts=
+ ((field_count-param->hidden_field_count)+
+ (share->uniques ? MY_TEST(null_pack_length) : 0));
+ keyinfo->ext_key_parts= keyinfo->user_defined_key_parts;
table->distinct= 1;
share->keys= 1;
if (!(key_part_info= (KEY_PART_INFO*)
alloc_root(&table->mem_root,
- keyinfo->key_parts * sizeof(KEY_PART_INFO))))
+ keyinfo->user_defined_key_parts * sizeof(KEY_PART_INFO))))
goto err;
- bzero((void*) key_part_info, keyinfo->key_parts * sizeof(KEY_PART_INFO));
+ bzero((void*) key_part_info, keyinfo->user_defined_key_parts * sizeof(KEY_PART_INFO));
table->keys_in_use_for_query.set_bit(0);
share->keys_in_use.set_bit(0);
table->key_info= table->s->key_info= keyinfo;
@@ -15597,6 +16503,10 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields,
keyinfo->key_length= 0; // Will compute the sum of the parts below.
keyinfo->name= (char*) "distinct_key";
keyinfo->algorithm= HA_KEY_ALG_UNDEF;
+ keyinfo->is_statistics_from_stat_tables= FALSE;
+ keyinfo->read_stats= NULL;
+ keyinfo->collected_stats= NULL;
+
/*
Needed by non-merged semi-joins: SJ-Materialized table must have a valid
rec_per_key array, because it participates in join optimization. Since
@@ -15606,7 +16516,7 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields,
(For table record count, we calculate and set JOIN_TAB::found_records,
see get_delayed_table_estimates()).
*/
- size_t rpk_size= keyinfo->key_parts* sizeof(keyinfo->rec_per_key[0]);
+ size_t rpk_size= keyinfo->user_defined_key_parts * sizeof(keyinfo->rec_per_key[0]);
if (!(keyinfo->rec_per_key= (ulong*) alloc_root(&table->mem_root,
rpk_size)))
goto err;
@@ -15767,7 +16677,7 @@ TABLE *create_virtual_tmp_table(THD *thd, List<Create_field> &field_list)
&share, sizeof(*share),
&field, (field_count + 1) * sizeof(Field*),
&blob_field, (field_count+1) *sizeof(uint),
- &bitmaps, bitmap_buffer_size(field_count)*4,
+ &bitmaps, bitmap_buffer_size(field_count)*5,
NullS))
return 0;
@@ -15778,7 +16688,6 @@ TABLE *create_virtual_tmp_table(THD *thd, List<Create_field> &field_list)
table->temp_pool_slot= MY_BIT_NONE;
share->blob_field= blob_field;
share->fields= field_count;
- share->blob_ptr_size= portable_sizeof_char_ptr;
setup_tmp_table_column_bitmaps(table, bitmaps);
/* Create all fields and calculate the total length of record */
@@ -15875,19 +16784,23 @@ bool open_tmp_table(TABLE *table)
HA_OPEN_TMP_TABLE |
HA_OPEN_INTERNAL_TABLE)))
{
- table->file->print_error(error,MYF(0)); /* purecov: inspected */
- table->db_stat=0;
- return(1);
+ table->file->print_error(error, MYF(0)); /* purecov: inspected */
+ table->db_stat= 0;
+ return 1;
}
table->db_stat= HA_OPEN_KEYFILE+HA_OPEN_RNDFILE;
- (void) table->file->extra(HA_EXTRA_QUICK); /* Faster */
- table->created= TRUE;
- return(0);
-}
+ (void) table->file->extra(HA_EXTRA_QUICK); /* Faster */
+ if (!table->created)
+ {
+ table->created= TRUE;
+ table->in_use->inc_status_created_tmp_tables();
+ }
+ return 0;
+}
-#if defined(WITH_ARIA_STORAGE_ENGINE) && defined(USE_ARIA_FOR_TMP_TABLES)
+#ifdef USE_ARIA_FOR_TMP_TABLES
/*
Create internal (MyISAM or Maria) temporary table
@@ -15903,12 +16816,12 @@ bool open_tmp_table(TABLE *table)
Create an internal emporary table according to passed description. The is
assumed to have one unique index or constraint.
- The passed array or ENGINE_COLUMNDEF structures must have this form:
+ The passed array or TMP_ENGINE_COLUMNDEF structures must have this form:
1. 1-byte column (afaiu for 'deleted' flag) (note maybe not 1-byte
when there are many nullable columns)
2. Table columns
- 3. One free ENGINE_COLUMNDEF element (*recinfo points here)
+ 3. One free TMP_ENGINE_COLUMNDEF element (*recinfo points here)
This function may use the free element to create hash column for unique
constraint.
@@ -15920,8 +16833,8 @@ bool open_tmp_table(TABLE *table)
bool create_internal_tmp_table(TABLE *table, KEY *keyinfo,
- ENGINE_COLUMNDEF *start_recinfo,
- ENGINE_COLUMNDEF **recinfo,
+ TMP_ENGINE_COLUMNDEF *start_recinfo,
+ TMP_ENGINE_COLUMNDEF **recinfo,
ulonglong options)
{
int error;
@@ -15935,11 +16848,11 @@ bool create_internal_tmp_table(TABLE *table, KEY *keyinfo,
{ // Get keys for ni_create
bool using_unique_constraint=0;
HA_KEYSEG *seg= (HA_KEYSEG*) alloc_root(&table->mem_root,
- sizeof(*seg) * keyinfo->key_parts);
+ sizeof(*seg) * keyinfo->user_defined_key_parts);
if (!seg)
goto err;
- bzero(seg, sizeof(*seg) * keyinfo->key_parts);
+ bzero(seg, sizeof(*seg) * keyinfo->user_defined_key_parts);
/*
Note that a similar check is performed during
subquery_types_allow_materialization. See MDEV-7122 for more details as
@@ -15947,7 +16860,7 @@ bool create_internal_tmp_table(TABLE *table, KEY *keyinfo,
all tmp_table engines.
*/
if (keyinfo->key_length > table->file->max_key_length() ||
- keyinfo->key_parts > table->file->max_key_parts() ||
+ keyinfo->user_defined_key_parts > table->file->max_key_parts() ||
share->uniques)
{
if (!share->uniques && !(keyinfo->flags & HA_NOSAME))
@@ -15962,7 +16875,7 @@ bool create_internal_tmp_table(TABLE *table, KEY *keyinfo,
share->uniques= 1;
using_unique_constraint=1;
bzero((char*) &uniquedef,sizeof(uniquedef));
- uniquedef.keysegs=keyinfo->key_parts;
+ uniquedef.keysegs=keyinfo->user_defined_key_parts;
uniquedef.seg=seg;
uniquedef.null_are_equal=1;
@@ -15978,10 +16891,10 @@ bool create_internal_tmp_table(TABLE *table, KEY *keyinfo,
/* Create a key */
bzero((char*) &keydef,sizeof(keydef));
keydef.flag= keyinfo->flags & HA_NOSAME;
- keydef.keysegs= keyinfo->key_parts;
+ keydef.keysegs= keyinfo->user_defined_key_parts;
keydef.seg= seg;
}
- for (uint i=0; i < keyinfo->key_parts ; i++,seg++)
+ for (uint i=0; i < keyinfo->user_defined_key_parts ; i++,seg++)
{
Field *field=keyinfo->key_part[i].field;
seg->flag= 0;
@@ -15993,7 +16906,8 @@ bool create_internal_tmp_table(TABLE *table, KEY *keyinfo,
seg->type=
((keyinfo->key_part[i].key_type & FIELDFLAG_BINARY) ?
HA_KEYTYPE_VARBINARY2 : HA_KEYTYPE_VARTEXT2);
- seg->bit_start= (uint8)(field->pack_length() - share->blob_ptr_size);
+ seg->bit_start= (uint8)(field->pack_length() -
+ portable_sizeof_char_ptr);
seg->flag= HA_BLOB_PART;
seg->length=0; // Whole blob in unique constraint
}
@@ -16048,36 +16962,22 @@ bool create_internal_tmp_table(TABLE *table, KEY *keyinfo,
start_recinfo,
share->uniques, &uniquedef,
&create_info,
- HA_CREATE_TMP_TABLE)))
+ HA_CREATE_TMP_TABLE | HA_CREATE_INTERNAL_TABLE)))
{
table->file->print_error(error,MYF(0)); /* purecov: inspected */
table->db_stat=0;
goto err;
}
- status_var_increment(table->in_use->status_var.created_tmp_disk_tables);
+ table->in_use->inc_status_created_tmp_disk_tables();
+ table->in_use->inc_status_created_tmp_tables();
table->in_use->query_plan_flags|= QPLAN_TMP_DISK;
share->db_record_offset= 1;
+ table->created= TRUE;
DBUG_RETURN(0);
err:
DBUG_RETURN(1);
}
-
-bool create_internal_tmp_table_from_heap(THD *thd, TABLE *table,
- ENGINE_COLUMNDEF *start_recinfo,
- ENGINE_COLUMNDEF **recinfo,
- int error,
- bool ignore_last_dupp_key_error,
- bool *is_duplicate)
-{
- return create_internal_tmp_table_from_heap2(thd, table,
- start_recinfo, recinfo, error,
- ignore_last_dupp_key_error,
- maria_hton,
- "converting HEAP to Aria",
- is_duplicate);
-}
-
#else
/*
@@ -16095,12 +16995,12 @@ bool create_internal_tmp_table_from_heap(THD *thd, TABLE *table,
Create an internal emporary table according to passed description. The is
assumed to have one unique index or constraint.
- The passed array or ENGINE_COLUMNDEF structures must have this form:
+ The passed array or TMP_ENGINE_COLUMNDEF structures must have this form:
1. 1-byte column (afaiu for 'deleted' flag) (note maybe not 1-byte
when there are many nullable columns)
2. Table columns
- 3. One free ENGINE_COLUMNDEF element (*recinfo points here)
+ 3. One free TMP_ENGINE_COLUMNDEF element (*recinfo points here)
This function may use the free element to create hash column for unique
constraint.
@@ -16113,8 +17013,8 @@ bool create_internal_tmp_table_from_heap(THD *thd, TABLE *table,
/* Create internal MyISAM temporary table */
bool create_internal_tmp_table(TABLE *table, KEY *keyinfo,
- ENGINE_COLUMNDEF *start_recinfo,
- ENGINE_COLUMNDEF **recinfo,
+ TMP_ENGINE_COLUMNDEF *start_recinfo,
+ TMP_ENGINE_COLUMNDEF **recinfo,
ulonglong options)
{
int error;
@@ -16127,11 +17027,11 @@ bool create_internal_tmp_table(TABLE *table, KEY *keyinfo,
{ // Get keys for ni_create
bool using_unique_constraint=0;
HA_KEYSEG *seg= (HA_KEYSEG*) alloc_root(&table->mem_root,
- sizeof(*seg) * keyinfo->key_parts);
+ sizeof(*seg) * keyinfo->user_defined_key_parts);
if (!seg)
goto err;
- bzero(seg, sizeof(*seg) * keyinfo->key_parts);
+ bzero(seg, sizeof(*seg) * keyinfo->user_defined_key_parts);
/*
Note that a similar check is performed during
subquery_types_allow_materialization. See MDEV-7122 for more details as
@@ -16139,7 +17039,7 @@ bool create_internal_tmp_table(TABLE *table, KEY *keyinfo,
all tmp_table engines.
*/
if (keyinfo->key_length > table->file->max_key_length() ||
- keyinfo->key_parts > table->file->max_key_parts() ||
+ keyinfo->user_defined_key_parts > table->file->max_key_parts() ||
share->uniques)
{
/* Can't create a key; Make a unique constraint instead of a key */
@@ -16147,7 +17047,7 @@ bool create_internal_tmp_table(TABLE *table, KEY *keyinfo,
share->uniques= 1;
using_unique_constraint=1;
bzero((char*) &uniquedef,sizeof(uniquedef));
- uniquedef.keysegs=keyinfo->key_parts;
+ uniquedef.keysegs=keyinfo->user_defined_key_parts;
uniquedef.seg=seg;
uniquedef.null_are_equal=1;
@@ -16164,10 +17064,10 @@ bool create_internal_tmp_table(TABLE *table, KEY *keyinfo,
bzero((char*) &keydef,sizeof(keydef));
keydef.flag= ((keyinfo->flags & HA_NOSAME) | HA_BINARY_PACK_KEY |
HA_PACK_KEY);
- keydef.keysegs= keyinfo->key_parts;
+ keydef.keysegs= keyinfo->user_defined_key_parts;
keydef.seg= seg;
}
- for (uint i=0; i < keyinfo->key_parts ; i++,seg++)
+ for (uint i=0; i < keyinfo->user_defined_key_parts ; i++,seg++)
{
Field *field=keyinfo->key_part[i].field;
seg->flag= 0;
@@ -16179,7 +17079,7 @@ bool create_internal_tmp_table(TABLE *table, KEY *keyinfo,
seg->type=
((keyinfo->key_part[i].key_type & FIELDFLAG_BINARY) ?
HA_KEYTYPE_VARBINARY2 : HA_KEYTYPE_VARTEXT2);
- seg->bit_start= (uint8)(field->pack_length() - share->blob_ptr_size);
+ seg->bit_start= (uint8)(field->pack_length() - portable_sizeof_char_ptr);
seg->flag= HA_BLOB_PART;
seg->length=0; // Whole blob in unique constraint
}
@@ -16216,7 +17116,7 @@ bool create_internal_tmp_table(TABLE *table, KEY *keyinfo,
start_recinfo,
share->uniques, &uniquedef,
&create_info,
- HA_CREATE_TMP_TABLE |
+ HA_CREATE_TMP_TABLE | HA_CREATE_INTERNAL_TABLE |
((share->db_create_options & HA_OPTION_PACK_RECORD) ?
HA_PACK_RECORD : 0)
)))
@@ -16225,7 +17125,8 @@ bool create_internal_tmp_table(TABLE *table, KEY *keyinfo,
table->db_stat=0;
goto err;
}
- status_var_increment(table->in_use->status_var.created_tmp_disk_tables);
+ table->in_use->inc_status_created_tmp_disk_tables();
+ table->in_use->inc_status_created_tmp_tables();
table->in_use->query_plan_flags|= QPLAN_TMP_DISK;
share->db_record_offset= 1;
table->created= TRUE;
@@ -16234,27 +17135,7 @@ bool create_internal_tmp_table(TABLE *table, KEY *keyinfo,
DBUG_RETURN(1);
}
-
-/**
- If a HEAP table gets full, create a MyISAM table and copy all rows to this
-*/
-
-bool create_internal_tmp_table_from_heap(THD *thd, TABLE *table,
- ENGINE_COLUMNDEF *start_recinfo,
- ENGINE_COLUMNDEF **recinfo,
- int error,
- bool ignore_last_dupp_key_error,
- bool *is_duplicate)
-{
- return create_internal_tmp_table_from_heap2(thd, table,
- start_recinfo, recinfo, error,
- ignore_last_dupp_key_error,
- myisam_hton,
- "converting HEAP to MyISAM",
- is_duplicate);
-}
-
-#endif /* WITH_MARIA_STORAGE_ENGINE */
+#endif /* USE_ARIA_FOR_TMP_TABLES */
/*
@@ -16263,21 +17144,19 @@ bool create_internal_tmp_table_from_heap(THD *thd, TABLE *table,
*/
-static bool
-create_internal_tmp_table_from_heap2(THD *thd, TABLE *table,
- ENGINE_COLUMNDEF *start_recinfo,
- ENGINE_COLUMNDEF **recinfo,
- int error,
- bool ignore_last_dupp_key_error,
- handlerton *hton,
- const char *proc_info,
- bool *is_duplicate)
+bool
+create_internal_tmp_table_from_heap(THD *thd, TABLE *table,
+ TMP_ENGINE_COLUMNDEF *start_recinfo,
+ TMP_ENGINE_COLUMNDEF **recinfo,
+ int error,
+ bool ignore_last_dupp_key_error,
+ bool *is_duplicate)
{
TABLE new_table;
TABLE_SHARE share;
const char *save_proc_info;
int write_err= 0;
- DBUG_ENTER("create_internal_tmp_table_from_heap2");
+ DBUG_ENTER("create_internal_tmp_table_from_heap");
if (is_duplicate)
*is_duplicate= FALSE;
@@ -16294,13 +17173,19 @@ create_internal_tmp_table_from_heap2(THD *thd, TABLE *table,
new_table= *table;
share= *table->s;
new_table.s= &share;
- new_table.s->db_plugin= ha_lock_engine(thd, hton);
+ new_table.s->db_plugin= ha_lock_engine(thd, TMP_ENGINE_HTON);
if (!(new_table.file= get_new_handler(&share, &new_table.mem_root,
new_table.s->db_type())))
DBUG_RETURN(1); // End of memory
+ if (new_table.file->set_ha_share_ref(&share.ha_share))
+ {
+ delete new_table.file;
+ DBUG_RETURN(1);
+ }
+
save_proc_info=thd->proc_info;
- thd_proc_info(thd, proc_info);
+ THD_STAGE_INFO(thd, stage_converting_heap_to_myisam);
new_table.no_rows= table->no_rows;
if (create_internal_tmp_table(&new_table, table->key_info, start_recinfo,
@@ -16336,7 +17221,7 @@ create_internal_tmp_table_from_heap2(THD *thd, TABLE *table,
DBUG_EXECUTE_IF("raise_error", write_err= HA_ERR_FOUND_DUPP_KEY ;);
if (write_err)
goto err;
- if (thd->killed)
+ if (thd->check_killed())
{
thd->send_kill_message();
goto err_killed;
@@ -16373,8 +17258,8 @@ create_internal_tmp_table_from_heap2(THD *thd, TABLE *table,
table->file->change_table_ptr(table, table->s);
table->use_all_columns();
if (save_proc_info)
- thd_proc_info(thd, save_proc_info == copy_to_tmp_table ?
- "Copying to tmp table on disk" : save_proc_info);
+ thd_proc_info(thd, (!strcmp(save_proc_info,"Copying to tmp table") ?
+ "Copying to tmp table on disk" : save_proc_info));
DBUG_RETURN(0);
err:
@@ -16403,7 +17288,7 @@ free_tmp_table(THD *thd, TABLE *entry)
entry->alias.c_ptr()));
save_proc_info=thd->proc_info;
- thd_proc_info(thd, "removing tmp table");
+ THD_STAGE_INFO(thd, stage_removing_tmp_table);
if (entry->file && entry->created)
{
@@ -16603,6 +17488,14 @@ do_select(JOIN *join,List<Item> *fields,TABLE *table,Procedure *procedure)
else
{
DBUG_ASSERT(join->table_count);
+
+ DBUG_EXECUTE_IF("show_explain_probe_do_select",
+ if (dbug_user_var_equals_int(join->thd,
+ "show_explain_probe_select_id",
+ join->select_lex->select_number))
+ dbug_serve_apcs(join->thd, 1);
+ );
+
if (join->outer_ref_cond && !join->outer_ref_cond->val_int())
error= NESTED_LOOP_NO_MORE_ROWS;
else
@@ -16733,7 +17626,7 @@ sub_select_cache(JOIN *join, JOIN_TAB *join_tab, bool end_of_records)
rc= sub_select(join, join_tab, end_of_records);
DBUG_RETURN(rc);
}
- if (join->thd->killed)
+ if (join->thd->check_killed())
{
/* The user has aborted the execution of the query */
join->thd->send_kill_message();
@@ -16930,7 +17823,7 @@ sub_select(JOIN *join,JOIN_TAB *join_tab,bool end_of_records)
if (join_tab->on_precond && !join_tab->on_precond->val_int())
rc= NESTED_LOOP_NO_MORE_ROWS;
}
- join->thd->warning_info->reset_current_row_for_warning();
+ join->thd->get_stmt_da()->reset_current_row_for_warning();
if (rc != NESTED_LOOP_NO_MORE_ROWS &&
(rc= join_tab_execution_startup(join_tab)) < 0)
@@ -17023,12 +17916,14 @@ evaluate_join_record(JOIN *join, JOIN_TAB *join_tab,
DBUG_ENTER("evaluate_join_record");
DBUG_PRINT("enter",
("evaluate_join_record join: %p join_tab: %p"
- " cond: %p error: %d", join, join_tab, select_cond, error));
+ " cond: %p error: %d alias %s",
+ join, join_tab, select_cond, error,
+ join_tab->table->alias.ptr()));
if (error > 0 || (join->thd->is_error())) // Fatal error
DBUG_RETURN(NESTED_LOOP_ERROR);
if (error < 0)
DBUG_RETURN(NESTED_LOOP_NO_MORE_ROWS);
- if (join->thd->killed) // Aborted by user
+ if (join->thd->check_killed()) // Aborted by user
{
join->thd->send_kill_message();
DBUG_RETURN(NESTED_LOOP_KILLED); /* purecov: inspected */
@@ -17039,7 +17934,7 @@ evaluate_join_record(JOIN *join, JOIN_TAB *join_tab,
if (select_cond)
{
- select_cond_result= test(select_cond->val_int());
+ select_cond_result= MY_TEST(select_cond->val_int());
/* check for errors evaluating the condition */
if (join->thd->is_error())
@@ -17136,6 +18031,7 @@ evaluate_join_record(JOIN *join, JOIN_TAB *join_tab,
if (join_tab->check_weed_out_table && found)
{
int res= join_tab->check_weed_out_table->sj_weedout_check_row(join->thd);
+ DBUG_PRINT("info", ("weedout_check: %d", res));
if (res == -1)
DBUG_RETURN(NESTED_LOOP_ERROR);
else if (res == 1)
@@ -17156,15 +18052,15 @@ evaluate_join_record(JOIN *join, JOIN_TAB *join_tab,
(See above join->return_tab= tab).
*/
join->examined_rows++;
- DBUG_PRINT("counts", ("join->examined_rows++: %lu",
- (ulong) join->examined_rows));
+ DBUG_PRINT("counts", ("join->examined_rows++: %lu found: %d",
+ (ulong) join->examined_rows, (int) found));
if (found)
{
enum enum_nested_loop_state rc;
/* A match from join_tab is found for the current partial join. */
rc= (*join_tab->next_select)(join, join_tab+1, 0);
- join->thd->warning_info->inc_current_row_for_warning();
+ join->thd->get_stmt_da()->inc_current_row_for_warning();
if (rc != NESTED_LOOP_OK && rc != NESTED_LOOP_NO_MORE_ROWS)
DBUG_RETURN(rc);
if (return_tab < join->return_tab)
@@ -17186,7 +18082,7 @@ evaluate_join_record(JOIN *join, JOIN_TAB *join_tab,
}
else
{
- join->thd->warning_info->inc_current_row_for_warning();
+ join->thd->get_stmt_da()->inc_current_row_for_warning();
join_tab->read_record.unlock_row(join_tab);
}
}
@@ -17197,7 +18093,7 @@ evaluate_join_record(JOIN *join, JOIN_TAB *join_tab,
with the beginning coinciding with the current partial join.
*/
join->examined_rows++;
- join->thd->warning_info->inc_current_row_for_warning();
+ join->thd->get_stmt_da()->inc_current_row_for_warning();
join_tab->read_record.unlock_row(join_tab);
}
DBUG_RETURN(NESTED_LOOP_OK);
@@ -17309,13 +18205,8 @@ int report_error(TABLE *table, int error)
*/
if (error != HA_ERR_LOCK_DEADLOCK && error != HA_ERR_LOCK_WAIT_TIMEOUT
&& error != HA_ERR_TABLE_DEF_CHANGED && !table->in_use->killed)
- {
- push_warning_printf(table->in_use, MYSQL_ERROR::WARN_LEVEL_WARN, error,
- "Got error %d when reading table %`s.%`s",
- error, table->s->db.str, table->s->table_name.str);
sql_print_error("Got error %d when reading table '%s'",
error, table->s->path.str);
- }
table->file->print_error(error,MYF(0));
return 1;
}
@@ -17378,7 +18269,7 @@ join_read_const_table(JOIN_TAB *tab, POSITION *pos)
{
if ((error=join_read_system(tab)))
{ // Info for DESCRIBE
- tab->info="const row not found";
+ tab->info= ET_CONST_ROW_NOT_FOUND;
/* Mark for EXPLAIN that the row was not found */
pos->records_read=0.0;
pos->ref_depend_map= 0;
@@ -17404,7 +18295,7 @@ join_read_const_table(JOIN_TAB *tab, POSITION *pos)
table->disable_keyread();
if (error)
{
- tab->info="unique row not found";
+ tab->info= ET_UNIQUE_ROW_NOT_FOUND;
/* Mark for EXPLAIN that the row was not found */
pos->records_read=0.0;
pos->ref_depend_map= 0;
@@ -17432,7 +18323,7 @@ join_read_const_table(JOIN_TAB *tab, POSITION *pos)
(*tab->on_expr_ref)->update_used_tables();
DBUG_ASSERT((*tab->on_expr_ref)->const_item());
#endif
- if ((table->null_row= test((*tab->on_expr_ref)->val_int() == 0)))
+ if ((table->null_row= MY_TEST((*tab->on_expr_ref)->val_int() == 0)))
mark_as_null_row(table);
}
if (!table->null_row)
@@ -17820,6 +18711,14 @@ int read_first_record_seq(JOIN_TAB *tab)
static int
test_if_quick_select(JOIN_TAB *tab)
{
+ DBUG_EXECUTE_IF("show_explain_probe_test_if_quick_select",
+ if (dbug_user_var_equals_int(tab->join->thd,
+ "show_explain_probe_select_id",
+ tab->join->select_lex->select_number))
+ dbug_serve_apcs(tab->join->thd, 1);
+ );
+
+
delete tab->select->quick;
tab->select->quick=0;
return tab->select->test_quick_select(tab->join->thd, tab->keys,
@@ -18070,7 +18969,25 @@ end_send(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
if ((error= join->result->send_data(*join->fields)))
DBUG_RETURN(error < 0 ? NESTED_LOOP_OK : NESTED_LOOP_ERROR);
}
- if (++join->send_records >= join->unit->select_limit_cnt &&
+
+ ++join->send_records;
+ if (join->send_records >= join->unit->select_limit_cnt &&
+ !join->do_send_rows)
+ {
+ /*
+ If filesort is used for sorting, stop after select_limit_cnt+1
+ records are read. Because of optimization in some cases it can
+ provide only select_limit_cnt+1 records.
+ */
+ if (join->order && join->sortorder &&
+ join->filesort_found_rows &&
+ join->select_options & OPTION_FOUND_ROWS)
+ {
+ DBUG_PRINT("info", ("filesort NESTED_LOOP_QUERY_LIMIT"));
+ DBUG_RETURN(NESTED_LOOP_QUERY_LIMIT);
+ }
+ }
+ if (join->send_records >= join->unit->select_limit_cnt &&
join->do_send_rows)
{
if (join->select_options & OPTION_FOUND_ROWS)
@@ -18294,7 +19211,7 @@ end_write(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
}
}
end:
- if (join->thd->killed)
+ if (join->thd->check_killed())
{
join->thd->send_kill_message();
DBUG_RETURN(NESTED_LOOP_KILLED); /* purecov: inspected */
@@ -18323,7 +19240,16 @@ end_update(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
for (group=table->group ; group ; group=group->next)
{
Item *item= *group->item;
- item->save_org_in_field(group->field);
+ if (group->fast_field_copier_setup != group->field)
+ {
+ DBUG_PRINT("info", ("new setup 0x%lx -> 0x%lx",
+ (ulong)group->fast_field_copier_setup,
+ (ulong)group->field));
+ group->fast_field_copier_setup= group->field;
+ group->fast_field_copier_func=
+ item->setup_fast_field_copier(group->field);
+ }
+ item->save_org_in_field(group->field, group->fast_field_copier_func);
/* Store in the used key if the field was 0 */
if (item->maybe_null)
group->buff[-1]= (char) group->field->is_null();
@@ -18365,7 +19291,7 @@ end_update(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
}
join->send_records++;
end:
- if (join->thd->killed)
+ if (join->thd->check_killed())
{
join->thd->send_kill_message();
DBUG_RETURN(NESTED_LOOP_KILLED); /* purecov: inspected */
@@ -18415,7 +19341,7 @@ end_unique_update(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
DBUG_RETURN(NESTED_LOOP_ERROR); /* purecov: inspected */
}
}
- if (join->thd->killed)
+ if (join->thd->check_killed())
{
join->thd->send_kill_message();
DBUG_RETURN(NESTED_LOOP_KILLED); /* purecov: inspected */
@@ -18493,7 +19419,7 @@ end_write_group(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
if (join->procedure)
join->procedure->add();
end:
- if (join->thd->killed)
+ if (join->thd->check_killed())
{
join->thd->send_kill_message();
DBUG_RETURN(NESTED_LOOP_KILLED); /* purecov: inspected */
@@ -18971,7 +19897,7 @@ static int test_if_order_by_key(ORDER *order, TABLE *table, uint idx,
{
KEY_PART_INFO *key_part,*key_part_end;
key_part=table->key_info[idx].key_part;
- key_part_end=key_part+table->key_info[idx].key_parts;
+ key_part_end=key_part+table->key_info[idx].user_defined_key_parts;
key_part_map const_key_parts=table->const_key_parts[idx];
int reverse=0;
uint key_parts;
@@ -18990,7 +19916,7 @@ static int test_if_order_by_key(ORDER *order, TABLE *table, uint idx,
for (; const_key_parts & 1 ; const_key_parts>>= 1)
key_part++;
- if (key_part == key_part_end)
+ if (key_part >= key_part_end)
{
/*
We are at the end of the key. Check if the engine has the primary
@@ -19013,7 +19939,7 @@ static int test_if_order_by_key(ORDER *order, TABLE *table, uint idx,
(we have to stop as first not continous primary key part)
*/
for (key_part_end= key_part,
- end= key_part+table->key_info[table->s->primary_key].key_parts;
+ end= key_part+table->key_info[table->s->primary_key].user_defined_key_parts;
key_part_end < end; key_part_end++, pk_part_idx++)
{
/* Found hole in the pk_parts; Abort */
@@ -19031,8 +19957,7 @@ static int test_if_order_by_key(ORDER *order, TABLE *table, uint idx,
Test if the primary key parts were all const (i.e. there's one row).
The sorting doesn't matter.
*/
- if (key_part ==
- start+table->key_info[table->s->primary_key].key_parts &&
+ if (key_part == start+table->key_info[table->s->primary_key].user_defined_key_parts &&
reverse == 0)
{
key_parts= 0;
@@ -19058,7 +19983,7 @@ static int test_if_order_by_key(ORDER *order, TABLE *table, uint idx,
}
if (on_pk_suffix)
{
- uint used_key_parts_secondary= table->key_info[idx].key_parts;
+ uint used_key_parts_secondary= table->key_info[idx].user_defined_key_parts;
uint used_key_parts_pk=
(uint) (key_part - table->key_info[table->s->primary_key].key_part);
key_parts= used_key_parts_pk + used_key_parts_secondary;
@@ -19111,6 +20036,7 @@ uint find_shortest_key(TABLE *table, const key_map *usable_keys)
min_cost= cost;
best=nr;
}
+ DBUG_ASSERT(best < MAX_KEY);
}
}
}
@@ -19169,7 +20095,7 @@ test_if_subkey(ORDER *order, TABLE *table, uint ref, uint ref_key_parts,
{
if (usable_keys->is_set(nr) &&
table->key_info[nr].key_length < min_length &&
- table->key_info[nr].key_parts >= ref_key_parts &&
+ table->key_info[nr].user_defined_key_parts >= ref_key_parts &&
is_subkey(table->key_info[nr].key_part, ref_key_part,
ref_key_part_end) &&
test_if_order_by_key(order, table, nr))
@@ -19227,7 +20153,7 @@ list_contains_unique_index(TABLE *table,
KEY_PART_INFO *key_part, *key_part_end;
for (key_part=keyinfo->key_part,
- key_part_end=key_part+ keyinfo->key_parts;
+ key_part_end=key_part+ keyinfo->user_defined_key_parts;
key_part < key_part_end;
key_part++)
{
@@ -19500,7 +20426,7 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit,
uint saved_best_key_parts= 0;
int best_key_direction= 0;
JOIN *join= tab->join;
- ha_rows table_records= table->file->stats.records;
+ ha_rows table_records= table->stat_records();
test_if_cheaper_ordering(tab, order, table, usable_keys,
ref_key, select_limit,
@@ -19536,7 +20462,7 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit,
order_direction= best_key_direction;
/*
saved_best_key_parts is actual number of used keyparts found by the
- test_if_order_by_key function. It could differ from keyinfo->key_parts,
+ test_if_order_by_key function. It could differ from keyinfo->user_defined_key_parts,
thus we have to restore it in case of desc order as it affects
QUICK_SELECT_DESC behaviour.
*/
@@ -19616,7 +20542,7 @@ check_reverse_order:
{
tab->ref.key= -1;
tab->ref.key_parts= 0;
- if (select_limit < table->file->stats.records)
+ if (select_limit < table->stat_records())
tab->limit= select_limit;
table->disable_keyread();
}
@@ -19771,6 +20697,8 @@ create_sort_index(THD *thd, JOIN *join, ORDER *order,
{
uint length= 0;
ha_rows examined_rows;
+ ha_rows found_rows;
+ ha_rows filesort_retval= HA_POS_ERROR;
TABLE *table;
SQL_SELECT *select;
JOIN_TAB *tab;
@@ -19859,7 +20787,8 @@ create_sort_index(THD *thd, JOIN *join, ORDER *order,
goto err; /* purecov: inspected */
table->sort.io_cache=(IO_CACHE*) my_malloc(sizeof(IO_CACHE),
- MYF(MY_WME | MY_ZEROFILL));
+ MYF(MY_WME | MY_ZEROFILL|
+ MY_THREAD_SPECIFIC));
table->status=0; // May be wrong if quick_select
if (!tab->preread_init_done && tab->preread_init())
@@ -19903,9 +20832,11 @@ create_sort_index(THD *thd, JOIN *join, ORDER *order,
if (table->s->tmp_table)
table->file->info(HA_STATUS_VARIABLE); // Get record count
- table->sort.found_records=filesort(thd, table,join->sortorder, length,
- select, filesort_limit, 0,
- &examined_rows);
+ filesort_retval= filesort(thd, table, join->sortorder, length,
+ select, filesort_limit, 0,
+ &examined_rows, &found_rows);
+ table->sort.found_records= filesort_retval;
+ tab->records= join->select_options & OPTION_FOUND_ROWS ? found_rows : filesort_retval;
if (quick_created)
{
@@ -19923,72 +20854,6 @@ create_sort_index(THD *thd, JOIN *join, ORDER *order,
*(join->pre_sort_join_tab)= *tab;
- /*TODO: here, close the index scan, cancel index-only read. */
- tab->records= table->sort.found_records; // For SQL_CALC_ROWS
-#if 0
- /* MariaDB doesn't need the following: */
- if (select)
- {
- /*
- We need to preserve tablesort's output resultset here, because
- QUICK_INDEX_MERGE_SELECT::~QUICK_INDEX_MERGE_SELECT (called by
- SQL_SELECT::cleanup()) may free it assuming it's the result of the quick
- select operation that we no longer need. Note that all the other parts of
- this data structure are cleaned up when
- QUICK_INDEX_MERGE_SELECT::get_next encounters end of data, so the next
- SQL_SELECT::cleanup() call changes sort.io_cache alone.
- */
- IO_CACHE *tablesort_result_cache;
-
- tablesort_result_cache= table->sort.io_cache;
- table->sort.io_cache= NULL;
-
- if (select->quick &&
- select->quick->get_type() == QUICK_SELECT_I::QS_TYPE_GROUP_MIN_MAX)
- {
- tab->filesort_used_loose_index_scan= true;
-
- QUICK_GROUP_MIN_MAX_SELECT *minmax_quick=
- static_cast<QUICK_GROUP_MIN_MAX_SELECT*>(select->quick);
- if (minmax_quick->is_agg_distinct())
- tab->filesort_used_loose_index_scan_agg_distinct= true;
- }
-
- /*
- If a quick object was created outside of create_sort_index()
- that might be reused, then do not call select->cleanup() since
- it will delete the quick object.
- */
- if (!keep_quick)
- {
- select->cleanup();
-
- // If we deleted the quick object we need to clear quick_keys
- table->quick_keys.clear_all();
- table->intersect_keys.clear_all();
- }
- else
- {
- // Need to close the index scan in order to re-use the handler
- tab->select->quick->range_end();
- }
-
- /*
- The select object is now ready for the next use. To avoid that
- the select object is used when reading the records in sorted
- order we set the pointer to it to NULL. The select pointer will
- be restored from the saved_select pointer when this select
- operation is completed (@see JOIN::exec). This ensures that it
- will be re-used when filesort is used by subqueries that are
- executed multiple times.
- */
- tab->saved_select= tab->select;
- tab->select= NULL;
-
- // Restore the output resultset
- table->sort.io_cache= tablesort_result_cache;
- }
-#endif
tab->select=NULL;
tab->set_select_cond(NULL, __LINE__);
tab->type=JT_ALL; // Read with normal read_record
@@ -19999,12 +20864,11 @@ create_sort_index(THD *thd, JOIN *join, ORDER *order,
goto err;
tab->join->examined_rows+=examined_rows;
- DBUG_RETURN(table->sort.found_records == HA_POS_ERROR);
+ DBUG_RETURN(filesort_retval == HA_POS_ERROR);
err:
DBUG_RETURN(-1);
}
-
void JOIN::clean_pre_sort_join_tab()
{
//TABLE *table= pre_sort_join_tab->table;
@@ -20013,29 +20877,6 @@ void JOIN::clean_pre_sort_join_tab()
the table already deleted by st_select_lex_unit::cleanup().
We rely on that fake_select_lex didn't have quick select.
*/
-#if 0
- if (pre_sort_join_tab->select && pre_sort_join_tab->select->quick)
- {
- /*
- We need to preserve tablesort's output resultset here, because
- QUICK_INDEX_MERGE_SELECT::~QUICK_INDEX_MERGE_SELECT (called by
- SQL_SELECT::cleanup()) may free it assuming it's the result of the quick
- select operation that we no longer need. Note that all the other parts of
- this data structure are cleaned up when
- QUICK_INDEX_MERGE_SELECT::get_next encounters end of data, so the next
- SQL_SELECT::cleanup() call changes sort.io_cache alone.
- */
- IO_CACHE *tablesort_result_cache;
-
- tablesort_result_cache= table->sort.io_cache;
- table->sort.io_cache= NULL;
- pre_sort_join_tab->select->cleanup();
- table->quick_keys.clear_all(); // as far as we cleanup select->quick
- table->intersect_keys.clear_all();
- table->sort.io_cache= tablesort_result_cache;
- }
-#endif
- //table->disable_keyread(); // Restore if we used indexes
if (pre_sort_join_tab->select && pre_sort_join_tab->select->quick)
{
pre_sort_join_tab->select->cleanup();
@@ -20159,7 +21000,7 @@ static int remove_dup_with_compare(THD *thd, TABLE *table, Field **first_field,
error= file->ha_rnd_next(record);
for (;;)
{
- if (thd->killed)
+ if (thd->check_killed())
{
thd->send_kill_message();
error=0;
@@ -20280,7 +21121,7 @@ static int remove_dup_with_hash_index(THD *thd, TABLE *table,
for (;;)
{
uchar *org_key_pos;
- if (thd->killed)
+ if (thd->check_killed())
{
thd->send_kill_message();
error=0;
@@ -20352,11 +21193,11 @@ SORT_FIELD *make_unireg_sortorder(ORDER *order, uint *length,
count++;
if (!sortorder)
sortorder= (SORT_FIELD*) sql_alloc(sizeof(SORT_FIELD) *
- (max(count, *length) + 1));
+ (MY_MAX(count, *length) + 1));
pos= sort= sortorder;
if (!pos)
- return 0;
+ DBUG_RETURN(0);
for (;order;order=order->next,pos++)
{
@@ -20388,6 +21229,7 @@ SORT_FIELD *make_unireg_sortorder(ORDER *order, uint *length,
else
pos->item= item;
pos->reverse=! order->asc;
+ DBUG_ASSERT(pos->field != NULL || pos->item != NULL);
}
*length=count;
DBUG_RETURN(sort);
@@ -20486,7 +21328,7 @@ cp_buffer_from_ref(THD *thd, TABLE *table, TABLE_REF *ref)
ref_pointer_array and all_fields are updated.
- @param[in] thd Pointer to current thread structure
+ @param[in] thd Pointer to current thread structure
@param[in,out] ref_pointer_array All select, group and order by fields
@param[in] tables List of tables to search in (usually
FROM clause)
@@ -20532,7 +21374,7 @@ find_order_in_list(THD *thd, Item **ref_pointer_array, TABLE_LIST *tables,
order->in_field_list= 1;
order->counter= count;
order->counter_used= 1;
- return FALSE;
+ return FALSE;
}
/* Lookup the current GROUP/ORDER field in the SELECT clause. */
select_item= find_item_in_list(order_item, fields, &counter,
@@ -20547,7 +21389,7 @@ find_order_in_list(THD *thd, Item **ref_pointer_array, TABLE_LIST *tables,
Item *view_ref= NULL;
/*
If we have found field not by its alias in select list but by its
- original field name, we should additionaly check if we have conflict
+ original field name, we should additionally check if we have conflict
for this name (in case if we would perform lookup in all tables).
*/
if (resolution == RESOLVED_BEHIND_ALIAS && !order_item->fixed &&
@@ -20600,7 +21442,8 @@ find_order_in_list(THD *thd, Item **ref_pointer_array, TABLE_LIST *tables,
warning so the user knows that the field from the FROM clause
overshadows the column reference from the SELECT list.
*/
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_NON_UNIQ_ERROR,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
+ ER_NON_UNIQ_ERROR,
ER(ER_NON_UNIQ_ERROR),
((Item_ident*) order_item)->field_name,
current_thd->where);
@@ -20983,7 +21826,7 @@ test_if_subpart(ORDER *a,ORDER *b)
else
return 0;
}
- return test(!b);
+ return MY_TEST(!b);
}
/**
@@ -21585,7 +22428,7 @@ change_to_use_tmp_fields(THD *thd, Item **ref_pointer_array,
We are replacing the argument of Item_func_set_user_var after its value
has been read. The argument's null_value should be set by now, so we
must set it explicitly for the replacement argument since the null_value
- may be read without any preceeding call to val_*().
+ may be read without any preceding call to val_*().
*/
new_field->update_null_value();
List<Item> list;
@@ -22373,162 +23216,359 @@ void JOIN::clear()
}
}
-/**
- EXPLAIN handling.
- Send a description about what how the select will be done to stdout.
+/*
+ Print an EXPLAIN line with all NULLs and given message in the 'Extra' column
*/
-static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
- bool distinct,const char *message)
+int print_explain_message_line(select_result_sink *result,
+ uint8 options,
+ uint select_number,
+ const char *select_type,
+ ha_rows *rows,
+ const char *message)
{
- List<Item> field_list;
- List<Item> item_list;
- THD *thd=join->thd;
- select_result *result=join->result;
Item *item_null= new Item_null();
- CHARSET_INFO *cs= system_charset_info;
- int quick_type;
- DBUG_ENTER("select_describe");
- DBUG_PRINT("info", ("Select 0x%lx, type %s, message %s",
- (ulong)join->select_lex, join->select_lex->type,
- message ? message : "NULL"));
- /* Don't log this into the slow query log */
- thd->server_status&= ~(SERVER_QUERY_NO_INDEX_USED | SERVER_QUERY_NO_GOOD_INDEX_USED);
- join->unit->offset_limit_cnt= 0;
+ List<Item> item_list;
- /*
- NOTE: the number/types of items pushed into item_list must be in sync with
- EXPLAIN column types as they're "defined" in THD::send_explain_fields()
- */
- if (message)
- {
- item_list.push_back(new Item_int((int32)
- join->select_lex->select_number));
- item_list.push_back(new Item_string(join->select_lex->type,
- strlen(join->select_lex->type), cs));
- for (uint i=0 ; i < 7; i++)
- item_list.push_back(item_null);
- if (join->thd->lex->describe & DESCRIBE_PARTITIONS)
- item_list.push_back(item_null);
- if (join->thd->lex->describe & DESCRIBE_EXTENDED)
- item_list.push_back(item_null);
+ item_list.push_back(new Item_int((int32) select_number));
+ item_list.push_back(new Item_string_sys(select_type));
+ /* `table` */
+ item_list.push_back(item_null);
- item_list.push_back(new Item_string(message,strlen(message),cs));
- if (result->send_data(item_list))
- join->error= 1;
+ /* `partitions` */
+ if (options & DESCRIBE_PARTITIONS)
+ item_list.push_back(item_null);
+
+ /* type, possible_keys, key, key_len, ref */
+ for (uint i=0 ; i < 5; i++)
+ item_list.push_back(item_null);
+
+ /* `rows` */
+ if (rows)
+ {
+ item_list.push_back(new Item_int(*rows,
+ MY_INT64_NUM_DECIMAL_DIGITS));
}
- else if (join->select_lex == join->unit->fake_select_lex)
+ else
+ item_list.push_back(item_null);
+
+ /* `filtered` */
+ if (options & DESCRIBE_EXTENDED)
+ item_list.push_back(item_null);
+
+ /* `Extra` */
+ if (message)
+ item_list.push_back(new Item_string_sys(message));
+ else
+ item_list.push_back(item_null);
+
+ if (result->send_data(item_list))
+ return 1;
+ return 0;
+}
+
+
+/*
+ Make a comma-separated list of possible_keys names and add it into the string
+*/
+
+void make_possible_keys_line(TABLE *table, key_map possible_keys, String *line)
+{
+ if (!possible_keys.is_clear_all())
{
- /*
- here we assume that the query will return at least two rows, so we
- show "filesort" in EXPLAIN. Of course, sometimes we'll be wrong
- and no filesort will be actually done, but executing all selects in
- the UNION to provide precise EXPLAIN information will hardly be
- appreciated :)
- */
- char table_name_buffer[SAFE_NAME_LEN];
- item_list.empty();
- /* id */
- item_list.push_back(new Item_null);
- /* select_type */
- item_list.push_back(new Item_string(join->select_lex->type,
- strlen(join->select_lex->type),
- cs));
- /* table */
- {
- SELECT_LEX *sl= join->unit->first_select();
- uint len= 6, lastop= 0;
- memcpy(table_name_buffer, STRING_WITH_LEN("<union"));
- for (; sl && len + lastop + 5 < NAME_LEN; sl= sl->next_select())
- {
- len+= lastop;
- lastop= my_snprintf(table_name_buffer + len, NAME_LEN - len,
- "%u,", sl->select_number);
- }
- if (sl || len + lastop >= NAME_LEN)
- {
- memcpy(table_name_buffer + len, STRING_WITH_LEN("...>") + 1);
- len+= 4;
- }
- else
+ uint j;
+ for (j=0 ; j < table->s->keys ; j++)
+ {
+ if (possible_keys.is_set(j))
{
- len+= lastop;
- table_name_buffer[len - 1]= '>'; // change ',' to '>'
+ if (line->length())
+ line->append(',');
+ line->append(table->key_info[j].name,
+ strlen(table->key_info[j].name),
+ system_charset_info);
}
- item_list.push_back(new Item_string(table_name_buffer, len, cs));
}
- /* partitions */
- if (join->thd->lex->describe & DESCRIBE_PARTITIONS)
+ }
+}
+
+/*
+ Print an EXPLAIN output row, based on information provided in the parameters
+
+ @note
+ Parameters that may have NULL value in EXPLAIN output, should be passed
+ (char*)NULL.
+
+ @return
+ 0 - OK
+ 1 - OOM Error
+*/
+
+int print_explain_row(select_result_sink *result,
+ uint8 options,
+ uint select_number,
+ const char *select_type,
+ const char *table_name,
+ const char *partitions,
+ enum join_type jtype,
+ const char *possible_keys,
+ const char *index,
+ const char *key_len,
+ const char *ref,
+ ha_rows *rows,
+ const char *extra)
+{
+ Item *item_null= new Item_null();
+ List<Item> item_list;
+ Item *item;
+
+ item_list.push_back(new Item_int((int32) select_number));
+ item_list.push_back(new Item_string_sys(select_type));
+ item_list.push_back(new Item_string_sys(table_name));
+ if (options & DESCRIBE_PARTITIONS)
+ {
+ if (partitions)
+ {
+ item_list.push_back(new Item_string_sys(partitions));
+ }
+ else
item_list.push_back(item_null);
- /* type */
- item_list.push_back(new Item_string(join_type_str[JT_ALL],
- strlen(join_type_str[JT_ALL]),
- cs));
- /* possible_keys */
- item_list.push_back(item_null);
- /* key*/
+ }
+
+ const char *jtype_str= join_type_str[jtype];
+ item_list.push_back(new Item_string_sys(jtype_str));
+
+ item= possible_keys? new Item_string_sys(possible_keys) : item_null;
+ item_list.push_back(item);
+
+ /* 'index */
+ item= index ? new Item_string_sys(index) : item_null;
+ item_list.push_back(item);
+
+ /* 'key_len */
+ item= key_len ? new Item_string_sys(key_len) : item_null;
+ item_list.push_back(item);
+
+ /* 'ref' */
+ item= ref ? new Item_string_sys(ref) : item_null;
+ item_list.push_back(item);
+
+ /* 'rows' */
+ if (rows)
+ {
+ item_list.push_back(new Item_int(*rows,
+ MY_INT64_NUM_DECIMAL_DIGITS));
+ }
+ else
item_list.push_back(item_null);
- /* key_len */
+
+ /* 'filtered' */
+ const double filtered=100.0;
+ if (options & DESCRIBE_EXTENDED)
+ item_list.push_back(new Item_float(filtered, 2));
+
+ /* 'Extra' */
+ if (extra)
+ item_list.push_back(new Item_string_sys(extra));
+ else
item_list.push_back(item_null);
- /* ref */
+
+ if (result->send_data(item_list))
+ return 1;
+ return 0;
+}
+
+
+int print_fake_select_lex_join(select_result_sink *result, bool on_the_fly,
+ SELECT_LEX *select_lex, uint8 explain_flags)
+{
+ Item *item_null= new Item_null();
+ List<Item> item_list;
+ if (on_the_fly)
+ select_lex->set_explain_type(on_the_fly);
+ /*
+ here we assume that the query will return at least two rows, so we
+ show "filesort" in EXPLAIN. Of course, sometimes we'll be wrong
+ and no filesort will be actually done, but executing all selects in
+ the UNION to provide precise EXPLAIN information will hardly be
+ appreciated :)
+ */
+ char table_name_buffer[SAFE_NAME_LEN];
+ item_list.empty();
+ /* id */
+ item_list.push_back(new Item_null);
+ /* select_type */
+ item_list.push_back(new Item_string_sys(select_lex->type));
+ /* table */
+ {
+ SELECT_LEX *sl= select_lex->master_unit()->first_select();
+ uint len= 6, lastop= 0;
+ memcpy(table_name_buffer, STRING_WITH_LEN("<union"));
+ for (; sl && len + lastop + 5 < NAME_LEN; sl= sl->next_select())
+ {
+ len+= lastop;
+ lastop= my_snprintf(table_name_buffer + len, NAME_LEN - len,
+ "%u,", sl->select_number);
+ }
+ if (sl || len + lastop >= NAME_LEN)
+ {
+ memcpy(table_name_buffer + len, STRING_WITH_LEN("...>") + 1);
+ len+= 4;
+ }
+ else
+ {
+ len+= lastop;
+ table_name_buffer[len - 1]= '>'; // change ',' to '>'
+ }
+ item_list.push_back(new Item_string_sys(table_name_buffer, len));
+ }
+ /* partitions */
+ if (explain_flags & DESCRIBE_PARTITIONS)
item_list.push_back(item_null);
- /* in_rows */
- if (join->thd->lex->describe & DESCRIBE_EXTENDED)
- item_list.push_back(item_null);
- /* rows */
+ /* type */
+ item_list.push_back(new Item_string_sys(join_type_str[JT_ALL]));
+
+ /* possible_keys */
+ item_list.push_back(item_null);
+ /* key*/
+ item_list.push_back(item_null);
+ /* key_len */
+ item_list.push_back(item_null);
+ /* ref */
+ item_list.push_back(item_null);
+ /* in_rows */
+ if (explain_flags & DESCRIBE_EXTENDED)
item_list.push_back(item_null);
- /* extra */
- if (join->unit->global_parameters->order_list.first)
- item_list.push_back(new Item_string("Using filesort",
- 14, cs));
- else
- item_list.push_back(new Item_string("", 0, cs));
+ /* rows */
+ item_list.push_back(item_null);
+ /* extra */
+ if (select_lex->master_unit()->global_parameters->order_list.first)
+ item_list.push_back(new Item_string_sys("Using filesort", 14));
+ else
+ item_list.push_back(new Item_string_sys("", 0));
+
+ if (result->send_data(item_list))
+ return 1;
+ return 0;
+}
+
+
+/*
+ Append MRR information from quick select to the given string
+*/
- if (result->send_data(item_list))
- join->error= 1;
+void explain_append_mrr_info(QUICK_RANGE_SELECT *quick, String *res)
+{
+ char mrr_str_buf[128];
+ mrr_str_buf[0]=0;
+ int len;
+ handler *h= quick->head->file;
+ len= h->multi_range_read_explain_info(quick->mrr_flags, mrr_str_buf,
+ sizeof(mrr_str_buf));
+ if (len > 0)
+ {
+ //res->append(STRING_WITH_LEN("; "));
+ res->append(mrr_str_buf, len);
+ }
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// TODO: join with make_possible_keys_line ?
+void append_possible_keys(String *str, TABLE *table, key_map possible_keys)
+{
+ uint j;
+ for (j=0 ; j < table->s->keys ; j++)
+ {
+ if (possible_keys.is_set(j))
+ {
+ if (str->length())
+ str->append(',');
+ str->append(table->key_info[j].name,
+ strlen(table->key_info[j].name),
+ system_charset_info);
+ }
+ }
+}
+
+
+/*
+ Save Query Plan Footprint
+
+ @note
+ Currently, this function may be called multiple times
+*/
+
+int JOIN::save_explain_data_intern(Explain_query *output, bool need_tmp_table,
+ bool need_order, bool distinct,
+ const char *message)
+{
+ Explain_node *UNINIT_VAR(explain_node);
+ JOIN *join= this; /* Legacy: this code used to be a non-member function */
+ THD *thd=join->thd;
+ const CHARSET_INFO *cs= system_charset_info;
+ int quick_type;
+ int error= 0;
+ DBUG_ENTER("JOIN::save_explain_data_intern");
+ DBUG_PRINT("info", ("Select 0x%lx, type %s, message %s",
+ (ulong)join->select_lex, join->select_lex->type,
+ message ? message : "NULL"));
+ DBUG_ASSERT(have_query_plan == QEP_AVAILABLE);
+ /* Don't log this into the slow query log */
+
+ if (message)
+ {
+ Explain_select *xpl_sel;
+ explain_node= xpl_sel= new (output->mem_root) Explain_select;
+ join->select_lex->set_explain_type(true);
+
+ xpl_sel->select_id= join->select_lex->select_number;
+ xpl_sel->select_type= join->select_lex->type;
+ xpl_sel->message= message;
+ /* Setting xpl_sel->message means that all other members are invalid */
+ output->add_node(xpl_sel);
+ }
+ else if (join->select_lex == join->unit->fake_select_lex)
+ {
+ /* Do nothing, Explain_union will create and print fake_select_lex */
}
else if (!join->select_lex->master_unit()->derived ||
join->select_lex->master_unit()->derived->is_materialized_derived())
{
+ Explain_select *xpl_sel;
+ explain_node= xpl_sel= new (output->mem_root) Explain_select;
table_map used_tables=0;
- bool printing_materialize_nest= FALSE;
- uint select_id= join->select_lex->select_number;
+ join->select_lex->set_explain_type(true);
+ xpl_sel->select_id= join->select_lex->select_number;
+ xpl_sel->select_type= join->select_lex->type;
+
+ JOIN_TAB* const first_top_tab= first_breadth_first_tab(join, WALK_OPTIMIZATION_TABS);
for (JOIN_TAB *tab= first_breadth_first_tab(join, WALK_OPTIMIZATION_TABS); tab;
tab= next_breadth_first_tab(join, WALK_OPTIMIZATION_TABS, tab))
{
+ uint select_id;
if (tab->bush_root_tab)
{
JOIN_TAB *first_sibling= tab->bush_root_tab->bush_children->start;
select_id= first_sibling->emb_sj_nest->sj_subq_pred->get_identifier();
- printing_materialize_nest= TRUE;
}
-
+ else
+ select_id= join->select_lex->select_number;
+
TABLE *table=tab->table;
TABLE_LIST *table_list= tab->table->pos_in_table_list;
- char buff[512];
- char buff1[512], buff2[512], buff3[512], buff4[512];
- char keylen_str_buf[64];
+ char buff4[512];
my_bool key_read;
- String extra(buff, sizeof(buff),cs);
char table_name_buffer[SAFE_NAME_LEN];
- String tmp1(buff1,sizeof(buff1),cs);
- String tmp2(buff2,sizeof(buff2),cs);
- String tmp3(buff3,sizeof(buff3),cs);
String tmp4(buff4,sizeof(buff4),cs);
- char hash_key_prefix[]= "#hash#";
KEY *key_info= 0;
uint key_len= 0;
- bool is_hj= tab->type == JT_HASH || tab->type ==JT_HASH_NEXT;
-
- extra.length(0);
- tmp1.length(0);
- tmp2.length(0);
- tmp3.length(0);
tmp4.length(0);
quick_type= -1;
+ QUICK_SELECT_I *quick= NULL;
+ JOIN_TAB *saved_join_tab= NULL;
/* Don't show eliminated tables */
if (table->map & join->eliminated_tables)
@@ -22537,27 +23577,27 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
continue;
}
- item_list.empty();
- /* id */
- item_list.push_back(new Item_uint((uint32)select_id));
- /* select_type */
- const char* stype= printing_materialize_nest? "MATERIALIZED" :
- join->select_lex->type;
- item_list.push_back(new Item_string(stype, strlen(stype), cs));
-
- if ((tab->type == JT_ALL || tab->type == JT_HASH) &&
- tab->select && tab->select->quick)
+ if (join->table_access_tabs == join->join_tab &&
+ tab == (first_top_tab + join->const_tables) && pre_sort_join_tab)
{
- quick_type= tab->select->quick->get_type();
- if ((quick_type == QUICK_SELECT_I::QS_TYPE_INDEX_MERGE) ||
- (quick_type == QUICK_SELECT_I::QS_TYPE_INDEX_INTERSECT) ||
- (quick_type == QUICK_SELECT_I::QS_TYPE_ROR_INTERSECT) ||
- (quick_type == QUICK_SELECT_I::QS_TYPE_ROR_UNION))
- tab->type= tab->type == JT_ALL ? JT_INDEX_MERGE : JT_HASH_INDEX_MERGE;
- else
- tab->type= tab->type == JT_ALL ? JT_RANGE : JT_HASH_RANGE;
+ saved_join_tab= tab;
+ tab= pre_sort_join_tab;
}
+ Explain_table_access *eta= new (output->mem_root) Explain_table_access;
+ xpl_sel->add_table(eta);
+ eta->key.set(thd->mem_root, NULL, (uint)-1);
+ eta->quick_info= NULL;
+
+ /* id */
+ if (tab->bush_root_tab)
+ eta->sjm_nest_select_id= select_id;
+ else
+ eta->sjm_nest_select_id= 0;
+
+ /* select_type */
+ xpl_sel->select_type= join->select_lex->type;
+
/* table */
if (table->derived_select_number)
{
@@ -22565,7 +23605,7 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
int len= my_snprintf(table_name_buffer, sizeof(table_name_buffer)-1,
"<derived%u>",
table->derived_select_number);
- item_list.push_back(new Item_string(table_name_buffer, len, cs));
+ eta->table_name.copy(table_name_buffer, len, cs);
}
else if (tab->bush_children)
{
@@ -22575,60 +23615,54 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
sizeof(table_name_buffer)-1,
"<subquery%d>",
ctab->emb_sj_nest->sj_subq_pred->get_identifier());
- item_list.push_back(new Item_string(table_name_buffer, len, cs));
+ eta->table_name.copy(table_name_buffer, len, cs);
}
else
{
TABLE_LIST *real_table= table->pos_in_table_list;
- item_list.push_back(new Item_string(real_table->alias,
- strlen(real_table->alias), cs));
+ eta->table_name.copy(real_table->alias, strlen(real_table->alias), cs);
}
+
/* "partitions" column */
- if (join->thd->lex->describe & DESCRIBE_PARTITIONS)
{
#ifdef WITH_PARTITION_STORAGE_ENGINE
partition_info *part_info;
if (!table->derived_select_number &&
(part_info= table->part_info))
{
- Item_string *item_str= new Item_string(cs);
- make_used_partitions_str(part_info, &item_str->str_value);
- item_list.push_back(item_str);
+ make_used_partitions_str(part_info, &eta->used_partitions);
+ eta->used_partitions_set= true;
}
else
- item_list.push_back(item_null);
+ eta->used_partitions_set= false;
#else
/* just produce empty column if partitioning is not compiled in */
- item_list.push_back(item_null);
+ eta->used_partitions_set= false;
#endif
}
+
/* "type" column */
- item_list.push_back(new Item_string(join_type_str[tab->type],
- strlen(join_type_str[tab->type]),
- cs));
- /* Build "possible_keys" value and add it to item_list */
- if (!tab->keys.is_clear_all())
- {
- uint j;
- for (j=0 ; j < table->s->keys ; j++)
- {
- if (tab->keys.is_set(j))
- {
- if (tmp1.length())
- tmp1.append(',');
- tmp1.append(table->key_info[j].name,
- strlen(table->key_info[j].name),
- system_charset_info);
- }
- }
+ enum join_type tab_type= tab->type;
+ if ((tab->type == JT_ALL || tab->type == JT_HASH) &&
+ tab->select && tab->select->quick && tab->use_quick != 2)
+ {
+ quick= tab->select->quick;
+ quick_type= tab->select->quick->get_type();
+ if ((quick_type == QUICK_SELECT_I::QS_TYPE_INDEX_MERGE) ||
+ (quick_type == QUICK_SELECT_I::QS_TYPE_INDEX_INTERSECT) ||
+ (quick_type == QUICK_SELECT_I::QS_TYPE_ROR_INTERSECT) ||
+ (quick_type == QUICK_SELECT_I::QS_TYPE_ROR_UNION))
+ tab_type= tab->type == JT_ALL ? JT_INDEX_MERGE : JT_HASH_INDEX_MERGE;
+ else
+ tab_type= tab->type == JT_ALL ? JT_RANGE : JT_HASH_RANGE;
}
- if (tmp1.length())
- item_list.push_back(new Item_string(tmp1.ptr(),tmp1.length(),cs));
- else
- item_list.push_back(item_null);
+ eta->type= tab_type;
+
+ /* Build "possible_keys" value */
+ append_possible_keys(&eta->possible_keys_str, table, tab->keys);
- /* Build "key", "key_len", and "ref" values and add them to item_list */
- if (tab->type == JT_NEXT)
+ /* Build "key", "key_len", and "ref" */
+ if (tab_type == JT_NEXT)
{
key_info= table->key_info+tab->index;
key_len= key_info->key_length;
@@ -22638,56 +23672,55 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
key_info= tab->get_keyinfo_by_key_no(tab->ref.key);
key_len= tab->ref.key_length;
}
- if (key_info)
+
+ /*
+ In STRAIGHT_JOIN queries, there can be join tabs with JT_CONST type
+ that still have quick selects.
+ */
+ if (tab->select && tab->select->quick && tab_type != JT_CONST)
{
- register uint length;
- if (is_hj)
- tmp2.append(hash_key_prefix, strlen(hash_key_prefix), cs);
- tmp2.append(key_info->name, strlen(key_info->name), cs);
- length= (longlong10_to_str(key_len, keylen_str_buf, 10) -
- keylen_str_buf);
- tmp3.append(keylen_str_buf, length, cs);
- if (tab->ref.key_parts)
+ eta->quick_info= tab->select->quick->get_explain(thd->mem_root);
+ }
+
+ if (key_info) /* 'index' or 'ref' access */
+ {
+ eta->key.set(thd->mem_root, key_info->name, key_len);
+
+ if (tab->ref.key_parts && tab_type != JT_FT)
{
- for (store_key **ref=tab->ref.key_copy ; *ref ; ref++)
+ store_key **ref=tab->ref.key_copy;
+ for (uint kp= 0; kp < tab->ref.key_parts; kp++)
{
if (tmp4.length())
tmp4.append(',');
- tmp4.append((*ref)->name(), strlen((*ref)->name()), cs);
+
+ if ((key_part_map(1) << kp) & tab->ref.const_ref_part_map)
+ tmp4.append("const");
+ else
+ {
+ tmp4.append((*ref)->name(), strlen((*ref)->name()), cs);
+ ref++;
+ }
}
}
}
- if (is_hj && tab->type != JT_HASH)
+
+ if (tab_type == JT_HASH_NEXT) /* full index scan + hash join */
{
- tmp2.append(':');
- tmp3.append(':');
+ eta->hash_next_key.set(thd->mem_root,
+ table->key_info[tab->index].name,
+ table->key_info[tab->index].key_length);
}
- if (tab->type == JT_HASH_NEXT)
+
+ if (key_info)
{
- register uint length;
- key_info= table->key_info+tab->index;
- key_len= key_info->key_length;
- tmp2.append(key_info->name, strlen(key_info->name), cs);
- length= (longlong10_to_str(key_len, keylen_str_buf, 10) -
- keylen_str_buf);
- tmp3.append(keylen_str_buf, length, cs);
- }
- if (tab->type != JT_CONST && tab->select && tab->select->quick)
- tab->select->quick->add_keys_and_lengths(&tmp2, &tmp3);
- if (key_info || (tab->select && tab->select->quick))
- {
- if (tmp2.length())
- item_list.push_back(new Item_string(tmp2.ptr(),tmp2.length(),cs));
- else
- item_list.push_back(item_null);
- if (tmp3.length())
- item_list.push_back(new Item_string(tmp3.ptr(),tmp3.length(),cs));
- else
- item_list.push_back(item_null);
- if (key_info && tab->type != JT_NEXT)
- item_list.push_back(new Item_string(tmp4.ptr(),tmp4.length(),cs));
+ if (key_info && tab_type != JT_NEXT)
+ {
+ eta->ref.copy(tmp4);
+ eta->ref_set= true;
+ }
else
- item_list.push_back(item_null);
+ eta->ref_set= false;
}
else
{
@@ -22697,141 +23730,130 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
{
const char *tmp_buff;
int f_idx;
+ StringBuffer<64> key_name_buf;
if (table_list->has_db_lookup_value)
{
+ /* The "key" has the name of the column referring to the database */
f_idx= table_list->schema_table->idx_field1;
tmp_buff= table_list->schema_table->fields_info[f_idx].field_name;
- tmp2.append(tmp_buff, strlen(tmp_buff), cs);
+ key_name_buf.append(tmp_buff, strlen(tmp_buff), cs);
}
if (table_list->has_table_lookup_value)
{
if (table_list->has_db_lookup_value)
- tmp2.append(',');
+ key_name_buf.append(',');
+
f_idx= table_list->schema_table->idx_field2;
tmp_buff= table_list->schema_table->fields_info[f_idx].field_name;
- tmp2.append(tmp_buff, strlen(tmp_buff), cs);
+ key_name_buf.append(tmp_buff, strlen(tmp_buff), cs);
}
- if (tmp2.length())
- item_list.push_back(new Item_string(tmp2.ptr(),tmp2.length(),cs));
- else
- item_list.push_back(item_null);
+
+ if (key_name_buf.length())
+ eta->key.set(thd->mem_root, key_name_buf.c_ptr_safe(), -1);
}
- else
- item_list.push_back(item_null);
- item_list.push_back(item_null);
- item_list.push_back(item_null);
+ eta->ref_set= false;
}
- /* Add "rows" field to item_list. */
+ /* "rows" */
if (table_list /* SJM bushes don't have table_list */ &&
table_list->schema_table)
{
- /* in_rows */
- if (join->thd->lex->describe & DESCRIBE_EXTENDED)
- item_list.push_back(item_null);
- /* rows */
- item_list.push_back(item_null);
+ /* I_S tables have rows=extra=NULL */
+ eta->rows_set= false;
+ eta->filtered_set= false;
}
else
{
- ha_rows examined_rows= tab->get_examined_rows();
+ double examined_rows= tab->get_examined_rows();
- item_list.push_back(new Item_int((longlong) (ulonglong) examined_rows,
- MY_INT64_NUM_DECIMAL_DIGITS));
+ eta->rows_set= true;
+ eta->rows= (ha_rows) examined_rows;
- /* Add "filtered" field to item_list. */
- if (join->thd->lex->describe & DESCRIBE_EXTENDED)
+ /* "filtered" */
+ float f= 0.0;
+ if (examined_rows)
{
- float f= 0.0;
- if (examined_rows)
+ double pushdown_cond_selectivity= tab->cond_selectivity;
+ if (pushdown_cond_selectivity == 1.0)
f= (float) (100.0 * tab->records_read / examined_rows);
- set_if_smaller(f, 100.0);
- item_list.push_back(new Item_float(f, 2));
+ else
+ f= (float) (100.0 * pushdown_cond_selectivity);
}
+ set_if_smaller(f, 100.0);
+ eta->filtered_set= true;
+ eta->filtered= f;
}
- /* Build "Extra" field and add it to item_list. */
+ /* Build "Extra" field and save it */
key_read=table->key_read;
- if ((tab->type == JT_NEXT || tab->type == JT_CONST) &&
+ if ((tab_type == JT_NEXT || tab_type == JT_CONST) &&
table->covering_keys.is_set(tab->index))
key_read=1;
if (quick_type == QUICK_SELECT_I::QS_TYPE_ROR_INTERSECT &&
- !((QUICK_ROR_INTERSECT_SELECT*)tab->select->quick)->need_to_fetch_row)
+ !((QUICK_ROR_INTERSECT_SELECT*)quick)->need_to_fetch_row)
key_read=1;
if (tab->info)
- item_list.push_back(new Item_string(tab->info,strlen(tab->info),cs));
+ {
+ eta->push_extra(tab->info);
+ }
else if (tab->packed_info & TAB_INFO_HAVE_VALUE)
{
if (tab->packed_info & TAB_INFO_USING_INDEX)
- extra.append(STRING_WITH_LEN("; Using index"));
+ eta->push_extra(ET_USING_INDEX);
if (tab->packed_info & TAB_INFO_USING_WHERE)
- extra.append(STRING_WITH_LEN("; Using where"));
+ eta->push_extra(ET_USING_WHERE);
if (tab->packed_info & TAB_INFO_FULL_SCAN_ON_NULL)
- extra.append(STRING_WITH_LEN("; Full scan on NULL key"));
- /* Skip initial "; "*/
- const char *str= extra.ptr();
- uint32 len= extra.length();
- if (len)
- {
- str += 2;
- len -= 2;
- }
- item_list.push_back(new Item_string(str, len, cs));
+ eta->push_extra(ET_FULL_SCAN_ON_NULL_KEY);
}
else
{
uint keyno= MAX_KEY;
if (tab->ref.key_parts)
keyno= tab->ref.key;
- else if (tab->select && tab->select->quick)
- keyno = tab->select->quick->index;
+ else if (tab->select && quick)
+ keyno = quick->index;
if (keyno != MAX_KEY && keyno == table->file->pushed_idx_cond_keyno &&
table->file->pushed_idx_cond)
- extra.append(STRING_WITH_LEN("; Using index condition"));
+ eta->push_extra(ET_USING_INDEX_CONDITION);
else if (tab->cache_idx_cond)
- extra.append(STRING_WITH_LEN("; Using index condition(BKA)"));
+ eta->push_extra(ET_USING_INDEX_CONDITION_BKA);
if (quick_type == QUICK_SELECT_I::QS_TYPE_ROR_UNION ||
quick_type == QUICK_SELECT_I::QS_TYPE_ROR_INTERSECT ||
quick_type == QUICK_SELECT_I::QS_TYPE_INDEX_INTERSECT ||
quick_type == QUICK_SELECT_I::QS_TYPE_INDEX_MERGE)
{
- extra.append(STRING_WITH_LEN("; Using "));
- tab->select->quick->add_info_string(&extra);
+ eta->push_extra(ET_USING);
}
if (tab->select)
{
if (tab->use_quick == 2)
{
- /* 4 bits per 1 hex digit + terminating '\0' */
- char buf[MAX_KEY / 4 + 1];
- extra.append(STRING_WITH_LEN("; Range checked for each "
- "record (index map: 0x"));
- extra.append(tab->keys.print(buf));
- extra.append(')');
+ eta->push_extra(ET_RANGE_CHECKED_FOR_EACH_RECORD);
+ eta->range_checked_map= tab->keys;
}
- else if (tab->select->cond)
+ else if (tab->select->cond ||
+ (tab->cache_select && tab->cache_select->cond))
{
const COND *pushed_cond= tab->table->file->pushed_cond;
- if (((thd->variables.optimizer_switch &
- OPTIMIZER_SWITCH_ENGINE_CONDITION_PUSHDOWN) ||
- (tab->table->file->ha_table_flags() &
- HA_MUST_USE_TABLE_CONDITION_PUSHDOWN)) &&
- pushed_cond)
+ if (thd->use_cond_push(tab->table->file) && pushed_cond)
{
- extra.append(STRING_WITH_LEN("; Using where with pushed "
- "condition"));
- if (thd->lex->describe & DESCRIBE_EXTENDED)
+ eta->push_extra(ET_USING_WHERE_WITH_PUSHED_CONDITION);
+ /*
+ psergey-todo: what to do? This was useful with NDB only.
+
+ if (explain_flags & DESCRIBE_EXTENDED)
{
extra.append(STRING_WITH_LEN(": "));
((COND *)pushed_cond)->print(&extra, QT_ORDINARY);
}
+ */
}
else
- extra.append(STRING_WITH_LEN("; Using where"));
+ eta->push_extra(ET_USING_WHERE);
}
}
if (table_list /* SJM bushes don't have table_list */ &&
@@ -22839,19 +23861,20 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
table_list->schema_table->i_s_requested_object & OPTIMIZE_I_S_TABLE)
{
if (!table_list->table_open_method)
- extra.append(STRING_WITH_LEN("; Skip_open_table"));
+ eta->push_extra(ET_SKIP_OPEN_TABLE);
else if (table_list->table_open_method == OPEN_FRM_ONLY)
- extra.append(STRING_WITH_LEN("; Open_frm_only"));
+ eta->push_extra(ET_OPEN_FRM_ONLY);
else
- extra.append(STRING_WITH_LEN("; Open_full_table"));
+ eta->push_extra(ET_OPEN_FULL_TABLE);
+ /* psergey-note: the following has a bug.*/
if (table_list->has_db_lookup_value &&
table_list->has_table_lookup_value)
- extra.append(STRING_WITH_LEN("; Scanned 0 databases"));
+ eta->push_extra(ET_SCANNED_0_DATABASES);
else if (table_list->has_db_lookup_value ||
table_list->has_table_lookup_value)
- extra.append(STRING_WITH_LEN("; Scanned 1 database"));
+ eta->push_extra(ET_SCANNED_1_DATABASE);
else
- extra.append(STRING_WITH_LEN("; Scanned all databases"));
+ eta->push_extra(ET_SCANNED_ALL_DATABASES);
}
if (key_read)
{
@@ -22859,69 +23882,52 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
{
QUICK_GROUP_MIN_MAX_SELECT *qgs=
(QUICK_GROUP_MIN_MAX_SELECT *) tab->select->quick;
- extra.append(STRING_WITH_LEN("; Using index for group-by"));
- qgs->append_loose_scan_type(&extra);
+ eta->push_extra(ET_USING_INDEX_FOR_GROUP_BY);
+ eta->loose_scan_is_scanning= qgs->loose_scan_is_scanning();
}
else
- extra.append(STRING_WITH_LEN("; Using index"));
+ eta->push_extra(ET_USING_INDEX);
}
if (table->reginfo.not_exists_optimize)
- extra.append(STRING_WITH_LEN("; Not exists"));
+ eta->push_extra(ET_NOT_EXISTS);
- /*
- if (quick_type == QUICK_SELECT_I::QS_TYPE_RANGE &&
- !(((QUICK_RANGE_SELECT*)(tab->select->quick))->mrr_flags &
- HA_MRR_USE_DEFAULT_IMPL))
- {
- extra.append(STRING_WITH_LEN("; Using MRR"));
- }
- */
if (quick_type == QUICK_SELECT_I::QS_TYPE_RANGE)
{
- char mrr_str_buf[128];
- mrr_str_buf[0]=0;
- int len;
- uint mrr_flags=
- ((QUICK_RANGE_SELECT*)(tab->select->quick))->mrr_flags;
- len= table->file->multi_range_read_explain_info(mrr_flags,
- mrr_str_buf,
- sizeof(mrr_str_buf));
- if (len > 0)
- {
- extra.append(STRING_WITH_LEN("; "));
- extra.append(mrr_str_buf, len);
- }
+ explain_append_mrr_info((QUICK_RANGE_SELECT*)(tab->select->quick),
+ &eta->mrr_type);
+ if (eta->mrr_type.length() > 0)
+ eta->push_extra(ET_USING_MRR);
}
if (need_tmp_table)
{
need_tmp_table=0;
- extra.append(STRING_WITH_LEN("; Using temporary"));
+ xpl_sel->using_temporary= true;
}
if (need_order)
{
need_order=0;
- extra.append(STRING_WITH_LEN("; Using filesort"));
+ xpl_sel->using_filesort= true;
}
if (distinct & test_all_bits(used_tables,
join->select_list_used_tables))
- extra.append(STRING_WITH_LEN("; Distinct"));
+ eta->push_extra(ET_DISTINCT);
if (tab->loosescan_match_tab)
{
- extra.append(STRING_WITH_LEN("; LooseScan"));
+ eta->push_extra(ET_LOOSESCAN);
}
if (tab->first_weedout_table)
- extra.append(STRING_WITH_LEN("; Start temporary"));
+ eta->push_extra(ET_START_TEMPORARY);
if (tab->check_weed_out_table)
- extra.append(STRING_WITH_LEN("; End temporary"));
+ eta->push_extra(ET_END_TEMPORARY);
else if (tab->do_firstmatch)
{
- if (tab->do_firstmatch == join->join_tab - 1)
- extra.append(STRING_WITH_LEN("; FirstMatch"));
+ if (tab->do_firstmatch == /*join->join_tab*/ first_top_tab - 1)
+ eta->push_extra(ET_FIRST_MATCH);
else
{
- extra.append(STRING_WITH_LEN("; FirstMatch("));
+ eta->push_extra(ET_FIRST_MATCH);
TABLE *prev_table=tab->do_firstmatch->table;
if (prev_table->derived_select_number)
{
@@ -22930,11 +23936,10 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
int len= my_snprintf(namebuf, sizeof(namebuf)-1,
"<derived%u>",
prev_table->derived_select_number);
- extra.append(namebuf, len);
+ eta->firstmatch_table_name.append(namebuf, len);
}
else
- extra.append(prev_table->pos_in_table_list->alias);
- extra.append(STRING_WITH_LEN(")"));
+ eta->firstmatch_table_name.append(prev_table->pos_in_table_list->alias);
}
}
@@ -22942,34 +23947,84 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
{
if (tab->ref.cond_guards[part])
{
- extra.append(STRING_WITH_LEN("; Full scan on NULL key"));
+ eta->push_extra(ET_FULL_SCAN_ON_NULL_KEY);
break;
}
}
if (tab->cache)
{
- extra.append(STRING_WITH_LEN("; Using join buffer"));
- tab->cache->print_explain_comment(&extra);
+ eta->push_extra(ET_USING_JOIN_BUFFER);
+ tab->cache->save_explain_data(&eta->bka_type);
}
-
- /* Skip initial "; "*/
- const char *str= extra.ptr();
- uint32 len= extra.length();
- if (len)
- {
- str += 2;
- len -= 2;
- }
- item_list.push_back(new Item_string(str, len, cs));
}
+ if (saved_join_tab)
+ tab= saved_join_tab;
+
// For next iteration
used_tables|=table->map;
- if (result->send_data(item_list))
- join->error= 1;
}
+ output->add_node(xpl_sel);
}
+
+ for (SELECT_LEX_UNIT *unit= join->select_lex->first_inner_unit();
+ unit;
+ unit= unit->next_unit())
+ {
+ /*
+ Display subqueries only if
+ (1) they are not parts of ON clauses that were eliminated by table
+ elimination.
+ (2) they are not merged derived tables
+ */
+ if (!(unit->item && unit->item->eliminated) && // (1)
+ (!unit->derived || unit->derived->is_materialized_derived())) // (2)
+ {
+ explain_node->add_child(unit->first_select()->select_number);
+ }
+ }
+
+ if (!error && select_lex->is_top_level_node())
+ output->query_plan_ready();
+
+
+ DBUG_RETURN(error);
+}
+
+
+/*
+ This function serves as "shortcut point" for EXPLAIN queries.
+
+ The EXPLAIN statement executes just like its SELECT counterpart would
+ execute, except that JOIN::exec() will call select_describe() instead of
+ actually executing the query.
+
+ Inside select_describe():
+ - Query plan is updated with latest QEP choices made at the start of
+ JOIN::exec().
+ - the proces of "almost execution" is invoked for the children subqueries.
+
+ Overall, select_describe() is a legacy of old EXPLAIN implementation and
+ should be removed.
+*/
+
+static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
+ bool distinct,const char *message)
+{
+ THD *thd=join->thd;
+ select_result *result=join->result;
+ DBUG_ENTER("select_describe");
+
+ /* Update the QPF with latest values of using_temporary, using_filesort */
+ Explain_select *explain_sel;
+ uint select_nr= join->select_lex->select_number;
+ if ((explain_sel= thd->lex->explain->get_select(select_nr)))
+ {
+ explain_sel->using_temporary= need_tmp_table;
+ explain_sel->using_filesort= need_order;
+ }
+
for (SELECT_LEX_UNIT *unit= join->select_lex->first_inner_unit();
unit;
unit= unit->next_unit())
@@ -23012,13 +24067,13 @@ bool mysql_explain_union(THD *thd, SELECT_LEX_UNIT *unit, select_result *result)
for (SELECT_LEX *sl= first; sl; sl= sl->next_select())
{
- sl->set_explain_type();
+ sl->set_explain_type(FALSE);
sl->options|= SELECT_DESCRIBE;
}
if (unit->is_union())
{
- unit->fake_select_lex->select_number= UINT_MAX; // jost for initialization
+ unit->fake_select_lex->select_number= FAKE_SELECT_LEX_ID; // jost for initialization
unit->fake_select_lex->type= "UNION RESULT";
unit->fake_select_lex->options|= SELECT_DESCRIBE;
if (!(res= unit->prepare(thd, result, SELECT_NO_UNLOCK | SELECT_DESCRIBE)))
@@ -23128,6 +24183,7 @@ static void print_join(THD *thd,
List_iterator_fast<TABLE_LIST> ti(*tables);
TABLE_LIST **table;
uint non_const_tables= 0;
+ DBUG_ENTER("print_join");
for (TABLE_LIST *t= ti++; t ; t= ti++)
{
@@ -23141,13 +24197,13 @@ static void print_join(THD *thd,
if (!non_const_tables)
{
str->append(STRING_WITH_LEN("dual"));
- return; // all tables were optimized away
+ DBUG_VOID_RETURN; // all tables were optimized away
}
ti.rewind();
if (!(table= (TABLE_LIST **)thd->alloc(sizeof(TABLE_LIST*) *
non_const_tables)))
- return; // out of memory
+ DBUG_VOID_RETURN; // out of memory
TABLE_LIST *tmp, **t= table + (non_const_tables - 1);
while ((tmp= ti++))
@@ -23186,6 +24242,7 @@ static void print_join(THD *thd,
}
print_table_array(thd, eliminated_tables, str, table,
table + non_const_tables, query_type);
+ DBUG_VOID_RETURN;
}
/**
@@ -23311,6 +24368,22 @@ void TABLE_LIST::print(THD *thd, table_map eliminated_tables, String *str,
append_identifier(thd, str, table_name, table_name_length);
cmp_name= table_name;
}
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+ if (partition_names && partition_names->elements)
+ {
+ int i, num_parts= partition_names->elements;
+ List_iterator<String> name_it(*(partition_names));
+ str->append(STRING_WITH_LEN(" PARTITION ("));
+ for (i= 1; i <= num_parts; i++)
+ {
+ String *name= name_it++;
+ append_identifier(thd, str, name->c_ptr(), name->length());
+ if (i != num_parts)
+ str->append(',');
+ }
+ str->append(')');
+ }
+#endif /* WITH_PARTITION_STORAGE_ENGINE */
}
if (my_strcasecmp(table_alias_charset, cmp_name, alias))
{
@@ -23694,7 +24767,8 @@ JOIN::reoptimize(Item *added_where, table_map join_tables,
reset_query_plan();
if (!keyuse.buffer &&
- my_init_dynamic_array(&keyuse, sizeof(KEYUSE), 20, 64))
+ my_init_dynamic_array(&keyuse, sizeof(KEYUSE), 20, 64,
+ MYF(MY_THREAD_SPECIFIC)))
{
delete_dynamic(&added_keyuse);
return REOPT_ERROR;
@@ -23827,9 +24901,9 @@ test_if_cheaper_ordering(const JOIN_TAB *tab, ORDER *order, TABLE *table,
int best_key= -1;
bool is_best_covering= FALSE;
double fanout= 1;
- ha_rows table_records= table->file->stats.records;
+ ha_rows table_records= table->stat_records();
bool group= join && join->group && order == join->group_list;
- ha_rows ref_key_quick_rows= HA_POS_ERROR;
+ ha_rows refkey_rows_estimate= table->quick_condition_rows;
const bool has_limit= (select_limit_arg != HA_POS_ERROR);
/*
@@ -23855,10 +24929,6 @@ test_if_cheaper_ordering(const JOIN_TAB *tab, ORDER *order, TABLE *table,
else
keys= usable_keys;
- if (ref_key >= 0 && ref_key != MAX_KEY &&
- table->covering_keys.is_set(ref_key))
- ref_key_quick_rows= table->quick_rows[ref_key];
-
if (join)
{
uint tablenr= tab - join->join_tab;
@@ -23869,6 +24939,22 @@ test_if_cheaper_ordering(const JOIN_TAB *tab, ORDER *order, TABLE *table,
else
read_time= table->file->scan_time();
+ /*
+ Calculate the selectivity of the ref_key for REF_ACCESS. For
+ RANGE_ACCESS we use table->quick_condition_rows.
+ */
+ if (ref_key >= 0 && tab->type == JT_REF)
+ {
+ if (table->quick_keys.is_set(ref_key))
+ refkey_rows_estimate= table->quick_rows[ref_key];
+ else
+ {
+ const KEY *ref_keyinfo= table->key_info + ref_key;
+ refkey_rows_estimate= ref_keyinfo->rec_per_key[tab->ref.key_parts - 1];
+ }
+ set_if_bigger(refkey_rows_estimate, 1);
+ }
+
for (nr=0; nr < table->s->keys ; nr++)
{
int direction;
@@ -23909,17 +24995,17 @@ test_if_cheaper_ordering(const JOIN_TAB *tab, ORDER *order, TABLE *table,
if (group)
{
/*
- Used_key_parts can be larger than keyinfo->key_parts
+ Used_key_parts can be larger than keyinfo->user_defined_key_parts
when using a secondary index clustered with a primary
key (e.g. as in Innodb).
See Bug #28591 for details.
*/
- uint used_index_parts= keyinfo->key_parts;
+ uint used_index_parts= keyinfo->user_defined_key_parts;
uint used_pk_parts= 0;
if (used_key_parts > used_index_parts)
used_pk_parts= used_key_parts-used_index_parts;
rec_per_key= used_key_parts ?
- keyinfo->rec_per_key[used_key_parts-1] : 1;
+ keyinfo->actual_rec_per_key(used_key_parts-1) : 1;
/* Take into account the selectivity of the used pk prefix */
if (used_pk_parts)
{
@@ -23929,19 +25015,19 @@ test_if_cheaper_ordering(const JOIN_TAB *tab, ORDER *order, TABLE *table,
of the primary key are considered unknown we assume
they are equal to 1.
*/
- if (used_key_parts == pkinfo->key_parts ||
+ if (used_key_parts == pkinfo->user_defined_key_parts ||
pkinfo->rec_per_key[0] == 0)
rec_per_key= 1;
if (rec_per_key > 1)
{
- rec_per_key*= pkinfo->rec_per_key[used_pk_parts-1];
- rec_per_key/= pkinfo->rec_per_key[0];
+ rec_per_key*= pkinfo->actual_rec_per_key(used_pk_parts-1);
+ rec_per_key/= pkinfo->actual_rec_per_key(0);
/*
The value of rec_per_key for the extended key has
to be adjusted accordingly if some components of
the secondary key are included in the primary key.
*/
- for(uint i= 0; i < used_pk_parts; i++)
+ for(uint i= 1; i < used_pk_parts; i++)
{
if (pkinfo->key_part[i].field->key_start.is_set(nr))
{
@@ -23949,10 +25035,9 @@ test_if_cheaper_ordering(const JOIN_TAB *tab, ORDER *order, TABLE *table,
We presume here that for any index rec_per_key[i] != 0
if rec_per_key[0] != 0.
*/
- DBUG_ASSERT(pkinfo->rec_per_key[i]);
- DBUG_ASSERT(i > 0);
- rec_per_key*= pkinfo->rec_per_key[i-1];
- rec_per_key/= pkinfo->rec_per_key[i];
+ DBUG_ASSERT(pkinfo->actual_rec_per_key(i));
+ rec_per_key*= pkinfo->actual_rec_per_key(i-1);
+ rec_per_key/= pkinfo->actual_rec_per_key(i);
}
}
}
@@ -23986,18 +25071,18 @@ test_if_cheaper_ordering(const JOIN_TAB *tab, ORDER *order, TABLE *table,
with ref_key. Thus, to select first N records we have to scan
N/selectivity(ref_key) index entries.
selectivity(ref_key) = #scanned_records/#table_records =
- table->quick_condition_rows/table_records.
+ refkey_rows_estimate/table_records.
In any case we can't select more than #table_records.
- N/(table->quick_condition_rows/table_records) > table_records
- <=> N > table->quick_condition_rows.
- */
- if (select_limit > table->quick_condition_rows)
+ N/(refkey_rows_estimate/table_records) > table_records
+ <=> N > refkey_rows_estimate.
+ */
+ if (select_limit > refkey_rows_estimate)
select_limit= table_records;
else
select_limit= (ha_rows) (select_limit *
(double) table_records /
- table->quick_condition_rows);
- rec_per_key= keyinfo->rec_per_key[keyinfo->key_parts-1];
+ refkey_rows_estimate);
+ rec_per_key= keyinfo->actual_rec_per_key(keyinfo->user_defined_key_parts-1);
set_if_bigger(rec_per_key, 1);
/*
Here we take into account the fact that rows are
@@ -24011,24 +25096,28 @@ test_if_cheaper_ordering(const JOIN_TAB *tab, ORDER *order, TABLE *table,
index entry.
*/
index_scan_time= select_limit/rec_per_key *
- min(rec_per_key, table->file->scan_time());
+ MY_MIN(rec_per_key, table->file->scan_time());
if ((ref_key < 0 && (group || table->force_index || is_covering)) ||
index_scan_time < read_time)
{
ha_rows quick_records= table_records;
+ ha_rows refkey_select_limit= (ref_key >= 0 &&
+ table->covering_keys.is_set(ref_key)) ?
+ refkey_rows_estimate :
+ HA_POS_ERROR;
if ((is_best_covering && !is_covering) ||
- (is_covering && ref_key_quick_rows < select_limit))
+ (is_covering && refkey_select_limit < select_limit))
continue;
if (table->quick_keys.is_set(nr))
quick_records= table->quick_rows[nr];
if (best_key < 0 ||
- (select_limit <= min(quick_records,best_records) ?
- keyinfo->key_parts < best_key_parts :
+ (select_limit <= MY_MIN(quick_records,best_records) ?
+ keyinfo->user_defined_key_parts < best_key_parts :
quick_records < best_records) ||
(!is_best_covering && is_covering))
{
best_key= nr;
- best_key_parts= keyinfo->key_parts;
+ best_key_parts= keyinfo->user_defined_key_parts;
if (saved_best_key_parts)
*saved_best_key_parts= used_key_parts;
best_records= quick_records;
@@ -24061,6 +25150,8 @@ test_if_cheaper_ordering(const JOIN_TAB *tab, ORDER *order, TABLE *table,
@param table Table to find a key
@param select Pointer to access/update select->quick (if any)
@param limit LIMIT clause parameter
+ @param [out] scanned_limit How many records we expect to scan
+ Valid if *need_sort=FALSE.
@param [out] need_sort TRUE if filesort needed
@param [out] reverse
TRUE if the key is reversed again given ORDER (undefined if key == MAX_KEY)
@@ -24078,7 +25169,8 @@ test_if_cheaper_ordering(const JOIN_TAB *tab, ORDER *order, TABLE *table,
*/
uint get_index_for_order(ORDER *order, TABLE *table, SQL_SELECT *select,
- ha_rows limit, bool *need_sort, bool *reverse)
+ ha_rows limit, ha_rows *scanned_limit,
+ bool *need_sort, bool *reverse)
{
if (!order)
{
@@ -24107,7 +25199,8 @@ uint get_index_for_order(ORDER *order, TABLE *table, SQL_SELECT *select,
switch (test_if_order_by_key(order, table, select->quick->index,
&used_key_parts)) {
case 1: // desired order
- *need_sort= FALSE;
+ *need_sort= FALSE;
+ *scanned_limit= MY_MIN(limit, select->quick->records);
return select->quick->index;
case 0: // unacceptable order
*need_sort= TRUE;
@@ -24120,6 +25213,7 @@ uint get_index_for_order(ORDER *order, TABLE *table, SQL_SELECT *select,
{
select->set_quick(reverse_quick);
*need_sort= FALSE;
+ *scanned_limit= MY_MIN(limit, select->quick->records);
return select->quick->index;
}
else
@@ -24138,7 +25232,7 @@ uint get_index_for_order(ORDER *order, TABLE *table, SQL_SELECT *select,
Update quick_condition_rows since single table UPDATE/DELETE procedures
don't call make_join_statistics() and leave this variable uninitialized.
*/
- table->quick_condition_rows= table->file->stats.records;
+ table->quick_condition_rows= table->stat_records();
int key, direction;
if (test_if_cheaper_ordering(NULL, order, table,
@@ -24148,6 +25242,7 @@ uint get_index_for_order(ORDER *order, TABLE *table, SQL_SELECT *select,
!is_key_used(table, key, table->write_set))
{
*need_sort= FALSE;
+ *scanned_limit= limit;
*reverse= (direction < 0);
return key;
}
@@ -24156,6 +25251,78 @@ uint get_index_for_order(ORDER *order, TABLE *table, SQL_SELECT *select,
return MAX_KEY;
}
+/*
+ Count how much times conditions are true for several first rows of the table
+
+ @param thd thread handle
+ @param rows_to_read how much rows to check
+ @param table table which should be checked
+ @conds conds list of conditions and countars for them
+
+ @return number of really checked rows or 0 in case of error or empty table
+*/
+
+ulong check_selectivity(THD *thd,
+ ulong rows_to_read,
+ TABLE *table,
+ List<COND_STATISTIC> *conds)
+{
+ ulong count= 0;
+ COND_STATISTIC *cond;
+ List_iterator_fast<COND_STATISTIC> it(*conds);
+ handler *file= table->file;
+ uchar *record= table->record[0];
+ int error= 0;
+ DBUG_ENTER("check_selectivity");
+
+ DBUG_ASSERT(rows_to_read > 0);
+ while ((cond= it++))
+ {
+ DBUG_ASSERT(cond->cond);
+ DBUG_ASSERT(cond->cond->used_tables() == table->map);
+ cond->positive= 0;
+ }
+ it.rewind();
+
+ if (file->ha_rnd_init_with_error(1))
+ DBUG_RETURN(0);
+ do
+ {
+ error= file->ha_rnd_next(record);
+
+ if (thd->killed)
+ {
+ thd->send_kill_message();
+ count= 0;
+ goto err;
+ }
+ if (error)
+ {
+ if (error == HA_ERR_RECORD_DELETED)
+ continue;
+ if (error == HA_ERR_END_OF_FILE)
+ break;
+ goto err;
+ }
+
+ count++;
+ while ((cond= it++))
+ {
+ if (cond->cond->val_bool())
+ cond->positive++;
+ }
+ it.rewind();
+
+ } while (count < rows_to_read);
+
+ file->ha_rnd_end();
+ DBUG_RETURN(count);
+
+err:
+ DBUG_PRINT("error", ("error %d", error));
+ file->ha_rnd_end();
+ DBUG_RETURN(0);
+}
/**
@} (end of group Query_Optimizer)
diff --git a/sql/sql_select.h b/sql/sql_select.h
index 4650bc24c68..18a649bc47a 100644
--- a/sql/sql_select.h
+++ b/sql/sql_select.h
@@ -2,7 +2,7 @@
#define SQL_SELECT_INCLUDED
/* Copyright (c) 2000, 2013, Oracle and/or its affiliates.
- Copyright (c) 2008, 2013, Monty Program Ab.
+ Copyright (c) 2008, 2015, MariaDB
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
@@ -29,33 +29,10 @@
#endif
#include "procedure.h"
-#include <myisam.h>
#include "sql_array.h" /* Array */
#include "records.h" /* READ_RECORD */
#include "opt_range.h" /* SQL_SELECT, QUICK_SELECT_I */
-
-#if defined(WITH_ARIA_STORAGE_ENGINE)
-#include <maria.h>
-#endif
-#if defined(USE_ARIA_FOR_TMP_TABLES)
-#define TMP_ENGINE_HTON maria_hton
-inline uint tmp_table_max_key_length() {
- return maria_max_key_length();
-}
-
-inline uint tmp_table_max_key_parts() {
- return maria_max_key_segments();
-}
-#else
-#define TMP_ENGINE_HTON myisam_hton
-inline uint tmp_table_max_key_length() {
- return MI_MAX_KEY_LENGTH;
-}
-inline uint tmp_table_max_key_parts() {
- return MI_MAX_KEY_SEG;
-}
-#endif
/* Values in optimize */
#define KEY_OPTIMIZE_EXISTS 1
#define KEY_OPTIMIZE_REF_OR_NULL 2
@@ -114,6 +91,13 @@ typedef struct st_table_ref
uchar *key_buff; ///< value to look for with key
uchar *key_buff2; ///< key_buff+key_length
store_key **key_copy; //
+
+ /*
+ Bitmap of key parts which refer to constants. key_copy only has copiers for
+ non-const key parts.
+ */
+ key_part_map const_ref_part_map;
+
Item **items; ///< val()'s for each keypart
/*
Array of pointers to trigger variables. Some/all of the pointers may be
@@ -204,6 +188,12 @@ int rr_sequential(READ_RECORD *info);
int rr_sequential_and_unpack(READ_RECORD *info);
+#include "sql_explain.h"
+
+/**************************************************************************************
+ * New EXPLAIN structures END
+ *************************************************************************************/
+
class JOIN_CACHE;
class SJ_TMP_TABLE;
class JOIN_TAB_RANGE;
@@ -258,7 +248,8 @@ typedef struct st_join_table {
JOIN_TAB_RANGE *bush_children;
/* Special content for EXPLAIN 'Extra' column or NULL if none */
- const char *info;
+ enum explain_extra_tag info;
+
/*
Bitmap of TAB_INFO_* bits that encodes special line for EXPLAIN 'Extra'
column, or 0 if there is no info.
@@ -295,8 +286,11 @@ typedef struct st_join_table {
*/
double read_time;
- /* psergey-todo: make the below have type double, like POSITION::records_read? */
- ha_rows records_read;
+ /* Copy of POSITION::records_read, set by get_best_combination() */
+ double records_read;
+
+ /* The selectivity of the conditions that can be pushed to the table */
+ double cond_selectivity;
/* Startup cost for execution */
double startup_cost;
@@ -529,7 +523,7 @@ typedef struct st_join_table {
ha_rows get_examined_rows();
bool preread_init();
- bool is_sjm_nest() { return test(bush_children); }
+ bool is_sjm_nest() { return MY_TEST(bush_children); }
bool access_from_tables_is_allowed(table_map used_tables,
table_map sjm_lookup_tables)
@@ -540,6 +534,7 @@ typedef struct st_join_table {
!(used_sjm_lookup_tables & ~emb_sj_nest->sj_inner_tables));
}
+ void remove_redundant_bnl_scan_conds();
} JOIN_TAB;
@@ -721,8 +716,7 @@ public:
struct st_position *pos,
struct st_position *loose_scan_pos);
friend bool get_best_combination(JOIN *join);
- friend int setup_semijoin_dups_elimination(JOIN *join, ulonglong options,
- uint no_jbuf_after);
+ friend int setup_semijoin_loosescan(JOIN *join);
friend void fix_semijoin_strategies_for_picked_join_order(JOIN *join);
};
@@ -779,6 +773,9 @@ typedef struct st_position :public Sql_alloc
*/
double records_read;
+ /* The selectivity of the pushed down conditions */
+ double cond_selectivity;
+
/*
Cost accessing the table in course of the entire complete join execution,
i.e. cost of one access method use (e.g. 'range' or 'ref' scan ) times
@@ -787,7 +784,7 @@ typedef struct st_position :public Sql_alloc
double read_time;
/* Cumulative cost and record count for the join prefix */
- COST_VECT prefix_cost;
+ Cost_estimate prefix_cost;
double prefix_record_count;
/*
@@ -949,7 +946,7 @@ public:
*/
JOIN_TAB *table_access_tabs;
uint top_table_access_tabs_count;
-
+
JOIN_TAB **map2table; ///< mapping between table indexes and JOIN_TABs
JOIN_TAB *join_tab_save; ///< saved join_tab for subquery reexecution
@@ -1121,6 +1118,12 @@ public:
restore_no_rows_in_result() in ::reinit()
*/
bool no_rows_in_result_called;
+
+ /**
+ This is set if SQL_CALC_ROWS was calculated by filesort()
+ and should be taken from the appropriate JOIN_TAB
+ */
+ bool filesort_found_rows;
/**
Copy of this JOIN to be used with temporary tables.
@@ -1221,8 +1224,14 @@ public:
const char *zero_result_cause; ///< not 0 if exec must return zero result
bool union_part; ///< this subselect is part of union
+
+ enum join_optimization_state { NOT_OPTIMIZED=0,
+ OPTIMIZATION_IN_PROGRESS=1,
+ OPTIMIZATION_DONE=2};
bool optimized; ///< flag to avoid double optimization in EXPLAIN
bool initialized; ///< flag to avoid double init_execution calls
+
+ enum { QEP_NOT_PRESENT_YET, QEP_AVAILABLE, QEP_DELETED} have_query_plan;
/*
Additional WHERE and HAVING predicates to be considered for IN=>EXISTS
@@ -1290,7 +1299,7 @@ public:
lock= thd_arg->lock;
select_lex= 0; //for safety
tmp_join= 0;
- select_distinct= test(select_options & SELECT_DISTINCT);
+ select_distinct= MY_TEST(select_options & SELECT_DISTINCT);
no_order= 0;
simple_order= 0;
simple_group= 0;
@@ -1305,6 +1314,7 @@ public:
ref_pointer_array_size= 0;
zero_result_cause= 0;
optimized= 0;
+ have_query_plan= QEP_NOT_PRESENT_YET;
initialized= 0;
cleaned= 0;
cond_equal= 0;
@@ -1330,6 +1340,8 @@ public:
pre_sort_join_tab= NULL;
emb_sjm_nest= NULL;
sjm_lookup_tables= 0;
+
+ exec_saved_explain= false;
/*
The following is needed because JOIN::cleanup(true) may be called for
joins for which JOIN::optimize was aborted with an error before a proper
@@ -1338,15 +1350,24 @@ public:
table_access_tabs= NULL;
}
+ /*
+ TRUE <=> There was a JOIN::exec() call, which saved this JOIN's EXPLAIN.
+ The idea is that we also save at the end of JOIN::optimize(), but that
+ might not be the final plan.
+ */
+ bool exec_saved_explain;
+
int prepare(Item ***rref_pointer_array, TABLE_LIST *tables, uint wind_num,
COND *conds, uint og_num, ORDER *order, bool skip_order_by,
ORDER *group, Item *having, ORDER *proc_param, SELECT_LEX *select,
SELECT_LEX_UNIT *unit);
bool prepare_stage2();
int optimize();
+ int optimize_inner();
int reinit();
int init_execution();
void exec();
+ void exec_inner();
int destroy();
void restore_tmp();
bool alloc_func_list();
@@ -1422,7 +1443,7 @@ public:
void set_allowed_join_cache_types();
bool is_allowed_hash_join_access()
{
- return test(allowed_join_cache_types & JOIN_CACHE_HASHED_BIT) &&
+ return MY_TEST(allowed_join_cache_types & JOIN_CACHE_HASHED_BIT) &&
max_allowed_join_cache_level > JOIN_CACHE_HASHED_BIT;
}
/*
@@ -1441,7 +1462,7 @@ public:
return ((const_tables != table_count &&
((select_distinct || !simple_order || !simple_group) ||
(group_list && order) ||
- test(select_options & OPTION_BUFFER_RESULT))) ||
+ MY_TEST(select_options & OPTION_BUFFER_RESULT))) ||
(rollup.state != ROLLUP::STATE_NONE && select_distinct));
}
bool choose_subquery_plan(table_map join_tables);
@@ -1460,6 +1481,11 @@ public:
{
return (unit->item && unit->item->is_in_predicate());
}
+ void save_explain_data(Explain_query *output, bool can_overwrite,
+ bool need_tmp_table, bool need_order, bool distinct);
+ int save_explain_data_intern(Explain_query *output, bool need_tmp_table,
+ bool need_order, bool distinct,
+ const char *message);
private:
/**
TRUE if the query contains an aggregate function but has no GROUP
@@ -1523,21 +1549,8 @@ public:
store_key(THD *thd, Field *field_arg, uchar *ptr, uchar *null, uint length)
:null_key(0), null_ptr(null), err(0)
{
- if (field_arg->type() == MYSQL_TYPE_BLOB
- || field_arg->type() == MYSQL_TYPE_GEOMETRY)
- {
- /*
- Key segments are always packed with a 2 byte length prefix.
- See mi_rkey for details.
- */
- to_field= new Field_varstring(ptr, length, 2, null, 1,
- Field::NONE, field_arg->field_name,
- field_arg->table->s, field_arg->charset());
- to_field->init(field_arg->table);
- }
- else
- to_field=field_arg->new_key_field(thd->mem_root, field_arg->table,
- ptr, null, 1);
+ to_field=field_arg->new_key_field(thd->mem_root, field_arg->table,
+ ptr, length, null, 1);
}
store_key(store_key &arg)
:Sql_alloc(), null_key(arg.null_key), to_field(arg.to_field),
@@ -1775,10 +1788,6 @@ Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type,
bool table_cant_handle_bit_fields,
bool make_copy_field,
uint convert_blob_length);
-bool create_internal_tmp_table(TABLE *table, KEY *keyinfo,
- ENGINE_COLUMNDEF *start_recinfo,
- ENGINE_COLUMNDEF **recinfo,
- ulonglong options, my_bool big_tables);
/*
General routine to change field->ptr of a NULL-terminated array of Field
@@ -1813,8 +1822,12 @@ inline bool optimizer_flag(THD *thd, uint flag)
return (thd->variables.optimizer_switch & flag);
}
+int print_fake_select_lex_join(select_result_sink *result, bool on_the_fly,
+ SELECT_LEX *select_lex, uint8 select_options);
+
uint get_index_for_order(ORDER *order, TABLE *table, SQL_SELECT *select,
- ha_rows limit, bool *need_sort, bool *reverse);
+ ha_rows limit, ha_rows *scanned_limit,
+ bool *need_sort, bool *reverse);
ORDER *simple_remove_const(ORDER *order, COND *where);
bool const_expression_in_where(COND *cond, Item *comp_item,
Field *comp_field= NULL,
@@ -1828,6 +1841,31 @@ void eliminate_tables(JOIN *join);
/* Index Condition Pushdown entry point function */
void push_index_cond(JOIN_TAB *tab, uint keyno);
+#define OPT_LINK_EQUAL_FIELDS 1
+
+/* EXPLAIN-related utility functions */
+int print_explain_message_line(select_result_sink *result,
+ uint8 options,
+ uint select_number,
+ const char *select_type,
+ ha_rows *rows,
+ const char *message);
+void explain_append_mrr_info(QUICK_RANGE_SELECT *quick, String *res);
+int print_explain_row(select_result_sink *result,
+ uint8 options,
+ uint select_number,
+ const char *select_type,
+ const char *table_name,
+ const char *partitions,
+ enum join_type jtype,
+ const char *possible_keys,
+ const char *index,
+ const char *key_len,
+ const char *ref,
+ ha_rows *rows,
+ const char *extra);
+void make_possible_keys_line(TABLE *table, key_map possible_keys, String *line);
+
/****************************************************************************
Temporary table support for SQL Runtime
***************************************************************************/
@@ -1844,17 +1882,30 @@ TABLE *create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields,
bool keep_row_order= FALSE);
void free_tmp_table(THD *thd, TABLE *entry);
bool create_internal_tmp_table_from_heap(THD *thd, TABLE *table,
- ENGINE_COLUMNDEF *start_recinfo,
- ENGINE_COLUMNDEF **recinfo,
+ TMP_ENGINE_COLUMNDEF *start_recinfo,
+ TMP_ENGINE_COLUMNDEF **recinfo,
int error, bool ignore_last_dupp_key_error,
bool *is_duplicate);
bool create_internal_tmp_table(TABLE *table, KEY *keyinfo,
- ENGINE_COLUMNDEF *start_recinfo,
- ENGINE_COLUMNDEF **recinfo,
+ TMP_ENGINE_COLUMNDEF *start_recinfo,
+ TMP_ENGINE_COLUMNDEF **recinfo,
ulonglong options);
bool open_tmp_table(TABLE *table);
void setup_tmp_table_column_bitmaps(TABLE *table, uchar *bitmaps);
double prev_record_reads(POSITION *positions, uint idx, table_map found_ref);
void fix_list_after_tbl_changes(SELECT_LEX *new_parent, List<TABLE_LIST> *tlist);
+struct st_cond_statistic
+{
+ Item *cond;
+ Field *field_arg;
+ ulong positive;
+};
+typedef struct st_cond_statistic COND_STATISTIC;
+
+ulong check_selectivity(THD *thd,
+ ulong rows_to_read,
+ TABLE *table,
+ List<COND_STATISTIC> *conds);
+
#endif /* SQL_SELECT_INCLUDED */
diff --git a/sql/sql_servers.cc b/sql/sql_servers.cc
index dad7ab152ed..78d11a6bebf 100644
--- a/sql/sql_servers.cc
+++ b/sql/sql_servers.cc
@@ -33,12 +33,12 @@
currently running transactions etc will not be disrupted.
*/
+#include <my_global.h>
#include "sql_priv.h"
#include "sql_servers.h"
#include "unireg.h"
#include "sql_base.h" // close_mysql_tables
#include "records.h" // init_read_record, end_read_record
-#include "hash_filo.h"
#include <m_ctype.h>
#include <stdarg.h>
#include "sp_head.h"
@@ -158,7 +158,7 @@ bool servers_init(bool dont_read_servers_table)
}
/* Initialize the mem root for data */
- init_sql_alloc(&mem, ACL_ALLOC_BLOCK_SIZE, 0);
+ init_sql_alloc(&mem, ACL_ALLOC_BLOCK_SIZE, 0, MYF(MY_THREAD_SPECIFIC));
if (dont_read_servers_table)
goto end;
@@ -177,8 +177,6 @@ bool servers_init(bool dont_read_servers_table)
*/
return_val= servers_reload(thd);
delete thd;
- /* Remember that we don't have a THD */
- my_pthread_setspecific_ptr(THR_THD, 0);
end:
DBUG_RETURN(return_val);
@@ -209,7 +207,7 @@ static bool servers_load(THD *thd, TABLE_LIST *tables)
my_hash_reset(&servers_cache);
free_root(&mem, MYF(0));
- init_sql_alloc(&mem, ACL_ALLOC_BLOCK_SIZE, 0);
+ init_sql_alloc(&mem, ACL_ALLOC_BLOCK_SIZE, 0, MYF(0));
if (init_read_record(&read_record_info,thd,table=tables[0].table,NULL,1,0,
FALSE))
@@ -265,9 +263,9 @@ bool servers_reload(THD *thd)
Execution might have been interrupted; only print the error message
if an error condition has been raised.
*/
- if (thd->stmt_da->is_error())
+ if (thd->get_stmt_da()->is_error())
sql_print_error("Can't open and lock privilege tables: %s",
- thd->stmt_da->message());
+ thd->get_stmt_da()->message());
return_val= FALSE;
goto end;
}
@@ -632,7 +630,7 @@ int drop_server(THD *thd, LEX_SERVER_OPTIONS *server_options)
if (close_cached_connection_tables(thd, &name))
{
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_UNKNOWN_ERROR, "Server connection in use");
}
@@ -1061,7 +1059,7 @@ int alter_server(THD *thd, LEX_SERVER_OPTIONS *server_options)
if (close_cached_connection_tables(thd, &name))
{
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_UNKNOWN_ERROR, "Server connection in use");
}
diff --git a/sql/sql_show.cc b/sql/sql_show.cc
index be45eecabf7..aef73ccf205 100644
--- a/sql/sql_show.cc
+++ b/sql/sql_show.cc
@@ -17,7 +17,7 @@
/* Function with list databases, tables or fields */
-#include "my_global.h" /* NO_EMBEDDED_ACCESS_CHECKS */
+#include "sql_plugin.h" // Includes my_global.h
#include "sql_priv.h"
#include "unireg.h"
#include "sql_acl.h" // fill_schema_*_privileges
@@ -45,6 +45,7 @@
#include "set_var.h"
#include "sql_trigger.h"
#include "sql_derived.h"
+#include "sql_statistics.h"
#include "sql_connect.h"
#include "authors.h"
#include "contributors.h"
@@ -56,13 +57,11 @@
#include <my_dir.h>
#include "lock.h" // MYSQL_OPEN_IGNORE_FLUSH
#include "debug_sync.h"
-#include "datadict.h" // dd_frm_type()
#include "keycaches.h"
#if !defined(MYSQL_MAX_VARIABLE_VALUE_LEN)
#define MYSQL_MAX_VARIABLE_VALUE_LEN 1024
#endif // !defined(MYSQL_MAX_VARIABLE_VALUE_LEN)
-#define STR_OR_NIL(S) ((S) ? (S) : "<nil>")
#ifdef WITH_PARTITION_STORAGE_ENGINE
#include "ha_partition.h"
@@ -95,6 +94,8 @@ enum enum_i_s_events_fields
ISE_DB_CL
};
+#define USERNAME_WITH_HOST_CHAR_LENGTH (USERNAME_CHAR_LENGTH + HOSTNAME_LENGTH + 2)
+
#ifndef NO_EMBEDDED_ACCESS_CHECKS
static const char *grant_names[]={
"select","insert","update","delete","create","drop","reload","shutdown",
@@ -119,8 +120,47 @@ static void get_cs_converted_string_value(THD *thd,
bool use_hex);
#endif
+static int show_create_view(THD *thd, TABLE_LIST *table, String *buff);
+
static COND * make_cond_for_info_schema(COND *cond, TABLE_LIST *table);
+/**
+ Condition pushdown used for INFORMATION_SCHEMA / SHOW queries.
+ This structure is to implement an optimization when
+ accessing data dictionary data in the INFORMATION_SCHEMA
+ or SHOW commands.
+ When the query contain a TABLE_SCHEMA or TABLE_NAME clause,
+ narrow the search for data based on the constraints given.
+*/
+typedef struct st_lookup_field_values
+{
+ /**
+ Value of a TABLE_SCHEMA clause.
+ Note that this value length may exceed @c NAME_LEN.
+ @sa wild_db_value
+ */
+ LEX_STRING db_value;
+ /**
+ Value of a TABLE_NAME clause.
+ Note that this value length may exceed @c NAME_LEN.
+ @sa wild_table_value
+ */
+ LEX_STRING table_value;
+ /**
+ True when @c db_value is a LIKE clause,
+ false when @c db_value is an '=' clause.
+ */
+ bool wild_db_value;
+ /**
+ True when @c table_value is a LIKE clause,
+ false when @c table_value is an '=' clause.
+ */
+ bool wild_table_value;
+} LOOKUP_FIELD_VALUES;
+
+
+bool get_lookup_field_values(THD *, COND *, TABLE_LIST *, LOOKUP_FIELD_VALUES *);
+
/***************************************************************************
** List all table types supported
***************************************************************************/
@@ -159,7 +199,6 @@ static my_bool show_plugins(THD *thd, plugin_ref plugin,
cs);
switch (plugin_state(plugin)) {
- /* case PLUGIN_IS_FREED: does not happen */
case PLUGIN_IS_DELETED:
table->field[2]->store(STRING_WITH_LEN("DELETED"), cs);
break;
@@ -172,6 +211,9 @@ static my_bool show_plugins(THD *thd, plugin_ref plugin,
case PLUGIN_IS_DISABLED:
table->field[2]->store(STRING_WITH_LEN("DISABLED"), cs);
break;
+ case PLUGIN_IS_FREED: // filtered in fill_plugins, used in fill_all_plugins
+ table->field[2]->store(STRING_WITH_LEN("NOT INSTALLED"), cs);
+ break;
default:
DBUG_ASSERT(0);
}
@@ -269,6 +311,65 @@ int fill_plugins(THD *thd, TABLE_LIST *tables, COND *cond)
}
+int fill_all_plugins(THD *thd, TABLE_LIST *tables, COND *cond)
+{
+ DBUG_ENTER("fill_all_plugins");
+ TABLE *table= tables->table;
+ LOOKUP_FIELD_VALUES lookup;
+
+ if (get_lookup_field_values(thd, cond, tables, &lookup))
+ DBUG_RETURN(0);
+
+ if (lookup.db_value.str && !lookup.db_value.str[0])
+ DBUG_RETURN(0); // empty string never matches a valid SONAME
+
+ MY_DIR *dirp= my_dir(opt_plugin_dir, MY_THREAD_SPECIFIC);
+ if (!dirp)
+ {
+ my_error(ER_CANT_READ_DIR, MYF(0), opt_plugin_dir, my_errno);
+ DBUG_RETURN(1);
+ }
+
+ if (!lookup.db_value.str)
+ plugin_dl_foreach(thd, 0, show_plugins, table);
+
+ const char *wstr= lookup.db_value.str, *wend= wstr + lookup.db_value.length;
+ for (uint i=0; i < (uint) dirp->number_of_files; i++)
+ {
+ FILEINFO *file= dirp->dir_entry+i;
+ LEX_STRING dl= { file->name, strlen(file->name) };
+ const char *dlend= dl.str + dl.length;
+ const size_t so_ext_len= sizeof(SO_EXT) - 1;
+
+ if (strcasecmp(dlend - so_ext_len, SO_EXT))
+ continue;
+
+ if (lookup.db_value.str)
+ {
+ if (lookup.wild_db_value)
+ {
+ if (my_wildcmp(files_charset_info, dl.str, dlend, wstr, wend,
+ wild_prefix, wild_one, wild_many))
+ continue;
+ }
+ else
+ {
+ if (my_strnncoll(files_charset_info,
+ (uchar*)dl.str, dl.length,
+ (uchar*)lookup.db_value.str, lookup.db_value.length))
+ continue;
+ }
+ }
+
+ plugin_dl_foreach(thd, &dl, show_plugins, table);
+ thd->clear_error();
+ }
+
+ my_dirend(dirp);
+ DBUG_RETURN(0);
+}
+
+
/***************************************************************************
** List all Authors.
** If you can update it, you get to be in it :)
@@ -282,7 +383,7 @@ bool mysqld_show_authors(THD *thd)
field_list.push_back(new Item_empty_string("Name",40));
field_list.push_back(new Item_empty_string("Location",40));
- field_list.push_back(new Item_empty_string("Comment",80));
+ field_list.push_back(new Item_empty_string("Comment",512));
if (protocol->send_result_set_metadata(&field_list,
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
@@ -316,7 +417,7 @@ bool mysqld_show_contributors(THD *thd)
field_list.push_back(new Item_empty_string("Name",40));
field_list.push_back(new Item_empty_string("Location",40));
- field_list.push_back(new Item_empty_string("Comment",80));
+ field_list.push_back(new Item_empty_string("Comment", 512));
if (protocol->send_result_set_metadata(&field_list,
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
@@ -457,7 +558,7 @@ bool
ignore_db_dirs_init()
{
return my_init_dynamic_array(&ignore_db_dirs_array, sizeof(LEX_STRING *),
- 0, 0);
+ 0, 0, MYF(0));
}
@@ -688,6 +789,11 @@ db_name_is_in_ignore_db_dirs_list(const char *directory)
return my_hash_search(&ignore_db_dirs_hash, (uchar *) buff, buff_len)!=NULL;
}
+enum find_files_result {
+ FIND_FILES_OK,
+ FIND_FILES_OOM,
+ FIND_FILES_DIR
+};
/*
find_files() - find files in a given directory.
@@ -696,11 +802,10 @@ db_name_is_in_ignore_db_dirs_list(const char *directory)
find_files()
thd thread handler
files put found files in this list
- db database name to set in TABLE_LIST structure
+ db database name to search tables in
+ or NULL to search for databases
path path to database
wild filter for found files
- dir read databases in path if TRUE, read .frm files in
- database otherwise
RETURN
FIND_FILES_OK success
@@ -709,64 +814,40 @@ db_name_is_in_ignore_db_dirs_list(const char *directory)
*/
-find_files_result
-find_files(THD *thd, List<LEX_STRING> *files, const char *db,
- const char *path, const char *wild, bool dir)
+static find_files_result
+find_files(THD *thd, Dynamic_array<LEX_STRING*> *files, LEX_STRING *db,
+ const char *path, const LEX_STRING *wild)
{
- uint i;
- char *ext;
MY_DIR *dirp;
- FILEINFO *file;
- LEX_STRING *file_name= 0;
- uint file_name_len;
-#ifndef NO_EMBEDDED_ACCESS_CHECKS
- uint col_access=thd->col_access;
-#endif
- uint wild_length= 0;
- TABLE_LIST table_list;
+ Discovered_table_list tl(thd, files, wild);
DBUG_ENTER("find_files");
- if (wild)
- {
- if (!wild[0])
- wild= 0;
- else
- wild_length= strlen(wild);
- }
-
- bzero((char*) &table_list,sizeof(table_list));
-
- if (!(dirp = my_dir(path,MYF(dir ? MY_WANT_STAT : 0))))
+ if (!(dirp = my_dir(path, MY_THREAD_SPECIFIC | (db ? 0 : MY_WANT_STAT))))
{
if (my_errno == ENOENT)
- my_error(ER_BAD_DB_ERROR, MYF(ME_BELL+ME_WAITTANG), db);
+ my_error(ER_BAD_DB_ERROR, MYF(ME_BELL | ME_WAITTANG), db->str);
else
- my_error(ER_CANT_READ_DIR, MYF(ME_BELL+ME_WAITTANG), path, my_errno);
+ my_error(ER_CANT_READ_DIR, MYF(ME_BELL | ME_WAITTANG), path, my_errno);
DBUG_RETURN(FIND_FILES_DIR);
}
- for (i=0 ; i < (uint) dirp->number_off_files ; i++)
+ if (!db) /* Return databases */
{
- char uname[SAFE_NAME_LEN + 1]; /* Unencoded name */
- file=dirp->dir_entry+i;
- if (dir)
- { /* Return databases */
- if ((file->name[0] == '.' &&
- ((file->name[1] == '.' && file->name[2] == '\0') ||
- file->name[1] == '\0')))
- continue; /* . or .. */
+ for (uint i=0; i < (uint) dirp->number_of_files; i++)
+ {
+ FILEINFO *file= dirp->dir_entry+i;
#ifdef USE_SYMDIR
char *ext;
char buff[FN_REFLEN];
if (my_use_symdir && !strcmp(ext=fn_ext(file->name), ".sym"))
{
- /* Only show the sym file if it points to a directory */
- char *end;
+ /* Only show the sym file if it points to a directory */
+ char *end;
*ext=0; /* Remove extension */
- unpack_dirname(buff, file->name);
- end= strend(buff);
- if (end != buff && end[-1] == FN_LIBCHAR)
- end[-1]= 0; // Remove end FN_LIBCHAR
+ unpack_dirname(buff, file->name);
+ end= strend(buff);
+ if (end != buff && end[-1] == FN_LIBCHAR)
+ end[-1]= 0; // Remove end FN_LIBCHAR
if (!mysql_file_stat(key_file_misc, buff, file->mystat, MYF(0)))
continue;
}
@@ -777,70 +858,25 @@ find_files(THD *thd, List<LEX_STRING> *files, const char *db,
if (is_in_ignore_db_dirs_list(file->name))
continue;
- file_name_len= filename_to_tablename(file->name, uname, sizeof(uname));
- if (wild)
- {
- if (lower_case_table_names)
- {
- if (my_wildcmp(files_charset_info,
- uname, uname + file_name_len,
- wild, wild + wild_length,
- wild_prefix, wild_one, wild_many))
- continue;
- }
- else if (wild_compare(uname, wild, 0))
- continue;
- }
- }
- else
- {
- // Return only .frm files which aren't temp files.
- if (my_strcasecmp(system_charset_info, ext=fn_rext(file->name),reg_ext) ||
- is_prefix(file->name, tmp_file_prefix))
- continue;
- *ext=0;
- file_name_len= filename_to_tablename(file->name, uname, sizeof(uname));
- if (wild)
- {
- if (lower_case_table_names)
- {
- if (my_wildcmp(files_charset_info,
- uname, uname + file_name_len,
- wild, wild + wild_length,
- wild_prefix, wild_one,wild_many))
- continue;
- }
- else if (wild_compare(uname, wild, 0))
- continue;
- }
- }
-#ifndef NO_EMBEDDED_ACCESS_CHECKS
- /* Don't show tables where we don't have any privileges */
- if (db && !(col_access & TABLE_ACLS))
- {
- table_list.db= (char*) db;
- table_list.db_length= strlen(db);
- table_list.table_name= uname;
- table_list.table_name_length= file_name_len;
- table_list.grant.privilege=col_access;
- if (check_grant(thd, TABLE_ACLS, &table_list, TRUE, 1, TRUE))
- continue;
- }
-#endif
- if (!(file_name=
- thd->make_lex_string(file_name, uname, file_name_len, TRUE)) ||
- files->push_back(file_name))
- {
- my_dirend(dirp);
- DBUG_RETURN(FIND_FILES_OOM);
+ if (tl.add_file(file->name))
+ goto err;
}
+ tl.sort();
+ }
+ else
+ {
+ if (ha_discover_table_names(thd, db, dirp, &tl, false))
+ goto err;
}
- DBUG_PRINT("info",("found: %d files", files->elements));
- my_dirend(dirp);
- (void) ha_find_files(thd, db, path, wild, dir, files);
+ DBUG_PRINT("info",("found: %zu files", files->elements()));
+ my_dirend(dirp);
DBUG_RETURN(FIND_FILES_OK);
+
+err:
+ my_dirend(dirp);
+ DBUG_RETURN(FIND_FILES_OOM);
}
@@ -881,7 +917,7 @@ public:
m_view_access_denied_message_ptr(NULL)
{
- m_sctx = test(m_top_view->security_ctx) ?
+ m_sctx= MY_TEST(m_top_view->security_ctx) ?
m_top_view->security_ctx : thd->security_ctx;
}
@@ -908,8 +944,8 @@ public:
}
bool handle_condition(THD *thd, uint sql_errno, const char * /* sqlstate */,
- MYSQL_ERROR::enum_warning_level level,
- const char *message, MYSQL_ERROR ** /* cond_hdl */)
+ Sql_condition::enum_warning_level level,
+ const char *message, Sql_condition ** /* cond_hdl */)
{
/*
The handler does not handle the errors raised by itself.
@@ -943,7 +979,7 @@ public:
case ER_NO_SUCH_TABLE_IN_ENGINE:
/* Established behavior: warn if underlying tables, columns, or functions
are missing. */
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_VIEW_INVALID,
ER(ER_VIEW_INVALID),
m_top_view->get_db_name(),
@@ -999,9 +1035,10 @@ mysqld_show_create(THD *thd, TABLE_LIST *table_list)
{
/*
- Use open_tables() directly rather than open_normal_and_derived_tables().
- This ensures that close_thread_tables() is not called if open tables fails
- and the error is ignored. This allows us to handle broken views nicely.
+ Use open_tables() directly rather than
+ open_normal_and_derived_tables(). This ensures that
+ close_thread_tables() is not called if open tables fails and the
+ error is ignored. This allows us to handle broken views nicely.
*/
uint counter;
Show_create_error_handler view_error_suppressor(thd, table_list);
@@ -1029,16 +1066,15 @@ mysqld_show_create(THD *thd, TABLE_LIST *table_list)
buffer.set_charset(table_list->view_creation_ctx->get_client_cs());
if ((table_list->view ?
- view_store_create_info(thd, table_list, &buffer) :
- store_create_info(thd, table_list, &buffer, NULL,
- FALSE /* show_database */)))
+ show_create_view(thd, table_list, &buffer) :
+ show_create_table(thd, table_list, &buffer, NULL, WITHOUT_DB_NAME)))
goto exit;
if (table_list->view)
{
field_list.push_back(new Item_empty_string("View",NAME_CHAR_LEN));
field_list.push_back(new Item_empty_string("Create View",
- max(buffer.length(),1024)));
+ MY_MAX(buffer.length(),1024)));
field_list.push_back(new Item_empty_string("character_set_client",
MY_CS_NAME_SIZE));
field_list.push_back(new Item_empty_string("collation_connection",
@@ -1049,7 +1085,7 @@ mysqld_show_create(THD *thd, TABLE_LIST *table_list)
field_list.push_back(new Item_empty_string("Table",NAME_CHAR_LEN));
// 1024 is for not to confuse old clients
field_list.push_back(new Item_empty_string("Create Table",
- max(buffer.length(),1024)));
+ MY_MAX(buffer.length(),1024)));
}
if (protocol->send_result_set_metadata(&field_list,
@@ -1095,7 +1131,8 @@ exit:
DBUG_RETURN(error);
}
-bool mysqld_show_create_db(THD *thd, char *dbname,
+bool mysqld_show_create_db(THD *thd, LEX_STRING *dbname,
+ LEX_STRING *orig_dbname,
HA_CREATE_INFO *create_info)
{
char buff[2048];
@@ -1113,32 +1150,32 @@ bool mysqld_show_create_db(THD *thd, char *dbname,
if (test_all_bits(sctx->master_access, DB_ACLS))
db_access=DB_ACLS;
else
- db_access= (acl_get(sctx->host, sctx->ip, sctx->priv_user, dbname, 0) |
+ db_access= (acl_get(sctx->host, sctx->ip, sctx->priv_user, dbname->str, 0) |
sctx->master_access);
- if (!(db_access & DB_ACLS) && check_grant_db(thd,dbname))
+ if (!(db_access & DB_ACLS) && check_grant_db(thd,dbname->str))
{
status_var_increment(thd->status_var.access_denied_errors);
my_error(ER_DBACCESS_DENIED_ERROR, MYF(0),
- sctx->priv_user, sctx->host_or_ip, dbname);
+ sctx->priv_user, sctx->host_or_ip, dbname->str);
general_log_print(thd,COM_INIT_DB,ER(ER_DBACCESS_DENIED_ERROR),
- sctx->priv_user, sctx->host_or_ip, dbname);
+ sctx->priv_user, sctx->host_or_ip, orig_dbname->str);
DBUG_RETURN(TRUE);
}
#endif
- if (is_infoschema_db(dbname))
+ if (is_infoschema_db(dbname->str))
{
- dbname= INFORMATION_SCHEMA_NAME.str;
+ *dbname= INFORMATION_SCHEMA_NAME;
create.default_table_charset= system_charset_info;
}
else
{
- if (check_db_dir_existence(dbname))
+ if (check_db_dir_existence(dbname->str))
{
- my_error(ER_BAD_DB_ERROR, MYF(0), dbname);
+ my_error(ER_BAD_DB_ERROR, MYF(0), dbname->str);
DBUG_RETURN(TRUE);
}
- load_db_opt_by_name(thd, dbname, &create);
+ load_db_opt_by_name(thd, dbname->str, &create);
}
List<Item> field_list;
field_list.push_back(new Item_empty_string("Database",NAME_CHAR_LEN));
@@ -1149,12 +1186,12 @@ bool mysqld_show_create_db(THD *thd, char *dbname,
DBUG_RETURN(TRUE);
protocol->prepare_for_resend();
- protocol->store(dbname, strlen(dbname), system_charset_info);
+ protocol->store(orig_dbname->str, orig_dbname->length, system_charset_info);
buffer.length(0);
buffer.append(STRING_WITH_LEN("CREATE DATABASE "));
if (create_options & HA_LEX_CREATE_IF_NOT_EXISTS)
buffer.append(STRING_WITH_LEN("/*!32312 IF NOT EXISTS*/ "));
- append_identifier(thd, &buffer, dbname, strlen(dbname));
+ append_identifier(thd, &buffer, dbname->str, dbname->length);
if (create.default_table_charset)
{
@@ -1283,9 +1320,22 @@ append_identifier(THD *thd, String *packet, const char *name, uint length)
it's a keyword
*/
+ /*
+ Special code for swe7. It encodes the letter "E WITH ACUTE" on
+ the position 0x60, where backtick normally resides.
+ In swe7 we cannot append 0x60 using system_charset_info,
+ because it cannot be converted to swe7 and will be replaced to
+ question mark '?'. Use &my_charset_bin to avoid this.
+ It will prevent conversion and will append the backtick as is.
+ */
+ CHARSET_INFO *quote_charset= q == 0x60 &&
+ (packet->charset()->state & MY_CS_NONASCII) &&
+ packet->charset()->mbmaxlen == 1 ?
+ &my_charset_bin : system_charset_info;
+
(void) packet->reserve(length*2 + 2);
quote_char= (char) q;
- if (packet->append(&quote_char, 1, system_charset_info))
+ if (packet->append(&quote_char, 1, quote_charset))
return true;
for (name_end= name+length ; name < name_end ; name+= length)
@@ -1302,12 +1352,12 @@ append_identifier(THD *thd, String *packet, const char *name, uint length)
if (!length)
length= 1;
if (length == 1 && chr == (uchar) quote_char &&
- packet->append(&quote_char, 1, system_charset_info))
+ packet->append(&quote_char, 1, quote_charset))
return true;
if (packet->append(name, length, system_charset_info))
return true;
}
- return packet->append(&quote_char, 1, system_charset_info);
+ return packet->append(&quote_char, 1, quote_charset);
}
@@ -1377,8 +1427,35 @@ static void append_directory(THD *thd, String *packet, const char *dir_type,
#define LIST_PROCESS_HOST_LEN 64
-static bool get_field_default_value(THD *thd, Field *timestamp_field,
- Field *field, String *def_value,
+
+/**
+ Print "ON UPDATE" clause of a field into a string.
+
+ @param timestamp_field Pointer to timestamp field of a table.
+ @param field The field to generate ON UPDATE clause for.
+ @bool lcase Whether to print in lower case.
+ @return false on success, true on error.
+*/
+static bool print_on_update_clause(Field *field, String *val, bool lcase)
+{
+ DBUG_ASSERT(val->charset()->mbminlen == 1);
+ val->length(0);
+ if (field->has_update_default_function())
+ {
+ if (lcase)
+ val->append(STRING_WITH_LEN("on update "));
+ else
+ val->append(STRING_WITH_LEN("ON UPDATE "));
+ val->append(STRING_WITH_LEN("CURRENT_TIMESTAMP"));
+ if (field->decimals() > 0)
+ val->append_parenthesized(field->decimals());
+ return true;
+ }
+ return false;
+}
+
+
+static bool get_field_default_value(THD *thd, Field *field, String *def_value,
bool quoted)
{
bool has_default;
@@ -1389,8 +1466,7 @@ static bool get_field_default_value(THD *thd, Field *timestamp_field,
We are using CURRENT_TIMESTAMP instead of NOW because it is
more standard
*/
- has_now_default= (timestamp_field == field &&
- field->unireg_check != Field::TIMESTAMP_UN_FIELD);
+ has_now_default= field->has_insert_default_function();
has_default= (field_type != FIELD_TYPE_BLOB &&
!(field->flags & NO_DEFAULT_VALUE_FLAG) &&
@@ -1402,7 +1478,11 @@ static bool get_field_default_value(THD *thd, Field *timestamp_field,
if (has_default)
{
if (has_now_default)
+ {
def_value->append(STRING_WITH_LEN("CURRENT_TIMESTAMP"));
+ if (field->decimals() > 0)
+ def_value->append_parenthesized(field->decimals());
+ }
else if (!field->is_null())
{ // Not null by default
char tmp[MAX_FIELD_WIDTH];
@@ -1451,13 +1531,34 @@ static bool get_field_default_value(THD *thd, Field *timestamp_field,
@param thd thread handler
@param packet string to append
@param opt list of options
+ @param check_options only print known options
+ @param rules list of known options
*/
static void append_create_options(THD *thd, String *packet,
- engine_option_value *opt)
+ engine_option_value *opt,
+ bool check_options,
+ ha_create_table_option *rules)
{
+ bool in_comment= false;
for(; opt; opt= opt->next)
{
+ if (check_options)
+ {
+ if (is_engine_option_known(opt, rules))
+ {
+ if (in_comment)
+ packet->append(STRING_WITH_LEN(" */"));
+ in_comment= false;
+ }
+ else
+ {
+ if (!in_comment)
+ packet->append(STRING_WITH_LEN(" /*"));
+ in_comment= true;
+ }
+ }
+
DBUG_ASSERT(opt->value.str);
packet->append(' ');
append_identifier(thd, packet, opt->name.str, opt->name.length);
@@ -1467,13 +1568,15 @@ static void append_create_options(THD *thd, String *packet,
else
packet->append(opt->value.str, opt->value.length);
}
+ if (in_comment)
+ packet->append(STRING_WITH_LEN(" */"));
}
/*
Build a CREATE TABLE statement for a table.
SYNOPSIS
- store_create_info()
+ show_create_table()
thd The thread
table_list A list containing one table to write statement
for.
@@ -1483,11 +1586,7 @@ static void append_create_options(THD *thd, String *packet,
to tailor the format of the statement. Can be
NULL, in which case only SQL_MODE is considered
when building the statement.
- show_database If true, then print the database before the table
- name. The database name is only printed in the event
- that it is different from the current database.
- If false, then do not print the database before
- the table name.
+ with_db_name Add database name to table name
NOTE
Currently always return 0, but might return error code in the
@@ -1497,8 +1596,9 @@ static void append_create_options(THD *thd, String *packet,
0 OK
*/
-int store_create_info(THD *thd, TABLE_LIST *table_list, String *packet,
- HA_CREATE_INFO *create_info_arg, bool show_database)
+int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet,
+ HA_CREATE_INFO *create_info_arg,
+ enum_with_db_name with_db_name)
{
List<Item> field_list;
char tmp[MAX_FIELD_WIDTH], *for_str, buff[128], def_value_buf[MAX_FIELD_WIDTH];
@@ -1512,29 +1612,39 @@ int store_create_info(THD *thd, TABLE_LIST *table_list, String *packet,
handler *file= table->file;
TABLE_SHARE *share= table->s;
HA_CREATE_INFO create_info;
-#ifdef WITH_PARTITION_STORAGE_ENGINE
- bool show_table_options= FALSE;
-#endif /* WITH_PARTITION_STORAGE_ENGINE */
- bool foreign_db_mode= (thd->variables.sql_mode & (MODE_POSTGRESQL |
- MODE_ORACLE |
- MODE_MSSQL |
- MODE_DB2 |
- MODE_MAXDB |
- MODE_ANSI)) != 0;
- bool limited_mysql_mode= (thd->variables.sql_mode & (MODE_NO_FIELD_OPTIONS |
- MODE_MYSQL323 |
- MODE_MYSQL40)) != 0;
+ sql_mode_t sql_mode= thd->variables.sql_mode;
+ bool foreign_db_mode= sql_mode & (MODE_POSTGRESQL | MODE_ORACLE |
+ MODE_MSSQL | MODE_DB2 |
+ MODE_MAXDB | MODE_ANSI);
+ bool limited_mysql_mode= sql_mode & (MODE_NO_FIELD_OPTIONS | MODE_MYSQL323 |
+ MODE_MYSQL40);
+ bool show_table_options= !(sql_mode & MODE_NO_TABLE_OPTIONS) &&
+ !foreign_db_mode;
+ bool check_options= !(sql_mode & MODE_IGNORE_BAD_TABLE_OPTIONS) &&
+ !create_info_arg;
+ handlerton *hton;
my_bitmap_map *old_map;
int error= 0;
- DBUG_ENTER("store_create_info");
+ DBUG_ENTER("show_create_table");
DBUG_PRINT("enter",("table: %s", table->s->table_name.str));
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+ if (table->part_info)
+ hton= table->part_info->default_engine_type;
+ else
+#endif
+ hton= file->ht;
+
restore_record(table, s->default_values); // Get empty record
+ packet->append(STRING_WITH_LEN("CREATE "));
+ if (create_info_arg &&
+ (create_info_arg->org_options & HA_LEX_CREATE_REPLACE ||
+ create_info_arg->table_was_deleted))
+ packet->append(STRING_WITH_LEN("OR REPLACE "));
if (share->tmp_table)
- packet->append(STRING_WITH_LEN("CREATE TEMPORARY TABLE "));
- else
- packet->append(STRING_WITH_LEN("CREATE TABLE "));
+ packet->append(STRING_WITH_LEN("TEMPORARY "));
+ packet->append(STRING_WITH_LEN("TABLE "));
if (create_info_arg &&
(create_info_arg->options & HA_LEX_CREATE_IF_NOT_EXISTS))
packet->append(STRING_WITH_LEN("IF NOT EXISTS "));
@@ -1557,7 +1667,7 @@ int store_create_info(THD *thd, TABLE_LIST *table_list, String *packet,
avoid having to update gazillions of tests and result files, but
it also saves a few bytes of the binary log.
*/
- if (show_database)
+ if (with_db_name == WITH_DB_NAME)
{
const LEX_STRING *const db=
table_list->schema_table ? &INFORMATION_SCHEMA_NAME : &table->s->db;
@@ -1596,8 +1706,7 @@ int store_create_info(THD *thd, TABLE_LIST *table_list, String *packet,
field->sql_type(type);
packet->append(type.ptr(), type.length(), system_charset_info);
- if (field->has_charset() &&
- !(thd->variables.sql_mode & (MODE_MYSQL323 | MODE_MYSQL40)))
+ if (field->has_charset() && !(sql_mode & (MODE_MYSQL323 | MODE_MYSQL40)))
{
if (field->charset() != share->table_charset)
{
@@ -1640,19 +1749,21 @@ int store_create_info(THD *thd, TABLE_LIST *table_list, String *packet,
}
if (!field->vcol_info &&
- get_field_default_value(thd, table->timestamp_field,
- field, &def_value, 1))
+ get_field_default_value(thd, field, &def_value, 1))
{
packet->append(STRING_WITH_LEN(" DEFAULT "));
packet->append(def_value.ptr(), def_value.length(), system_charset_info);
}
- if (!limited_mysql_mode && table->timestamp_field == field &&
- field->unireg_check != Field::TIMESTAMP_DN_FIELD)
- packet->append(STRING_WITH_LEN(" ON UPDATE CURRENT_TIMESTAMP"));
+ if (!limited_mysql_mode && print_on_update_clause(field, &def_value, false))
+ {
+ packet->append(STRING_WITH_LEN(" "));
+ packet->append(def_value);
+ }
+
if (field->unireg_check == Field::NEXT_NUMBER &&
- !(thd->variables.sql_mode & MODE_NO_FIELD_OPTIONS))
+ !(sql_mode & MODE_NO_FIELD_OPTIONS))
packet->append(STRING_WITH_LEN(" AUTO_INCREMENT"));
if (field->comment.length)
@@ -1660,7 +1771,8 @@ int store_create_info(THD *thd, TABLE_LIST *table_list, String *packet,
packet->append(STRING_WITH_LEN(" COMMENT "));
append_unescaped(packet, field->comment.str, field->comment.length);
}
- append_create_options(thd, packet, field->option_list);
+ append_create_options(thd, packet, field->option_list, check_options,
+ hton->field_options);
}
key_info= table->key_info;
@@ -1701,7 +1813,7 @@ int store_create_info(THD *thd, TABLE_LIST *table_list, String *packet,
packet->append(STRING_WITH_LEN(" ("));
- for (uint j=0 ; j < key_info->key_parts ; j++,key_part++)
+ for (uint j=0 ; j < key_info->user_defined_key_parts ; j++,key_part++)
{
if (j)
packet->append(',');
@@ -1714,13 +1826,8 @@ int store_create_info(THD *thd, TABLE_LIST *table_list, String *packet,
table->field[key_part->fieldnr-1]->key_length() &&
!(key_info->flags & (HA_FULLTEXT | HA_SPATIAL))))
{
- char *end;
- buff[0] = '(';
- end= int10_to_str((long) key_part->length /
- key_part->field->charset()->mbmaxlen,
- buff + 1,10);
- *end++ = ')';
- packet->append(buff,(uint) (end-buff));
+ packet->append_parenthesized((long) key_part->length /
+ key_part->field->charset()->mbmaxlen);
}
}
packet->append(')');
@@ -1732,7 +1839,8 @@ int store_create_info(THD *thd, TABLE_LIST *table_list, String *packet,
append_identifier(thd, packet, parser_name->str, parser_name->length);
packet->append(STRING_WITH_LEN(" */ "));
}
- append_create_options(thd, packet, key_info->option_list);
+ append_create_options(thd, packet, key_info->option_list, check_options,
+ hton->index_options);
}
/*
@@ -1747,12 +1855,8 @@ int store_create_info(THD *thd, TABLE_LIST *table_list, String *packet,
}
packet->append(STRING_WITH_LEN("\n)"));
- if (!(thd->variables.sql_mode & MODE_NO_TABLE_OPTIONS) && !foreign_db_mode)
+ if (show_table_options)
{
-#ifdef WITH_PARTITION_STORAGE_ENGINE
- show_table_options= TRUE;
-#endif /* WITH_PARTITION_STORAGE_ENGINE */
-
/*
IF check_create_info
THEN add ENGINE only if it was used when creating the table
@@ -1760,19 +1864,11 @@ int store_create_info(THD *thd, TABLE_LIST *table_list, String *packet,
if (!create_info_arg ||
(create_info_arg->used_fields & HA_CREATE_USED_ENGINE))
{
- if (thd->variables.sql_mode & (MODE_MYSQL323 | MODE_MYSQL40))
+ if (sql_mode & (MODE_MYSQL323 | MODE_MYSQL40))
packet->append(STRING_WITH_LEN(" TYPE="));
else
packet->append(STRING_WITH_LEN(" ENGINE="));
-#ifdef WITH_PARTITION_STORAGE_ENGINE
- if (table->part_info)
- packet->append(ha_resolve_storage_engine_name(
- table->part_info->default_engine_type));
- else
- packet->append(file->table_type());
-#else
- packet->append(file->table_type());
-#endif
+ packet->append(hton_name(hton));
}
/*
@@ -1794,9 +1890,7 @@ int store_create_info(THD *thd, TABLE_LIST *table_list, String *packet,
packet->append(buff, (uint) (end - buff));
}
- if (share->table_charset &&
- !(thd->variables.sql_mode & MODE_MYSQL323) &&
- !(thd->variables.sql_mode & MODE_MYSQL40))
+ if (share->table_charset && !(sql_mode & (MODE_MYSQL323 | MODE_MYSQL40)))
{
/*
IF check_create_info
@@ -1843,6 +1937,22 @@ int store_create_info(THD *thd, TABLE_LIST *table_list, String *packet,
packet->append(STRING_WITH_LEN(" PACK_KEYS=1"));
if (create_info.options & HA_OPTION_NO_PACK_KEYS)
packet->append(STRING_WITH_LEN(" PACK_KEYS=0"));
+ if (share->db_create_options & HA_OPTION_STATS_PERSISTENT)
+ packet->append(STRING_WITH_LEN(" STATS_PERSISTENT=1"));
+ if (share->db_create_options & HA_OPTION_NO_STATS_PERSISTENT)
+ packet->append(STRING_WITH_LEN(" STATS_PERSISTENT=0"));
+ if (share->stats_auto_recalc == HA_STATS_AUTO_RECALC_ON)
+ packet->append(STRING_WITH_LEN(" STATS_AUTO_RECALC=1"));
+ else if (share->stats_auto_recalc == HA_STATS_AUTO_RECALC_OFF)
+ packet->append(STRING_WITH_LEN(" STATS_AUTO_RECALC=0"));
+ if (share->stats_sample_pages != 0)
+ {
+ char *end;
+ packet->append(STRING_WITH_LEN(" STATS_SAMPLE_PAGES="));
+ end= longlong10_to_str(share->stats_sample_pages, buff, 10);
+ packet->append(buff, (uint) (end - buff));
+ }
+
/* We use CHECKSUM, instead of TABLE_CHECKSUM, for backward compability */
if (create_info.options & HA_OPTION_CHECKSUM)
packet->append(STRING_WITH_LEN(" CHECKSUM=1"));
@@ -1881,7 +1991,8 @@ int store_create_info(THD *thd, TABLE_LIST *table_list, String *packet,
packet->append(STRING_WITH_LEN(" CONNECTION="));
append_unescaped(packet, share->connect_string.str, share->connect_string.length);
}
- append_create_options(thd, packet, share->option_list);
+ append_create_options(thd, packet, share->option_list, check_options,
+ hton->table_options);
append_directory(thd, packet, "DATA", create_info.data_file_name);
append_directory(thd, packet, "INDEX", create_info.index_file_name);
}
@@ -1955,7 +2066,7 @@ static void store_key_options(THD *thd, String *packet, TABLE *table,
end= longlong10_to_str(key_info->block_size, buff, 10);
packet->append(buff, (uint) (end - buff));
}
- DBUG_ASSERT(test(key_info->flags & HA_USES_COMMENT) ==
+ DBUG_ASSERT(MY_TEST(key_info->flags & HA_USES_COMMENT) ==
(key_info->comment.length > 0));
if (key_info->flags & HA_USES_COMMENT)
{
@@ -2024,8 +2135,11 @@ void append_definer(THD *thd, String *buffer, const LEX_STRING *definer_user,
{
buffer->append(STRING_WITH_LEN("DEFINER="));
append_identifier(thd, buffer, definer_user->str, definer_user->length);
- buffer->append('@');
- append_identifier(thd, buffer, definer_host->str, definer_host->length);
+ if (definer_host->str && definer_host->str[0])
+ {
+ buffer->append('@');
+ append_identifier(thd, buffer, definer_host->str, definer_host->length);
+ }
buffer->append(' ');
}
@@ -2047,8 +2161,7 @@ view_store_options(THD *thd, TABLE_LIST *table, String *buff)
view_store_options4(thd, table, buff, false);
}
-int
-view_store_create_info(THD *thd, TABLE_LIST *table, String *buff)
+static int show_create_view(THD *thd, TABLE_LIST *table, String *buff)
{
my_bool compact_view_name= TRUE;
my_bool foreign_db_mode= (thd->variables.sql_mode & (MODE_POSTGRESQL |
@@ -2137,10 +2250,6 @@ public:
double progress;
};
-#ifdef HAVE_EXPLICIT_TEMPLATE_INSTANTIATION
-template class I_List<thread_info>;
-#endif
-
static const char *thread_state_info(THD *tmp)
{
#ifndef EMBEDDED_LIBRARY
@@ -2148,7 +2257,7 @@ static const char *thread_state_info(THD *tmp)
{
if (tmp->net.reading_or_writing == 2)
return "Writing to net";
- else if (tmp->command == COM_SLEEP)
+ else if (tmp->get_command() == COM_SLEEP)
return "";
else
return "Reading from net";
@@ -2228,7 +2337,7 @@ void mysqld_list_processes(THD *thd,const char *user, bool verbose)
thd_info->host= thd->strdup(tmp_sctx->host_or_ip[0] ?
tmp_sctx->host_or_ip :
tmp_sctx->host ? tmp_sctx->host : "");
- thd_info->command=(int) tmp->command;
+ thd_info->command=(int) tmp->get_command();
mysql_mutex_lock(&tmp->LOCK_thd_data);
if ((thd_info->db= tmp->db)) // Safe test
thd_info->db= thd->strdup(thd_info->db);
@@ -2243,7 +2352,7 @@ void mysqld_list_processes(THD *thd,const char *user, bool verbose)
/* Lock THD mutex that protects its data when looking at it. */
if (tmp->query())
{
- uint length= min(max_query_length, tmp->query_length());
+ uint length= MY_MIN(max_query_length, tmp->query_length());
char *q= thd->strmake(tmp->query(),length);
/* Safety: in case strmake failed, we set length to 0. */
thd_info->query_string=
@@ -2256,7 +2365,7 @@ void mysqld_list_processes(THD *thd,const char *user, bool verbose)
*/
if (tmp->progress.max_counter)
{
- uint max_stage= max(tmp->progress.max_stage, 1);
+ uint max_stage= MY_MAX(tmp->progress.max_stage, 1);
thd_info->progress= (((tmp->progress.stage / (double) max_stage) +
((tmp->progress.counter /
(double) tmp->progress.max_counter) /
@@ -2306,6 +2415,259 @@ void mysqld_list_processes(THD *thd,const char *user, bool verbose)
DBUG_VOID_RETURN;
}
+
+/*
+ Produce EXPLAIN data.
+
+ This function is APC-scheduled to be run in the context of the thread that
+ we're producing EXPLAIN for.
+*/
+
+void Show_explain_request::call_in_target_thread()
+{
+ Query_arena backup_arena;
+ bool printed_anything= FALSE;
+
+ /*
+ Change the arena because JOIN::print_explain and co. are going to allocate
+ items. Let them allocate them on our arena.
+ */
+ target_thd->set_n_backup_active_arena((Query_arena*)request_thd,
+ &backup_arena);
+
+ query_str.copy(target_thd->query(),
+ target_thd->query_length(),
+ target_thd->query_charset());
+
+ DBUG_ASSERT(current_thd == target_thd);
+ set_current_thd(request_thd);
+ if (target_thd->lex->print_explain(explain_buf, 0 /* explain flags*/,
+ &printed_anything))
+ {
+ failed_to_produce= TRUE;
+ }
+ set_current_thd(target_thd);
+
+ if (!printed_anything)
+ failed_to_produce= TRUE;
+
+ target_thd->restore_active_arena((Query_arena*)request_thd, &backup_arena);
+}
+
+
+int select_result_explain_buffer::send_data(List<Item> &items)
+{
+ int res;
+ THD *cur_thd= current_thd;
+ DBUG_ENTER("select_result_explain_buffer::send_data");
+
+ /*
+ Switch to the recieveing thread, so that we correctly count memory used
+ by it. This is needed as it's the receiving thread that will free the
+ memory.
+ */
+ set_current_thd(thd);
+ fill_record(thd, dst_table, dst_table->field, items, TRUE, FALSE);
+ res= dst_table->file->ha_write_tmp_row(dst_table->record[0]);
+ set_current_thd(cur_thd);
+ DBUG_RETURN(MY_TEST(res));
+}
+
+bool select_result_text_buffer::send_result_set_metadata(List<Item> &fields, uint flag)
+{
+ n_columns= fields.elements;
+ return append_row(fields, true /*send item names */);
+ return send_data(fields);
+}
+
+
+int select_result_text_buffer::send_data(List<Item> &items)
+{
+ return append_row(items, false /*send item values */);
+}
+
+int select_result_text_buffer::append_row(List<Item> &items, bool send_names)
+{
+ List_iterator<Item> it(items);
+ Item *item;
+ char **row;
+ int column= 0;
+
+ if (!(row= (char**) thd->alloc(sizeof(char*) * n_columns)))
+ return true;
+ rows.push_back(row);
+
+ while ((item= it++))
+ {
+ DBUG_ASSERT(column < n_columns);
+ StringBuffer<32> buf;
+ const char *data_ptr;
+ size_t data_len;
+ if (send_names)
+ {
+ data_ptr= item->name;
+ data_len= strlen(item->name);
+ }
+ else
+ {
+ String *res;
+ res= item->val_str(&buf);
+ if (item->null_value)
+ {
+ data_ptr= "NULL";
+ data_len=4;
+ }
+ else
+ {
+ data_ptr= res->c_ptr_safe();
+ data_len= res->length();
+ }
+ }
+
+ char *ptr= (char*)thd->alloc(data_len + 1);
+ memcpy(ptr, data_ptr, data_len + 1);
+ row[column]= ptr;
+
+ column++;
+ }
+ return false;
+}
+
+
+void select_result_text_buffer::save_to(String *res)
+{
+ List_iterator<char*> it(rows);
+ char **row;
+ res->append("#\n");
+ while ((row= it++))
+ {
+ res->append("# explain: ");
+ for (int i=0; i < n_columns; i++)
+ {
+ if (i)
+ res->append('\t');
+ res->append(row[i]);
+ }
+ res->append("\n");
+ }
+ res->append("#\n");
+}
+
+
+/*
+ Store the SHOW EXPLAIN output in the temporary table.
+*/
+
+int fill_show_explain(THD *thd, TABLE_LIST *table, COND *cond)
+{
+ const char *calling_user;
+ THD *tmp;
+ my_thread_id thread_id;
+ DBUG_ENTER("fill_show_explain");
+
+ DBUG_ASSERT(cond==NULL);
+ thread_id= thd->lex->value_list.head()->val_int();
+ calling_user= (thd->security_ctx->master_access & PROCESS_ACL) ? NullS :
+ thd->security_ctx->priv_user;
+
+ if ((tmp= find_thread_by_id(thread_id)))
+ {
+ Security_context *tmp_sctx= tmp->security_ctx;
+ /*
+ If calling_user==NULL, calling thread has SUPER or PROCESS
+ privilege, and so can do SHOW EXPLAIN on any user.
+
+ if calling_user!=NULL, he's only allowed to view SHOW EXPLAIN on
+ his own threads.
+ */
+ if (calling_user && (!tmp_sctx->user || strcmp(calling_user,
+ tmp_sctx->user)))
+ {
+ my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), "PROCESS");
+ mysql_mutex_unlock(&tmp->LOCK_thd_data);
+ DBUG_RETURN(1);
+ }
+
+ if (tmp == thd)
+ {
+ mysql_mutex_unlock(&tmp->LOCK_thd_data);
+ my_error(ER_TARGET_NOT_EXPLAINABLE, MYF(0));
+ DBUG_RETURN(1);
+ }
+
+ bool bres;
+ /*
+ Ok we've found the thread of interest and it won't go away because
+ we're holding its LOCK_thd data. Post it a SHOW EXPLAIN request.
+ */
+ bool timed_out;
+ int timeout_sec= 30;
+ Show_explain_request explain_req;
+ select_result_explain_buffer *explain_buf;
+
+ explain_buf= new select_result_explain_buffer(thd, table->table);
+
+ explain_req.explain_buf= explain_buf;
+ explain_req.target_thd= tmp;
+ explain_req.request_thd= thd;
+ explain_req.failed_to_produce= FALSE;
+
+ /* Ok, we have a lock on target->LOCK_thd_data, can call: */
+ bres= tmp->apc_target.make_apc_call(thd, &explain_req, timeout_sec, &timed_out);
+
+ if (bres || explain_req.failed_to_produce)
+ {
+ if (thd->killed)
+ thd->send_kill_message();
+ else if (timed_out)
+ my_error(ER_LOCK_WAIT_TIMEOUT, MYF(0));
+ else
+ my_error(ER_TARGET_NOT_EXPLAINABLE, MYF(0));
+
+ bres= TRUE;
+ }
+ else
+ {
+ /*
+ Push the query string as a warning. The query may be in a different
+ charset than the charset that's used for error messages, so, convert it
+ if needed.
+ */
+ CHARSET_INFO *fromcs= explain_req.query_str.charset();
+ CHARSET_INFO *tocs= error_message_charset_info;
+ char *warning_text;
+ if (!my_charset_same(fromcs, tocs))
+ {
+ uint conv_length= 1 + tocs->mbmaxlen * explain_req.query_str.length() /
+ fromcs->mbminlen;
+ uint dummy_errors;
+ char *to, *p;
+ if (!(to= (char*)thd->alloc(conv_length + 1)))
+ DBUG_RETURN(1);
+ p= to;
+ p+= copy_and_convert(to, conv_length, tocs,
+ explain_req.query_str.c_ptr(),
+ explain_req.query_str.length(), fromcs,
+ &dummy_errors);
+ *p= 0;
+ warning_text= to;
+ }
+ else
+ warning_text= explain_req.query_str.c_ptr_safe();
+
+ push_warning(thd, Sql_condition::WARN_LEVEL_NOTE,
+ ER_YES, warning_text);
+ }
+ DBUG_RETURN(bres);
+ }
+ else
+ {
+ my_error(ER_NO_SUCH_THREAD, MYF(0), thread_id);
+ DBUG_RETURN(1);
+ }
+}
+
+
int fill_schema_processlist(THD* thd, TABLE_LIST* tables, COND* cond)
{
TABLE *table= tables->table;
@@ -2372,8 +2734,8 @@ int fill_schema_processlist(THD* thd, TABLE_LIST* tables, COND* cond)
"Killed" : 0))))
table->field[4]->store(val, strlen(val), cs);
else
- table->field[4]->store(command_name[tmp->command].str,
- command_name[tmp->command].length, cs);
+ table->field[4]->store(command_name[tmp->get_command()].str,
+ command_name[tmp->get_command()].length, cs);
/* MYSQL_TIME */
ulonglong start_utime= tmp->start_time * HRTIME_RESOLUTION + tmp->start_time_sec_part;
ulonglong utime= start_utime && start_utime < unow.val
@@ -2399,7 +2761,7 @@ int fill_schema_processlist(THD* thd, TABLE_LIST* tables, COND* cond)
if (tmp->query())
{
table->field[7]->store(tmp->query(),
- min(PROCESS_LIST_INFO_WIDTH,
+ MY_MIN(PROCESS_LIST_INFO_WIDTH,
tmp->query_length()), cs);
table->field[7]->set_notnull();
}
@@ -2417,6 +2779,21 @@ int fill_schema_processlist(THD* thd, TABLE_LIST* tables, COND* cond)
}
mysql_mutex_unlock(&tmp->LOCK_thd_data);
+ /*
+ This may become negative if we free a memory allocated by another
+ thread in this thread. However it's better that we notice it eventually
+ than hide it.
+ */
+ table->field[12]->store((longlong) (tmp->status_var.memory_used +
+ sizeof(THD)),
+ FALSE);
+ table->field[12]->set_notnull();
+ table->field[13]->store((longlong) tmp->get_examined_row_count(), TRUE);
+ table->field[13]->set_notnull();
+
+ /* QUERY_ID */
+ table->field[14]->store(tmp->query_id, TRUE);
+
if (schema_table_store_record(thd, table))
{
mysql_mutex_unlock(&LOCK_thread_count);
@@ -2439,7 +2816,7 @@ static bool status_vars_inited= 0;
C_MODE_START
static int show_var_cmp(const void *var1, const void *var2)
{
- return strcmp(((SHOW_VAR*)var1)->name, ((SHOW_VAR*)var2)->name);
+ return strcasecmp(((SHOW_VAR*)var1)->name, ((SHOW_VAR*)var2)->name);
}
C_MODE_END
@@ -2487,9 +2864,9 @@ int add_status_vars(SHOW_VAR *list)
{
int res= 0;
if (status_vars_inited)
- mysql_mutex_lock(&LOCK_status);
+ mysql_mutex_lock(&LOCK_show_status);
if (!all_status_vars.buffer && // array is not allocated yet - do it now
- my_init_dynamic_array(&all_status_vars, sizeof(SHOW_VAR), 200, 20))
+ my_init_dynamic_array(&all_status_vars, sizeof(SHOW_VAR), 200, 20, MYF(0)))
{
res= 1;
goto err;
@@ -2502,7 +2879,7 @@ int add_status_vars(SHOW_VAR *list)
sort_dynamic(&all_status_vars, show_var_cmp);
err:
if (status_vars_inited)
- mysql_mutex_unlock(&LOCK_status);
+ mysql_mutex_unlock(&LOCK_show_status);
return res;
}
@@ -2536,7 +2913,7 @@ void reset_status_vars()
catch-all cleanup function, cleans up everything no matter what
DESCRIPTION
- This function is not strictly required if all add_to_status/
+ This function is not strictly required if all add_status_vars/
remove_status_vars are properly paired, but it's a safety measure that
deletes everything from the all_status_vars[] even if some
remove_status_vars were forgotten
@@ -2564,7 +2941,7 @@ void remove_status_vars(SHOW_VAR *list)
{
if (status_vars_inited)
{
- mysql_mutex_lock(&LOCK_status);
+ mysql_mutex_lock(&LOCK_show_status);
SHOW_VAR *all= dynamic_element(&all_status_vars, 0, SHOW_VAR *);
for (; list->name; list++)
@@ -2585,7 +2962,7 @@ void remove_status_vars(SHOW_VAR *list)
}
}
shrink_var_array(&all_status_vars);
- mysql_mutex_unlock(&LOCK_status);
+ mysql_mutex_unlock(&LOCK_show_status);
}
else
{
@@ -2637,21 +3014,80 @@ static bool show_status_array(THD *thd, const char *wild,
*prefix_end++= '_';
len=name_buffer + sizeof(name_buffer) - prefix_end;
+#ifdef WITH_WSREP
+ bool is_wsrep_var= FALSE;
+ /*
+ This is a workaround for lp:1306875 (PBX) to skip switching of wsrep
+ status variable name's first letter to uppercase. This is an optimization
+ for status variables defined under wsrep plugin.
+ TODO: remove once lp:1306875 has been addressed.
+ */
+ if (*prefix && !my_strcasecmp(system_charset_info, prefix, "wsrep"))
+ {
+ is_wsrep_var= TRUE;
+ }
+#endif /* WITH_WSREP */
+
for (; variables->name; variables++)
{
+ bool wild_checked;
strnmov(prefix_end, variables->name, len);
name_buffer[sizeof(name_buffer)-1]=0; /* Safety */
+
+#ifdef WITH_WSREP
+ /*
+ If the prefix is NULL, that means we are looking into the status variables
+ defined directly under mysqld.cc. Do not capitalize wsrep status variable
+ names until lp:1306875 has been fixed.
+ TODO: remove once lp:1306875 has been addressed.
+ */
+ if (!(*prefix) && !strncasecmp(name_buffer, "wsrep", strlen("wsrep")))
+ {
+ is_wsrep_var= TRUE;
+ }
+#endif /* WITH_WSREP */
+
if (ucase_names)
my_caseup_str(system_charset_info, name_buffer);
+ else
+ {
+ my_casedn_str(system_charset_info, name_buffer);
+ DBUG_ASSERT(name_buffer[0] >= 'a');
+ DBUG_ASSERT(name_buffer[0] <= 'z');
+
+#ifdef WITH_WSREP
+ // TODO: remove once lp:1306875 has been addressed.
+ if (status_var && (is_wsrep_var == FALSE))
+#else
+ /* traditionally status variables have a first letter uppercased */
+ if (status_var)
+#endif /* WITH_WSREP */
+ name_buffer[0]-= 'a' - 'A';
+ }
+
restore_record(table, s->default_values);
table->field[0]->store(name_buffer, strlen(name_buffer),
system_charset_info);
+
+ /*
+ Compare name for types that can't return arrays. We do this to not
+ calculate the value for function variables that we will not access
+ */
+ if ((variables->type != SHOW_FUNC && variables->type != SHOW_ARRAY))
+ {
+ if (wild && wild[0] && wild_case_compare(system_charset_info,
+ name_buffer, wild))
+ continue;
+ wild_checked= 1; // Avoid checking it again
+ }
+
/*
- if var->type is SHOW_FUNC, call the function.
- Repeat as necessary, if new var is again SHOW_FUNC
+ if var->type is SHOW_FUNC or SHOW_SIMPLE_FUNC, call the function.
+ Repeat as necessary, if new var is again one of the above
*/
- for (var=variables; var->type == SHOW_FUNC; var= &tmp)
+ for (var=variables; var->type == SHOW_FUNC ||
+ var->type == SHOW_SIMPLE_FUNC; var= &tmp)
((mysql_show_var_func)(var->value))(thd, &tmp, buff);
SHOW_TYPE show_type=var->type;
@@ -2662,8 +3098,9 @@ static bool show_status_array(THD *thd, const char *wild,
}
else
{
- if (!(wild && wild[0] && wild_case_compare(system_charset_info,
- name_buffer, wild)) &&
+ if ((wild_checked ||
+ (wild && wild[0] && wild_case_compare(system_charset_info,
+ name_buffer, wild))) &&
(!cond || cond->val_int()))
{
char *value=var->value;
@@ -2808,7 +3245,7 @@ static int aggregate_user_stats(HASH *all_user_stats, HASH *agg_user_stats)
{
DBUG_ENTER("aggregate_user_stats");
if (my_hash_init(agg_user_stats, system_charset_info,
- max(all_user_stats->records, 1),
+ MY_MAX(all_user_stats->records, 1),
0, 0, (my_hash_get_key)get_key_user_stats,
(my_hash_free_key)free_user_stats, 0))
{
@@ -2828,7 +3265,8 @@ static int aggregate_user_stats(HASH *all_user_stats, HASH *agg_user_stats)
{
// First entry for this role.
if (!(agg_user= (USER_STATS*) my_malloc(sizeof(USER_STATS),
- MYF(MY_WME | MY_ZEROFILL))))
+ MYF(MY_WME | MY_ZEROFILL|
+ MY_THREAD_SPECIFIC))))
{
sql_print_error("Malloc in aggregate_user_stats failed");
DBUG_RETURN(1);
@@ -3136,41 +3574,6 @@ void calc_sum_of_all_status(STATUS_VAR *to)
/* This is only used internally, but we need it here as a forward reference */
extern ST_SCHEMA_TABLE schema_tables[];
-/**
- Condition pushdown used for INFORMATION_SCHEMA / SHOW queries.
- This structure is to implement an optimization when
- accessing data dictionary data in the INFORMATION_SCHEMA
- or SHOW commands.
- When the query contain a TABLE_SCHEMA or TABLE_NAME clause,
- narrow the search for data based on the constraints given.
-*/
-typedef struct st_lookup_field_values
-{
- /**
- Value of a TABLE_SCHEMA clause.
- Note that this value length may exceed @c NAME_LEN.
- @sa wild_db_value
- */
- LEX_STRING db_value;
- /**
- Value of a TABLE_NAME clause.
- Note that this value length may exceed @c NAME_LEN.
- @sa wild_table_value
- */
- LEX_STRING table_value;
- /**
- True when @c db_value is a LIKE clause,
- false when @c db_value is an '=' clause.
- */
- bool wild_db_value;
- /**
- True when @c table_value is a LIKE clause,
- false when @c table_value is an '=' clause.
- */
- bool wild_table_value;
-} LOOKUP_FIELD_VALUES;
-
-
/*
Store record to I_S table, convert HEAP table
to MyISAM if necessary
@@ -3278,8 +3681,8 @@ bool get_lookup_value(THD *thd, Item_func *item_func,
(uchar *) item_field->field_name,
strlen(item_field->field_name), 0))
{
- thd->make_lex_string(&lookup_field_vals->db_value, tmp_str->ptr(),
- tmp_str->length(), FALSE);
+ thd->make_lex_string(&lookup_field_vals->db_value,
+ tmp_str->ptr(), tmp_str->length());
}
/* Lookup value is table name */
else if (!cs->coll->strnncollsp(cs, (uchar *) field_name2,
@@ -3287,8 +3690,8 @@ bool get_lookup_value(THD *thd, Item_func *item_func,
(uchar *) item_field->field_name,
strlen(item_field->field_name), 0))
{
- thd->make_lex_string(&lookup_field_vals->table_value, tmp_str->ptr(),
- tmp_str->length(), FALSE);
+ thd->make_lex_string(&lookup_field_vals->table_value,
+ tmp_str->ptr(), tmp_str->length());
}
}
return 0;
@@ -3465,7 +3868,7 @@ bool get_lookup_field_values(THD *thd, COND *cond, TABLE_LIST *tables,
LOOKUP_FIELD_VALUES *lookup_field_values)
{
LEX *lex= thd->lex;
- const char *wild= lex->wild ? lex->wild->ptr() : NullS;
+ String *wild= lex->wild;
bool rc= 0;
bzero((char*) lookup_field_values, sizeof(LOOKUP_FIELD_VALUES));
@@ -3473,8 +3876,8 @@ bool get_lookup_field_values(THD *thd, COND *cond, TABLE_LIST *tables,
case SQLCOM_SHOW_DATABASES:
if (wild)
{
- thd->make_lex_string(&lookup_field_values->db_value,
- wild, strlen(wild), 0);
+ thd->make_lex_string(&lookup_field_values->db_value,
+ wild->ptr(), wild->length());
lookup_field_values->wild_db_value= 1;
}
break;
@@ -3483,14 +3886,25 @@ bool get_lookup_field_values(THD *thd, COND *cond, TABLE_LIST *tables,
case SQLCOM_SHOW_TRIGGERS:
case SQLCOM_SHOW_EVENTS:
thd->make_lex_string(&lookup_field_values->db_value,
- lex->select_lex.db, strlen(lex->select_lex.db), 0);
+ lex->select_lex.db, strlen(lex->select_lex.db));
if (wild)
{
thd->make_lex_string(&lookup_field_values->table_value,
- wild, strlen(wild), 0);
+ wild->ptr(), wild->length());
lookup_field_values->wild_table_value= 1;
}
break;
+ case SQLCOM_SHOW_PLUGINS:
+ if (lex->ident.str)
+ thd->make_lex_string(&lookup_field_values->db_value,
+ lex->ident.str, lex->ident.length);
+ else if (lex->wild)
+ {
+ thd->make_lex_string(&lookup_field_values->db_value,
+ lex->wild->ptr(), lex->wild->length());
+ lookup_field_values->wild_db_value= 1;
+ }
+ break;
default:
/*
The "default" is for queries over I_S.
@@ -3533,23 +3947,15 @@ enum enum_schema_tables get_schema_table_idx(ST_SCHEMA_TABLE *schema_table)
wild wild string
idx_field_vals idx_field_vals->db_name contains db name or
wild string
- with_i_schema returns 1 if we added 'IS' name to list
- otherwise returns 0
RETURN
zero success
non-zero error
*/
-int make_db_list(THD *thd, List<LEX_STRING> *files,
- LOOKUP_FIELD_VALUES *lookup_field_vals,
- bool *with_i_schema)
+int make_db_list(THD *thd, Dynamic_array<LEX_STRING*> *files,
+ LOOKUP_FIELD_VALUES *lookup_field_vals)
{
- LEX_STRING *i_s_name_copy= 0;
- i_s_name_copy= thd->make_lex_string(i_s_name_copy,
- INFORMATION_SCHEMA_NAME.str,
- INFORMATION_SCHEMA_NAME.length, TRUE);
- *with_i_schema= 0;
if (lookup_field_vals->wild_db_value)
{
/*
@@ -3562,12 +3968,11 @@ int make_db_list(THD *thd, List<LEX_STRING> *files,
INFORMATION_SCHEMA_NAME.str,
lookup_field_vals->db_value.str))
{
- *with_i_schema= 1;
- if (files->push_back(i_s_name_copy))
+ if (files->append_val(&INFORMATION_SCHEMA_NAME))
return 1;
}
- return (find_files(thd, files, NullS, mysql_data_home,
- lookup_field_vals->db_value.str, 1) != FIND_FILES_OK);
+ return find_files(thd, files, 0, mysql_data_home,
+ &lookup_field_vals->db_value);
}
@@ -3591,12 +3996,11 @@ int make_db_list(THD *thd, List<LEX_STRING> *files,
if (is_infoschema_db(lookup_field_vals->db_value.str,
lookup_field_vals->db_value.length))
{
- *with_i_schema= 1;
- if (files->push_back(i_s_name_copy))
+ if (files->append_val(&INFORMATION_SCHEMA_NAME))
return 1;
return 0;
}
- if (files->push_back(&lookup_field_vals->db_value))
+ if (files->append_val(&lookup_field_vals->db_value))
return 1;
return 0;
}
@@ -3605,17 +4009,15 @@ int make_db_list(THD *thd, List<LEX_STRING> *files,
Create list of existing databases. It is used in case
of select from information schema table
*/
- if (files->push_back(i_s_name_copy))
+ if (files->append_val(&INFORMATION_SCHEMA_NAME))
return 1;
- *with_i_schema= 1;
- return (find_files(thd, files, NullS,
- mysql_data_home, NullS, 1) != FIND_FILES_OK);
+ return find_files(thd, files, 0, mysql_data_home, &null_lex_str);
}
struct st_add_schema_table
{
- List<LEX_STRING> *files;
+ Dynamic_array<LEX_STRING*> *files;
const char *wild;
};
@@ -3625,7 +4027,7 @@ static my_bool add_schema_table(THD *thd, plugin_ref plugin,
{
LEX_STRING *file_name= 0;
st_add_schema_table *data= (st_add_schema_table *)p_data;
- List<LEX_STRING> *file_list= data->files;
+ Dynamic_array<LEX_STRING*> *file_list= data->files;
const char *wild= data->wild;
ST_SCHEMA_TABLE *schema_table= plugin_data(plugin, ST_SCHEMA_TABLE *);
DBUG_ENTER("add_schema_table");
@@ -3645,16 +4047,16 @@ static my_bool add_schema_table(THD *thd, plugin_ref plugin,
DBUG_RETURN(0);
}
- if ((file_name= thd->make_lex_string(file_name, schema_table->table_name,
- strlen(schema_table->table_name),
- TRUE)) &&
- !file_list->push_back(file_name))
+ if ((file_name= thd->make_lex_string(schema_table->table_name,
+ strlen(schema_table->table_name))) &&
+ !file_list->append(file_name))
DBUG_RETURN(0);
DBUG_RETURN(1);
}
-int schema_tables_add(THD *thd, List<LEX_STRING> *files, const char *wild)
+int schema_tables_add(THD *thd, Dynamic_array<LEX_STRING*> *files,
+ const char *wild)
{
LEX_STRING *file_name= 0;
ST_SCHEMA_TABLE *tmp_schema_table= schema_tables;
@@ -3678,9 +4080,9 @@ int schema_tables_add(THD *thd, List<LEX_STRING> *files, const char *wild)
continue;
}
if ((file_name=
- thd->make_lex_string(file_name, tmp_schema_table->table_name,
- strlen(tmp_schema_table->table_name), TRUE)) &&
- !files->push_back(file_name))
+ thd->make_lex_string(tmp_schema_table->table_name,
+ strlen(tmp_schema_table->table_name))) &&
+ !files->append(file_name))
continue;
DBUG_RETURN(1);
}
@@ -3705,7 +4107,6 @@ int schema_tables_add(THD *thd, List<LEX_STRING> *files, const char *wild)
@param[in] table_names List of table names in database
@param[in] lex pointer to LEX struct
@param[in] lookup_field_vals pointer to LOOKUP_FIELD_VALUE struct
- @param[in] with_i_schema TRUE means that we add I_S tables to list
@param[in] db_name database name
@return Operation status
@@ -3715,9 +4116,9 @@ int schema_tables_add(THD *thd, List<LEX_STRING> *files, const char *wild)
*/
static int
-make_table_name_list(THD *thd, List<LEX_STRING> *table_names, LEX *lex,
- LOOKUP_FIELD_VALUES *lookup_field_vals,
- bool with_i_schema, LEX_STRING *db_name)
+make_table_name_list(THD *thd, Dynamic_array<LEX_STRING*> *table_names,
+ LEX *lex, LOOKUP_FIELD_VALUES *lookup_field_vals,
+ LEX_STRING *db_name)
{
char path[FN_REFLEN + 1];
build_table_filename(path, sizeof(path) - 1, db_name->str, "", "", 0);
@@ -3732,32 +4133,23 @@ make_table_name_list(THD *thd, List<LEX_STRING> *table_names, LEX *lex,
*/
return 0;
}
-
- if (with_i_schema)
+ if (db_name == &INFORMATION_SCHEMA_NAME)
{
LEX_STRING *name;
ST_SCHEMA_TABLE *schema_table=
find_schema_table(thd, lookup_field_vals->table_value.str);
if (schema_table && !schema_table->hidden)
{
- if (!(name=
- thd->make_lex_string(NULL, schema_table->table_name,
- strlen(schema_table->table_name), TRUE)) ||
- table_names->push_back(name))
+ if (!(name= thd->make_lex_string(schema_table->table_name,
+ strlen(schema_table->table_name))) ||
+ table_names->append(name))
return 1;
}
}
else
{
- if (table_names->push_back(&lookup_field_vals->table_value))
+ if (table_names->append_val(&lookup_field_vals->table_value))
return 1;
- /*
- Check that table is relevant in current transaction.
- (used for ndb engine, see ndbcluster_find_files(), ha_ndbcluster.cc)
- */
- (void) ha_find_files(thd, db_name->str, path,
- lookup_field_vals->table_value.str, 0,
- table_names);
}
return 0;
}
@@ -3766,12 +4158,12 @@ make_table_name_list(THD *thd, List<LEX_STRING> *table_names, LEX *lex,
This call will add all matching the wildcards (if specified) IS tables
to the list
*/
- if (with_i_schema)
+ if (db_name == &INFORMATION_SCHEMA_NAME)
return (schema_tables_add(thd, table_names,
lookup_field_vals->table_value.str));
- find_files_result res= find_files(thd, table_names, db_name->str, path,
- lookup_field_vals->table_value.str, 0);
+ find_files_result res= find_files(thd, table_names, db_name, path,
+ &lookup_field_vals->table_value);
if (res != FIND_FILES_OK)
{
/*
@@ -3867,10 +4259,10 @@ fill_schema_table_by_open(THD *thd, bool is_show_fields_or_keys,
These copies are used for make_table_list() while unaltered values
are passed to process_table() functions.
*/
- if (!thd->make_lex_string(&db_name, orig_db_name->str,
- orig_db_name->length, FALSE) ||
- !thd->make_lex_string(&table_name, orig_table_name->str,
- orig_table_name->length, FALSE))
+ if (!thd->make_lex_string(&db_name,
+ orig_db_name->str, orig_db_name->length) ||
+ !thd->make_lex_string(&table_name,
+ orig_table_name->str, orig_table_name->length))
goto end;
/*
@@ -3911,12 +4303,13 @@ fill_schema_table_by_open(THD *thd, bool is_show_fields_or_keys,
'only_view_structure()'.
*/
lex->sql_command= SQLCOM_SHOW_FIELDS;
- result= open_normal_and_derived_tables(thd, table_list,
- (MYSQL_OPEN_IGNORE_FLUSH |
- MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL |
- (can_deadlock ?
- MYSQL_OPEN_FAIL_ON_MDL_CONFLICT : 0)),
- DT_PREPARE | DT_CREATE);
+ result= (open_temporary_tables(thd, table_list) ||
+ open_normal_and_derived_tables(thd, table_list,
+ (MYSQL_OPEN_IGNORE_FLUSH |
+ MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL |
+ (can_deadlock ?
+ MYSQL_OPEN_FAIL_ON_MDL_CONFLICT : 0)),
+ DT_PREPARE | DT_CREATE));
/*
Restore old value of sql_command back as it is being looked at in
process_table() function.
@@ -3936,13 +4329,15 @@ fill_schema_table_by_open(THD *thd, bool is_show_fields_or_keys,
Again we don't do this for SHOW COLUMNS/KEYS because
of backward compatibility.
*/
- if (!is_show_fields_or_keys && result && thd->is_error() &&
- thd->stmt_da->sql_errno() == ER_NO_SUCH_TABLE)
+ if (!is_show_fields_or_keys && result &&
+ (thd->get_stmt_da()->sql_errno() == ER_NO_SUCH_TABLE ||
+ thd->get_stmt_da()->sql_errno() == ER_WRONG_OBJECT))
{
/*
Hide error for a non-existing table.
For example, this error can occur when we use a where condition
- with a db name and table, but the table does not exist.
+ with a db name and table, but the table does not exist or
+ there is a view with the same name.
*/
result= false;
thd->clear_error();
@@ -3994,7 +4389,6 @@ end:
@param[in] table TABLE struct for I_S table
@param[in] db_name database name
@param[in] table_name table name
- @param[in] with_i_schema I_S table if TRUE
@return Operation status
@retval 0 success
@@ -4002,38 +4396,29 @@ end:
*/
static int fill_schema_table_names(THD *thd, TABLE_LIST *tables,
- LEX_STRING *db_name, LEX_STRING *table_name,
- bool with_i_schema)
+ LEX_STRING *db_name, LEX_STRING *table_name)
{
TABLE *table= tables->table;
- if (with_i_schema)
+ if (db_name == &INFORMATION_SCHEMA_NAME)
{
table->field[3]->store(STRING_WITH_LEN("SYSTEM VIEW"),
system_charset_info);
}
else if (tables->table_open_method != SKIP_OPEN_TABLE)
{
- enum legacy_db_type not_used;
- char path[FN_REFLEN + 1];
- (void) build_table_filename(path, sizeof(path) - 1, db_name->str,
- table_name->str, reg_ext, 0);
- switch (dd_frm_type(thd, path, &not_used)) {
- case FRMTYPE_ERROR:
- table->field[3]->store(STRING_WITH_LEN("ERROR"),
- system_charset_info);
- break;
- case FRMTYPE_TABLE:
- table->field[3]->store(STRING_WITH_LEN("BASE TABLE"),
- system_charset_info);
- break;
- case FRMTYPE_VIEW:
- table->field[3]->store(STRING_WITH_LEN("VIEW"),
- system_charset_info);
- break;
- default:
- DBUG_ASSERT(0);
+ CHARSET_INFO *cs= system_charset_info;
+ handlerton *hton;
+ if (ha_table_exists(thd, db_name->str, table_name->str, &hton))
+ {
+ if (hton == view_pseudo_hton)
+ table->field[3]->store(STRING_WITH_LEN("VIEW"), cs);
+ else
+ table->field[3]->store(STRING_WITH_LEN("BASE TABLE"), cs);
}
- if (thd->is_error() && thd->stmt_da->sql_errno() == ER_NO_SUCH_TABLE)
+ else
+ table->field[3]->store(STRING_WITH_LEN("ERROR"), cs);
+
+ if (thd->is_error() && thd->get_stmt_da()->sql_errno() == ER_NO_SUCH_TABLE)
{
thd->clear_error();
return 0;
@@ -4077,7 +4462,7 @@ uint get_table_open_method(TABLE_LIST *tables,
for (ptr=tables->table->field; (field= *ptr) ; ptr++)
{
star_table_open_method=
- min(star_table_open_method,
+ MY_MIN(star_table_open_method,
schema_table->fields_info[field_indx].open_method);
if (bitmap_is_set(tables->table->read_set, field->field_index))
{
@@ -4187,10 +4572,6 @@ static int fill_schema_table_from_frm(THD *thd, TABLE_LIST *tables,
TABLE tbl;
TABLE_LIST table_list;
uint res= 0;
- int not_used;
- my_hash_value_type hash_value;
- char key[MAX_DBKEY_LENGTH];
- uint key_length;
char db_name_buff[NAME_LEN + 1], table_name_buff[NAME_LEN + 1];
bzero((char*) &table_list, sizeof(TABLE_LIST));
@@ -4243,7 +4624,7 @@ static int fill_schema_table_from_frm(THD *thd, TABLE_LIST *tables,
*/
DBUG_ASSERT(can_deadlock);
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_WARN_I_S_SKIPPED_TABLE,
ER(ER_WARN_I_S_SKIPPED_TABLE),
table_list.db, table_list.table_name);
@@ -4252,7 +4633,7 @@ static int fill_schema_table_from_frm(THD *thd, TABLE_LIST *tables,
if (schema_table->i_s_requested_object & OPEN_TRIGGER_ONLY)
{
- init_sql_alloc(&tbl.mem_root, TABLE_ALLOC_BLOCK_SIZE, 0);
+ init_sql_alloc(&tbl.mem_root, TABLE_ALLOC_BLOCK_SIZE, 0, MYF(0));
if (!Table_triggers_list::check_n_load(thd, db_name->str,
table_name->str, &tbl, 1))
{
@@ -4265,15 +4646,11 @@ static int fill_schema_table_from_frm(THD *thd, TABLE_LIST *tables,
goto end;
}
- key_length= create_table_def_key(thd, key, &table_list, 0);
- hash_value= my_calc_hash(&table_def_cache, (uchar*) key, key_length);
- mysql_mutex_lock(&LOCK_open);
- share= get_table_share(thd, &table_list, key,
- key_length, OPEN_VIEW, &not_used, hash_value);
+ share= tdc_acquire_share_shortlived(thd, &table_list, GTS_TABLE | GTS_VIEW);
if (!share)
{
res= 0;
- goto end_unlock;
+ goto end;
}
if (share->is_view)
@@ -4293,10 +4670,7 @@ static int fill_schema_table_from_frm(THD *thd, TABLE_LIST *tables,
res= 1;
goto end_share;
}
- }
- if (share->is_view)
- {
if (open_new_frm(thd, share, table_name->str,
(uint) (HA_OPEN_KEYFILE | HA_OPEN_RNDFILE |
HA_GET_INDEX | HA_TRY_READ_ONLY),
@@ -4322,11 +4696,9 @@ static int fill_schema_table_from_frm(THD *thd, TABLE_LIST *tables,
free_root(&tbl.mem_root, MYF(0));
}
-end_share:
- release_table_share(share);
-end_unlock:
- mysql_mutex_unlock(&LOCK_open);
+end_share:
+ tdc_release_share(share);
end:
/*
@@ -4359,20 +4731,20 @@ public:
bool handle_condition(THD *thd,
uint sql_errno,
const char* sqlstate,
- MYSQL_ERROR::enum_warning_level level,
+ Sql_condition::enum_warning_level level,
const char* msg,
- MYSQL_ERROR ** cond_hdl)
+ Sql_condition ** cond_hdl)
{
if (sql_errno == ER_PARSE_ERROR ||
sql_errno == ER_TRG_NO_DEFINER ||
sql_errno == ER_TRG_NO_CREATION_CTX)
return true;
- if (level != MYSQL_ERROR::WARN_LEVEL_ERROR)
+ if (level != Sql_condition::WARN_LEVEL_ERROR)
return false;
- if (!thd->stmt_da->is_error())
- thd->stmt_da->set_error_status(thd, sql_errno, msg, sqlstate);
+ if (!thd->get_stmt_da()->is_error())
+ thd->get_stmt_da()->set_error_status(sql_errno, msg, sqlstate, *cond_hdl);
return true; // handled!
}
};
@@ -4401,14 +4773,12 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond)
{
LEX *lex= thd->lex;
TABLE *table= tables->table;
+ TABLE_LIST table_acl_check;
SELECT_LEX *lsel= tables->schema_select_lex;
ST_SCHEMA_TABLE *schema_table= tables->schema_table;
LOOKUP_FIELD_VALUES lookup_field_vals;
- LEX_STRING *db_name, *table_name;
- bool with_i_schema;
enum enum_schema_tables schema_table_idx;
- List<LEX_STRING> db_names;
- List_iterator_fast<LEX_STRING> it(db_names);
+ Dynamic_array<LEX_STRING*> db_names;
COND *partial_cond= 0;
int error= 1;
Open_tables_backup open_tables_state_backup;
@@ -4471,9 +4841,9 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond)
goto err;
}
- DBUG_PRINT("INDEX VALUES",("db_name='%s', table_name='%s'",
- STR_OR_NIL(lookup_field_vals.db_value.str),
- STR_OR_NIL(lookup_field_vals.table_value.str)));
+ DBUG_PRINT("info",("db_name='%s', table_name='%s'",
+ lookup_field_vals.db_value.str,
+ lookup_field_vals.table_value.str));
if (!lookup_field_vals.wild_db_value && !lookup_field_vals.wild_table_value)
{
@@ -4510,11 +4880,13 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond)
goto err;
}
- if (make_db_list(thd, &db_names, &lookup_field_vals, &with_i_schema))
+ bzero((char*) &table_acl_check, sizeof(table_acl_check));
+
+ if (make_db_list(thd, &db_names, &lookup_field_vals))
goto err;
- it.rewind(); /* To get access to new elements in basis list */
- while ((db_name= it++))
+ for (size_t i=0; i < db_names.elements(); i++)
{
+ LEX_STRING *db_name= db_names.at(i);
DBUG_ASSERT(db_name->length <= NAME_LEN);
#ifndef NO_EMBEDDED_ACCESS_CHECKS
if (!(check_access(thd, SELECT_ACL, db_name->str,
@@ -4524,19 +4896,31 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond)
acl_get(sctx->host, sctx->ip, sctx->priv_user, db_name->str, 0))
#endif
{
- List<LEX_STRING> table_names;
+ Dynamic_array<LEX_STRING*> table_names;
int res= make_table_name_list(thd, &table_names, lex,
- &lookup_field_vals,
- with_i_schema, db_name);
+ &lookup_field_vals, db_name);
if (res == 2) /* Not fatal error, continue */
continue;
if (res)
goto err;
- List_iterator_fast<LEX_STRING> it_files(table_names);
- while ((table_name= it_files++))
+ for (size_t i=0; i < table_names.elements(); i++)
{
+ LEX_STRING *table_name= table_names.at(i);
DBUG_ASSERT(table_name->length <= NAME_LEN);
+
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ if (!(thd->col_access & TABLE_ACLS))
+ {
+ table_acl_check.db= db_name->str;
+ table_acl_check.db_length= db_name->length;
+ table_acl_check.table_name= table_name->str;
+ table_acl_check.table_name_length= table_name->length;
+ table_acl_check.grant.privilege= thd->col_access;
+ if (check_grant(thd, TABLE_ACLS, &table_acl_check, TRUE, 1, TRUE))
+ continue;
+ }
+#endif
restore_record(table, s->default_values);
table->field[schema_table->idx_field1]->
store(db_name->str, db_name->length, system_charset_info);
@@ -4564,18 +4948,18 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond)
/* SHOW TABLE NAMES command */
if (schema_table_idx == SCH_TABLE_NAMES)
{
- if (fill_schema_table_names(thd, tables, db_name,
- table_name, with_i_schema))
+ if (fill_schema_table_names(thd, tables, db_name, table_name))
continue;
}
- else if (schema_table_idx == SCH_TRIGGERS && with_i_schema)
+ else if (schema_table_idx == SCH_TRIGGERS &&
+ db_name == &INFORMATION_SCHEMA_NAME)
{
continue;
}
else
{
if (!(table_open_method & ~OPEN_FRM_ONLY) &&
- !with_i_schema)
+ db_name != &INFORMATION_SCHEMA_NAME)
{
if (!fill_schema_table_from_frm(thd, tables, schema_table,
db_name, table_name,
@@ -4595,11 +4979,6 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond)
}
}
}
- /*
- If we have information schema its always the first table and only
- the first table. Reset for other tables.
- */
- with_i_schema= 0;
}
}
@@ -4631,9 +5010,7 @@ int fill_schema_schemata(THD *thd, TABLE_LIST *tables, COND *cond)
*/
LOOKUP_FIELD_VALUES lookup_field_vals;
- List<LEX_STRING> db_names;
- LEX_STRING *db_name;
- bool with_i_schema;
+ Dynamic_array<LEX_STRING*> db_names;
HA_CREATE_INFO create;
TABLE *table= tables->table;
#ifndef NO_EMBEDDED_ACCESS_CHECKS
@@ -4646,15 +5023,14 @@ int fill_schema_schemata(THD *thd, TABLE_LIST *tables, COND *cond)
DBUG_PRINT("INDEX VALUES",("db_name: %s table_name: %s",
lookup_field_vals.db_value.str,
lookup_field_vals.table_value.str));
- if (make_db_list(thd, &db_names, &lookup_field_vals,
- &with_i_schema))
+ if (make_db_list(thd, &db_names, &lookup_field_vals))
DBUG_RETURN(1);
/*
If we have lookup db value we should check that the database exists
*/
if(lookup_field_vals.db_value.str && !lookup_field_vals.wild_db_value &&
- !with_i_schema)
+ db_names.at(0) != &INFORMATION_SCHEMA_NAME)
{
char path[FN_REFLEN+16];
uint path_len;
@@ -4668,16 +5044,15 @@ int fill_schema_schemata(THD *thd, TABLE_LIST *tables, COND *cond)
DBUG_RETURN(0);
}
- List_iterator_fast<LEX_STRING> it(db_names);
- while ((db_name=it++))
+ for (size_t i=0; i < db_names.elements(); i++)
{
+ LEX_STRING *db_name= db_names.at(i);
DBUG_ASSERT(db_name->length <= NAME_LEN);
- if (with_i_schema) // information schema name is always first in list
+ if (db_name == &INFORMATION_SCHEMA_NAME)
{
if (store_schema_shemata(thd, table, db_name,
system_charset_info))
DBUG_RETURN(1);
- with_i_schema= 0;
continue;
}
#ifndef NO_EMBEDDED_ACCESS_CHECKS
@@ -4732,7 +5107,7 @@ static int get_schema_tables_record(THD *thd, TABLE_LIST *tables,
}
else
{
- char option_buff[350];
+ char option_buff[512];
String str(option_buff,sizeof(option_buff), system_charset_info);
TABLE *show_table= tables->table;
TABLE_SHARE *share= show_table->s;
@@ -4762,7 +5137,7 @@ static int get_schema_tables_record(THD *thd, TABLE_LIST *tables,
if (share->db_type() == partition_hton &&
share->partition_info_str_len)
{
- tmp_db_type= share->default_part_db_type;
+ tmp_db_type= plugin_hton(share->default_part_plugin);
is_partitioned= TRUE;
}
#endif
@@ -4797,6 +5172,23 @@ static int get_schema_tables_record(THD *thd, TABLE_LIST *tables,
if (share->db_create_options & HA_OPTION_NO_PACK_KEYS)
str.qs_append(STRING_WITH_LEN(" pack_keys=0"));
+ if (share->db_create_options & HA_OPTION_STATS_PERSISTENT)
+ str.qs_append(STRING_WITH_LEN(" stats_persistent=1"));
+
+ if (share->db_create_options & HA_OPTION_NO_STATS_PERSISTENT)
+ str.qs_append(STRING_WITH_LEN(" stats_persistent=0"));
+
+ if (share->stats_auto_recalc == HA_STATS_AUTO_RECALC_ON)
+ str.qs_append(STRING_WITH_LEN(" stats_auto_recalc=1"));
+ else if (share->stats_auto_recalc == HA_STATS_AUTO_RECALC_OFF)
+ str.qs_append(STRING_WITH_LEN(" stats_auto_recalc=0"));
+
+ if (share->stats_sample_pages != 0)
+ {
+ str.qs_append(STRING_WITH_LEN(" stats_sample_pages="));
+ str.qs_append(share->stats_sample_pages);
+ }
+
/* We use CHECKSUM, instead of TABLE_CHECKSUM, for backward compability */
if (share->db_create_options & HA_OPTION_CHECKSUM)
str.qs_append(STRING_WITH_LEN(" checksum=1"));
@@ -4832,7 +5224,7 @@ static int get_schema_tables_record(THD *thd, TABLE_LIST *tables,
str.qs_append(STRING_WITH_LEN(" transactional="));
str.qs_append(ha_choice_values[(uint) share->transactional]);
}
- append_create_options(thd, &str, share->option_list);
+ append_create_options(thd, &str, share->option_list, false, 0);
if (str.length())
table->field[19]->store(str.ptr()+1, str.length()-1, cs);
@@ -4944,15 +5336,12 @@ err:
column with the error text, and clear the error so that the operation
can continue.
*/
- const char *error= thd->is_error() ? thd->stmt_da->message() : "";
+ const char *error= thd->get_stmt_da()->message();
table->field[20]->store(error, strlen(error), cs);
- if (thd->is_error())
- {
- push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
- thd->stmt_da->sql_errno(), thd->stmt_da->message());
- thd->clear_error();
- }
+ push_warning(thd, Sql_condition::WARN_LEVEL_WARN,
+ thd->get_stmt_da()->sql_errno(), error);
+ thd->clear_error();
}
DBUG_RETURN(schema_table_store_record(thd, table));
@@ -5101,7 +5490,7 @@ static int get_schema_column_record(THD *thd, TABLE_LIST *tables,
const char *wild= lex->wild ? lex->wild->ptr() : NullS;
CHARSET_INFO *cs= system_charset_info;
TABLE *show_table;
- Field **ptr, *field, *timestamp_field;
+ Field **ptr, *field;
int count;
DBUG_ENTER("get_schema_column_record");
@@ -5113,9 +5502,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
*/
- if (thd->is_error())
- push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
- thd->stmt_da->sql_errno(), thd->stmt_da->message());
+ push_warning(thd, Sql_condition::WARN_LEVEL_WARN,
+ thd->get_stmt_da()->sql_errno(),
+ thd->get_stmt_da()->message());
thd->clear_error();
res= 0;
}
@@ -5125,7 +5514,6 @@ static int get_schema_column_record(THD *thd, TABLE_LIST *tables,
show_table= tables->table;
count= 0;
ptr= show_table->field;
- timestamp_field= show_table->timestamp_field;
show_table->use_all_columns(); // Required for default
restore_record(show_table, s->default_values);
@@ -5148,7 +5536,7 @@ static int get_schema_column_record(THD *thd, TABLE_LIST *tables,
#ifndef NO_EMBEDDED_ACCESS_CHECKS
uint col_access;
check_access(thd,SELECT_ACL, db_name->str,
- &tables->grant.privilege, 0, 0, test(tables->schema_table));
+ &tables->grant.privilege, 0, 0, MY_TEST(tables->schema_table));
col_access= get_column_grant(thd, &tables->grant,
db_name->str, table_name->str,
field->field_name) & COL_ACLS;
@@ -5173,7 +5561,7 @@ static int get_schema_column_record(THD *thd, TABLE_LIST *tables,
cs);
table->field[4]->store((longlong) count, TRUE);
- if (get_field_default_value(thd, timestamp_field, field, &type, 0))
+ if (get_field_default_value(thd, field, &type, 0))
{
table->field[5]->store(type.ptr(), type.length(), cs);
table->field[5]->set_notnull();
@@ -5190,10 +5578,8 @@ static int get_schema_column_record(THD *thd, TABLE_LIST *tables,
if (field->unireg_check == Field::NEXT_NUMBER)
table->field[17]->store(STRING_WITH_LEN("auto_increment"), cs);
- if (timestamp_field == field &&
- field->unireg_check != Field::TIMESTAMP_DN_FIELD)
- table->field[17]->store(STRING_WITH_LEN("on update CURRENT_TIMESTAMP"),
- cs);
+ if (print_on_update_clause(field, &type, true))
+ table->field[17]->store(type.ptr(), type.length(), cs);
if (field->vcol_info)
{
if (field->stored_in_db)
@@ -5246,7 +5632,7 @@ static my_bool iter_schema_engines(THD *thd, plugin_ref plugin,
void *ptable)
{
TABLE *table= (TABLE *) ptable;
- handlerton *hton= plugin_data(plugin, handlerton *);
+ handlerton *hton= plugin_hton(plugin);
const char *wild= thd->lex->wild ? thd->lex->wild->ptr() : NullS;
CHARSET_INFO *scs= system_charset_info;
handlerton *default_type= ha_default_handlerton(thd);
@@ -5289,13 +5675,13 @@ static my_bool iter_schema_engines(THD *thd, plugin_ref plugin,
table->field[1]->store(option_name, strlen(option_name), scs);
table->field[2]->store(plugin_decl(plugin)->descr,
strlen(plugin_decl(plugin)->descr), scs);
- tmp= &yesno[test(hton->commit)];
+ tmp= &yesno[MY_TEST(hton->commit)];
table->field[3]->store(tmp->str, tmp->length, scs);
table->field[3]->set_notnull();
- tmp= &yesno[test(hton->prepare)];
+ tmp= &yesno[MY_TEST(hton->prepare)];
table->field[4]->store(tmp->str, tmp->length, scs);
table->field[4]->set_notnull();
- tmp= &yesno[test(hton->savepoint_set)];
+ tmp= &yesno[MY_TEST(hton->savepoint_set)];
table->field[5]->store(tmp->str, tmp->length, scs);
table->field[5]->set_notnull();
@@ -5514,16 +5900,16 @@ bool store_schema_params(THD *thd, TABLE *table, TABLE *proc_table,
for (uint i= 0 ; i < params ; i++)
{
const char *tmp_buff;
- sp_variable_t *spvar= spcont->find_variable(i);
+ sp_variable *spvar= spcont->find_variable(i);
field_def= &spvar->field_def;
switch (spvar->mode) {
- case sp_param_in:
+ case sp_variable::MODE_IN:
tmp_buff= "IN";
break;
- case sp_param_out:
+ case sp_variable::MODE_OUT:
tmp_buff= "OUT";
break;
- case sp_param_inout:
+ case sp_variable::MODE_INOUT:
tmp_buff= "INOUT";
break;
default:
@@ -5788,8 +6174,9 @@ static int get_schema_stat_record(THD *thd, TABLE_LIST *tables,
rather than in SHOW KEYS
*/
if (thd->is_error())
- push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
- thd->stmt_da->sql_errno(), thd->stmt_da->message());
+ push_warning(thd, Sql_condition::WARN_LEVEL_WARN,
+ thd->get_stmt_da()->sql_errno(),
+ thd->get_stmt_da()->message());
thd->clear_error();
res= 0;
}
@@ -5800,14 +6187,17 @@ static int get_schema_stat_record(THD *thd, TABLE_LIST *tables,
TABLE *show_table= tables->table;
KEY *key_info=show_table->s->key_info;
if (show_table->file)
+ {
show_table->file->info(HA_STATUS_VARIABLE |
HA_STATUS_NO_LOCK |
HA_STATUS_TIME);
+ set_statistics_for_table(thd, show_table);
+ }
for (uint i=0 ; i < show_table->s->keys ; i++,key_info++)
{
KEY_PART_INFO *key_part= key_info->key_part;
const char *str;
- for (uint j=0 ; j < key_info->key_parts ; j++,key_part++)
+ for (uint j=0 ; j < key_info->user_defined_key_parts ; j++,key_part++)
{
restore_record(table, s->default_values);
table->field[0]->store(STRING_WITH_LEN("def"), cs);
@@ -5833,8 +6223,8 @@ static int get_schema_stat_record(THD *thd, TABLE_LIST *tables,
KEY *key=show_table->key_info+i;
if (key->rec_per_key[j])
{
- ha_rows records=(show_table->file->stats.records /
- key->rec_per_key[j]);
+ ha_rows records= (ha_rows) ((double) show_table->stat_records() /
+ key->actual_rec_per_key(j));
table->field[9]->store((longlong) records, TRUE);
table->field[9]->set_notnull();
}
@@ -5858,7 +6248,7 @@ static int get_schema_stat_record(THD *thd, TABLE_LIST *tables,
else
table->field[14]->store("", 0, cs);
table->field[14]->set_notnull();
- DBUG_ASSERT(test(key_info->flags & HA_USES_COMMENT) ==
+ DBUG_ASSERT(MY_TEST(key_info->flags & HA_USES_COMMENT) ==
(key_info->comment.length > 0));
if (key_info->flags & HA_USES_COMMENT)
table->field[15]->store(key_info->comment.str,
@@ -5970,7 +6360,7 @@ static int get_schema_views_record(THD *thd, TABLE_LIST *tables,
*/
while ((item= it++))
{
- if ((field= item->filed_for_view_update()) && field->field &&
+ if ((field= item->field_for_view_update()) && field->field &&
!field->field->table->pos_in_table_list->schema_table)
{
updatable_view= 1;
@@ -6007,8 +6397,9 @@ static int get_schema_views_record(THD *thd, TABLE_LIST *tables,
if (schema_table_store_record(thd, table))
DBUG_RETURN(1);
if (res && thd->is_error())
- push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
- thd->stmt_da->sql_errno(), thd->stmt_da->message());
+ push_warning(thd, Sql_condition::WARN_LEVEL_WARN,
+ thd->get_stmt_da()->sql_errno(),
+ thd->get_stmt_da()->message());
}
if (res)
thd->clear_error();
@@ -6041,8 +6432,9 @@ static int get_schema_constraints_record(THD *thd, TABLE_LIST *tables,
if (res)
{
if (thd->is_error())
- push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
- thd->stmt_da->sql_errno(), thd->stmt_da->message());
+ push_warning(thd, Sql_condition::WARN_LEVEL_WARN,
+ thd->get_stmt_da()->sql_errno(),
+ thd->get_stmt_da()->message());
thd->clear_error();
DBUG_RETURN(0);
}
@@ -6147,8 +6539,9 @@ static int get_schema_triggers_record(THD *thd, TABLE_LIST *tables,
if (res)
{
if (thd->is_error())
- push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
- thd->stmt_da->sql_errno(), thd->stmt_da->message());
+ push_warning(thd, Sql_condition::WARN_LEVEL_WARN,
+ thd->get_stmt_da()->sql_errno(),
+ thd->get_stmt_da()->message());
thd->clear_error();
DBUG_RETURN(0);
}
@@ -6228,8 +6621,9 @@ static int get_schema_key_column_usage_record(THD *thd,
if (res)
{
if (thd->is_error())
- push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
- thd->stmt_da->sql_errno(), thd->stmt_da->message());
+ push_warning(thd, Sql_condition::WARN_LEVEL_WARN,
+ thd->get_stmt_da()->sql_errno(),
+ thd->get_stmt_da()->message());
thd->clear_error();
DBUG_RETURN(0);
}
@@ -6248,7 +6642,7 @@ static int get_schema_key_column_usage_record(THD *thd,
continue;
uint f_idx= 0;
KEY_PART_INFO *key_part= key_info->key_part;
- for (uint j=0 ; j < key_info->key_parts ; j++,key_part++)
+ for (uint j=0 ; j < key_info->user_defined_key_parts ; j++,key_part++)
{
if (key_part->field)
{
@@ -6443,7 +6837,7 @@ static void store_schema_partitions_record(THD *thd, TABLE *schema_table,
strlen(part_elem->tablespace_name), cs);
else
{
- char *ts= showing_table->file->get_tablespace_name(thd,0,0);
+ char *ts= showing_table->s->tablespace;
if(ts)
table->field[24]->store(ts, strlen(ts), cs);
else
@@ -6518,8 +6912,9 @@ static int get_schema_partitions_record(THD *thd, TABLE_LIST *tables,
if (res)
{
if (thd->is_error())
- push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
- thd->stmt_da->sql_errno(), thd->stmt_da->message());
+ push_warning(thd, Sql_condition::WARN_LEVEL_WARN,
+ thd->get_stmt_da()->sql_errno(),
+ thd->get_stmt_da()->message());
thd->clear_error();
DBUG_RETURN(0);
}
@@ -6770,7 +7165,7 @@ copy_event_to_schema_table(THD *thd, TABLE *sch_table, TABLE *event_table)
if (et.load_from_row(thd, event_table))
{
- my_error(ER_CANNOT_LOAD_FROM_TABLE, MYF(0), event_table->alias.c_ptr());
+ my_error(ER_CANNOT_LOAD_FROM_TABLE_V2, MYF(0), "mysql", "event");
DBUG_RETURN(1);
}
@@ -7009,14 +7404,20 @@ int fill_status(THD *thd, TABLE_LIST *tables, COND *cond)
if (partial_cond)
partial_cond->val_int();
- mysql_mutex_lock(&LOCK_status);
if (option_type == OPT_GLOBAL)
+ {
+ /* We only hold LOCK_status for summary status vars */
+ mysql_mutex_lock(&LOCK_status);
calc_sum_of_all_status(&tmp);
+ mysql_mutex_unlock(&LOCK_status);
+ }
+
+ mysql_mutex_lock(&LOCK_show_status);
res= show_status_array(thd, wild,
(SHOW_VAR *)all_status_vars.buffer,
option_type, tmp1, "", tables->table,
upper_case_names, partial_cond);
- mysql_mutex_unlock(&LOCK_status);
+ mysql_mutex_unlock(&LOCK_show_status);
DBUG_RETURN(res);
}
@@ -7050,8 +7451,9 @@ get_referential_constraints_record(THD *thd, TABLE_LIST *tables,
if (res)
{
if (thd->is_error())
- push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
- thd->stmt_da->sql_errno(), thd->stmt_da->message());
+ push_warning(thd, Sql_condition::WARN_LEVEL_WARN,
+ thd->get_stmt_da()->sql_errno(),
+ thd->get_stmt_da()->message());
thd->clear_error();
DBUG_RETURN(0);
}
@@ -7394,7 +7796,7 @@ TABLE *create_schema_table(THD *thd, TABLE_LIST *table_list)
DBUG_RETURN(0);
my_bitmap_map* bitmaps=
(my_bitmap_map*) thd->alloc(bitmap_buffer_size(field_count));
- bitmap_init(&table->def_read_set, (my_bitmap_map*) bitmaps, field_count,
+ my_bitmap_init(&table->def_read_set, (my_bitmap_map*) bitmaps, field_count,
FALSE);
table->read_set= &table->def_read_set;
bitmap_clear_all(table->read_set);
@@ -7694,16 +8096,22 @@ int make_schema_select(THD *thd, SELECT_LEX *sel,
We have to make non const db_name & table_name
because of lower_case_table_names
*/
- thd->make_lex_string(&db, INFORMATION_SCHEMA_NAME.str,
- INFORMATION_SCHEMA_NAME.length, 0);
- thd->make_lex_string(&table, schema_table->table_name,
- strlen(schema_table->table_name), 0);
- if (schema_table->old_format(thd, schema_table) || /* Handle old syntax */
- !sel->add_table_to_list(thd, new Table_ident(thd, db, table, 0),
+ if (!thd->make_lex_string(&db, INFORMATION_SCHEMA_NAME.str,
+ INFORMATION_SCHEMA_NAME.length))
+ DBUG_RETURN(1);
+
+ if (!thd->make_lex_string(&table, schema_table->table_name,
+ strlen(schema_table->table_name)))
+ DBUG_RETURN(1);
+
+ if (schema_table->old_format(thd, schema_table))
+
+ DBUG_RETURN(1);
+
+ if (!sel->add_table_to_list(thd, new Table_ident(thd, db, table, 0),
0, 0, TL_READ, MDL_SHARED_READ))
- {
DBUG_RETURN(1);
- }
+
DBUG_RETURN(0);
}
@@ -7727,12 +8135,13 @@ bool get_schema_tables_result(JOIN *join,
THD *thd= join->thd;
LEX *lex= thd->lex;
bool result= 0;
- const char *old_proc_info;
+ PSI_stage_info org_stage;
DBUG_ENTER("get_schema_tables_result");
Warnings_only_error_handler err_handler;
thd->push_internal_handler(&err_handler);
- old_proc_info= thd_proc_info(thd, "Filling schema table");
+ thd->enter_stage(&stage_filling_schema_table, &org_stage, __func__, __FILE__,
+ __LINE__);
JOIN_TAB *tab;
for (tab= first_linear_tab(join, WITHOUT_BUSH_ROOTS, WITH_CONST_TABLES);
@@ -7793,9 +8202,22 @@ bool get_schema_tables_result(JOIN *join,
else
table_list->table->file->stats.records= 0;
+
+ Item *cond= tab->select_cond;
+ if (tab->cache_select && tab->cache_select->cond)
+ {
+ /*
+ If join buffering is used, we should use the condition that is
+ attached to the join cache. Cache condition has a part of WHERE that
+ can be checked when we're populating this table.
+ join_tab->select_cond is of no interest, because it only has
+ conditions that depend on both this table and previous tables in the
+ join order.
+ */
+ cond= tab->cache_select->cond;
+ }
- if (table_list->schema_table->fill_table(thd, table_list,
- tab->select_cond))
+ if (table_list->schema_table->fill_table(thd, table_list, cond))
{
result= 1;
join->error= 1;
@@ -7821,15 +8243,15 @@ bool get_schema_tables_result(JOIN *join,
It also means that an audit plugin cannot process the error correctly
either. See also thd->clear_error()
*/
- thd->warning_info->push_warning(thd,
- thd->stmt_da->sql_errno(),
- thd->stmt_da->get_sqlstate(),
- MYSQL_ERROR::WARN_LEVEL_ERROR,
- thd->stmt_da->message());
+ thd->get_stmt_da()->push_warning(thd,
+ thd->get_stmt_da()->sql_errno(),
+ thd->get_stmt_da()->get_sqlstate(),
+ Sql_condition::WARN_LEVEL_ERROR,
+ thd->get_stmt_da()->message());
}
else if (result)
my_error(ER_UNKNOWN_ERROR, MYF(0));
- thd_proc_info(thd, old_proc_info);
+ THD_STAGE_INFO(thd, org_stage);
DBUG_RETURN(result);
}
@@ -7844,7 +8266,7 @@ static my_bool run_hton_fill_schema_table(THD *thd, plugin_ref plugin,
{
struct run_hton_fill_schema_table_args *args=
(run_hton_fill_schema_table_args *) arg;
- handlerton *hton= plugin_data(plugin, handlerton *);
+ handlerton *hton= plugin_hton(plugin);
if (hton->fill_is_table && hton->state == SHOW_OPTION_YES)
hton->fill_is_table(hton, thd, args->tables, args->cond,
get_schema_table_idx(args->tables->schema_table));
@@ -8070,6 +8492,22 @@ ST_FIELD_INFO collation_fields_info[]=
};
+ST_FIELD_INFO applicable_roles_fields_info[]=
+{
+ {"GRANTEE", USERNAME_WITH_HOST_CHAR_LENGTH, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
+ {"ROLE_NAME", USERNAME_CHAR_LENGTH, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
+ {"IS_GRANTABLE", 3, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
+ {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}
+};
+
+
+ST_FIELD_INFO enabled_roles_fields_info[]=
+{
+ {"ROLE_NAME", USERNAME_CHAR_LENGTH, MYSQL_TYPE_STRING, 0, MY_I_S_MAYBE_NULL, 0, SKIP_OPEN_TABLE},
+ {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}
+};
+
+
ST_FIELD_INFO engines_fields_info[]=
{
{"ENGINE", 64, MYSQL_TYPE_STRING, 0, 0, "Engine", SKIP_OPEN_TABLE},
@@ -8223,7 +8661,7 @@ ST_FIELD_INFO view_fields_info[]=
ST_FIELD_INFO user_privileges_fields_info[]=
{
- {"GRANTEE", 81, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
+ {"GRANTEE", USERNAME_WITH_HOST_CHAR_LENGTH, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
{"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
{"PRIVILEGE_TYPE", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
{"IS_GRANTABLE", 3, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
@@ -8233,7 +8671,7 @@ ST_FIELD_INFO user_privileges_fields_info[]=
ST_FIELD_INFO schema_privileges_fields_info[]=
{
- {"GRANTEE", 81, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
+ {"GRANTEE", USERNAME_WITH_HOST_CHAR_LENGTH, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
{"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
{"TABLE_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
{"PRIVILEGE_TYPE", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
@@ -8244,7 +8682,7 @@ ST_FIELD_INFO schema_privileges_fields_info[]=
ST_FIELD_INFO table_privileges_fields_info[]=
{
- {"GRANTEE", 81, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
+ {"GRANTEE", USERNAME_WITH_HOST_CHAR_LENGTH, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
{"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
{"TABLE_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
{"TABLE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
@@ -8256,7 +8694,7 @@ ST_FIELD_INFO table_privileges_fields_info[]=
ST_FIELD_INFO column_privileges_fields_info[]=
{
- {"GRANTEE", 81, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
+ {"GRANTEE", USERNAME_WITH_HOST_CHAR_LENGTH, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
{"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
{"TABLE_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
{"TABLE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
@@ -8439,6 +8877,9 @@ ST_FIELD_INFO processlist_fields_info[]=
{"MAX_STAGE", 2, MYSQL_TYPE_TINY, 0, 0, "Max_stage", SKIP_OPEN_TABLE},
{"PROGRESS", 703, MYSQL_TYPE_DECIMAL, 0, 0, "Progress",
SKIP_OPEN_TABLE},
+ {"MEMORY_USED", 7, MYSQL_TYPE_LONG, 0, 0, "Memory_used", SKIP_OPEN_TABLE},
+ {"EXAMINED_ROWS", 7, MYSQL_TYPE_LONG, 0, 0, "Examined_rows", SKIP_OPEN_TABLE},
+ {"QUERY_ID", 4, MYSQL_TYPE_LONGLONG, 0, 0, 0, SKIP_OPEN_TABLE},
{0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}
};
@@ -8448,7 +8889,7 @@ ST_FIELD_INFO plugin_fields_info[]=
{"PLUGIN_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, "Name",
SKIP_OPEN_TABLE},
{"PLUGIN_VERSION", 20, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
- {"PLUGIN_STATUS", 10, MYSQL_TYPE_STRING, 0, 0, "Status", SKIP_OPEN_TABLE},
+ {"PLUGIN_STATUS", 16, MYSQL_TYPE_STRING, 0, 0, "Status", SKIP_OPEN_TABLE},
{"PLUGIN_TYPE", 80, MYSQL_TYPE_STRING, 0, 0, "Type", SKIP_OPEN_TABLE},
{"PLUGIN_TYPE_VERSION", 20, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
{"PLUGIN_LIBRARY", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, "Library",
@@ -8628,6 +9069,32 @@ ST_FIELD_INFO keycache_fields_info[]=
};
+ST_FIELD_INFO show_explain_fields_info[]=
+{
+ /* field_name, length, type, value, field_flags, old_name*/
+ {"id", 3, MYSQL_TYPE_LONGLONG, 0 /*value*/, MY_I_S_MAYBE_NULL, "id",
+ SKIP_OPEN_TABLE},
+ {"select_type", 19, MYSQL_TYPE_STRING, 0 /*value*/, 0, "select_type",
+ SKIP_OPEN_TABLE},
+ {"table", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0 /*value*/, MY_I_S_MAYBE_NULL,
+ "table", SKIP_OPEN_TABLE},
+ {"type", 15, MYSQL_TYPE_STRING, 0, MY_I_S_MAYBE_NULL, "type", SKIP_OPEN_TABLE},
+ {"possible_keys", NAME_CHAR_LEN*MAX_KEY, MYSQL_TYPE_STRING, 0/*value*/,
+ MY_I_S_MAYBE_NULL, "possible_keys", SKIP_OPEN_TABLE},
+ {"key", NAME_CHAR_LEN*MAX_KEY, MYSQL_TYPE_STRING, 0/*value*/,
+ MY_I_S_MAYBE_NULL, "key", SKIP_OPEN_TABLE},
+ {"key_len", NAME_CHAR_LEN*MAX_KEY, MYSQL_TYPE_STRING, 0/*value*/,
+ MY_I_S_MAYBE_NULL, "key_len", SKIP_OPEN_TABLE},
+ {"ref", NAME_CHAR_LEN*MAX_REF_PARTS, MYSQL_TYPE_STRING, 0/*value*/,
+ MY_I_S_MAYBE_NULL, "ref", SKIP_OPEN_TABLE},
+ {"rows", 10, MYSQL_TYPE_LONGLONG, 0/*value*/, MY_I_S_MAYBE_NULL, "rows",
+ SKIP_OPEN_TABLE},
+ {"Extra", 255, MYSQL_TYPE_STRING, 0/*value*/, 0 /*flags*/, "Extra",
+ SKIP_OPEN_TABLE},
+ {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}
+};
+
+
/*
Description of ST_FIELD_INFO in table.h
@@ -8637,6 +9104,10 @@ ST_FIELD_INFO keycache_fields_info[]=
ST_SCHEMA_TABLE schema_tables[]=
{
+ {"ALL_PLUGINS", plugin_fields_info, create_schema_table,
+ fill_all_plugins, make_old_format, 0, 5, -1, 0, 0},
+ {"APPLICABLE_ROLES", applicable_roles_fields_info, create_schema_table,
+ fill_schema_applicable_roles, 0, 0, -1, -1, 0, 0},
{"CHARACTER_SETS", charsets_fields_info, create_schema_table,
fill_schema_charsets, make_character_sets_old_format, 0, -1, -1, 0, 0},
{"CLIENT_STATISTICS", client_stats_fields_info, create_schema_table,
@@ -8650,6 +9121,8 @@ ST_SCHEMA_TABLE schema_tables[]=
OPTIMIZE_I_S_TABLE|OPEN_VIEW_FULL},
{"COLUMN_PRIVILEGES", column_privileges_fields_info, create_schema_table,
fill_schema_column_privileges, 0, 0, -1, -1, 0, 0},
+ {"ENABLED_ROLES", enabled_roles_fields_info, create_schema_table,
+ fill_schema_enabled_roles, 0, 0, -1, -1, 0, 0},
{"ENGINES", engines_fields_info, create_schema_table,
fill_schema_engines, make_old_format, 0, -1, -1, 0, 0},
#ifdef HAVE_EVENT_SCHEDULER
@@ -8659,6 +9132,8 @@ ST_SCHEMA_TABLE schema_tables[]=
{"EVENTS", events_fields_info, create_schema_table,
0, make_old_format, 0, -1, -1, 0, 0},
#endif
+ {"EXPLAIN", show_explain_fields_info, create_schema_table, fill_show_explain,
+ make_old_format, 0, -1, -1, TRUE /*hidden*/ , 0},
{"FILES", files_fields_info, create_schema_table,
hton_fill_schema_table, 0, 0, -1, -1, 0, 0},
{"GLOBAL_STATUS", variables_fields_info, create_schema_table,
@@ -8734,18 +9209,13 @@ ST_SCHEMA_TABLE schema_tables[]=
};
-#ifdef HAVE_EXPLICIT_TEMPLATE_INSTANTIATION
-template class List_iterator_fast<char>;
-template class List<char>;
-#endif
-
int initialize_schema_table(st_plugin_int *plugin)
{
ST_SCHEMA_TABLE *schema_table;
DBUG_ENTER("initialize_schema_table");
if (!(schema_table= (ST_SCHEMA_TABLE *)my_malloc(sizeof(ST_SCHEMA_TABLE),
- MYF(MY_WME | MY_ZEROFILL))))
+ MYF(MY_WME | MY_ZEROFILL))))
DBUG_RETURN(1);
/* Historical Requirement */
plugin->data= schema_table; // shortcut for the future
@@ -8868,7 +9338,7 @@ static bool show_create_trigger_impl(THD *thd,
Item_empty_string *stmt_fld=
new Item_empty_string("SQL Original Statement",
- max(trg_sql_original_stmt.length, 1024));
+ MY_MAX(trg_sql_original_stmt.length, 1024));
stmt_fld->maybe_null= TRUE;
@@ -8975,6 +9445,9 @@ TABLE_LIST *get_trigger_table(THD *thd, const sp_name *trg_name)
db= trg_name->m_db;
db.str= thd->strmake(db.str, db.length);
+ if (lower_case_table_names)
+ db.length= my_casedn_str(files_charset_info, db.str);
+
tbl_name.str= thd->strmake(tbl_name.str, tbl_name.length);
if (db.str == NULL || tbl_name.str == NULL)
diff --git a/sql/sql_show.h b/sql/sql_show.h
index 864216ebc06..b66cd30c0ce 100644
--- a/sql/sql_show.h
+++ b/sql/sql_show.h
@@ -20,6 +20,7 @@
#include "sql_list.h" /* List */
#include "handler.h" /* enum_schema_tables */
#include "table.h" /* enum_schema_table_state */
+#include "my_apc.h"
/* Forward declarations */
class JOIN;
@@ -27,21 +28,13 @@ class String;
class THD;
class sp_name;
struct TABLE_LIST;
-struct st_ha_create_information;
typedef class st_select_lex SELECT_LEX;
-typedef st_ha_create_information HA_CREATE_INFO;
struct LEX;
typedef struct st_mysql_show_var SHOW_VAR;
typedef struct st_schema_table ST_SCHEMA_TABLE;
struct TABLE;
typedef struct system_status_var STATUS_VAR;
-enum find_files_result {
- FIND_FILES_OK,
- FIND_FILES_OOM,
- FIND_FILES_DIR
-};
-
/* Used by handlers to store things in schema tables */
#define IS_FILES_FILE_ID 0
#define IS_FILES_FILE_NAME 1
@@ -82,12 +75,10 @@ enum find_files_result {
#define IS_FILES_STATUS 36
#define IS_FILES_EXTRA 37
-find_files_result find_files(THD *thd, List<LEX_STRING> *files, const char *db,
- const char *path, const char *wild, bool dir);
-
-int store_create_info(THD *thd, TABLE_LIST *table_list, String *packet,
- HA_CREATE_INFO *create_info_arg, bool show_database);
-int view_store_create_info(THD *thd, TABLE_LIST *table, String *buff);
+typedef enum { WITHOUT_DB_NAME, WITH_DB_NAME } enum_with_db_name;
+int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet,
+ HA_CREATE_INFO *create_info_arg,
+ enum_with_db_name with_db_name);
int copy_event_to_schema_table(THD *thd, TABLE *sch_table, TABLE *event_table);
@@ -96,7 +87,9 @@ bool append_identifier(THD *thd, String *packet, const char *name,
void mysqld_list_fields(THD *thd,TABLE_LIST *table, const char *wild);
int mysqld_dump_create_info(THD *thd, TABLE_LIST *table_list, int fd);
bool mysqld_show_create(THD *thd, TABLE_LIST *table_list);
-bool mysqld_show_create_db(THD *thd, char *dbname, HA_CREATE_INFO *create);
+bool mysqld_show_create_db(THD *thd, LEX_STRING *db_name,
+ LEX_STRING *orig_db_name,
+ HA_CREATE_INFO *create);
void mysqld_list_processes(THD *thd,const char *user,bool verbose);
int mysqld_show_status(THD *thd);
@@ -134,6 +127,31 @@ enum enum_schema_tables get_schema_table_idx(ST_SCHEMA_TABLE *schema_table);
/* These functions were under INNODB_COMPATIBILITY_HOOKS */
int get_quote_char_for_identifier(THD *thd, const char *name, uint length);
+THD *find_thread_by_id(longlong id, bool query_id= false);
+
+class select_result_explain_buffer;
+/*
+ SHOW EXPLAIN request object.
+*/
+
+class Show_explain_request : public Apc_target::Apc_call
+{
+public:
+ THD *target_thd; /* thd that we're running SHOW EXPLAIN for */
+ THD *request_thd; /* thd that run SHOW EXPLAIN command */
+
+ /* If true, there was some error when producing EXPLAIN output. */
+ bool failed_to_produce;
+
+ /* SHOW EXPLAIN will be stored here */
+ select_result_explain_buffer *explain_buf;
+
+ /* Query that we've got SHOW EXPLAIN for */
+ String query_str;
+
+ /* Overloaded virtual function */
+ void call_in_target_thread();
+};
/* Handle the ignored database directories list for SHOW/I_S. */
bool ignore_db_dirs_init();
diff --git a/sql/sql_signal.cc b/sql/sql_signal.cc
index e671ad9526f..374a24f75e5 100644
--- a/sql/sql_signal.cc
+++ b/sql/sql_signal.cc
@@ -13,6 +13,7 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+#include <my_global.h>
#include "sql_priv.h"
#include "sp_head.h"
#include "sp_pcontext.h"
@@ -88,9 +89,10 @@ void Set_signal_information::clear()
memset(m_item, 0, sizeof(m_item));
}
-void Signal_common::assign_defaults(MYSQL_ERROR *cond,
+void Sql_cmd_common_signal::assign_defaults(
+ Sql_condition *cond,
bool set_level_code,
- MYSQL_ERROR::enum_warning_level level,
+ Sql_condition::enum_warning_level level,
int sqlcode)
{
if (set_level_code)
@@ -102,7 +104,7 @@ void Signal_common::assign_defaults(MYSQL_ERROR *cond,
cond->set_builtin_message_text(ER(sqlcode));
}
-void Signal_common::eval_defaults(THD *thd, MYSQL_ERROR *cond)
+void Sql_cmd_common_signal::eval_defaults(THD *thd, Sql_condition *cond)
{
DBUG_ASSERT(cond);
@@ -114,8 +116,8 @@ void Signal_common::eval_defaults(THD *thd, MYSQL_ERROR *cond)
/*
SIGNAL is restricted in sql_yacc.yy to only signal SQLSTATE conditions.
*/
- DBUG_ASSERT(m_cond->type == sp_cond_type::state);
- sqlstate= m_cond->sqlstate;
+ DBUG_ASSERT(m_cond->type == sp_condition_value::SQLSTATE);
+ sqlstate= m_cond->sql_state;
cond->set_sqlstate(sqlstate);
}
else
@@ -129,19 +131,19 @@ void Signal_common::eval_defaults(THD *thd, MYSQL_ERROR *cond)
{
/* SQLSTATE class "01": warning. */
assign_defaults(cond, set_defaults,
- MYSQL_ERROR::WARN_LEVEL_WARN, ER_SIGNAL_WARN);
+ Sql_condition::WARN_LEVEL_WARN, ER_SIGNAL_WARN);
}
else if ((sqlstate[0] == '0') && (sqlstate[1] == '2'))
{
/* SQLSTATE class "02": not found. */
assign_defaults(cond, set_defaults,
- MYSQL_ERROR::WARN_LEVEL_ERROR, ER_SIGNAL_NOT_FOUND);
+ Sql_condition::WARN_LEVEL_ERROR, ER_SIGNAL_NOT_FOUND);
}
else
{
/* other SQLSTATE classes : error. */
assign_defaults(cond, set_defaults,
- MYSQL_ERROR::WARN_LEVEL_ERROR, ER_SIGNAL_EXCEPTION);
+ Sql_condition::WARN_LEVEL_ERROR, ER_SIGNAL_EXCEPTION);
}
}
@@ -243,8 +245,7 @@ static int assign_condition_item(MEM_ROOT *mem_root, const char* name, THD *thd,
truncated= assign_fixed_string(mem_root, & my_charset_utf8_bin, 64, ci, str);
if (truncated)
{
- if (thd->variables.sql_mode & (MODE_STRICT_TRANS_TABLES |
- MODE_STRICT_ALL_TABLES))
+ if (thd->is_strict_mode())
{
thd->raise_error_printf(ER_COND_ITEM_TOO_LONG, name);
DBUG_RETURN(1);
@@ -257,26 +258,26 @@ static int assign_condition_item(MEM_ROOT *mem_root, const char* name, THD *thd,
}
-int Signal_common::eval_signal_informations(THD *thd, MYSQL_ERROR *cond)
+int Sql_cmd_common_signal::eval_signal_informations(THD *thd, Sql_condition *cond)
{
struct cond_item_map
{
enum enum_diag_condition_item_name m_item;
- String MYSQL_ERROR::*m_member;
+ String Sql_condition::*m_member;
};
static cond_item_map map[]=
{
- { DIAG_CLASS_ORIGIN, & MYSQL_ERROR::m_class_origin },
- { DIAG_SUBCLASS_ORIGIN, & MYSQL_ERROR::m_subclass_origin },
- { DIAG_CONSTRAINT_CATALOG, & MYSQL_ERROR::m_constraint_catalog },
- { DIAG_CONSTRAINT_SCHEMA, & MYSQL_ERROR::m_constraint_schema },
- { DIAG_CONSTRAINT_NAME, & MYSQL_ERROR::m_constraint_name },
- { DIAG_CATALOG_NAME, & MYSQL_ERROR::m_catalog_name },
- { DIAG_SCHEMA_NAME, & MYSQL_ERROR::m_schema_name },
- { DIAG_TABLE_NAME, & MYSQL_ERROR::m_table_name },
- { DIAG_COLUMN_NAME, & MYSQL_ERROR::m_column_name },
- { DIAG_CURSOR_NAME, & MYSQL_ERROR::m_cursor_name }
+ { DIAG_CLASS_ORIGIN, & Sql_condition::m_class_origin },
+ { DIAG_SUBCLASS_ORIGIN, & Sql_condition::m_subclass_origin },
+ { DIAG_CONSTRAINT_CATALOG, & Sql_condition::m_constraint_catalog },
+ { DIAG_CONSTRAINT_SCHEMA, & Sql_condition::m_constraint_schema },
+ { DIAG_CONSTRAINT_NAME, & Sql_condition::m_constraint_name },
+ { DIAG_CATALOG_NAME, & Sql_condition::m_catalog_name },
+ { DIAG_SCHEMA_NAME, & Sql_condition::m_schema_name },
+ { DIAG_TABLE_NAME, & Sql_condition::m_table_name },
+ { DIAG_COLUMN_NAME, & Sql_condition::m_column_name },
+ { DIAG_CURSOR_NAME, & Sql_condition::m_cursor_name }
};
Item *set;
@@ -289,7 +290,7 @@ int Signal_common::eval_signal_informations(THD *thd, MYSQL_ERROR *cond)
String *member;
const LEX_STRING *name;
- DBUG_ENTER("Signal_common::eval_signal_informations");
+ DBUG_ENTER("Sql_cmd_common_signal::eval_signal_informations");
for (i= FIRST_DIAG_SET_PROPERTY;
i <= LAST_DIAG_SET_PROPERTY;
@@ -348,8 +349,7 @@ int Signal_common::eval_signal_informations(THD *thd, MYSQL_ERROR *cond)
& utf8_text, str);
if (truncated)
{
- if (thd->variables.sql_mode & (MODE_STRICT_TRANS_TABLES |
- MODE_STRICT_ALL_TABLES))
+ if (thd->is_strict_mode())
{
thd->raise_error_printf(ER_COND_ITEM_TOO_LONG,
"MESSAGE_TEXT");
@@ -362,7 +362,7 @@ int Signal_common::eval_signal_informations(THD *thd, MYSQL_ERROR *cond)
/*
See the comments
- "Design notes about MYSQL_ERROR::m_message_text."
+ "Design notes about Sql_condition::m_message_text."
in file sql_error.cc
*/
String converted_text;
@@ -415,23 +415,23 @@ end:
DBUG_RETURN(result);
}
-bool Signal_common::raise_condition(THD *thd, MYSQL_ERROR *cond)
+bool Sql_cmd_common_signal::raise_condition(THD *thd, Sql_condition *cond)
{
bool result= TRUE;
- DBUG_ENTER("Signal_common::raise_condition");
+ DBUG_ENTER("Sql_cmd_common_signal::raise_condition");
- DBUG_ASSERT(m_lex->query_tables == NULL);
+ DBUG_ASSERT(thd->lex->query_tables == NULL);
eval_defaults(thd, cond);
if (eval_signal_informations(thd, cond))
DBUG_RETURN(result);
/* SIGNAL should not signal WARN_LEVEL_NOTE */
- DBUG_ASSERT((cond->m_level == MYSQL_ERROR::WARN_LEVEL_WARN) ||
- (cond->m_level == MYSQL_ERROR::WARN_LEVEL_ERROR));
+ DBUG_ASSERT((cond->m_level == Sql_condition::WARN_LEVEL_WARN) ||
+ (cond->m_level == Sql_condition::WARN_LEVEL_ERROR));
- MYSQL_ERROR *raised= NULL;
+ Sql_condition *raised= NULL;
raised= thd->raise_condition(cond->get_sql_errno(),
cond->get_sqlstate(),
cond->get_level(),
@@ -439,7 +439,7 @@ bool Signal_common::raise_condition(THD *thd, MYSQL_ERROR *cond)
if (raised)
raised->copy_opt_attributes(cond);
- if (cond->m_level == MYSQL_ERROR::WARN_LEVEL_WARN)
+ if (cond->m_level == Sql_condition::WARN_LEVEL_WARN)
{
my_ok(thd);
result= FALSE;
@@ -448,12 +448,12 @@ bool Signal_common::raise_condition(THD *thd, MYSQL_ERROR *cond)
DBUG_RETURN(result);
}
-bool Signal_statement::execute(THD *thd)
+bool Sql_cmd_signal::execute(THD *thd)
{
bool result= TRUE;
- MYSQL_ERROR cond(thd->mem_root);
+ Sql_condition cond(thd->mem_root);
- DBUG_ENTER("Signal_statement::execute");
+ DBUG_ENTER("Sql_cmd_signal::execute");
/*
WL#2110 SIGNAL specification says:
@@ -467,9 +467,9 @@ bool Signal_statement::execute(THD *thd)
This has roots in the SQL standard specification for SIGNAL.
*/
- thd->stmt_da->reset_diagnostics_area();
+ thd->get_stmt_da()->reset_diagnostics_area();
thd->set_row_count_func(0);
- thd->warning_info->clear_warning_info(thd->query_id);
+ thd->get_stmt_da()->clear_warning_info(thd->query_id);
result= raise_condition(thd, &cond);
@@ -477,14 +477,27 @@ bool Signal_statement::execute(THD *thd)
}
-bool Resignal_statement::execute(THD *thd)
+/**
+ Execute RESIGNAL SQL-statement.
+
+ @param thd Thread context.
+
+ @return Error status
+ @retval true in case of error
+ @retval false on success
+*/
+
+bool Sql_cmd_resignal::execute(THD *thd)
{
- Sql_condition_info *signaled;
+ Diagnostics_area *da= thd->get_stmt_da();
+ const sp_rcontext::Sql_condition_info *signaled;
int result= TRUE;
DBUG_ENTER("Resignal_statement::execute");
- thd->warning_info->m_warn_id= thd->query_id;
+ // This is a way to force sql_conditions from the current Warning_info to be
+ // passed to the caller's Warning_info.
+ da->set_warning_info_id(thd->query_id);
if (! thd->spcont || ! (signaled= thd->spcont->raised_condition()))
{
@@ -492,22 +505,38 @@ bool Resignal_statement::execute(THD *thd)
DBUG_RETURN(result);
}
- MYSQL_ERROR signaled_err(thd->mem_root);
- signaled_err.set(signaled->m_sql_errno,
- signaled->m_sql_state,
- signaled->m_level,
- signaled->m_message);
+ Sql_condition signaled_err(thd->mem_root);
+ signaled_err.set(signaled->sql_errno,
+ signaled->sql_state,
+ signaled->level,
+ signaled->message);
- if (m_cond == NULL)
+ if (m_cond)
{
- /* RESIGNAL without signal_value */
- result= raise_condition(thd, &signaled_err);
- DBUG_RETURN(result);
+ query_cache_abort(&thd->query_cache_tls);
+
+ /* Keep handled conditions. */
+ da->unmark_sql_conditions_from_removal();
+
+ /* Check if the old condition still exists. */
+ if (da->has_sql_condition(signaled->message, strlen(signaled->message)))
+ {
+ /* Make room for the new RESIGNAL condition. */
+ da->reserve_space(thd, 1);
+ }
+ else
+ {
+ /* Make room for old condition + the new RESIGNAL condition. */
+ da->reserve_space(thd, 2);
+
+ da->push_warning(thd, &signaled_err);
+ }
}
/* RESIGNAL with signal_value */
result= raise_condition(thd, &signaled_err);
DBUG_RETURN(result);
+
}
diff --git a/sql/sql_signal.h b/sql/sql_signal.h
index 058457a3639..2a508eed5bf 100644
--- a/sql/sql_signal.h
+++ b/sql/sql_signal.h
@@ -18,27 +18,25 @@
#define SQL_SIGNAL_H
/**
- Signal_common represents the common properties of the SIGNAL and RESIGNAL
- statements.
+ Sql_cmd_common_signal represents the common properties of the
+ SIGNAL and RESIGNAL statements.
*/
-class Signal_common : public Sql_statement
+class Sql_cmd_common_signal : public Sql_cmd
{
protected:
/**
Constructor.
- @param lex the LEX structure for this statement.
@param cond the condition signaled if any, or NULL.
@param set collection of signal condition item assignments.
*/
- Signal_common(LEX *lex,
- const sp_cond_type_t *cond,
- const Set_signal_information& set)
- : Sql_statement(lex),
+ Sql_cmd_common_signal(const sp_condition_value *cond,
+ const Set_signal_information& set)
+ : Sql_cmd(),
m_cond(cond),
m_set_signal_information(set)
{}
- virtual ~Signal_common()
+ virtual ~Sql_cmd_common_signal()
{}
/**
@@ -49,9 +47,9 @@ protected:
@param level the level to assign
@param sqlcode the sql code to assign
*/
- static void assign_defaults(MYSQL_ERROR *cond,
+ static void assign_defaults(Sql_condition *cond,
bool set_level_code,
- MYSQL_ERROR::enum_warning_level level,
+ Sql_condition::enum_warning_level level,
int sqlcode);
/**
@@ -60,7 +58,7 @@ protected:
@param thd the current thread.
@param cond the condition to update.
*/
- void eval_defaults(THD *thd, MYSQL_ERROR *cond);
+ void eval_defaults(THD *thd, Sql_condition *cond);
/**
Evaluate each signal condition items for this statement.
@@ -68,7 +66,7 @@ protected:
@param cond the condition to update.
@return 0 on success.
*/
- int eval_signal_informations(THD *thd, MYSQL_ERROR *cond);
+ int eval_signal_informations(THD *thd, Sql_condition *cond);
/**
Raise a SQL condition.
@@ -76,13 +74,13 @@ protected:
@param cond the condition to raise.
@return false on success.
*/
- bool raise_condition(THD *thd, MYSQL_ERROR *cond);
+ bool raise_condition(THD *thd, Sql_condition *cond);
/**
The condition to signal or resignal.
This member is optional and can be NULL (RESIGNAL).
*/
- const sp_cond_type_t *m_cond;
+ const sp_condition_value *m_cond;
/**
Collection of 'SET item = value' assignments in the
@@ -92,60 +90,56 @@ protected:
};
/**
- Signal_statement represents a SIGNAL statement.
+ Sql_cmd_signal represents a SIGNAL statement.
*/
-class Signal_statement : public Signal_common
+class Sql_cmd_signal : public Sql_cmd_common_signal
{
public:
/**
Constructor, used to represent a SIGNAL statement.
- @param lex the LEX structure for this statement.
@param cond the SQL condition to signal (required).
@param set the collection of signal informations to signal.
*/
- Signal_statement(LEX *lex,
- const sp_cond_type_t *cond,
- const Set_signal_information& set)
- : Signal_common(lex, cond, set)
+ Sql_cmd_signal(const sp_condition_value *cond,
+ const Set_signal_information& set)
+ : Sql_cmd_common_signal(cond, set)
{}
- virtual ~Signal_statement()
+ virtual ~Sql_cmd_signal()
{}
- /**
- Execute a SIGNAL statement at runtime.
- @param thd the current thread.
- @return false on success.
- */
+ virtual enum_sql_command sql_command_code() const
+ {
+ return SQLCOM_SIGNAL;
+ }
+
virtual bool execute(THD *thd);
};
/**
- Resignal_statement represents a RESIGNAL statement.
+ Sql_cmd_resignal represents a RESIGNAL statement.
*/
-class Resignal_statement : public Signal_common
+class Sql_cmd_resignal : public Sql_cmd_common_signal
{
public:
/**
Constructor, used to represent a RESIGNAL statement.
- @param lex the LEX structure for this statement.
@param cond the SQL condition to resignal (optional, may be NULL).
@param set the collection of signal informations to resignal.
*/
- Resignal_statement(LEX *lex,
- const sp_cond_type_t *cond,
- const Set_signal_information& set)
- : Signal_common(lex, cond, set)
+ Sql_cmd_resignal(const sp_condition_value *cond,
+ const Set_signal_information& set)
+ : Sql_cmd_common_signal(cond, set)
{}
- virtual ~Resignal_statement()
+ virtual ~Sql_cmd_resignal()
{}
- /**
- Execute a RESIGNAL statement at runtime.
- @param thd the current thread.
- @return 0 on success.
- */
+ virtual enum_sql_command sql_command_code() const
+ {
+ return SQLCOM_RESIGNAL;
+ }
+
virtual bool execute(THD *thd);
};
diff --git a/sql/sql_sort.h b/sql/sql_sort.h
index f1a3a2f9d8b..d30ddfb6eec 100644
--- a/sql/sql_sort.h
+++ b/sql/sql_sort.h
@@ -16,6 +16,7 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+#include "m_string.h" /* memset */
#include "my_global.h" /* uchar */
#include "my_base.h" /* ha_rows */
#include "my_sys.h" /* qsort2_cmp */
@@ -27,7 +28,6 @@ typedef struct st_sort_field SORT_FIELD;
class Field;
struct TABLE;
-
/* Defines used by filesort and uniques */
#define MERGEBUFF 7
@@ -65,41 +65,51 @@ struct BUFFPEK_COMPARE_CONTEXT
void *key_compare_arg;
};
-typedef struct st_sort_param {
- uint rec_length; /* Length of sorted records */
- uint sort_length; /* Length of sorted columns */
- uint ref_length; /* Length of record ref. */
- uint addon_length; /* Length of added packed fields */
- uint res_length; /* Length of records in final sorted file/buffer */
- uint keys; /* Max keys / buffer */
+
+class Sort_param {
+public:
+ uint rec_length; // Length of sorted records.
+ uint sort_length; // Length of sorted columns.
+ uint ref_length; // Length of record ref.
+ uint addon_length; // Length of added packed fields.
+ uint res_length; // Length of records in final sorted file/buffer.
+ uint max_keys_per_buffer; // Max keys / buffer.
uint min_dupl_count;
- ha_rows max_rows,examined_rows;
- TABLE *sort_form; /* For quicker make_sortkey */
+ ha_rows max_rows; // Select limit, or HA_POS_ERROR if unlimited.
+ ha_rows examined_rows; // Number of examined rows.
+ TABLE *sort_form; // For quicker make_sortkey.
SORT_FIELD *local_sortorder;
SORT_FIELD *end;
- SORT_ADDON_FIELD *addon_field; /* Descriptors for companion fields */
+ SORT_ADDON_FIELD *addon_field; // Descriptors for companion fields.
uchar *unique_buff;
bool not_killable;
char* tmp_buffer;
- /* The fields below are used only by Unique class */
+ // The fields below are used only by Unique class.
qsort2_cmp compare;
BUFFPEK_COMPARE_CONTEXT cmp_context;
-} SORTPARAM;
+ Sort_param()
+ {
+ memset(this, 0, sizeof(*this));
+ }
+ void init_for_filesort(uint sortlen, TABLE *table,
+ ulong max_length_for_sort_data,
+ ha_rows maxrows, bool sort_positions);
+};
-int merge_many_buff(SORTPARAM *param, uchar *sort_buffer,
+
+int merge_many_buff(Sort_param *param, uchar *sort_buffer,
BUFFPEK *buffpek,
uint *maxbuffer, IO_CACHE *t_file);
uint read_to_buffer(IO_CACHE *fromfile,BUFFPEK *buffpek,
uint sort_length);
-int merge_buffers(SORTPARAM *param,IO_CACHE *from_file,
- IO_CACHE *to_file, uchar *sort_buffer,
- BUFFPEK *lastbuff,BUFFPEK *Fb,
- BUFFPEK *Tb,int flag);
-int merge_index(SORTPARAM *param, uchar *sort_buffer,
+int merge_buffers(Sort_param *param,IO_CACHE *from_file,
+ IO_CACHE *to_file, uchar *sort_buffer,
+ BUFFPEK *lastbuff,BUFFPEK *Fb,
+ BUFFPEK *Tb,int flag);
+int merge_index(Sort_param *param, uchar *sort_buffer,
BUFFPEK *buffpek, uint maxbuffer,
IO_CACHE *tempfile, IO_CACHE *outfile);
-
void reuse_freed_buff(QUEUE *queue, BUFFPEK *reuse, uint key_length);
#endif /* SQL_SORT_INCLUDED */
diff --git a/sql/sql_statistics.cc b/sql/sql_statistics.cc
new file mode 100644
index 00000000000..e86c84040b4
--- /dev/null
+++ b/sql/sql_statistics.cc
@@ -0,0 +1,3840 @@
+/* Copyright (C) 2009 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 */
+
+/**
+ @file
+
+ @brief
+ functions to update persitent statistical tables and to read from them
+
+ @defgroup Query_Optimizer Query Optimizer
+ @{
+*/
+
+#include <my_global.h>
+#include "sql_base.h"
+#include "key.h"
+#include "sql_statistics.h"
+#include "opt_range.h"
+#include "my_atomic.h"
+
+/*
+ The system variable 'use_stat_tables' can take one of the
+ following values:
+ "never", "complementary", "preferably".
+ If the values of the variable 'use_stat_tables' is set to
+ "never then any statistical data from the persistent statistical tables
+ is ignored by the optimizer.
+ If the value of the variable 'use_stat_tables' is set to
+ "complementary" then a particular statistical characteristic is used
+ by the optimizer only if the database engine does not provide similar
+ statistics. For example, 'nulls_ratio' for table columns currently
+ are not provided by any engine. So optimizer uses this statistical data
+ from the statistical tables. At the same time it does not use
+ 'avg_frequency' for any index prefix from the statistical tables since
+ the a similar statistical characteristic 'records_per_key' can be
+ requested from the database engine.
+ If the value the variable 'use_stat_tables' is set to
+ "preferably" the optimizer uses a particular statistical data only if
+ it can't be found in the statistical data.
+ If an ANALYZE command is executed then it results in collecting
+ statistical data for the tables specified by the command and storing
+ the collected statistics in the persistent statistical tables only
+ when the value of the variable 'use_stat_tables' is not
+ equal to "never".
+*/
+
+/* Currently there are only 3 persistent statistical tables */
+static const uint STATISTICS_TABLES= 3;
+
+/*
+ The names of the statistical tables in this array must correspond the
+ definitions of the tables in the file ../scripts/mysql_system_tables.sql
+*/
+static const LEX_STRING stat_table_name[STATISTICS_TABLES]=
+{
+ { C_STRING_WITH_LEN("table_stats") },
+ { C_STRING_WITH_LEN("column_stats") },
+ { C_STRING_WITH_LEN("index_stats") }
+};
+
+/* Name of database to which the statistical tables belong */
+static const LEX_STRING stat_tables_db_name= { C_STRING_WITH_LEN("mysql") };
+
+
+/**
+ @details
+ The function builds a list of TABLE_LIST elements for system statistical
+ tables using array of TABLE_LIST passed as a parameter.
+ The lock type of each element is set to TL_READ if for_write = FALSE,
+ otherwise it is set to TL_WRITE.
+*/
+
+static
+inline void init_table_list_for_stat_tables(TABLE_LIST *tables, bool for_write)
+{
+ uint i;
+
+ memset((char *) &tables[0], 0, sizeof(TABLE_LIST) * STATISTICS_TABLES);
+
+ for (i= 0; i < STATISTICS_TABLES; i++)
+ {
+ tables[i].db= stat_tables_db_name.str;
+ tables[i].db_length= stat_tables_db_name.length;
+ tables[i].alias= tables[i].table_name= stat_table_name[i].str;
+ tables[i].table_name_length= stat_table_name[i].length;
+ tables[i].lock_type= for_write ? TL_WRITE : TL_READ;
+ if (i < STATISTICS_TABLES - 1)
+ tables[i].next_global= tables[i].next_local=
+ tables[i].next_name_resolution_table= &tables[i+1];
+ if (i != 0)
+ tables[i].prev_global= &tables[i-1].next_global;
+ }
+}
+
+
+/**
+ @details
+ The function builds a TABLE_LIST containing only one element 'tbl' for
+ the statistical table called 'stat_tab_name'.
+ The lock type of the element is set to TL_READ if for_write = FALSE,
+ otherwise it is set to TL_WRITE.
+*/
+
+static
+inline void init_table_list_for_single_stat_table(TABLE_LIST *tbl,
+ const LEX_STRING *stat_tab_name,
+ bool for_write)
+{
+ memset((char *) tbl, 0, sizeof(TABLE_LIST));
+
+ tbl->db= stat_tables_db_name.str;
+ tbl->db_length= stat_tables_db_name.length;
+ tbl->alias= tbl->table_name= stat_tab_name->str;
+ tbl->table_name_length= stat_tab_name->length;
+ tbl->lock_type= for_write ? TL_WRITE : TL_READ;
+}
+
+
+/**
+ @brief
+ Open all statistical tables and lock them
+*/
+
+static
+inline int open_stat_tables(THD *thd, TABLE_LIST *tables,
+ Open_tables_backup *backup,
+ bool for_write)
+{
+ init_table_list_for_stat_tables(tables, for_write);
+ init_mdl_requests(tables);
+ return open_system_tables_for_read(thd, tables, backup);
+}
+
+
+/**
+ @brief
+ Open a statistical table and lock it
+*/
+static
+inline int open_single_stat_table(THD *thd, TABLE_LIST *table,
+ const LEX_STRING *stat_tab_name,
+ Open_tables_backup *backup,
+ bool for_write)
+{
+ init_table_list_for_single_stat_table(table, stat_tab_name, for_write);
+ init_mdl_requests(table);
+ return open_system_tables_for_read(thd, table, backup);
+}
+
+
+/*
+ The class Column_statistics_collected is a helper class used to collect
+ statistics on a table column. The class is derived directly from
+ the class Column_statistics, and, additionally to the fields of the
+ latter, it contains the fields to accumulate the results of aggregation
+ for the number of nulls in the column and for the size of the column
+ values. There is also a container for distinct column values used
+ to calculate the average number of records per distinct column value.
+*/
+
+class Column_statistics_collected :public Column_statistics
+{
+
+private:
+ Field *column; /* The column to collect statistics on */
+ ha_rows nulls; /* To accumulate the number of nulls in the column */
+ ulonglong column_total_length; /* To accumulate the size of column values */
+ Count_distinct_field *count_distinct; /* The container for distinct
+ column values */
+
+ bool is_single_pk_col; /* TRUE <-> the only column of the primary key */
+
+public:
+
+ inline void init(THD *thd, Field * table_field);
+ inline bool add(ha_rows rowno);
+ inline void finish(ha_rows rows);
+ inline void cleanup();
+};
+
+
+/**
+ Stat_table is the base class for classes Table_stat, Column_stat and
+ Index_stat. The methods of these classes allow us to read statistical
+ data from statistical tables, write collected statistical data into
+ statistical tables and update statistical data in these tables
+ as well as update access fields belonging to the primary key and
+ delete records by prefixes of the primary key.
+ Objects of the classes Table_stat, Column_stat and Index stat are used
+ for reading/writing statistics from/into persistent tables table_stats,
+ column_stats and index_stats correspondingly. These tables are stored in
+ the system database 'mysql'.
+
+ Statistics is read and written always for a given database table t. When
+ an object of any of these classes is created a pointer to the TABLE
+ structure for this database table is passed as a parameter to the constructor
+ of the object. The other parameter is a pointer to the TABLE structure for
+ the corresponding statistical table st. So construction of an object to
+ read/write statistical data on table t from/into statistical table st
+ requires both table t and st to be opened.
+ In some cases the TABLE structure for table t may be undefined. Then
+ the objects of the classes Table_stat, Column_stat and Index stat are
+ created by the alternative constructor that require only the name
+ of the table t and the name of the database it belongs to. Currently the
+ alternative constructors are used only in the cases when some records
+ belonging to the table are to be deleted, or its keys are to be updated
+
+ Reading/writing statistical data from/into a statistical table is always
+ performed by a key. At the moment there is only one key defined for each
+ statistical table and this key is primary.
+ The primary key for the table table_stats is built as (db_name, table_name).
+ The primary key for the table column_stats is built as (db_name, table_name,
+ column_name).
+ The primary key for the table index_stats is built as (db_name, table_name,
+ index_name, prefix_arity).
+
+ Reading statistical data from a statistical table is performed by the
+ following pattern. First a table dependent method sets the values of the
+ the fields that comprise the lookup key. Then an implementation of the
+ method get_stat_values() declared in Stat_table as a pure virtual method
+ finds the row from the statistical table by the set key. If the row is
+ found the values of statistical fields are read from this row and are
+ distributed in the internal structures.
+
+ Let's assume the statistical data is read for table t from database db.
+
+ When statistical data is searched in the table table_stats first
+ Table_stat::set_key_fields() should set the fields of db_name and
+ table_name. Then get_stat_values looks for a row by the set key value,
+ and, if the row is found, reads the value from the column
+ table_stats.cardinality into the field read_stat.cardinality of the TABLE
+ structure for table t and sets the value of read_stat.cardinality_is_null
+ from this structure to FALSE. If the value of the 'cardinality' column
+ in the row is null or if no row is found read_stat.cardinality_is_null
+ is set to TRUE.
+
+ When statistical data is searched in the table column_stats first
+ Column_stat::set_key_fields() should set the fields of db_name, table_name
+ and column_name with column_name taken out of the only parameter f of the
+ Field* type passed to this method. After this get_stat_values looks
+ for a row by the set key value. If the row is found the values of statistical
+ data columns min_value, max_value, nulls_ratio, avg_length, avg_frequency,
+ hist_size, hist_type, histogram are read into internal structures. Values
+ of nulls_ratio, avg_length, avg_frequency, hist_size, hist_type, histogram
+ are read into the corresponding fields of the read_stat structure from
+ the Field object f, while values from min_value and max_value are copied
+ into the min_value and max_value record buffers attached to the TABLE
+ structure for table t.
+ If the value of a statistical column in the found row is null, then the
+ corresponding flag in the f->read_stat.column_stat_nulls bitmap is set off.
+ Otherwise the flag is set on. If no row is found for the column the all flags
+ in f->column_stat_nulls are set off.
+
+ When statistical data is searched in the table index_stats first
+ Index_stat::set_key_fields() has to be called to set the fields of db_name,
+ table_name, index_name and prefix_arity. The value of index_name is extracted
+ from the first parameter key_info of the KEY* type passed to the method.
+ This parameter specifies the index of interest idx. The second parameter
+ passed to the method specifies the arity k of the index prefix for which
+ statistical data is to be read. E.g. if the index idx consists of 3
+ components (p1,p2,p3) the table index_stats usually will contain 3 rows for
+ this index: the first - for the prefix (p1), the second - for the prefix
+ (p1,p2), and the third - for the the prefix (p1,p2,p3). After the key fields
+ has been set a call of get_stat_value looks for a row by the set key value.
+ If the row is found and the value of the avg_frequency column is not null
+ then this value is assigned to key_info->read_stat.avg_frequency[k].
+ Otherwise 0 is assigned to this element.
+
+ The method Stat_table::update_stat is used to write statistical data
+ collected in the internal structures into a statistical table st.
+ It is assumed that before any invocation of this method a call of the
+ function st.set_key_fields has set the values of the primary key fields
+ that serve to locate the row from the statistical table st where the
+ the collected statistical data from internal structures are to be written
+ to. The statistical data is written from the counterparts of the
+ statistical fields of internal structures into which it would be read
+ by the functions get_stat_values. The counterpart fields are used
+ only when statistics is collected
+ When updating/inserting a row from the statistical table st the method
+ Stat_table::update_stat calls the implementation of the pure virtual
+ method store_field_values to transfer statistical data from the fields
+ of internal structures to the fields of record buffer used for updates
+ of the statistical table st.
+*/
+
+class Stat_table
+{
+
+private:
+
+ /* Handler used for the retrieval of the statistical table stat_table */
+ handler *stat_file;
+
+ uint stat_key_length; /* Length of the key to access stat_table */
+ uchar *record[2]; /* Record buffers used to access/update stat_table */
+ uint stat_key_idx; /* The number of the key to access stat_table */
+
+ /* This is a helper function used only by the Stat_table constructors */
+ void common_init_stat_table()
+ {
+ stat_file= stat_table->file;
+ /* Currently any statistical table has only one key */
+ stat_key_idx= 0;
+ stat_key_info= &stat_table->key_info[stat_key_idx];
+ stat_key_length= stat_key_info->key_length;
+ record[0]= stat_table->record[0];
+ record[1]= stat_table->record[1];
+ }
+
+protected:
+
+ /* Statistical table to read statistics from or to update/delete */
+ TABLE *stat_table;
+ KEY *stat_key_info; /* Structure for the index to access stat_table */
+
+ /* Table for which statistical data is read / updated */
+ TABLE *table;
+ TABLE_SHARE *table_share; /* Table share for 'table */
+ LEX_STRING *db_name; /* Name of the database containing 'table' */
+ LEX_STRING *table_name; /* Name of the table 'table' */
+
+ void store_record_for_update()
+ {
+ store_record(stat_table, record[1]);
+ }
+
+ void store_record_for_lookup()
+ {
+ DBUG_ASSERT(record[0] == stat_table->record[0]);
+ }
+
+ bool update_record()
+ {
+ int err;
+ if ((err= stat_file->ha_update_row(record[1], record[0])) &&
+ err != HA_ERR_RECORD_IS_THE_SAME)
+ return TRUE;
+ /* Make change permanent and avoid 'table is marked as crashed' errors */
+ stat_file->extra(HA_EXTRA_FLUSH);
+ return FALSE;
+ }
+
+public:
+
+
+ /**
+ @details
+ This constructor has to be called by any constructor of the derived
+ classes. The constructor 'tunes' the private and protected members of
+ the constructed object to the statistical table 'stat_table' with the
+ statistical data of our interest and to the table 'tab' for which this
+ statistics has been collected.
+ */
+
+ Stat_table(TABLE *stat, TABLE *tab)
+ :stat_table(stat), table(tab)
+ {
+ table_share= tab->s;
+ common_init_stat_table();
+ db_name= &table_share->db;
+ table_name= &table_share->table_name;
+ }
+
+
+ /**
+ @details
+ This constructor has to be called by any constructor of the derived
+ classes. The constructor 'tunes' the private and protected members of
+ the constructed object to the statistical table 'stat_table' with the
+ statistical data of our interest and to the table t for which this
+ statistics has been collected. The table t is uniquely specified
+ by the database name 'db' and the table name 'tab'.
+ */
+
+ Stat_table(TABLE *stat, LEX_STRING *db, LEX_STRING *tab)
+ :stat_table(stat), table_share(NULL)
+ {
+ common_init_stat_table();
+ db_name= db;
+ table_name= tab;
+ }
+
+
+ virtual ~Stat_table() {}
+
+ /**
+ @brief
+ Store the given values of fields for database name and table name
+
+ @details
+ This is a purely virtual method.
+ The implementation for any derived class shall store the given
+ values of the database name and table name in the corresponding
+ fields of stat_table.
+
+ @note
+ The method is called by the update_table_name_key_parts function.
+ */
+
+ virtual void change_full_table_name(LEX_STRING *db, LEX_STRING *tab)= 0;
+
+
+ /**
+ @brief
+ Store statistical data into fields of the statistical table
+
+ @details
+ This is a purely virtual method.
+ The implementation for any derived class shall put the appropriate
+ statistical data into the corresponding fields of stat_table.
+
+ @note
+ The method is called by the update_stat function.
+ */
+
+ virtual void store_stat_fields()= 0;
+
+
+ /**
+ @brief
+ Read statistical data from fields of the statistical table
+
+ @details
+ This is a purely virtual method.
+ The implementation for any derived read shall read the appropriate
+ statistical data from the corresponding fields of stat_table.
+ */
+
+ virtual void get_stat_values()= 0;
+
+
+ /**
+ @brief
+ Find a record in the statistical table by a primary key
+
+ @details
+ The function looks for a record in stat_table by its primary key.
+ It assumes that the key fields have been already stored in the record
+ buffer of stat_table.
+
+ @retval
+ FALSE the record is not found
+ @retval
+ TRUE the record is found
+ */
+
+ bool find_stat()
+ {
+ uchar key[MAX_KEY_LENGTH];
+ key_copy(key, record[0], stat_key_info, stat_key_length);
+ return !stat_file->ha_index_read_idx_map(record[0], stat_key_idx, key,
+ HA_WHOLE_KEY, HA_READ_KEY_EXACT);
+ }
+
+
+ /**
+ @brief
+ Find a record in the statistical table by a key prefix value
+
+ @details
+ The function looks for a record in stat_table by the key value consisting
+ of 'prefix_parts' major components for the primary index.
+ It assumes that the key prefix fields have been already stored in the record
+ buffer of stat_table.
+
+ @retval
+ FALSE the record is not found
+ @retval
+ TRUE the record is found
+ */
+
+ bool find_next_stat_for_prefix(uint prefix_parts)
+ {
+ uchar key[MAX_KEY_LENGTH];
+ uint prefix_key_length= 0;
+ for (uint i= 0; i < prefix_parts; i++)
+ prefix_key_length+= stat_key_info->key_part[i].store_length;
+ key_copy(key, record[0], stat_key_info, prefix_key_length);
+ key_part_map prefix_map= (key_part_map) ((1 << prefix_parts) - 1);
+ return !stat_file->ha_index_read_idx_map(record[0], stat_key_idx, key,
+ prefix_map, HA_READ_KEY_EXACT);
+ }
+
+
+ /**
+ @brief
+ Update/insert a record in the statistical table with new statistics
+
+ @details
+ The function first looks for a record by its primary key in the statistical
+ table stat_table. If the record is found the function updates statistical
+ fields of the records. The data for these fields are taken from internal
+ structures containing info on the table 'table'. If the record is not
+ found the function inserts a new record with the primary key set to the
+ search key and the statistical data taken from the internal structures.
+ The function assumes that the key fields have been already stored in
+ the record buffer of stat_table.
+
+ @retval
+ FALSE success with the update/insert of the record
+ @retval
+ TRUE failure with the update/insert of the record
+
+ @note
+ The function calls the virtual method store_stat_fields to populate the
+ statistical fields of the updated/inserted row with new statistics.
+ */
+
+ bool update_stat()
+ {
+ if (find_stat())
+ {
+ store_record_for_update();
+ store_stat_fields();
+ return update_record();
+ }
+ else
+ {
+ int err;
+ store_stat_fields();
+ if ((err= stat_file->ha_write_row(record[0])))
+ return TRUE;
+ /* Make change permanent and avoid 'table is marked as crashed' errors */
+ stat_file->extra(HA_EXTRA_FLUSH);
+ }
+ return FALSE;
+ }
+
+
+ /**
+ @brief
+ Update the table name fields in the current record of stat_table
+
+ @details
+ The function updates the fields containing database name and table name
+ for the last found record in the statistical table stat_table.
+ The corresponding names for update is taken from the parameters
+ db and tab.
+
+ @retval
+ FALSE success with the update of the record
+ @retval
+ TRUE failure with the update of the record
+
+ @note
+ The function calls the virtual method change_full_table_name
+ to store the new names in the record buffer used for updates.
+ */
+
+ bool update_table_name_key_parts(LEX_STRING *db, LEX_STRING *tab)
+ {
+ store_record_for_update();
+ change_full_table_name(db, tab);
+ bool rc= update_record();
+ store_record_for_lookup();
+ return rc;
+ }
+
+
+ /**
+ @brief
+ Delete the current record of the statistical table stat_table
+
+ @details
+ The function deletes the last found record from the statistical
+ table stat_table.
+
+ @retval
+ FALSE success with the deletion of the record
+ @retval
+ TRUE failure with the deletion of the record
+ */
+
+ bool delete_stat()
+ {
+ int err;
+ if ((err= stat_file->ha_delete_row(record[0])))
+ return TRUE;
+ /* Make change permanent and avoid 'table is marked as crashed' errors */
+ stat_file->extra(HA_EXTRA_FLUSH);
+ return FALSE;
+ }
+
+ friend class Stat_table_write_iter;
+};
+
+
+/*
+ An object of the class Table_stat is created to read statistical
+ data on tables from the statistical table table_stats, to update
+ table_stats with such statistical data, or to update columns
+ of the primary key, or to delete the record by its primary key or
+ its prefix.
+ Rows from the statistical table are read and updated always by
+ primary key.
+*/
+
+class Table_stat: public Stat_table
+{
+
+private:
+
+ Field *db_name_field; /* Field for the column table_stats.db_name */
+ Field *table_name_field; /* Field for the column table_stats.table_name */
+
+ void common_init_table_stat()
+ {
+ db_name_field= stat_table->field[TABLE_STAT_DB_NAME];
+ table_name_field= stat_table->field[TABLE_STAT_TABLE_NAME];
+ }
+
+ void change_full_table_name(LEX_STRING *db, LEX_STRING *tab)
+ {
+ db_name_field->store(db->str, db->length, system_charset_info);
+ table_name_field->store(tab->str, tab->length, system_charset_info);
+ }
+
+public:
+
+ /**
+ @details
+ The constructor 'tunes' the private and protected members of the
+ constructed object for the statistical table table_stats to read/update
+ statistics on table 'tab'. The TABLE structure for the table table_stat
+ must be passed as a value for the parameter 'stat'.
+ */
+
+ Table_stat(TABLE *stat, TABLE *tab) :Stat_table(stat, tab)
+ {
+ common_init_table_stat();
+ }
+
+
+ /**
+ @details
+ The constructor 'tunes' the private and protected members of the
+ object constructed for the statistical table table_stat for
+ the future updates/deletes of the record concerning the table 'tab'
+ from the database 'db'.
+ */
+
+ Table_stat(TABLE *stat, LEX_STRING *db, LEX_STRING *tab)
+ :Stat_table(stat, db, tab)
+ {
+ common_init_table_stat();
+ }
+
+
+ /**
+ @brief
+ Set the key fields for the statistical table table_stat
+
+ @details
+ The function sets the values of the fields db_name and table_name
+ in the record buffer for the statistical table table_stat.
+ These fields comprise the primary key for the table.
+
+ @note
+ The function is supposed to be called before any use of the
+ method find_stat for an object of the Table_stat class.
+ */
+
+ void set_key_fields()
+ {
+ db_name_field->store(db_name->str, db_name->length, system_charset_info);
+ table_name_field->store(table_name->str, table_name->length,
+ system_charset_info);
+ }
+
+
+ /**
+ @brief
+ Store statistical data into statistical fields of table_stat
+
+ @details
+ This implementation of a purely virtual method sets the value of the
+ column 'cardinality' of the statistical table table_stat according to
+ the value of the flag write_stat.cardinality_is_null and the value of
+ the field write_stat.cardinality' from the TABLE structure for 'table'.
+ */
+
+ void store_stat_fields()
+ {
+ Field *stat_field= stat_table->field[TABLE_STAT_CARDINALITY];
+ if (table->collected_stats->cardinality_is_null)
+ stat_field->set_null();
+ else
+ {
+ stat_field->set_notnull();
+ stat_field->store(table->collected_stats->cardinality);
+ }
+ }
+
+
+ /**
+ @brief
+ Read statistical data from statistical fields of table_stat
+
+ @details
+ This implementation of a purely virtual method first looks for a record
+ the statistical table table_stat by its primary key set the record
+ buffer with the help of Table_stat::set_key_fields. Then, if the row is
+ found the function reads the value of the column 'cardinality' of the table
+ table_stat and sets the value of the flag read_stat.cardinality_is_null
+ and the value of the field read_stat.cardinality' from the TABLE structure
+ for 'table' accordingly.
+ */
+
+ void get_stat_values()
+ {
+ Table_statistics *read_stats= table_share->stats_cb.table_stats;
+ read_stats->cardinality_is_null= TRUE;
+ read_stats->cardinality= 0;
+ if (find_stat())
+ {
+ Field *stat_field= stat_table->field[TABLE_STAT_CARDINALITY];
+ if (!stat_field->is_null())
+ {
+ read_stats->cardinality_is_null= FALSE;
+ read_stats->cardinality= stat_field->val_int();
+ }
+ }
+ }
+
+};
+
+
+/*
+ An object of the class Column_stat is created to read statistical data
+ on table columns from the statistical table column_stats, to update
+ column_stats with such statistical data, or to update columns
+ of the primary key, or to delete the record by its primary key or
+ its prefix.
+ Rows from the statistical table are read and updated always by
+ primary key.
+*/
+
+class Column_stat: public Stat_table
+{
+
+private:
+
+ Field *db_name_field; /* Field for the column column_stats.db_name */
+ Field *table_name_field; /* Field for the column column_stats.table_name */
+ Field *column_name_field; /* Field for the column column_stats.column_name */
+
+ Field *table_field; /* Field from 'table' to read /update statistics on */
+
+ void common_init_column_stat_table()
+ {
+ db_name_field= stat_table->field[COLUMN_STAT_DB_NAME];
+ table_name_field= stat_table->field[COLUMN_STAT_TABLE_NAME];
+ column_name_field= stat_table->field[COLUMN_STAT_COLUMN_NAME];
+ }
+
+ void change_full_table_name(LEX_STRING *db, LEX_STRING *tab)
+ {
+ db_name_field->store(db->str, db->length, system_charset_info);
+ table_name_field->store(tab->str, tab->length, system_charset_info);
+ }
+
+public:
+
+ /**
+ @details
+ The constructor 'tunes' the private and protected members of the
+ constructed object for the statistical table column_stats to read/update
+ statistics on fields of the table 'tab'. The TABLE structure for the table
+ column_stats must be passed as a value for the parameter 'stat'.
+ */
+
+ Column_stat(TABLE *stat, TABLE *tab) :Stat_table(stat, tab)
+ {
+ common_init_column_stat_table();
+ }
+
+
+ /**
+ @details
+ The constructor 'tunes' the private and protected members of the
+ object constructed for the statistical table column_stats for
+ the future updates/deletes of the record concerning the table 'tab'
+ from the database 'db'.
+ */
+
+ Column_stat(TABLE *stat, LEX_STRING *db, LEX_STRING *tab)
+ :Stat_table(stat, db, tab)
+ {
+ common_init_column_stat_table();
+ }
+
+ /**
+ @brief
+ Set table name fields for the statistical table column_stats
+
+ @details
+ The function stores the values of the fields db_name and table_name
+ of the statistical table column_stats in the record buffer.
+ */
+
+ void set_full_table_name()
+ {
+ db_name_field->store(db_name->str, db_name->length, system_charset_info);
+ table_name_field->store(table_name->str, table_name->length,
+ system_charset_info);
+ }
+
+
+ /**
+ @brief
+ Set the key fields for the statistical table column_stats
+
+ @param
+ col Field for the 'table' column to read/update statistics on
+
+ @details
+ The function stores the values of the fields db_name, table_name and
+ column_name in the record buffer for the statistical table column_stats.
+ These fields comprise the primary key for the table.
+ It also sets table_field to the passed parameter.
+
+ @note
+ The function is supposed to be called before any use of the
+ method find_stat for an object of the Column_stat class.
+ */
+
+ void set_key_fields(Field *col)
+ {
+ set_full_table_name();
+ const char *column_name= col->field_name;
+ column_name_field->store(column_name, strlen(column_name),
+ system_charset_info);
+ table_field= col;
+ }
+
+
+ /**
+ @brief
+ Update the table name fields in the current record of stat_table
+
+ @details
+ The function updates the primary key fields containing database name,
+ table name, and column name for the last found record in the statistical
+ table column_stats.
+
+ @retval
+ FALSE success with the update of the record
+ @retval
+ TRUE failure with the update of the record
+ */
+
+ bool update_column_key_part(const char *col)
+ {
+ store_record_for_update();
+ set_full_table_name();
+ column_name_field->store(col, strlen(col), system_charset_info);
+ bool rc= update_record();
+ store_record_for_lookup();
+ return rc;
+ }
+
+
+ /**
+ @brief
+ Store statistical data into statistical fields of column_stats
+
+ @details
+ This implementation of a purely virtual method sets the value of the
+ columns 'min_value', 'max_value', 'nulls_ratio', 'avg_length',
+ 'avg_frequency', 'hist_size', 'hist_type' and 'histogram' of the
+ stistical table columns_stat according to the contents of the bitmap
+ write_stat.column_stat_nulls and the values of the fields min_value,
+ max_value, nulls_ratio, avg_length, avg_frequency, hist_size, hist_type
+ and histogram of the structure write_stat from the Field structure
+ for the field 'table_field'.
+ The value of the k-th column in the table columns_stat is set to NULL
+ if the k-th bit in the bitmap 'column_stat_nulls' is set to 1.
+
+ @note
+ A value from the field min_value/max_value is always converted
+ into a varbinary string. If the length of the column 'min_value'/'max_value'
+ is less than the length of the string the string is trimmed to fit the
+ length of the column.
+ */
+
+ void store_stat_fields()
+ {
+ char buff[MAX_FIELD_WIDTH];
+ String val(buff, sizeof(buff), &my_charset_bin);
+
+ for (uint i= COLUMN_STAT_MIN_VALUE; i <= COLUMN_STAT_HISTOGRAM; i++)
+ {
+ Field *stat_field= stat_table->field[i];
+ if (table_field->collected_stats->is_null(i))
+ stat_field->set_null();
+ else
+ {
+ stat_field->set_notnull();
+ switch (i) {
+ case COLUMN_STAT_MIN_VALUE:
+ if (table_field->type() == MYSQL_TYPE_BIT)
+ stat_field->store(table_field->collected_stats->min_value->val_int());
+ else
+ {
+ table_field->collected_stats->min_value->val_str(&val);
+ stat_field->store(val.ptr(), val.length(), &my_charset_bin);
+ }
+ break;
+ case COLUMN_STAT_MAX_VALUE:
+ if (table_field->type() == MYSQL_TYPE_BIT)
+ stat_field->store(table_field->collected_stats->max_value->val_int());
+ else
+ {
+ table_field->collected_stats->max_value->val_str(&val);
+ stat_field->store(val.ptr(), val.length(), &my_charset_bin);
+ }
+ break;
+ case COLUMN_STAT_NULLS_RATIO:
+ stat_field->store(table_field->collected_stats->get_nulls_ratio());
+ break;
+ case COLUMN_STAT_AVG_LENGTH:
+ stat_field->store(table_field->collected_stats->get_avg_length());
+ break;
+ case COLUMN_STAT_AVG_FREQUENCY:
+ stat_field->store(table_field->collected_stats->get_avg_frequency());
+ break;
+ case COLUMN_STAT_HIST_SIZE:
+ stat_field->store(table_field->collected_stats->histogram.get_size());
+ break;
+ case COLUMN_STAT_HIST_TYPE:
+ stat_field->store(table_field->collected_stats->histogram.get_type() +
+ 1);
+ break;
+ case COLUMN_STAT_HISTOGRAM:
+ const char * col_histogram=
+ (const char *) (table_field->collected_stats->histogram.get_values());
+ stat_field->store(col_histogram,
+ table_field->collected_stats->histogram.get_size(),
+ &my_charset_bin);
+ break;
+ }
+ }
+ }
+ }
+
+
+ /**
+ @brief
+ Read statistical data from statistical fields of column_stats
+
+ @details
+ This implementation of a purely virtual method first looks for a record
+ in the statistical table column_stats by its primary key set in the record
+ buffer with the help of Column_stat::set_key_fields. Then, if the row is
+ found, the function reads the values of the columns 'min_value',
+ 'max_value', 'nulls_ratio', 'avg_length', 'avg_frequency', 'hist_size' and
+ 'hist_type" of the table column_stat and sets accordingly the value of
+ the bitmap read_stat.column_stat_nulls' and the values of the fields
+ min_value, max_value, nulls_ratio, avg_length, avg_frequency, hist_size and
+ hist_type of the structure read_stat from the Field structure for the field
+ 'table_field'.
+ */
+
+ void get_stat_values()
+ {
+ table_field->read_stats->set_all_nulls();
+
+ if (table_field->read_stats->min_value)
+ table_field->read_stats->min_value->set_null();
+ if (table_field->read_stats->max_value)
+ table_field->read_stats->max_value->set_null();
+
+ if (find_stat())
+ {
+ char buff[MAX_FIELD_WIDTH];
+ String val(buff, sizeof(buff), &my_charset_bin);
+
+ for (uint i= COLUMN_STAT_MIN_VALUE; i <= COLUMN_STAT_HIST_TYPE; i++)
+ {
+ Field *stat_field= stat_table->field[i];
+
+ if (!stat_field->is_null() &&
+ (i > COLUMN_STAT_MAX_VALUE ||
+ (i == COLUMN_STAT_MIN_VALUE &&
+ table_field->read_stats->min_value) ||
+ (i == COLUMN_STAT_MAX_VALUE &&
+ table_field->read_stats->max_value)))
+ {
+ table_field->read_stats->set_not_null(i);
+
+ switch (i) {
+ case COLUMN_STAT_MIN_VALUE:
+ stat_field->val_str(&val);
+ table_field->read_stats->min_value->store(val.ptr(), val.length(),
+ &my_charset_bin);
+ break;
+ case COLUMN_STAT_MAX_VALUE:
+ stat_field->val_str(&val);
+ table_field->read_stats->max_value->store(val.ptr(), val.length(),
+ &my_charset_bin);
+ break;
+ case COLUMN_STAT_NULLS_RATIO:
+ table_field->read_stats->set_nulls_ratio(stat_field->val_real());
+ break;
+ case COLUMN_STAT_AVG_LENGTH:
+ table_field->read_stats->set_avg_length(stat_field->val_real());
+ break;
+ case COLUMN_STAT_AVG_FREQUENCY:
+ table_field->read_stats->set_avg_frequency(stat_field->val_real());
+ break;
+ case COLUMN_STAT_HIST_SIZE:
+ table_field->read_stats->histogram.set_size(stat_field->val_int());
+ break;
+ case COLUMN_STAT_HIST_TYPE:
+ Histogram_type hist_type= (Histogram_type) (stat_field->val_int() -
+ 1);
+ table_field->read_stats->histogram.set_type(hist_type);
+ break;
+ }
+ }
+ }
+ }
+ }
+
+
+ /**
+ @brief
+ Read histogram from of column_stats
+
+ @details
+ This method first looks for a record in the statistical table column_stats
+ by its primary key set the record buffer with the help of
+ Column_stat::set_key_fields. Then, if the row is found, the function reads
+ the value of the column 'histogram' of the table column_stat and sets
+ accordingly the corresponding bit in the bitmap read_stat.column_stat_nulls.
+ The method assumes that the value of histogram size and the pointer to
+ the histogram location has been already set in the fields size and values
+ of read_stats->histogram.
+ */
+
+ void get_histogram_value()
+ {
+ if (find_stat())
+ {
+ char buff[MAX_FIELD_WIDTH];
+ String val(buff, sizeof(buff), &my_charset_bin);
+ uint fldno= COLUMN_STAT_HISTOGRAM;
+ Field *stat_field= stat_table->field[fldno];
+ table_field->read_stats->set_not_null(fldno);
+ stat_field->val_str(&val);
+ memcpy(table_field->read_stats->histogram.get_values(),
+ val.ptr(), table_field->read_stats->histogram.get_size());
+ }
+ }
+
+};
+
+
+/*
+ An object of the class Index_stat is created to read statistical
+ data on tables from the statistical table table_stat, to update
+ index_stats with such statistical data, or to update columns
+ of the primary key, or to delete the record by its primary key or
+ its prefix.
+ Rows from the statistical table are read and updated always by
+ primary key.
+*/
+
+class Index_stat: public Stat_table
+{
+
+private:
+
+ Field *db_name_field; /* Field for the column index_stats.db_name */
+ Field *table_name_field; /* Field for the column index_stats.table_name */
+ Field *index_name_field; /* Field for the column index_stats.table_name */
+ Field *prefix_arity_field; /* Field for the column index_stats.prefix_arity */
+
+ KEY *table_key_info; /* Info on the index to read/update statistics on */
+ uint prefix_arity; /* Number of components of the index prefix of interest */
+
+ void common_init_index_stat_table()
+ {
+ db_name_field= stat_table->field[INDEX_STAT_DB_NAME];
+ table_name_field= stat_table->field[INDEX_STAT_TABLE_NAME];
+ index_name_field= stat_table->field[INDEX_STAT_INDEX_NAME];
+ prefix_arity_field= stat_table->field[INDEX_STAT_PREFIX_ARITY];
+ }
+
+ void change_full_table_name(LEX_STRING *db, LEX_STRING *tab)
+ {
+ db_name_field->store(db->str, db->length, system_charset_info);
+ table_name_field->store(tab->str, tab->length, system_charset_info);
+ }
+
+public:
+
+
+ /**
+ @details
+ The constructor 'tunes' the private and protected members of the
+ constructed object for the statistical table index_stats to read/update
+ statistics on prefixes of different indexes of the table 'tab'.
+ The TABLE structure for the table index_stats must be passed as a value
+ for the parameter 'stat'.
+ */
+
+ Index_stat(TABLE *stat, TABLE*tab) :Stat_table(stat, tab)
+ {
+ common_init_index_stat_table();
+ }
+
+
+ /**
+ @details
+ The constructor 'tunes' the private and protected members of the
+ object constructed for the statistical table index_stats for
+ the future updates/deletes of the record concerning the table 'tab'
+ from the database 'db'.
+ */
+
+ Index_stat(TABLE *stat, LEX_STRING *db, LEX_STRING *tab)
+ :Stat_table(stat, db, tab)
+ {
+ common_init_index_stat_table();
+ }
+
+
+ /**
+ @brief
+ Set table name fields for the statistical table index_stats
+
+ @details
+ The function stores the values of the fields db_name and table_name
+ of the statistical table index_stats in the record buffer.
+ */
+
+ void set_full_table_name()
+ {
+ db_name_field->store(db_name->str, db_name->length, system_charset_info);
+ table_name_field->store(table_name->str, table_name->length,
+ system_charset_info);
+ }
+
+ /**
+ @brief
+ Set the key fields of index_stats used to access records for index prefixes
+
+ @param
+ index_info Info for the index of 'table' to read/update statistics on
+
+ @details
+ The function sets the values of the fields db_name, table_name and
+ index_name in the record buffer for the statistical table index_stats.
+ It also sets table_key_info to the passed parameter.
+
+ @note
+ The function is supposed to be called before any use of the method
+ find_next_stat_for_prefix for an object of the Index_stat class.
+ */
+
+ void set_index_prefix_key_fields(KEY *index_info)
+ {
+ set_full_table_name();
+ char *index_name= index_info->name;
+ index_name_field->store(index_name, strlen(index_name),
+ system_charset_info);
+ table_key_info= index_info;
+ }
+
+
+ /**
+ @brief
+ Set the key fields for the statistical table index_stats
+
+ @param
+ index_info Info for the index of 'table' to read/update statistics on
+ @param
+ index_prefix_arity Number of components in the index prefix of interest
+
+ @details
+ The function sets the values of the fields db_name, table_name and
+ index_name, prefix_arity in the record buffer for the statistical
+ table index_stats. These fields comprise the primary key for the table.
+
+ @note
+ The function is supposed to be called before any use of the
+ method find_stat for an object of the Index_stat class.
+ */
+
+ void set_key_fields(KEY *index_info, uint index_prefix_arity)
+ {
+ set_index_prefix_key_fields(index_info);
+ prefix_arity= index_prefix_arity;
+ prefix_arity_field->store(index_prefix_arity, TRUE);
+ }
+
+
+ /**
+ @brief
+ Store statistical data into statistical fields of table index_stats
+
+ @details
+ This implementation of a purely virtual method sets the value of the
+ column 'avg_frequency' of the statistical table index_stats according to
+ the value of write_stat.avg_frequency[Index_stat::prefix_arity]
+ from the KEY_INFO structure 'table_key_info'.
+ If the value of write_stat. avg_frequency[Index_stat::prefix_arity] is
+ equal to 0, the value of the column is set to NULL.
+ */
+
+ void store_stat_fields()
+ {
+ Field *stat_field= stat_table->field[INDEX_STAT_AVG_FREQUENCY];
+ double avg_frequency=
+ table_key_info->collected_stats->get_avg_frequency(prefix_arity-1);
+ if (avg_frequency == 0)
+ stat_field->set_null();
+ else
+ {
+ stat_field->set_notnull();
+ stat_field->store(avg_frequency);
+ }
+ }
+
+
+ /**
+ @brief
+ Read statistical data from statistical fields of index_stats
+
+ @details
+ This implementation of a purely virtual method first looks for a record the
+ statistical table index_stats by its primary key set the record buffer with
+ the help of Index_stat::set_key_fields. If the row is found the function
+ reads the value of the column 'avg_freguency' of the table index_stat and
+ sets the value of read_stat.avg_frequency[Index_stat::prefix_arity]
+ from the KEY_INFO structure 'table_key_info' accordingly. If the value of
+ the column is NULL, read_stat.avg_frequency[Index_stat::prefix_arity] is
+ set to 0. Otherwise, read_stat.avg_frequency[Index_stat::prefix_arity] is
+ set to the value of the column.
+ */
+
+ void get_stat_values()
+ {
+ double avg_frequency= 0;
+ if(find_stat())
+ {
+ Field *stat_field= stat_table->field[INDEX_STAT_AVG_FREQUENCY];
+ if (!stat_field->is_null())
+ avg_frequency= stat_field->val_real();
+ }
+ table_key_info->read_stats->set_avg_frequency(prefix_arity-1, avg_frequency);
+ }
+
+};
+
+
+/*
+ An iterator to enumerate statistics table rows which allows to modify
+ the rows while reading them.
+
+ Used by RENAME TABLE handling to assign new dbname.tablename to statistic
+ rows.
+*/
+class Stat_table_write_iter
+{
+ Stat_table *owner;
+ IO_CACHE io_cache;
+ uchar *rowid_buf;
+ uint rowid_size;
+
+public:
+ Stat_table_write_iter(Stat_table *stat_table_arg)
+ : owner(stat_table_arg), rowid_buf(NULL),
+ rowid_size(owner->stat_file->ref_length)
+ {
+ my_b_clear(&io_cache);
+ }
+
+ /*
+ Initialize the iterator. It will return rows with n_keyparts matching the
+ curernt values.
+
+ @return false - OK
+ true - Error
+ */
+ bool init(uint n_keyparts)
+ {
+ if (!(rowid_buf= (uchar*)my_malloc(rowid_size, MYF(0))))
+ return true;
+
+ if (open_cached_file(&io_cache, mysql_tmpdir, TEMP_PREFIX,
+ 1024, MYF(MY_WME)))
+ return true;
+
+ handler *h= owner->stat_file;
+ uchar key[MAX_KEY_LENGTH];
+ uint prefix_len= 0;
+ for (uint i= 0; i < n_keyparts; i++)
+ prefix_len += owner->stat_key_info->key_part[i].store_length;
+
+ key_copy(key, owner->record[0], owner->stat_key_info,
+ prefix_len);
+ key_part_map prefix_map= (key_part_map) ((1 << n_keyparts) - 1);
+ h->ha_index_init(owner->stat_key_idx, false);
+ int res= h->ha_index_read_map(owner->record[0], key, prefix_map,
+ HA_READ_KEY_EXACT);
+ if (res)
+ {
+ reinit_io_cache(&io_cache, READ_CACHE, 0L, 0, 0);
+ /* "Key not found" is not considered an error */
+ return (res == HA_ERR_KEY_NOT_FOUND)? false: true;
+ }
+
+ do {
+ h->position(owner->record[0]);
+ my_b_write(&io_cache, h->ref, rowid_size);
+
+ } while (!h->ha_index_next_same(owner->record[0], key, prefix_len));
+
+ /* Prepare for reading */
+ reinit_io_cache(&io_cache, READ_CACHE, 0L, 0, 0);
+ h->ha_index_or_rnd_end();
+ if (h->ha_rnd_init(false))
+ return true;
+
+ return false;
+ }
+
+ /*
+ Read the next row.
+
+ @return
+ false OK
+ true No more rows or error.
+ */
+ bool get_next_row()
+ {
+ if (!my_b_inited(&io_cache) || my_b_read(&io_cache, rowid_buf, rowid_size))
+ return true; /* No more data */
+
+ handler *h= owner->stat_file;
+ /*
+ We should normally be able to find the row that we have rowid for. If we
+ don't, let's consider this an error.
+ */
+ int res= h->ha_rnd_pos(owner->record[0], rowid_buf);
+
+ return (res==0)? false : true;
+ }
+
+ void cleanup()
+ {
+ if (rowid_buf)
+ my_free(rowid_buf);
+ rowid_buf= NULL;
+ owner->stat_file->ha_index_or_rnd_end();
+ close_cached_file(&io_cache);
+ my_b_clear(&io_cache);
+ }
+
+ ~Stat_table_write_iter()
+ {
+ cleanup();
+ }
+};
+
+/*
+ Histogram_builder is a helper class that is used to build histograms
+ for columns
+*/
+
+class Histogram_builder
+{
+ Field *column; /* table field for which the histogram is built */
+ uint col_length; /* size of this field */
+ ha_rows records; /* number of records the histogram is built for */
+ Field *min_value; /* pointer to the minimal value for the field */
+ Field *max_value; /* pointer to the maximal value for the field */
+ Histogram *histogram; /* the histogram location */
+ uint hist_width; /* the number of points in the histogram */
+ double bucket_capacity; /* number of rows in a bucket of the histogram */
+ uint curr_bucket; /* number of the current bucket to be built */
+ ulonglong count; /* number of values retrieved */
+ ulonglong count_distinct; /* number of distinct values retrieved */
+
+public:
+ Histogram_builder(Field *col, uint col_len, ha_rows rows)
+ : column(col), col_length(col_len), records(rows)
+ {
+ Column_statistics *col_stats= col->collected_stats;
+ min_value= col_stats->min_value;
+ max_value= col_stats->max_value;
+ histogram= &col_stats->histogram;
+ hist_width= histogram->get_width();
+ bucket_capacity= (double) records / (hist_width + 1);
+ curr_bucket= 0;
+ count= 0;
+ count_distinct= 0;
+ }
+
+ ulonglong get_count_distinct() { return count_distinct; }
+
+ int next(void *elem, element_count elem_cnt)
+ {
+ count_distinct++;
+ count+= elem_cnt;
+ if (curr_bucket == hist_width)
+ return 0;
+ if (count > bucket_capacity * (curr_bucket + 1))
+ {
+ column->store_field_value((uchar *) elem, col_length);
+ histogram->set_value(curr_bucket,
+ column->pos_in_interval(min_value, max_value));
+ curr_bucket++;
+ while (curr_bucket != hist_width &&
+ count > bucket_capacity * (curr_bucket + 1))
+ {
+ histogram->set_prev_value(curr_bucket);
+ curr_bucket++;
+ }
+ }
+ return 0;
+ }
+};
+
+
+C_MODE_START
+
+int histogram_build_walk(void *elem, element_count elem_cnt, void *arg)
+{
+ Histogram_builder *hist_builder= (Histogram_builder *) arg;
+ return hist_builder->next(elem, elem_cnt);
+}
+
+C_MODE_END
+
+
+/*
+ The class Count_distinct_field is a helper class used to calculate
+ the number of distinct values for a column. The class employs the
+ Unique class for this purpose.
+ The class Count_distinct_field is used only by the function
+ collect_statistics_for_table to calculate the values for
+ column avg_frequency of the statistical table column_stats.
+*/
+
+class Count_distinct_field: public Sql_alloc
+{
+protected:
+
+ /* Field for which the number of distinct values is to be find out */
+ Field *table_field;
+ Unique *tree; /* The helper object to contain distinct values */
+ uint tree_key_length; /* The length of the keys for the elements of 'tree */
+
+public:
+
+ Count_distinct_field() {}
+
+ /**
+ @param
+ field Field for which the number of distinct values is
+ to be find out
+ @param
+ max_heap_table_size The limit for the memory used by the RB tree container
+ of the constructed Unique object 'tree'
+
+ @details
+ The constructor sets the values of 'table_field' and 'tree_key_length',
+ and then calls the 'new' operation to create a Unique object for 'tree'.
+ The type of 'field' and the value max_heap_table_size of determine the set
+ of the parameters to be passed to the constructor of the Unique object.
+ */
+
+ Count_distinct_field(Field *field, uint max_heap_table_size)
+ {
+ table_field= field;
+ tree_key_length= field->pack_length();
+
+ tree= new Unique((qsort_cmp2) simple_str_key_cmp, (void*) field,
+ tree_key_length, max_heap_table_size, 1);
+ }
+
+ virtual ~Count_distinct_field()
+ {
+ delete tree;
+ tree= NULL;
+ }
+
+ /*
+ @brief
+ Check whether the Unique object tree has been successfully created
+ */
+ bool exists()
+ {
+ return (tree != NULL);
+ }
+
+ /*
+ @brief
+ Add the value of 'field' to the container of the Unique object 'tree'
+ */
+ virtual bool add()
+ {
+ return tree->unique_add(table_field->ptr);
+ }
+
+ /*
+ @brief
+ Calculate the number of elements accumulated in the container of 'tree'
+ */
+ ulonglong get_value()
+ {
+ ulonglong count;
+ if (tree->elements == 0)
+ return (ulonglong) tree->elements_in_tree();
+ count= 0;
+ tree->walk(table_field->table, count_distinct_walk, (void*) &count);
+ return count;
+ }
+
+ /*
+ @brief
+ Build the histogram for the elements accumulated in the container of 'tree'
+ */
+ ulonglong get_value_with_histogram(ha_rows rows)
+ {
+ Histogram_builder hist_builder(table_field, tree_key_length, rows);
+ tree->walk(table_field->table, histogram_build_walk, (void *) &hist_builder);
+ return hist_builder.get_count_distinct();
+ }
+
+ /*
+ @brief
+ Get the size of the histogram in bytes built for table_field
+ */
+ uint get_hist_size()
+ {
+ return table_field->collected_stats->histogram.get_size();
+ }
+
+ /*
+ @brief
+ Get the pointer to the histogram built for table_field
+ */
+ uchar *get_histogram()
+ {
+ return table_field->collected_stats->histogram.get_values();
+ }
+
+};
+
+
+static
+int simple_ulonglong_key_cmp(void* arg, uchar* key1, uchar* key2)
+{
+ ulonglong *val1= (ulonglong *) key1;
+ ulonglong *val2= (ulonglong *) key2;
+ return *val1 > *val2 ? 1 : *val1 == *val2 ? 0 : -1;
+}
+
+
+/*
+ The class Count_distinct_field_bit is derived from the class
+ Count_distinct_field to be used only for fields of the MYSQL_TYPE_BIT type.
+ The class provides a different implementation for the method add
+*/
+
+class Count_distinct_field_bit: public Count_distinct_field
+{
+public:
+
+ Count_distinct_field_bit(Field *field, uint max_heap_table_size)
+ {
+ table_field= field;
+ tree_key_length= sizeof(ulonglong);
+
+ tree= new Unique((qsort_cmp2) simple_ulonglong_key_cmp,
+ (void*) &tree_key_length,
+ tree_key_length, max_heap_table_size, 1);
+ }
+
+ bool add()
+ {
+ longlong val= table_field->val_int();
+ return tree->unique_add(&val);
+ }
+};
+
+
+/*
+ The class Index_prefix_calc is a helper class used to calculate the values
+ for the column 'avg_frequency' of the statistical table index_stats.
+ For any table t from the database db and any k-component prefix of the
+ index i for this table the row from index_stats with the primary key
+ (db,t,i,k) must contain in the column 'avg_frequency' either NULL or
+ the number that is the ratio of N and V, where N is the number of index
+ entries without NULL values in the first k components of the index i,
+ and V is the number of distinct tuples composed of the first k components
+ encountered among these index entries.
+ Currently the objects of this class are used only by the function
+ collect_statistics_for_index.
+*/
+
+class Index_prefix_calc: public Sql_alloc
+{
+
+private:
+
+ /* Table containing index specified by index_info */
+ TABLE *index_table;
+ /* Info for the index i for whose prefix 'avg_frequency' is calculated */
+ KEY *index_info;
+ /* The maximum number of the components in the prefixes of interest */
+ uint prefixes;
+ bool empty;
+
+ /* This structure is created for every k components of the index i */
+ class Prefix_calc_state
+ {
+ public:
+ /*
+ The number of the scanned index entries without nulls
+ in the first k components
+ */
+ ulonglong entry_count;
+ /*
+ The number if the scanned index entries without nulls with
+ the last encountered k-component prefix
+ */
+ ulonglong prefix_count;
+ /* The values of the last encountered k-component prefix */
+ Cached_item *last_prefix;
+ };
+
+ /*
+ Array of structures used to calculate 'avg_frequency' for different
+ prefixes of the index i
+ */
+ Prefix_calc_state *calc_state;
+
+public:
+
+ bool is_single_comp_pk;
+
+ Index_prefix_calc(TABLE *table, KEY *key_info)
+ : index_table(table), index_info(key_info)
+ {
+ uint i;
+ Prefix_calc_state *state;
+ uint key_parts= table->actual_n_key_parts(key_info);
+ empty= TRUE;
+ prefixes= 0;
+ LINT_INIT(calc_state);
+
+ is_single_comp_pk= FALSE;
+ uint pk= table->s->primary_key;
+ if ((uint) (table->key_info - key_info) == pk &&
+ table->key_info[pk].user_defined_key_parts == 1)
+ {
+ prefixes= 1;
+ is_single_comp_pk= TRUE;
+ return;
+ }
+
+ if ((calc_state=
+ (Prefix_calc_state *) sql_alloc(sizeof(Prefix_calc_state)*key_parts)))
+ {
+ uint keyno= key_info-table->key_info;
+ for (i= 0, state= calc_state; i < key_parts; i++, state++)
+ {
+ /*
+ Do not consider prefixes containing a component that is only part
+ of the field. This limitation is set to avoid fetching data when
+ calculating the values of 'avg_frequency' for prefixes.
+ */
+ if (!key_info->key_part[i].field->part_of_key.is_set(keyno))
+ break;
+
+ if (!(state->last_prefix=
+ new Cached_item_field(key_info->key_part[i].field)))
+ break;
+ state->entry_count= state->prefix_count= 0;
+ prefixes++;
+ }
+ }
+ }
+
+
+ /**
+ @breif
+ Change the elements of calc_state after reading the next index entry
+
+ @details
+ This function is to be called at the index scan each time the next
+ index entry has been read into the record buffer.
+ For each of the index prefixes the function checks whether nulls
+ are encountered in any of the k components of the prefix.
+ If this is not the case the value of calc_state[k-1].entry_count
+ is incremented by 1. Then the function checks whether the value of
+ any of these k components has changed. If so, the value of
+ calc_state[k-1].prefix_count is incremented by 1.
+ */
+
+ void add()
+ {
+ uint i;
+ Prefix_calc_state *state;
+ uint first_changed= prefixes;
+ for (i= prefixes, state= calc_state+prefixes-1; i; i--, state--)
+ {
+ if (state->last_prefix->cmp())
+ first_changed= i-1;
+ }
+ if (empty)
+ {
+ first_changed= 0;
+ empty= FALSE;
+ }
+ for (i= 0, state= calc_state; i < prefixes; i++, state++)
+ {
+ if (state->last_prefix->null_value)
+ break;
+ if (i >= first_changed)
+ state->prefix_count++;
+ state->entry_count++;
+ }
+ }
+
+ /**
+ @brief
+ Calculate the values of avg_frequency for all prefixes of an index
+
+ @details
+ This function is to be called after the index scan to count the number
+ of distinct index prefixes has been done. The function calculates
+ the value of avg_frequency for the index prefix with k components
+ as calc_state[k-1].entry_count/calc_state[k-1].prefix_count.
+ If calc_state[k-1].prefix_count happens to be 0, the value of
+ avg_frequency[k-1] is set to 0, i.e. is considered as unknown.
+ */
+
+ void get_avg_frequency()
+ {
+ uint i;
+ Prefix_calc_state *state;
+
+ if (is_single_comp_pk)
+ {
+ index_info->collected_stats->set_avg_frequency(0, 1.0);
+ return;
+ }
+
+ for (i= 0, state= calc_state; i < prefixes; i++, state++)
+ {
+ if (i < prefixes)
+ {
+ double val= state->prefix_count == 0 ?
+ 0 : (double) state->entry_count / state->prefix_count;
+ index_info->collected_stats->set_avg_frequency(i, val);
+ }
+ }
+ }
+};
+
+
+/**
+ @brief
+ Create fields for min/max values to collect column statistics
+
+ @param
+ table Table the fields are created for
+
+ @details
+ The function first allocates record buffers to store min/max values
+ for 'table's fields. Then for each table field f it creates Field structures
+ that points to these buffers rather that to the record buffer as the
+ Field object for f does. The pointers of the created fields are placed
+ in the collected_stats structure of the Field object for f.
+ The function allocates the buffers for min/max values in the table
+ memory.
+
+ @note
+ The buffers allocated when min/max values are used to read statistics
+ from the persistent statistical tables differ from those buffers that
+ are used when statistics on min/max values for column is collected
+ as they are allocated in different mem_roots.
+ The same is true for the fields created for min/max values.
+*/
+
+static
+void create_min_max_statistical_fields_for_table(TABLE *table)
+{
+ uint rec_buff_length= table->s->rec_buff_length;
+
+ if ((table->collected_stats->min_max_record_buffers=
+ (uchar *) alloc_root(&table->mem_root, 2*rec_buff_length)))
+ {
+ uchar *record= table->collected_stats->min_max_record_buffers;
+ memset(record, 0, 2*rec_buff_length);
+
+ for (uint i=0; i < 2; i++, record+= rec_buff_length)
+ {
+ for (Field **field_ptr= table->field; *field_ptr; field_ptr++)
+ {
+ Field *fld;
+ Field *table_field= *field_ptr;
+ my_ptrdiff_t diff= record-table->record[0];
+ if (!bitmap_is_set(table->read_set, table_field->field_index))
+ continue;
+ if (!(fld= table_field->clone(&table->mem_root, table, diff, TRUE)))
+ continue;
+ if (i == 0)
+ table_field->collected_stats->min_value= fld;
+ else
+ table_field->collected_stats->max_value= fld;
+ }
+ }
+ }
+}
+
+
+/**
+ @brief
+ Create fields for min/max values to read column statistics
+
+ @param
+ thd Thread handler
+ @param
+ table_share Table share the fields are created for
+ @param
+ is_safe TRUE <-> at any time only one thread can perform the function
+
+ @details
+ The function first allocates record buffers to store min/max values
+ for 'table_share's fields. Then for each field f it creates Field structures
+ that points to these buffers rather that to the record buffer as the
+ Field object for f does. The pointers of the created fields are placed
+ in the read_stats structure of the Field object for f.
+ The function allocates the buffers for min/max values in the table share
+ memory.
+ If the parameter is_safe is TRUE then it is guaranteed that at any given time
+ only one thread is executed the code of the function.
+
+ @note
+ The buffers allocated when min/max values are used to collect statistics
+ from the persistent statistical tables differ from those buffers that
+ are used when statistics on min/max values for column is read as they
+ are allocated in different mem_roots.
+ The same is true for the fields created for min/max values.
+*/
+
+static
+void create_min_max_statistical_fields_for_table_share(THD *thd,
+ TABLE_SHARE *table_share)
+{
+ TABLE_STATISTICS_CB *stats_cb= &table_share->stats_cb;
+ Table_statistics *stats= stats_cb->table_stats;
+
+ if (stats->min_max_record_buffers)
+ return;
+
+ uint rec_buff_length= table_share->rec_buff_length;
+
+ if ((stats->min_max_record_buffers=
+ (uchar *) alloc_root(&stats_cb->mem_root, 2*rec_buff_length)))
+ {
+ uchar *record= stats->min_max_record_buffers;
+ memset(record, 0, 2*rec_buff_length);
+
+ for (uint i=0; i < 2; i++, record+= rec_buff_length)
+ {
+ for (Field **field_ptr= table_share->field; *field_ptr; field_ptr++)
+ {
+ Field *fld;
+ Field *table_field= *field_ptr;
+ my_ptrdiff_t diff= record - table_share->default_values;
+ if (!(fld= table_field->clone(&stats_cb->mem_root, diff)))
+ continue;
+ if (i == 0)
+ table_field->read_stats->min_value= fld;
+ else
+ table_field->read_stats->max_value= fld;
+ }
+ }
+ }
+
+}
+
+
+/**
+ @brief
+ Allocate memory for the table's statistical data to be collected
+
+ @param
+ table Table for which the memory for statistical data is allocated
+
+ @note
+ The function allocates the memory for the statistical data on 'table' with
+ the intention to collect the data there. The memory is allocated for
+ the statistics on the table, on the table's columns, and on the table's
+ indexes. The memory is allocated in the table's mem_root.
+
+ @retval
+ 0 If the memory for all statistical data has been successfully allocated
+ @retval
+ 1 Otherwise
+
+ @note
+ Each thread allocates its own memory to collect statistics on the table
+ It allows us, for example, to collect statistics on the different indexes
+ of the same table in parallel.
+*/
+
+int alloc_statistics_for_table(THD* thd, TABLE *table)
+{
+ Field **field_ptr;
+ uint fields;
+
+ DBUG_ENTER("alloc_statistics_for_table");
+
+
+ Table_statistics *table_stats=
+ (Table_statistics *) alloc_root(&table->mem_root,
+ sizeof(Table_statistics));
+
+ fields= table->s->fields ;
+ Column_statistics_collected *column_stats=
+ (Column_statistics_collected *) alloc_root(&table->mem_root,
+ sizeof(Column_statistics_collected) *
+ (fields+1));
+
+ uint keys= table->s->keys;
+ Index_statistics *index_stats=
+ (Index_statistics *) alloc_root(&table->mem_root,
+ sizeof(Index_statistics) * keys);
+
+ uint key_parts= table->s->ext_key_parts;
+ ulong *idx_avg_frequency= (ulong*) alloc_root(&table->mem_root,
+ sizeof(ulong) * key_parts);
+
+ uint columns= 0;
+ for (field_ptr= table->field; *field_ptr; field_ptr++)
+ {
+ if (bitmap_is_set(table->read_set, (*field_ptr)->field_index))
+ columns++;
+ }
+ uint hist_size= thd->variables.histogram_size;
+ Histogram_type hist_type= (Histogram_type) (thd->variables.histogram_type);
+ uchar *histogram= NULL;
+ if (hist_size > 0)
+ histogram= (uchar *) alloc_root(&table->mem_root, hist_size * columns);
+
+ if (!table_stats || !column_stats || !index_stats || !idx_avg_frequency ||
+ (hist_size && !histogram))
+ DBUG_RETURN(1);
+
+ table->collected_stats= table_stats;
+ table_stats->column_stats= column_stats;
+ table_stats->index_stats= index_stats;
+ table_stats->idx_avg_frequency= idx_avg_frequency;
+ table_stats->histograms= histogram;
+
+ memset(column_stats, 0, sizeof(Column_statistics) * (fields+1));
+
+ for (field_ptr= table->field; *field_ptr; field_ptr++, column_stats++)
+ {
+ (*field_ptr)->collected_stats= column_stats;
+ (*field_ptr)->collected_stats->max_value= NULL;
+ (*field_ptr)->collected_stats->min_value= NULL;
+ if (bitmap_is_set(table->read_set, (*field_ptr)->field_index))
+ {
+ column_stats->histogram.set_size(hist_size);
+ column_stats->histogram.set_type(hist_type);
+ column_stats->histogram.set_values(histogram);
+ histogram+= hist_size;
+ }
+ }
+
+ memset(idx_avg_frequency, 0, sizeof(ulong) * key_parts);
+
+ KEY *key_info, *end;
+ for (key_info= table->key_info, end= key_info + table->s->keys;
+ key_info < end;
+ key_info++, index_stats++)
+ {
+ key_info->collected_stats= index_stats;
+ key_info->collected_stats->init_avg_frequency(idx_avg_frequency);
+ idx_avg_frequency+= key_info->ext_key_parts;
+ }
+
+ create_min_max_statistical_fields_for_table(table);
+
+ DBUG_RETURN(0);
+}
+
+
+/**
+ @brief
+ Check whether any persistent statistics for the processed command is needed
+
+ @param
+ thd The thread handle
+
+ @details
+ The function checks whether any persitent statistics for the processed
+ command is needed to be read.
+
+ @retval
+ TRUE statistics is needed to be read
+ @retval
+ FALSE Otherwise
+*/
+
+static
+inline bool statistics_for_command_is_needed(THD *thd)
+{
+ if (thd->bootstrap || thd->variables.use_stat_tables == NEVER)
+ return FALSE;
+
+ switch(thd->lex->sql_command) {
+ case SQLCOM_SELECT:
+ case SQLCOM_INSERT:
+ case SQLCOM_INSERT_SELECT:
+ case SQLCOM_UPDATE:
+ case SQLCOM_UPDATE_MULTI:
+ case SQLCOM_DELETE:
+ case SQLCOM_DELETE_MULTI:
+ case SQLCOM_REPLACE:
+ case SQLCOM_REPLACE_SELECT:
+ break;
+ default:
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+/**
+ @brief
+ Allocate memory for the statistical data used by a table share
+
+ @param
+ thd Thread handler
+ @param
+ table_share Table share for which the memory for statistical data is allocated
+ @param
+ is_safe TRUE <-> at any time only one thread can perform the function
+
+ @note
+ The function allocates the memory for the statistical data on a table in the
+ table's share memory with the intention to read the statistics there from
+ the system persistent statistical tables mysql.table_stat, mysql.column_stats,
+ mysql.index_stats. The memory is allocated for the statistics on the table,
+ on the tables's columns, and on the table's indexes. The memory is allocated
+ in the table_share's mem_root.
+ If the parameter is_safe is TRUE then it is guaranteed that at any given time
+ only one thread is executed the code of the function.
+
+ @retval
+ 0 If the memory for all statistical data has been successfully allocated
+ @retval
+ 1 Otherwise
+
+ @note
+ The situation when more than one thread try to allocate memory for
+ statistical data is rare. It happens under the following scenario:
+ 1. One thread executes a query over table t with the system variable
+ 'use_stat_tables' set to 'never'.
+ 2. After this the second thread sets 'use_stat_tables' to 'preferably'
+ and executes a query over table t.
+ 3. Simultaneously the third thread sets 'use_stat_tables' to 'preferably'
+ and executes a query over table t.
+ Here the second and the third threads try to allocate the memory for
+ statistical data at the same time. The precautions are taken to
+ guarantee the correctness of the allocation.
+
+ @note
+ Currently the function always is called with the parameter is_safe set
+ to FALSE.
+*/
+
+int alloc_statistics_for_table_share(THD* thd, TABLE_SHARE *table_share,
+ bool is_safe)
+{
+
+ Field **field_ptr;
+ KEY *key_info, *end;
+ TABLE_STATISTICS_CB *stats_cb= &table_share->stats_cb;
+
+ DBUG_ENTER("alloc_statistics_for_table_share");
+
+ DEBUG_SYNC(thd, "statistics_mem_alloc_start1");
+ DEBUG_SYNC(thd, "statistics_mem_alloc_start2");
+
+ if (!statistics_for_command_is_needed(thd))
+ DBUG_RETURN(1);
+
+ if (!is_safe)
+ mysql_mutex_lock(&table_share->LOCK_share);
+
+ if (stats_cb->stats_can_be_read)
+ {
+ if (!is_safe)
+ mysql_mutex_unlock(&table_share->LOCK_share);
+ DBUG_RETURN(0);
+ }
+
+ Table_statistics *table_stats= stats_cb->table_stats;
+ if (!table_stats)
+ {
+ table_stats= (Table_statistics *) alloc_root(&stats_cb->mem_root,
+ sizeof(Table_statistics));
+ if (!table_stats)
+ {
+ if (!is_safe)
+ mysql_mutex_unlock(&table_share->LOCK_share);
+ DBUG_RETURN(1);
+ }
+ memset(table_stats, 0, sizeof(Table_statistics));
+ stats_cb->table_stats= table_stats;
+ }
+
+ uint fields= table_share->fields;
+ Column_statistics *column_stats= table_stats->column_stats;
+ if (!column_stats)
+ {
+ column_stats= (Column_statistics *) alloc_root(&stats_cb->mem_root,
+ sizeof(Column_statistics) *
+ (fields+1));
+ if (column_stats)
+ {
+ memset(column_stats, 0, sizeof(Column_statistics) * (fields+1));
+ table_stats->column_stats= column_stats;
+ for (field_ptr= table_share->field;
+ *field_ptr;
+ field_ptr++, column_stats++)
+ {
+ (*field_ptr)->read_stats= column_stats;
+ (*field_ptr)->read_stats->min_value= NULL;
+ (*field_ptr)->read_stats->max_value= NULL;
+ }
+ create_min_max_statistical_fields_for_table_share(thd, table_share);
+ }
+ }
+
+ uint keys= table_share->keys;
+ Index_statistics *index_stats= table_stats->index_stats;
+ if (!index_stats)
+ {
+ index_stats= (Index_statistics *) alloc_root(&stats_cb->mem_root,
+ sizeof(Index_statistics) *
+ keys);
+ if (index_stats)
+ {
+ table_stats->index_stats= index_stats;
+ for (key_info= table_share->key_info, end= key_info + keys;
+ key_info < end;
+ key_info++, index_stats++)
+ {
+ key_info->read_stats= index_stats;
+ }
+ }
+ }
+
+ uint key_parts= table_share->ext_key_parts;
+ ulong *idx_avg_frequency= table_stats->idx_avg_frequency;
+ if (!idx_avg_frequency)
+ {
+ idx_avg_frequency= (ulong*) alloc_root(&stats_cb->mem_root,
+ sizeof(ulong) * key_parts);
+ if (idx_avg_frequency)
+ {
+ memset(idx_avg_frequency, 0, sizeof(ulong) * key_parts);
+ table_stats->idx_avg_frequency= idx_avg_frequency;
+ for (key_info= table_share->key_info, end= key_info + keys;
+ key_info < end;
+ key_info++)
+ {
+ key_info->read_stats->init_avg_frequency(idx_avg_frequency);
+ idx_avg_frequency+= key_info->ext_key_parts;
+ }
+ }
+ }
+
+ if (column_stats && index_stats && idx_avg_frequency)
+ stats_cb->stats_can_be_read= TRUE;
+
+ if (!is_safe)
+ mysql_mutex_unlock(&table_share->LOCK_share);
+
+ DBUG_RETURN(0);
+}
+
+
+/**
+ @brief
+ Allocate memory for the histogram used by a table share
+
+ @param
+ thd Thread handler
+ @param
+ table_share Table share for which the memory for histogram data is allocated
+ @param
+ is_safe TRUE <-> at any time only one thread can perform the function
+
+ @note
+ The function allocates the memory for the histogram built for a table in the
+ table's share memory with the intention to read the data there from the
+ system persistent statistical table mysql.column_stats,
+ The memory is allocated in the table_share's mem_root.
+ If the parameter is_safe is TRUE then it is guaranteed that at any given time
+ only one thread is executed the code of the function.
+
+ @retval
+ 0 If the memory for all statistical data has been successfully allocated
+ @retval
+ 1 Otherwise
+
+ @note
+ Currently the function always is called with the parameter is_safe set
+ to FALSE.
+*/
+
+static
+int alloc_histograms_for_table_share(THD* thd, TABLE_SHARE *table_share,
+ bool is_safe)
+{
+ TABLE_STATISTICS_CB *stats_cb= &table_share->stats_cb;
+
+ DBUG_ENTER("alloc_histograms_for_table_share");
+
+ if (!is_safe)
+ mysql_mutex_lock(&table_share->LOCK_share);
+
+ if (stats_cb->histograms_can_be_read)
+ {
+ if (!is_safe)
+ mysql_mutex_unlock(&table_share->LOCK_share);
+ DBUG_RETURN(0);
+ }
+
+ Table_statistics *table_stats= stats_cb->table_stats;
+ ulong total_hist_size= table_stats->total_hist_size;
+
+ if (total_hist_size && !table_stats->histograms)
+ {
+ uchar *histograms= (uchar *) alloc_root(&stats_cb->mem_root,
+ total_hist_size);
+ if (!histograms)
+ {
+ if (!is_safe)
+ mysql_mutex_unlock(&table_share->LOCK_share);
+ DBUG_RETURN(1);
+ }
+ memset(histograms, 0, total_hist_size);
+ table_stats->histograms= histograms;
+ stats_cb->histograms_can_be_read= TRUE;
+ }
+
+ if (!is_safe)
+ mysql_mutex_unlock(&table_share->LOCK_share);
+
+ DBUG_RETURN(0);
+
+}
+
+/**
+ @brief
+ Initialize the aggregation fields to collect statistics on a column
+
+ @param
+ thd Thread handler
+ @param
+ table_field Column to collect statistics for
+*/
+
+inline
+void Column_statistics_collected::init(THD *thd, Field *table_field)
+{
+ uint max_heap_table_size= thd->variables.max_heap_table_size;
+ TABLE *table= table_field->table;
+ uint pk= table->s->primary_key;
+
+ is_single_pk_col= FALSE;
+
+ if (pk != MAX_KEY && table->key_info[pk].user_defined_key_parts == 1 &&
+ table->key_info[pk].key_part[0].fieldnr == table_field->field_index + 1)
+ is_single_pk_col= TRUE;
+
+ column= table_field;
+
+ set_all_nulls();
+
+ nulls= 0;
+ column_total_length= 0;
+ if (is_single_pk_col)
+ count_distinct= NULL;
+ if (table_field->flags & BLOB_FLAG)
+ count_distinct= NULL;
+ else
+ {
+ count_distinct=
+ table_field->type() == MYSQL_TYPE_BIT ?
+ new Count_distinct_field_bit(table_field, max_heap_table_size) :
+ new Count_distinct_field(table_field, max_heap_table_size);
+ }
+ if (count_distinct && !count_distinct->exists())
+ count_distinct= NULL;
+}
+
+
+/**
+ @brief
+ Perform aggregation for a row when collecting statistics on a column
+
+ @param
+ rowno The order number of the row
+*/
+
+inline
+bool Column_statistics_collected::add(ha_rows rowno)
+{
+
+ bool err= 0;
+ if (column->is_null())
+ nulls++;
+ else
+ {
+ column_total_length+= column->value_length();
+ if (min_value && column->update_min(min_value, rowno == nulls))
+ set_not_null(COLUMN_STAT_MIN_VALUE);
+ if (max_value && column->update_max(max_value, rowno == nulls))
+ set_not_null(COLUMN_STAT_MAX_VALUE);
+ if (count_distinct)
+ err= count_distinct->add();
+ }
+ return err;
+}
+
+
+/**
+ @brief
+ Get the results of aggregation when collecting the statistics on a column
+
+ @param
+ rows The total number of rows in the table
+*/
+
+inline
+void Column_statistics_collected::finish(ha_rows rows)
+{
+ double val;
+
+ if (rows)
+ {
+ val= (double) nulls / rows;
+ set_nulls_ratio(val);
+ set_not_null(COLUMN_STAT_NULLS_RATIO);
+ }
+ if (rows - nulls)
+ {
+ val= (double) column_total_length / (rows - nulls);
+ set_avg_length(val);
+ set_not_null(COLUMN_STAT_AVG_LENGTH);
+ }
+ if (count_distinct)
+ {
+ ulonglong distincts;
+ uint hist_size= count_distinct->get_hist_size();
+ if (hist_size == 0)
+ distincts= count_distinct->get_value();
+ else
+ distincts= count_distinct->get_value_with_histogram(rows - nulls);
+ if (distincts)
+ {
+ val= (double) (rows - nulls) / distincts;
+ set_avg_frequency(val);
+ set_not_null(COLUMN_STAT_AVG_FREQUENCY);
+ }
+ else
+ hist_size= 0;
+ histogram.set_size(hist_size);
+ set_not_null(COLUMN_STAT_HIST_SIZE);
+ if (hist_size && distincts)
+ {
+ set_not_null(COLUMN_STAT_HIST_TYPE);
+ histogram.set_values(count_distinct->get_histogram());
+ set_not_null(COLUMN_STAT_HISTOGRAM);
+ }
+ delete count_distinct;
+ count_distinct= NULL;
+ }
+ else if (is_single_pk_col)
+ {
+ val= 1.0;
+ set_avg_frequency(val);
+ set_not_null(COLUMN_STAT_AVG_FREQUENCY);
+ }
+}
+
+
+/**
+ @brief
+ Clean up auxiliary structures used for aggregation
+*/
+
+inline
+void Column_statistics_collected::cleanup()
+{
+ if (count_distinct)
+ {
+ delete count_distinct;
+ count_distinct= NULL;
+ }
+}
+
+
+/**
+ @brief
+ Collect statistical data on an index
+
+ @param
+ table The table the index belongs to
+ index The number of this index in the table
+
+ @details
+ The function collects the value of 'avg_frequency' for the prefixes
+ on an index from 'table'. The index is specified by its number.
+ If the scan is successful the calculated statistics is saved in the
+ elements of the array write_stat.avg_frequency of the KEY_INFO structure
+ for the index. The statistics for the prefix with k components is saved
+ in the element number k-1.
+
+ @retval
+ 0 If the statistics has been successfully collected
+ @retval
+ 1 Otherwise
+
+ @note
+ The function collects statistics for the index prefixes for one index
+ scan during which no data is fetched from the table records. That's why
+ statistical data for prefixes that contain part of a field is not
+ collected.
+ The function employs an object of the helper class Index_prefix_calc to
+ count for each index prefix the number of index entries without nulls and
+ the number of distinct entries among them.
+
+*/
+
+static
+int collect_statistics_for_index(THD *thd, TABLE *table, uint index)
+{
+ int rc= 0;
+ KEY *key_info= &table->key_info[index];
+ ha_rows rows= 0;
+
+ DBUG_ENTER("collect_statistics_for_index");
+
+ /* No statistics for FULLTEXT indexes. */
+ if (key_info->flags & HA_FULLTEXT)
+ DBUG_RETURN(rc);
+
+ Index_prefix_calc index_prefix_calc(table, key_info);
+
+ DEBUG_SYNC(table->in_use, "statistics_collection_start1");
+ DEBUG_SYNC(table->in_use, "statistics_collection_start2");
+
+ if (index_prefix_calc.is_single_comp_pk)
+ {
+ index_prefix_calc.get_avg_frequency();
+ DBUG_RETURN(rc);
+ }
+
+ table->key_read= 1;
+ table->file->extra(HA_EXTRA_KEYREAD);
+
+ table->file->ha_index_init(index, TRUE);
+ rc= table->file->ha_index_first(table->record[0]);
+ while (rc != HA_ERR_END_OF_FILE)
+ {
+ if (thd->killed)
+ break;
+
+ if (rc)
+ break;
+ rows++;
+ index_prefix_calc.add();
+ rc= table->file->ha_index_next(table->record[0]);
+ }
+ table->key_read= 0;
+ table->file->ha_index_end();
+
+ rc= (rc == HA_ERR_END_OF_FILE && !thd->killed) ? 0 : 1;
+
+ if (!rc)
+ index_prefix_calc.get_avg_frequency();
+
+ DBUG_RETURN(rc);
+}
+
+
+/**
+ @brief
+ Collect statistical data for a table
+
+ @param
+ thd The thread handle
+ @param
+ table The table to collect statistics on
+
+ @details
+ The function collects data for various statistical characteristics on
+ the table 'table'. These data is saved in the internal fields that could
+ be reached from 'table'. The data is prepared to be saved in the persistent
+ statistical table by the function update_statistics_for_table.
+ The collected statistical values are not placed in the same fields that
+ keep the statistical data used by the optimizer. Therefore, at any time,
+ there is no collision between the statistics being collected and the one
+ used by the optimizer to look for optimal query execution plans for other
+ clients.
+
+ @retval
+ 0 If the statistics has been successfully collected
+ @retval
+ 1 Otherwise
+
+ @note
+ The function first collects statistical data for statistical characteristics
+ to be saved in the statistical tables table_stat and column_stats. To do this
+ it performs a full table scan of 'table'. At this scan the function collects
+ statistics on each column of the table and count the total number of the
+ scanned rows. To calculate the value of 'avg_frequency' for a column the
+ function constructs an object of the helper class Count_distinct_field
+ (or its derivation). Currently this class cannot count the number of
+ distinct values for blob columns. So the value of 'avg_frequency' for
+ blob columns is always null.
+ After the full table scan the function calls collect_statistics_for_index
+ for each table index. The latter performs full index scan for each index.
+
+ @note
+ Currently the statistical data is collected indiscriminately for all
+ columns/indexes of 'table', for all statistical characteristics.
+ TODO. Collect only specified statistical characteristics for specified
+ columns/indexes.
+
+ @note
+ Currently the process of collecting statistical data is not optimized.
+ For example, 'avg_frequency' for a column could be copied from the
+ 'avg_frequency' collected for an index if this column is used as the
+ first component of the index. Min and min values for this column could
+ be extracted from the index as well.
+*/
+
+int collect_statistics_for_table(THD *thd, TABLE *table)
+{
+ int rc;
+ Field **field_ptr;
+ Field *table_field;
+ ha_rows rows= 0;
+ handler *file=table->file;
+
+ DBUG_ENTER("collect_statistics_for_table");
+
+ table->collected_stats->cardinality_is_null= TRUE;
+ table->collected_stats->cardinality= 0;
+
+ for (field_ptr= table->field; *field_ptr; field_ptr++)
+ {
+ table_field= *field_ptr;
+ if (!bitmap_is_set(table->read_set, table_field->field_index))
+ continue;
+ table_field->collected_stats->init(thd, table_field);
+ }
+
+ restore_record(table, s->default_values);
+
+ /* Perform a full table scan to collect statistics on 'table's columns */
+ if (!(rc= file->ha_rnd_init(TRUE)))
+ {
+ DEBUG_SYNC(table->in_use, "statistics_collection_start");
+
+ while ((rc= file->ha_rnd_next(table->record[0])) != HA_ERR_END_OF_FILE)
+ {
+ if (thd->killed)
+ break;
+
+ if (rc)
+ {
+ if (rc == HA_ERR_RECORD_DELETED)
+ continue;
+ break;
+ }
+
+ for (field_ptr= table->field; *field_ptr; field_ptr++)
+ {
+ table_field= *field_ptr;
+ if (!bitmap_is_set(table->read_set, table_field->field_index))
+ continue;
+ if ((rc= table_field->collected_stats->add(rows)))
+ break;
+ }
+ if (rc)
+ break;
+ rows++;
+ }
+ file->ha_rnd_end();
+ }
+ rc= (rc == HA_ERR_END_OF_FILE && !thd->killed) ? 0 : 1;
+
+ /*
+ Calculate values for all statistical characteristics on columns and
+ and for each field f of 'table' save them in the write_stat structure
+ from the Field object for f.
+ */
+ if (!rc)
+ {
+ table->collected_stats->cardinality_is_null= FALSE;
+ table->collected_stats->cardinality= rows;
+ }
+
+ bitmap_clear_all(table->write_set);
+ for (field_ptr= table->field; *field_ptr; field_ptr++)
+ {
+ table_field= *field_ptr;
+ if (!bitmap_is_set(table->read_set, table_field->field_index))
+ continue;
+ bitmap_set_bit(table->write_set, table_field->field_index);
+ if (!rc)
+ table_field->collected_stats->finish(rows);
+ else
+ table_field->collected_stats->cleanup();
+ }
+ bitmap_clear_all(table->write_set);
+
+ if (!rc)
+ {
+ uint key;
+ key_map::Iterator it(table->keys_in_use_for_query);
+
+ MY_BITMAP *save_read_set= table->read_set;
+ table->read_set= &table->tmp_set;
+ bitmap_set_all(table->read_set);
+
+ /* Collect statistics for indexes */
+ while ((key= it++) != key_map::Iterator::BITMAP_END)
+ {
+ if ((rc= collect_statistics_for_index(thd, table, key)))
+ break;
+ }
+
+ table->read_set= save_read_set;
+ }
+
+ DBUG_RETURN(rc);
+}
+
+
+/**
+ @brief
+ Update statistics for a table in the persistent statistical tables
+
+ @param
+ thd The thread handle
+ @param
+ table The table to collect statistics on
+
+ @details
+ For each statistical table st the function looks for the rows from this
+ table that contain statistical data on 'table'. If rows with given
+ statistical characteristics exist they are updated with the new statistical
+ values taken from internal structures for 'table'. Otherwise new rows
+ with these statistical characteristics are added into st.
+ It is assumed that values stored in the statistical tables are found and
+ saved by the function collect_statistics_for_table.
+
+ @retval
+ 0 If all statistical tables has been successfully updated
+ @retval
+ 1 Otherwise
+
+ @note
+ The function is called when executing the ANALYZE actions for 'table'.
+ The function first unlocks the opened table the statistics on which has
+ been collected, but does not closes it, so all collected statistical data
+ remains in internal structures for 'table'. Then the function opens the
+ statistical tables and writes the statistical data for 'table'into them.
+ It is not allowed just to open statistical tables for writing when some
+ other tables are locked for reading.
+ After the statistical tables have been opened they are updated one by one
+ with the new statistics on 'table'. Objects of the helper classes
+ Table_stat, Column_stat and Index_stat are employed for this.
+ After having been updated the statistical system tables are closed.
+*/
+
+int update_statistics_for_table(THD *thd, TABLE *table)
+{
+ TABLE_LIST tables[STATISTICS_TABLES];
+ Open_tables_backup open_tables_backup;
+ uint i;
+ int err;
+ enum_binlog_format save_binlog_format;
+ int rc= 0;
+ TABLE *stat_table;
+
+ DBUG_ENTER("update_statistics_for_table");
+
+ DEBUG_SYNC(thd, "statistics_update_start");
+
+ if (open_stat_tables(thd, tables, &open_tables_backup, TRUE))
+ {
+ thd->clear_error();
+ DBUG_RETURN(rc);
+ }
+
+ save_binlog_format= thd->set_current_stmt_binlog_format_stmt();
+
+ /* Update the statistical table table_stats */
+ stat_table= tables[TABLE_STAT].table;
+ Table_stat table_stat(stat_table, table);
+ restore_record(stat_table, s->default_values);
+ table_stat.set_key_fields();
+ err= table_stat.update_stat();
+ if (err)
+ rc= 1;
+
+ /* Update the statistical table colum_stats */
+ stat_table= tables[COLUMN_STAT].table;
+ Column_stat column_stat(stat_table, table);
+ for (Field **field_ptr= table->field; *field_ptr; field_ptr++)
+ {
+ Field *table_field= *field_ptr;
+ if (!bitmap_is_set(table->read_set, table_field->field_index))
+ continue;
+ restore_record(stat_table, s->default_values);
+ column_stat.set_key_fields(table_field);
+ err= column_stat.update_stat();
+ if (err && !rc)
+ rc= 1;
+ }
+
+ /* Update the statistical table index_stats */
+ stat_table= tables[INDEX_STAT].table;
+ uint key;
+ key_map::Iterator it(table->keys_in_use_for_query);
+ Index_stat index_stat(stat_table, table);
+
+ while ((key= it++) != key_map::Iterator::BITMAP_END)
+ {
+ KEY *key_info= table->key_info+key;
+ uint key_parts= table->actual_n_key_parts(key_info);
+ for (i= 0; i < key_parts; i++)
+ {
+ restore_record(stat_table, s->default_values);
+ index_stat.set_key_fields(key_info, i+1);
+ err= index_stat.update_stat();
+ if (err && !rc)
+ rc= 1;
+ }
+ }
+
+ thd->restore_stmt_binlog_format(save_binlog_format);
+
+ close_system_tables(thd, &open_tables_backup);
+
+ DBUG_RETURN(rc);
+}
+
+
+/**
+ @brief
+ Read statistics for a table from the persistent statistical tables
+
+ @param
+ thd The thread handle
+ @param
+ table The table to read statistics on
+ @param
+ stat_tables The array of TABLE_LIST objects for statistical tables
+
+ @details
+ For each statistical table the function looks for the rows from this
+ table that contain statistical data on 'table'. If such rows is found
+ the data from statistical columns of it is read into the appropriate
+ fields of internal structures for 'table'. Later at the query processing
+ this data are supposed to be used by the optimizer.
+ The parameter stat_tables should point to an array of TABLE_LIST
+ objects for all statistical tables linked into a list. All statistical
+ tables are supposed to be opened.
+ The function is called by read_statistics_for_tables_if_needed().
+
+ @retval
+ 0 If data has been successfully read for the table
+ @retval
+ 1 Otherwise
+
+ @note
+ Objects of the helper classes Table_stat, Column_stat and Index_stat
+ are employed to read statistical data from the statistical tables.
+ now.
+*/
+
+static
+int read_statistics_for_table(THD *thd, TABLE *table, TABLE_LIST *stat_tables)
+{
+ uint i;
+ TABLE *stat_table;
+ Field *table_field;
+ Field **field_ptr;
+ KEY *key_info, *key_info_end;
+ TABLE_SHARE *table_share= table->s;
+ Table_statistics *read_stats= table_share->stats_cb.table_stats;
+
+ DBUG_ENTER("read_statistics_for_table");
+
+ /* Read statistics from the statistical table table_stats */
+ stat_table= stat_tables[TABLE_STAT].table;
+ Table_stat table_stat(stat_table, table);
+ table_stat.set_key_fields();
+ table_stat.get_stat_values();
+
+ /* Read statistics from the statistical table column_stats */
+ stat_table= stat_tables[COLUMN_STAT].table;
+ ulong total_hist_size= 0;
+ Column_stat column_stat(stat_table, table);
+ for (field_ptr= table_share->field; *field_ptr; field_ptr++)
+ {
+ table_field= *field_ptr;
+ column_stat.set_key_fields(table_field);
+ column_stat.get_stat_values();
+ total_hist_size+= table_field->read_stats->histogram.get_size();
+ }
+ read_stats->total_hist_size= total_hist_size;
+
+ /* Read statistics from the statistical table index_stats */
+ stat_table= stat_tables[INDEX_STAT].table;
+ Index_stat index_stat(stat_table, table);
+ for (key_info= table_share->key_info,
+ key_info_end= key_info + table_share->keys;
+ key_info < key_info_end; key_info++)
+ {
+ uint key_parts= key_info->ext_key_parts;
+ for (i= 0; i < key_parts; i++)
+ {
+ index_stat.set_key_fields(key_info, i+1);
+ index_stat.get_stat_values();
+ }
+
+ key_part_map ext_key_part_map= key_info->ext_key_part_map;
+ if (key_info->user_defined_key_parts != key_info->ext_key_parts &&
+ key_info->read_stats->get_avg_frequency(key_info->user_defined_key_parts) == 0)
+ {
+ KEY *pk_key_info= table_share->key_info + table_share->primary_key;
+ uint k= key_info->user_defined_key_parts;
+ uint pk_parts= pk_key_info->user_defined_key_parts;
+ ha_rows n_rows= read_stats->cardinality;
+ double k_dist= n_rows / key_info->read_stats->get_avg_frequency(k-1);
+ uint m= 0;
+ for (uint j= 0; j < pk_parts; j++)
+ {
+ if (!(ext_key_part_map & 1 << j))
+ {
+ for (uint l= k; l < k + m; l++)
+ {
+ double avg_frequency=
+ pk_key_info->read_stats->get_avg_frequency(j-1);
+ set_if_smaller(avg_frequency, 1);
+ double val= pk_key_info->read_stats->get_avg_frequency(j) /
+ avg_frequency;
+ key_info->read_stats->set_avg_frequency (l, val);
+ }
+ }
+ else
+ {
+ double avg_frequency= pk_key_info->read_stats->get_avg_frequency(j);
+ key_info->read_stats->set_avg_frequency(k + m, avg_frequency);
+ m++;
+ }
+ }
+ for (uint l= k; l < k + m; l++)
+ {
+ double avg_frequency= key_info->read_stats->get_avg_frequency(l);
+ if (avg_frequency == 0 || read_stats->cardinality_is_null)
+ avg_frequency= 1;
+ else if (avg_frequency > 1)
+ {
+ avg_frequency/= k_dist;
+ set_if_bigger(avg_frequency, 1);
+ }
+ key_info->read_stats->set_avg_frequency(l, avg_frequency);
+ }
+ }
+ }
+
+ table->stats_is_read= TRUE;
+
+ DBUG_RETURN(0);
+}
+
+
+/**
+ @brief
+ Check whether any statistics is to be read for tables from a table list
+
+ @param
+ thd The thread handle
+ @param
+ tables The tables list for whose tables the check is to be done
+
+ @details
+ The function checks whether for any of the tables opened and locked for
+ a statement statistics from statistical tables is needed to be read.
+
+ @retval
+ TRUE statistics for any of the tables is needed to be read
+ @retval
+ FALSE Otherwise
+*/
+
+static
+bool statistics_for_tables_is_needed(THD *thd, TABLE_LIST *tables)
+{
+ if (!tables)
+ return FALSE;
+
+ if (!statistics_for_command_is_needed(thd))
+ return FALSE;
+
+ /*
+ Do not read statistics for any query over non-user tables.
+ If the query references some statistical tables, but not all
+ of them, reading the statistics may lead to a deadlock
+ */
+ for (TABLE_LIST *tl= tables; tl; tl= tl->next_global)
+ {
+ if (!tl->is_view_or_derived() && tl->table)
+ {
+ TABLE_SHARE *table_share= tl->table->s;
+ if (table_share &&
+ (table_share->table_category != TABLE_CATEGORY_USER ||
+ table_share->tmp_table != NO_TMP_TABLE))
+ return FALSE;
+ }
+ }
+
+ for (TABLE_LIST *tl= tables; tl; tl= tl->next_global)
+ {
+ if (!tl->is_view_or_derived() && tl->table)
+ {
+ TABLE_SHARE *table_share= tl->table->s;
+ if (table_share &&
+ table_share->stats_cb.stats_can_be_read &&
+ (!table_share->stats_cb.stats_is_read ||
+ (!table_share->stats_cb.histograms_are_read &&
+ thd->variables.optimizer_use_condition_selectivity > 3)))
+ return TRUE;
+ if (table_share->stats_cb.stats_is_read)
+ tl->table->stats_is_read= TRUE;
+ if (table_share->stats_cb.histograms_are_read)
+ tl->table->histograms_are_read= TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+
+/**
+ @brief
+ Read histogram for a table from the persistent statistical tables
+
+ @param
+ thd The thread handle
+ @param
+ table The table to read histograms for
+ @param
+ stat_tables The array of TABLE_LIST objects for statistical tables
+
+ @details
+ For the statistical table columns_stats the function looks for the rows
+ from this table that contain statistical data on 'table'. If such rows
+ are found the histograms from them are read into the memory allocated
+ for histograms of 'table'. Later at the query processing these histogram
+ are supposed to be used by the optimizer.
+ The parameter stat_tables should point to an array of TABLE_LIST
+ objects for all statistical tables linked into a list. All statistical
+ tables are supposed to be opened.
+ The function is called by read_statistics_for_tables_if_needed().
+
+ @retval
+ 0 If data has been successfully read for the table
+ @retval
+ 1 Otherwise
+
+ @note
+ Objects of the helper Column_stat are employed read histogram
+ from the statistical table column_stats now.
+*/
+
+static
+int read_histograms_for_table(THD *thd, TABLE *table, TABLE_LIST *stat_tables)
+{
+ TABLE_SHARE *table_share= table->s;
+
+ DBUG_ENTER("read_histograms_for_table");
+
+ if (!table_share->stats_cb.histograms_can_be_read)
+ {
+ (void) alloc_histograms_for_table_share(thd, table_share, FALSE);
+ }
+ if (table_share->stats_cb.histograms_can_be_read &&
+ !table_share->stats_cb.histograms_are_read)
+ {
+ Field **field_ptr;
+ uchar *histogram= table_share->stats_cb.table_stats->histograms;
+ TABLE *stat_table= stat_tables[COLUMN_STAT].table;
+ Column_stat column_stat(stat_table, table);
+ for (field_ptr= table_share->field; *field_ptr; field_ptr++)
+ {
+ Field *table_field= *field_ptr;
+ uint hist_size= table_field->read_stats->histogram.get_size();
+ if (hist_size)
+ {
+ column_stat.set_key_fields(table_field);
+ table_field->read_stats->histogram.set_values(histogram);
+ column_stat.get_histogram_value();
+ histogram+= hist_size;
+ }
+ }
+ }
+
+ DBUG_RETURN(0);
+}
+
+/**
+ @brief
+ Read statistics for tables from a table list if it is needed
+
+ @param
+ thd The thread handle
+ @param
+ tables The tables list for whose tables to read statistics
+
+ @details
+ The function first checks whether for any of the tables opened and locked
+ for a statement statistics from statistical tables is needed to be read.
+ Then, if so, it opens system statistical tables for read and reads
+ the statistical data from them for those tables from the list for which it
+ makes sense. Then the function closes system statistical tables.
+
+ @retval
+ 0 Statistics for tables was successfully read
+ @retval
+ 1 Otherwise
+*/
+
+int read_statistics_for_tables_if_needed(THD *thd, TABLE_LIST *tables)
+{
+ TABLE_LIST stat_tables[STATISTICS_TABLES];
+ Open_tables_backup open_tables_backup;
+
+ DBUG_ENTER("read_statistics_for_tables_if_needed");
+
+ DEBUG_SYNC(thd, "statistics_read_start");
+
+ if (!statistics_for_tables_is_needed(thd, tables))
+ DBUG_RETURN(0);
+
+ if (open_stat_tables(thd, stat_tables, &open_tables_backup, FALSE))
+ {
+ thd->clear_error();
+ DBUG_RETURN(1);
+ }
+
+ for (TABLE_LIST *tl= tables; tl; tl= tl->next_global)
+ {
+ if (!tl->is_view_or_derived() && tl->table)
+ {
+ TABLE_SHARE *table_share= tl->table->s;
+ if (table_share &&
+ table_share->stats_cb.stats_can_be_read &&
+ !table_share->stats_cb.stats_is_read)
+ {
+ (void) read_statistics_for_table(thd, tl->table, stat_tables);
+ table_share->stats_cb.stats_is_read= TRUE;
+ }
+ if (table_share->stats_cb.stats_is_read)
+ tl->table->stats_is_read= TRUE;
+ if (thd->variables.optimizer_use_condition_selectivity > 3 &&
+ table_share && !table_share->stats_cb.histograms_are_read)
+ {
+ (void) read_histograms_for_table(thd, tl->table, stat_tables);
+ table_share->stats_cb.histograms_are_read= TRUE;
+ }
+ if (table_share->stats_cb.stats_is_read)
+ tl->table->histograms_are_read= TRUE;
+ }
+ }
+
+ close_system_tables(thd, &open_tables_backup);
+
+ DBUG_RETURN(0);
+}
+
+
+/**
+ @brief
+ Delete statistics on a table from all statistical tables
+
+ @param
+ thd The thread handle
+ @param
+ db The name of the database the table belongs to
+ @param
+ tab The name of the table whose statistics is to be deleted
+
+ @details
+ The function delete statistics on the table called 'tab' of the database
+ 'db' from all statistical tables: table_stats, column_stats, index_stats.
+
+ @retval
+ 0 If all deletions are successful
+ @retval
+ 1 Otherwise
+
+ @note
+ The function is called when executing the statement DROP TABLE 'tab'.
+*/
+
+int delete_statistics_for_table(THD *thd, LEX_STRING *db, LEX_STRING *tab)
+{
+ int err;
+ enum_binlog_format save_binlog_format;
+ TABLE *stat_table;
+ TABLE_LIST tables[STATISTICS_TABLES];
+ Open_tables_backup open_tables_backup;
+ int rc= 0;
+
+ DBUG_ENTER("delete_statistics_for_table");
+
+ if (open_stat_tables(thd, tables, &open_tables_backup, TRUE))
+ {
+ thd->clear_error();
+ DBUG_RETURN(rc);
+ }
+
+ save_binlog_format= thd->set_current_stmt_binlog_format_stmt();
+
+ /* Delete statistics on table from the statistical table index_stats */
+ stat_table= tables[INDEX_STAT].table;
+ Index_stat index_stat(stat_table, db, tab);
+ index_stat.set_full_table_name();
+ while (index_stat.find_next_stat_for_prefix(2))
+ {
+ err= index_stat.delete_stat();
+ if (err & !rc)
+ rc= 1;
+ }
+
+ /* Delete statistics on table from the statistical table column_stats */
+ stat_table= tables[COLUMN_STAT].table;
+ Column_stat column_stat(stat_table, db, tab);
+ column_stat.set_full_table_name();
+ while (column_stat.find_next_stat_for_prefix(2))
+ {
+ err= column_stat.delete_stat();
+ if (err & !rc)
+ rc= 1;
+ }
+
+ /* Delete statistics on table from the statistical table table_stats */
+ stat_table= tables[TABLE_STAT].table;
+ Table_stat table_stat(stat_table, db, tab);
+ table_stat.set_key_fields();
+ if (table_stat.find_stat())
+ {
+ err= table_stat.delete_stat();
+ if (err & !rc)
+ rc= 1;
+ }
+
+ thd->restore_stmt_binlog_format(save_binlog_format);
+
+ close_system_tables(thd, &open_tables_backup);
+
+ DBUG_RETURN(rc);
+}
+
+
+/**
+ @brief
+ Delete statistics on a column of the specified table
+
+ @param
+ thd The thread handle
+ @param
+ tab The table the column belongs to
+ @param
+ col The field of the column whose statistics is to be deleted
+
+ @details
+ The function delete statistics on the column 'col' belonging to the table
+ 'tab' from the statistical table column_stats.
+
+ @retval
+ 0 If the deletion is successful
+ @retval
+ 1 Otherwise
+
+ @note
+ The function is called when dropping a table column or when changing
+ the definition of this column.
+*/
+
+int delete_statistics_for_column(THD *thd, TABLE *tab, Field *col)
+{
+ int err;
+ enum_binlog_format save_binlog_format;
+ TABLE *stat_table;
+ TABLE_LIST tables;
+ Open_tables_backup open_tables_backup;
+ int rc= 0;
+
+ DBUG_ENTER("delete_statistics_for_column");
+
+ if (open_single_stat_table(thd, &tables, &stat_table_name[1],
+ &open_tables_backup, TRUE))
+ {
+ thd->clear_error();
+ DBUG_RETURN(rc);
+ }
+
+ save_binlog_format= thd->set_current_stmt_binlog_format_stmt();
+
+ stat_table= tables.table;
+ Column_stat column_stat(stat_table, tab);
+ column_stat.set_key_fields(col);
+ if (column_stat.find_stat())
+ {
+ err= column_stat.delete_stat();
+ if (err)
+ rc= 1;
+ }
+
+ thd->restore_stmt_binlog_format(save_binlog_format);
+
+ close_system_tables(thd, &open_tables_backup);
+
+ DBUG_RETURN(rc);
+}
+
+
+/**
+ @brief
+ Delete statistics on an index of the specified table
+
+ @param
+ thd The thread handle
+ @param
+ tab The table the index belongs to
+ @param
+ key_info The descriptor of the index whose statistics is to be deleted
+ @param
+ ext_prefixes_only Delete statistics only on the index prefixes extended by
+ the components of the primary key
+
+ @details
+ The function delete statistics on the index specified by 'key_info'
+ defined on the table 'tab' from the statistical table index_stats.
+
+ @retval
+ 0 If the deletion is successful
+ @retval
+ 1 Otherwise
+
+ @note
+ The function is called when dropping an index, or dropping/changing the
+ definition of a column used in the definition of the index.
+*/
+
+int delete_statistics_for_index(THD *thd, TABLE *tab, KEY *key_info,
+ bool ext_prefixes_only)
+{
+ int err;
+ enum_binlog_format save_binlog_format;
+ TABLE *stat_table;
+ TABLE_LIST tables;
+ Open_tables_backup open_tables_backup;
+ int rc= 0;
+
+ DBUG_ENTER("delete_statistics_for_index");
+
+ if (open_single_stat_table(thd, &tables, &stat_table_name[2],
+ &open_tables_backup, TRUE))
+ {
+ thd->clear_error();
+ DBUG_RETURN(rc);
+ }
+
+ save_binlog_format= thd->set_current_stmt_binlog_format_stmt();
+
+ stat_table= tables.table;
+ Index_stat index_stat(stat_table, tab);
+ if (!ext_prefixes_only)
+ {
+ index_stat.set_index_prefix_key_fields(key_info);
+ while (index_stat.find_next_stat_for_prefix(3))
+ {
+ err= index_stat.delete_stat();
+ if (err && !rc)
+ rc= 1;
+ }
+ }
+ else
+ {
+ for (uint i= key_info->user_defined_key_parts; i < key_info->ext_key_parts; i++)
+ {
+ index_stat.set_key_fields(key_info, i+1);
+ if (index_stat.find_next_stat_for_prefix(4))
+ {
+ err= index_stat.delete_stat();
+ if (err && !rc)
+ rc= 1;
+ }
+ }
+ }
+
+ thd->restore_stmt_binlog_format(save_binlog_format);
+
+ close_system_tables(thd, &open_tables_backup);
+
+ DBUG_RETURN(rc);
+}
+
+
+/**
+ @brief
+ Rename a table in all statistical tables
+
+ @param
+ thd The thread handle
+ @param
+ db The name of the database the table belongs to
+ @param
+ tab The name of the table to be renamed in statistical tables
+ @param
+ new_tab The new name of the table
+
+ @details
+ The function replaces the name of the table 'tab' from the database 'db'
+ for 'new_tab' in all all statistical tables: table_stats, column_stats,
+ index_stats.
+
+ @retval
+ 0 If all updates of the table name are successful
+ @retval
+ 1 Otherwise
+
+ @note
+ The function is called when executing any statement that renames a table
+*/
+
+int rename_table_in_stat_tables(THD *thd, LEX_STRING *db, LEX_STRING *tab,
+ LEX_STRING *new_db, LEX_STRING *new_tab)
+{
+ int err;
+ enum_binlog_format save_binlog_format;
+ TABLE *stat_table;
+ TABLE_LIST tables[STATISTICS_TABLES];
+ Open_tables_backup open_tables_backup;
+ int rc= 0;
+
+ DBUG_ENTER("rename_table_in_stat_tables");
+
+ if (open_stat_tables(thd, tables, &open_tables_backup, TRUE))
+ {
+ thd->clear_error();
+ DBUG_RETURN(rc);
+ }
+
+ save_binlog_format= thd->set_current_stmt_binlog_format_stmt();
+
+ /* Rename table in the statistical table index_stats */
+ stat_table= tables[INDEX_STAT].table;
+ Index_stat index_stat(stat_table, db, tab);
+ index_stat.set_full_table_name();
+
+ Stat_table_write_iter index_iter(&index_stat);
+ if (index_iter.init(2))
+ rc= 1;
+ while (!index_iter.get_next_row())
+ {
+ err= index_stat.update_table_name_key_parts(new_db, new_tab);
+ if (err & !rc)
+ rc= 1;
+ index_stat.set_full_table_name();
+ }
+ index_iter.cleanup();
+
+ /* Rename table in the statistical table column_stats */
+ stat_table= tables[COLUMN_STAT].table;
+ Column_stat column_stat(stat_table, db, tab);
+ column_stat.set_full_table_name();
+ Stat_table_write_iter column_iter(&column_stat);
+ if (column_iter.init(2))
+ rc= 1;
+ while (!column_iter.get_next_row())
+ {
+ err= column_stat.update_table_name_key_parts(new_db, new_tab);
+ if (err & !rc)
+ rc= 1;
+ column_stat.set_full_table_name();
+ }
+ column_iter.cleanup();
+
+ /* Rename table in the statistical table table_stats */
+ stat_table= tables[TABLE_STAT].table;
+ Table_stat table_stat(stat_table, db, tab);
+ table_stat.set_key_fields();
+ if (table_stat.find_stat())
+ {
+ err= table_stat.update_table_name_key_parts(new_db, new_tab);
+ if (err & !rc)
+ rc= 1;
+ }
+
+ thd->restore_stmt_binlog_format(save_binlog_format);
+
+ close_system_tables(thd, &open_tables_backup);
+
+ DBUG_RETURN(rc);
+}
+
+
+/**
+ @brief
+ Rename a column in the statistical table column_stats
+
+ @param
+ thd The thread handle
+ @param
+ tab The table the column belongs to
+ @param
+ col The column to be renamed
+ @param
+ new_name The new column name
+
+ @details
+ The function replaces the name of the column 'col' belonging to the table
+ 'tab' for 'new_name' in the statistical table column_stats.
+
+ @retval
+ 0 If all updates of the table name are successful
+ @retval
+ 1 Otherwise
+
+ @note
+ The function is called when executing any statement that renames a column,
+ but does not change the column definition.
+*/
+
+int rename_column_in_stat_tables(THD *thd, TABLE *tab, Field *col,
+ const char *new_name)
+{
+ int err;
+ enum_binlog_format save_binlog_format;
+ TABLE *stat_table;
+ TABLE_LIST tables;
+ Open_tables_backup open_tables_backup;
+ int rc= 0;
+
+ DBUG_ENTER("rename_column_in_stat_tables");
+
+ if (tab->s->tmp_table != NO_TMP_TABLE)
+ DBUG_RETURN(0);
+
+ if (open_single_stat_table(thd, &tables, &stat_table_name[1],
+ &open_tables_backup, TRUE))
+ {
+ thd->clear_error();
+ DBUG_RETURN(rc);
+ }
+
+ save_binlog_format= thd->set_current_stmt_binlog_format_stmt();
+
+ /* Rename column in the statistical table table_stat */
+ stat_table= tables.table;
+ Column_stat column_stat(stat_table, tab);
+ column_stat.set_key_fields(col);
+ if (column_stat.find_stat())
+ {
+ err= column_stat.update_column_key_part(new_name);
+ if (err & !rc)
+ rc= 1;
+ }
+
+ thd->restore_stmt_binlog_format(save_binlog_format);
+
+ close_system_tables(thd, &open_tables_backup);
+
+ DBUG_RETURN(rc);
+}
+
+
+/**
+ @brief
+ Set statistics for a table that will be used by the optimizer
+
+ @param
+ thd The thread handle
+ @param
+ table The table to set statistics for
+
+ @details
+ Depending on the value of thd->variables.use_stat_tables
+ the function performs the settings for the table that will control
+ from where the statistical data used by the optimizer will be taken.
+*/
+
+void set_statistics_for_table(THD *thd, TABLE *table)
+{
+ TABLE_STATISTICS_CB *stats_cb= &table->s->stats_cb;
+ Table_statistics *read_stats= stats_cb->table_stats;
+ Use_stat_tables_mode use_stat_table_mode= get_use_stat_tables_mode(thd);
+ table->used_stat_records=
+ (use_stat_table_mode <= COMPLEMENTARY ||
+ !table->stats_is_read || read_stats->cardinality_is_null) ?
+ table->file->stats.records : read_stats->cardinality;
+ KEY *key_info, *key_info_end;
+ for (key_info= table->key_info, key_info_end= key_info+table->s->keys;
+ key_info < key_info_end; key_info++)
+ {
+ key_info->is_statistics_from_stat_tables=
+ (use_stat_table_mode > COMPLEMENTARY &&
+ table->stats_is_read &&
+ key_info->read_stats->avg_frequency_is_inited() &&
+ key_info->read_stats->get_avg_frequency(0) > 0.5);
+ }
+}
+
+
+/**
+ @brief
+ Get the average frequency for a column
+
+ @param
+ field The column whose average frequency is required
+
+ @retval
+ The required average frequency
+*/
+
+double get_column_avg_frequency(Field * field)
+{
+ double res;
+ TABLE *table= field->table;
+
+ /*
+ Statistics is shared by table instances and is accessed through
+ the table share. If table->s->field is not set for 'table', then
+ no column statistics is available for the table .
+ */
+ if (!table->s->field)
+ {
+ res= table->stat_records();
+ return res;
+ }
+
+ Column_statistics *col_stats= table->s->field[field->field_index]->read_stats;
+
+ if (!col_stats)
+ res= table->stat_records();
+ else
+ res= col_stats->get_avg_frequency();
+ return res;
+}
+
+
+/**
+ @brief
+ Estimate the number of rows in a column range using data from stat tables
+
+ @param
+ field The column whose range cardinality is to be estimated
+ @param
+ min_endp The left end of the range whose cardinality is required
+ @param
+ max_endp The right end of the range whose cardinality is required
+ @param
+ range_flag The range flags
+
+ @details
+ The function gets an estimate of the number of rows in a column range
+ using the statistical data from the table column_stats.
+
+ @retval
+ The required estimate of the rows in the column range
+*/
+
+double get_column_range_cardinality(Field *field,
+ key_range *min_endp,
+ key_range *max_endp,
+ uint range_flag)
+{
+ double res;
+ TABLE *table= field->table;
+ Column_statistics *col_stats= table->field[field->field_index]->read_stats;
+ double tab_records= table->stat_records();
+
+ if (!col_stats)
+ return tab_records;
+
+ double col_nulls= tab_records * col_stats->get_nulls_ratio();
+
+ double col_non_nulls= tab_records - col_nulls;
+
+ bool nulls_incl= field->null_ptr && min_endp && min_endp->key[0] &&
+ !(range_flag & NEAR_MIN);
+
+ if (col_non_nulls < 1)
+ {
+ if (nulls_incl)
+ res= col_nulls;
+ else
+ res= 0;
+ }
+ else if (min_endp && max_endp && min_endp->length == max_endp->length &&
+ !memcmp(min_endp->key, max_endp->key, min_endp->length))
+ {
+ if (nulls_incl)
+ {
+ /* This is null single point range */
+ res= col_nulls;
+ }
+ else
+ {
+ double avg_frequency= col_stats->get_avg_frequency();
+ res= avg_frequency;
+ /*
+ psergey-todo: what does check for min_value, max_value mean?
+ min/max_value are set to NULL in alloc_statistics_for_table() and
+ alloc_statistics_for_table_share(). Both functions will immediately
+ call create_min_max_statistical_fields_for_table and
+ create_min_max_statistical_fields_for_table_share() respectively,
+ which will set min/max_value to be valid pointers, unless OOM
+ occurs.
+ */
+ if (avg_frequency > 1.0 + 0.000001 &&
+ col_stats->min_value && col_stats->max_value)
+ {
+ Histogram *hist= &col_stats->histogram;
+ if (hist->is_available())
+ {
+ store_key_image_to_rec(field, (uchar *) min_endp->key,
+ field->key_length());
+ double pos= field->pos_in_interval(col_stats->min_value,
+ col_stats->max_value);
+ res= col_non_nulls *
+ hist->point_selectivity(pos,
+ avg_frequency / col_non_nulls);
+ }
+ }
+ else if (avg_frequency == 0.0)
+ {
+ /* This actually means there is no statistics data */
+ res= tab_records;
+ }
+ }
+ }
+ else
+ {
+ if (col_stats->min_value && col_stats->max_value)
+ {
+ double sel, min_mp_pos, max_mp_pos;
+
+ if (min_endp && !(field->null_ptr && min_endp->key[0]))
+ {
+ store_key_image_to_rec(field, (uchar *) min_endp->key,
+ field->key_length());
+ min_mp_pos= field->pos_in_interval(col_stats->min_value,
+ col_stats->max_value);
+ }
+ else
+ min_mp_pos= 0.0;
+ if (max_endp)
+ {
+ store_key_image_to_rec(field, (uchar *) max_endp->key,
+ field->key_length());
+ max_mp_pos= field->pos_in_interval(col_stats->min_value,
+ col_stats->max_value);
+ }
+ else
+ max_mp_pos= 1.0;
+
+ Histogram *hist= &col_stats->histogram;
+ if (!hist->is_available())
+ sel= (max_mp_pos - min_mp_pos);
+ else
+ sel= hist->range_selectivity(min_mp_pos, max_mp_pos);
+ res= col_non_nulls * sel;
+ set_if_bigger(res, col_stats->get_avg_frequency());
+ }
+ else
+ res= col_non_nulls;
+ if (nulls_incl)
+ res+= col_nulls;
+ }
+ return res;
+}
+
+
+
+/*
+ Estimate selectivity of "col=const" using a histogram
+
+ @param pos Position of the "const" between column's min_value and
+ max_value. This is a number in [0..1] range.
+ @param avg_sel Average selectivity of condition "col=const" in this table.
+ It is calcuated as (#non_null_values / #distinct_values).
+
+ @return
+ Expected condition selectivity (a number between 0 and 1)
+
+ @notes
+ [re_zero_length_buckets] If a bucket with zero value-length is in the
+ middle of the histogram, we will not have min==max. Example: suppose,
+ pos_value=0x12, and the histogram is:
+
+ #n #n+1 #n+2
+ ... 0x10 0x12 0x12 0x14 ...
+ |
+ +------------- bucket with zero value-length
+
+ Here, we will get min=#n+1, max=#n+2, and use the multi-bucket formula.
+
+ The problem happens at the histogram ends. if pos_value=0, and the
+ histogram is:
+
+ 0x00 0x10 ...
+
+ then min=0, max=0. This means pos_value is contained within bucket #0,
+ but on the other hand, histogram data says that the bucket has only one
+ value.
+*/
+
+double Histogram::point_selectivity(double pos, double avg_sel)
+{
+ double sel;
+ /* Find the bucket that contains the value 'pos'. */
+ uint min= find_bucket(pos, TRUE);
+ uint pos_value= (uint) (pos * prec_factor());
+
+ /* Find how many buckets this value occupies */
+ uint max= min;
+ while (max + 1 < get_width() && get_value(max + 1) == pos_value)
+ max++;
+
+ /*
+ A special case: we're looking at a single bucket, and that bucket has
+ zero value-length. Use the multi-bucket formula (attempt to use
+ single-bucket formula will cause divison by zero).
+
+ For more details see [re_zero_length_buckets] above.
+ */
+ if (max == min && get_value(max) == ((max==0)? 0 : get_value(max-1)))
+ max++;
+
+ if (max > min)
+ {
+ /*
+ The value occupies multiple buckets. Use start_bucket ... end_bucket as
+ selectivity.
+ */
+ double bucket_sel= 1.0/(get_width() + 1);
+ sel= bucket_sel * (max - min + 1);
+ }
+ else
+ {
+ /*
+ The value 'pos' fits within one single histogram bucket.
+
+ Histogram buckets have the same numbers of rows, but they cover
+ different ranges of values.
+
+ We assume that values are uniformly distributed across the [0..1] value
+ range.
+ */
+
+ /*
+ If all buckets covered value ranges of the same size, the width of
+ value range would be:
+ */
+ double avg_bucket_width= 1.0 / (get_width() + 1);
+
+ /*
+ Let's see what is the width of value range that our bucket is covering.
+ (min==max currently. they are kept in the formula just in case we
+ will want to extend it to handle multi-bucket case)
+ */
+ double inv_prec_factor= (double) 1.0 / prec_factor();
+ double current_bucket_width=
+ (max + 1 == get_width() ? 1.0 : (get_value(max) * inv_prec_factor)) -
+ (min == 0 ? 0.0 : (get_value(min-1) * inv_prec_factor));
+
+ DBUG_ASSERT(current_bucket_width); /* We shouldn't get a one zero-width bucket */
+
+ /*
+ So:
+ - each bucket has the same #rows
+ - values are unformly distributed across the [min_value,max_value] domain.
+
+ If a bucket has value range that's N times bigger then average, than
+ each value will have to have N times fewer rows than average.
+ */
+ sel= avg_sel * avg_bucket_width / current_bucket_width;
+
+ /*
+ (Q: if we just follow this proportion we may end up in a situation
+ where number of different values we expect to find in this bucket
+ exceeds the number of rows that this histogram has in a bucket. Are
+ we ok with this or we would want to have certain caps?)
+ */
+ }
+ return sel;
+}
+
diff --git a/sql/sql_statistics.h b/sql/sql_statistics.h
new file mode 100644
index 00000000000..46e5cef22d1
--- /dev/null
+++ b/sql/sql_statistics.h
@@ -0,0 +1,428 @@
+/* Copyright 2006-2008 MySQL AB, 2008 Sun Microsystems, Inc.
+
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#ifndef SQL_STATISTICS_H
+#define SQL_STATISTICS_H
+
+typedef
+enum enum_use_stat_tables_mode
+{
+ NEVER,
+ COMPLEMENTARY,
+ PEFERABLY,
+} Use_stat_tables_mode;
+
+typedef
+enum enum_histogram_type
+{
+ SINGLE_PREC_HB,
+ DOUBLE_PREC_HB
+} Histogram_type;
+
+enum enum_stat_tables
+{
+ TABLE_STAT,
+ COLUMN_STAT,
+ INDEX_STAT,
+};
+
+
+/*
+ These enumeration types comprise the dictionary of three
+ statistical tables table_stat, column_stat and index_stat
+ as they defined in ../scripts/mysql_system_tables.sql.
+
+ It would be nice if the declarations of these types were
+ generated automatically by the table definitions.
+*/
+
+enum enum_table_stat_col
+{
+ TABLE_STAT_DB_NAME,
+ TABLE_STAT_TABLE_NAME,
+ TABLE_STAT_CARDINALITY
+};
+
+enum enum_column_stat_col
+{
+ COLUMN_STAT_DB_NAME,
+ COLUMN_STAT_TABLE_NAME,
+ COLUMN_STAT_COLUMN_NAME,
+ COLUMN_STAT_MIN_VALUE,
+ COLUMN_STAT_MAX_VALUE,
+ COLUMN_STAT_NULLS_RATIO,
+ COLUMN_STAT_AVG_LENGTH,
+ COLUMN_STAT_AVG_FREQUENCY,
+ COLUMN_STAT_HIST_SIZE,
+ COLUMN_STAT_HIST_TYPE,
+ COLUMN_STAT_HISTOGRAM
+};
+
+enum enum_index_stat_col
+{
+ INDEX_STAT_DB_NAME,
+ INDEX_STAT_TABLE_NAME,
+ INDEX_STAT_INDEX_NAME,
+ INDEX_STAT_PREFIX_ARITY,
+ INDEX_STAT_AVG_FREQUENCY
+};
+
+inline
+Use_stat_tables_mode get_use_stat_tables_mode(THD *thd)
+{
+ return (Use_stat_tables_mode) (thd->variables.use_stat_tables);
+}
+
+int read_statistics_for_tables_if_needed(THD *thd, TABLE_LIST *tables);
+int collect_statistics_for_table(THD *thd, TABLE *table);
+int alloc_statistics_for_table_share(THD* thd, TABLE_SHARE *share,
+ bool is_safe);
+int alloc_statistics_for_table(THD *thd, TABLE *table);
+int update_statistics_for_table(THD *thd, TABLE *table);
+int delete_statistics_for_table(THD *thd, LEX_STRING *db, LEX_STRING *tab);
+int delete_statistics_for_column(THD *thd, TABLE *tab, Field *col);
+int delete_statistics_for_index(THD *thd, TABLE *tab, KEY *key_info,
+ bool ext_prefixes_only);
+int rename_table_in_stat_tables(THD *thd, LEX_STRING *db, LEX_STRING *tab,
+ LEX_STRING *new_db, LEX_STRING *new_tab);
+int rename_column_in_stat_tables(THD *thd, TABLE *tab, Field *col,
+ const char *new_name);
+void set_statistics_for_table(THD *thd, TABLE *table);
+
+double get_column_avg_frequency(Field * field);
+
+double get_column_range_cardinality(Field *field,
+ key_range *min_endp,
+ key_range *max_endp,
+ uint range_flag);
+
+class Histogram
+{
+
+private:
+ Histogram_type type;
+ uint8 size; /* Size of values array, in bytes */
+ uchar *values;
+
+ uint prec_factor()
+ {
+ switch (type) {
+ case SINGLE_PREC_HB:
+ return ((uint) (1 << 8) - 1);
+ case DOUBLE_PREC_HB:
+ return ((uint) (1 << 16) - 1);
+ }
+ return 1;
+ }
+
+public:
+ uint get_width()
+ {
+ switch (type) {
+ case SINGLE_PREC_HB:
+ return size;
+ case DOUBLE_PREC_HB:
+ return size / 2;
+ }
+ return 0;
+ }
+
+private:
+ uint get_value(uint i)
+ {
+ DBUG_ASSERT(i < get_width());
+ switch (type) {
+ case SINGLE_PREC_HB:
+ return (uint) (((uint8 *) values)[i]);
+ case DOUBLE_PREC_HB:
+ return (uint) uint2korr(values + i * 2);
+ }
+ return 0;
+ }
+
+ /* Find the bucket which value 'pos' falls into. */
+ uint find_bucket(double pos, bool first)
+ {
+ uint val= (uint) (pos * prec_factor());
+ int lp= 0;
+ int rp= get_width() - 1;
+ int d= get_width() / 2;
+ uint i= lp + d;
+ for ( ; d; d= (rp - lp) / 2, i= lp + d)
+ {
+ if (val == get_value(i))
+ break;
+ if (val < get_value(i))
+ rp= i;
+ else if (val > get_value(i + 1))
+ lp= i + 1;
+ else
+ break;
+ }
+
+ if (val > get_value(i) && i < (get_width() - 1))
+ i++;
+
+ if (val == get_value(i))
+ {
+ if (first)
+ {
+ while(i && val == get_value(i - 1))
+ i--;
+ }
+ else
+ {
+ while(i + 1 < get_width() && val == get_value(i + 1))
+ i++;
+ }
+ }
+ return i;
+ }
+
+public:
+
+ uint get_size() { return (uint) size; }
+
+ Histogram_type get_type() { return type; }
+
+ uchar *get_values() { return (uchar *) values; }
+
+ void set_size (ulonglong sz) { size= (uint8) sz; }
+
+ void set_type (Histogram_type t) { type= t; }
+
+ void set_values (uchar *vals) { values= (uchar *) vals; }
+
+ bool is_available() { return get_size() > 0 && get_values(); }
+
+ void set_value(uint i, double val)
+ {
+ switch (type) {
+ case SINGLE_PREC_HB:
+ ((uint8 *) values)[i]= (uint8) (val * prec_factor());
+ return;
+ case DOUBLE_PREC_HB:
+ int2store(values + i * 2, val * prec_factor());
+ return;
+ }
+ }
+
+ void set_prev_value(uint i)
+ {
+ switch (type) {
+ case SINGLE_PREC_HB:
+ ((uint8 *) values)[i]= ((uint8 *) values)[i-1];
+ return;
+ case DOUBLE_PREC_HB:
+ int2store(values + i * 2, uint2korr(values + i * 2 - 2));
+ return;
+ }
+ }
+
+ double range_selectivity(double min_pos, double max_pos)
+ {
+ double sel;
+ double bucket_sel= 1.0/(get_width() + 1);
+ uint min= find_bucket(min_pos, TRUE);
+ uint max= find_bucket(max_pos, FALSE);
+ sel= bucket_sel * (max - min + 1);
+ return sel;
+ }
+
+ /*
+ Estimate selectivity of "col=const" using a histogram
+ */
+ double point_selectivity(double pos, double avg_sel);
+};
+
+
+class Columns_statistics;
+class Index_statistics;
+
+static inline
+int rename_table_in_stat_tables(THD *thd, const char *db, const char *tab,
+ const char *new_db, const char *new_tab)
+{
+ LEX_STRING od= { const_cast<char*>(db), strlen(db) };
+ LEX_STRING ot= { const_cast<char*>(tab), strlen(tab) };
+ LEX_STRING nd= { const_cast<char*>(new_db), strlen(new_db) };
+ LEX_STRING nt= { const_cast<char*>(new_tab), strlen(new_tab) };
+ return rename_table_in_stat_tables(thd, &od, &ot, &nd, &nt);
+}
+
+
+/* Statistical data on a table */
+
+class Table_statistics
+{
+
+public:
+ my_bool cardinality_is_null; /* TRUE if the cardinality is unknown */
+ ha_rows cardinality; /* Number of rows in the table */
+ uchar *min_max_record_buffers; /* Record buffers for min/max values */
+ Column_statistics *column_stats; /* Array of statistical data for columns */
+ Index_statistics *index_stats; /* Array of statistical data for indexes */
+ ulong *idx_avg_frequency; /* Array of records per key for index prefixes */
+ ulong total_hist_size; /* Total size of all histograms */
+ uchar *histograms; /* Sequence of histograms */
+};
+
+
+/*
+ Statistical data on a column
+
+ Note: objects of this class may be "empty", where they have almost all fields
+ as zeros, for example, get_avg_frequency() will return 0.
+
+ objects are allocated in alloc_statistics_for_table[_share].
+*/
+
+class Column_statistics
+{
+
+private:
+ static const uint Scale_factor_nulls_ratio= 100000;
+ static const uint Scale_factor_avg_length= 100000;
+ static const uint Scale_factor_avg_frequency= 100000;
+
+public:
+ /*
+ Bitmap indicating what statistical characteristics
+ are available for the column
+ */
+ uint32 column_stat_nulls;
+
+ /* For the below two, see comments in get_column_range_cardinality() */
+ /* Minimum value for the column */
+ Field *min_value;
+ /* Maximum value for the column */
+ Field *max_value;
+
+private:
+
+ /*
+ The ratio Z/N multiplied by the scale factor Scale_factor_nulls_ratio,
+ where
+ N is the total number of rows,
+ Z is the number of nulls in the column
+ */
+ ulong nulls_ratio;
+
+ /*
+ Average number of bytes occupied by the representation of a
+ value of the column in memory buffers such as join buffer
+ multiplied by the scale factor Scale_factor_avg_length.
+ CHAR values are stripped of trailing spaces.
+ Flexible values are stripped of their length prefixes.
+ */
+ ulong avg_length;
+
+ /*
+ The ratio N/D multiplied by the scale factor Scale_factor_avg_frequency,
+ where
+ N is the number of rows with not null value in the column,
+ D the number of distinct values among them
+ */
+ ulong avg_frequency;
+
+public:
+
+ Histogram histogram;
+
+ void set_all_nulls()
+ {
+ column_stat_nulls=
+ ((1 << (COLUMN_STAT_HISTOGRAM-COLUMN_STAT_COLUMN_NAME))-1) <<
+ (COLUMN_STAT_COLUMN_NAME+1);
+ }
+
+ void set_not_null(uint stat_field_no)
+ {
+ column_stat_nulls&= ~(1 << stat_field_no);
+ }
+
+ bool is_null(uint stat_field_no)
+ {
+ return MY_TEST(column_stat_nulls & (1 << stat_field_no));
+ }
+
+ double get_nulls_ratio()
+ {
+ return (double) nulls_ratio / Scale_factor_nulls_ratio;
+ }
+
+ double get_avg_length()
+ {
+ return (double) avg_length / Scale_factor_avg_length;
+ }
+
+ double get_avg_frequency()
+ {
+ return (double) avg_frequency / Scale_factor_avg_frequency;
+ }
+
+ void set_nulls_ratio (double val)
+ {
+ nulls_ratio= (ulong) (val * Scale_factor_nulls_ratio);
+ }
+
+ void set_avg_length (double val)
+ {
+ avg_length= (ulong) (val * Scale_factor_avg_length);
+ }
+
+ void set_avg_frequency (double val)
+ {
+ avg_frequency= (ulong) (val * Scale_factor_avg_frequency);
+ }
+
+};
+
+
+/* Statistical data on an index prefixes */
+
+class Index_statistics
+{
+
+private:
+ static const uint Scale_factor_avg_frequency= 100000;
+ /*
+ The k-th element of this array contains the ratio N/D
+ multiplied by the scale factor Scale_factor_avg_frequency,
+ where N is the number of index entries without nulls
+ in the first k components, and D is the number of distinct
+ k-component prefixes among them
+ */
+ ulong *avg_frequency;
+
+public:
+
+ void init_avg_frequency(ulong *ptr) { avg_frequency= ptr; }
+
+ bool avg_frequency_is_inited() { return avg_frequency != NULL; }
+
+ double get_avg_frequency(uint i)
+ {
+ return (double) avg_frequency[i] / Scale_factor_avg_frequency;
+ }
+
+ void set_avg_frequency(uint i, double val)
+ {
+ avg_frequency[i]= (ulong) (val * Scale_factor_avg_frequency);
+ }
+
+};
+
+#endif /* SQL_STATISTICS_H */
diff --git a/sql/sql_string.cc b/sql/sql_string.cc
index 885f53ae36a..a7bfa6c1455 100644
--- a/sql/sql_string.cc
+++ b/sql/sql_string.cc
@@ -41,7 +41,9 @@ bool String::real_alloc(uint32 length)
if (Alloced_length < arg_length)
{
free();
- if (!(Ptr=(char*) my_malloc(arg_length,MYF(MY_WME))))
+ if (!(Ptr=(char*) my_malloc(arg_length,MYF(MY_WME |
+ (thread_specific ?
+ MY_THREAD_SPECIFIC : 0)))))
return TRUE;
Alloced_length=arg_length;
alloced=1;
@@ -89,10 +91,16 @@ bool String::realloc_raw(uint32 alloc_length)
return TRUE; /* Overflow */
if (alloced)
{
- if (!(new_ptr= (char*) my_realloc(Ptr,len,MYF(MY_WME))))
+ if (!(new_ptr= (char*) my_realloc(Ptr,len,
+ MYF(MY_WME |
+ (thread_specific ?
+ MY_THREAD_SPECIFIC : 0)))))
return TRUE; // Signal error
}
- else if ((new_ptr= (char*) my_malloc(len,MYF(MY_WME))))
+ else if ((new_ptr= (char*) my_malloc(len,
+ MYF(MY_WME |
+ (thread_specific ?
+ MY_THREAD_SPECIFIC : 0)))))
{
if (str_length > len - 1)
str_length= 0;
@@ -537,6 +545,24 @@ bool String::append(IO_CACHE* file, uint32 arg_length)
return FALSE;
}
+
+/**
+ Append a parenthesized number to String.
+ Used in various pieces of SHOW related code.
+
+ @param nr Number
+ @param radix Radix, optional parameter, 10 by default.
+*/
+bool String::append_parenthesized(long nr, int radix)
+{
+ char buff[64], *end;
+ buff[0]= '(';
+ end= int10_to_str(nr, buff + 1, radix);
+ *end++ = ')';
+ return append(buff, (uint) (end - buff));
+}
+
+
bool String::append_with_prefill(const char *s,uint32 arg_length,
uint32 full_length, char fill_char)
{
@@ -554,7 +580,7 @@ bool String::append_with_prefill(const char *s,uint32 arg_length,
return FALSE;
}
-uint32 String::numchars()
+uint32 String::numchars() const
{
return str_charset->cset->numchars(str_charset, Ptr, Ptr+str_length);
}
@@ -670,7 +696,7 @@ int String::reserve(uint32 space_needed, uint32 grow_by)
{
if (Alloced_length < str_length + space_needed)
{
- if (realloc(Alloced_length + max(space_needed, grow_by) - 1))
+ if (realloc(Alloced_length + MY_MAX(space_needed, grow_by) - 1))
return TRUE;
}
return FALSE;
@@ -757,7 +783,7 @@ int sortcmp(const String *s,const String *t, CHARSET_INFO *cs)
int stringcmp(const String *s,const String *t)
{
- uint32 s_len=s->length(),t_len=t->length(),len=min(s_len,t_len);
+ uint32 s_len=s->length(),t_len=t->length(),len=MY_MIN(s_len,t_len);
int cmp= memcmp(s->ptr(), t->ptr(), len);
return (cmp) ? cmp : (int) (s_len - t_len);
}
@@ -774,7 +800,7 @@ String *copy_if_not_alloced(String *to,String *from,uint32 from_length)
}
if (to->realloc(from_length))
return from; // Actually an error
- if ((to->str_length=min(from->str_length,from_length)))
+ if ((to->str_length=MY_MIN(from->str_length,from_length)))
memcpy(to->Ptr,from->Ptr,to->str_length);
to->str_charset=from->str_charset;
return to;
@@ -785,140 +811,6 @@ String *copy_if_not_alloced(String *to,String *from,uint32 from_length)
Help functions
****************************************************************************/
-/*
- copy a string from one character set to another
-
- SYNOPSIS
- copy_and_convert()
- to Store result here
- to_cs Character set of result string
- from Copy from here
- from_length Length of from string
- from_cs From character set
-
- NOTES
- 'to' must be big enough as form_length * to_cs->mbmaxlen
-
- RETURN
- length of bytes copied to 'to'
-*/
-
-
-static uint32
-copy_and_convert_extended(char *to, uint32 to_length, CHARSET_INFO *to_cs,
- const char *from, uint32 from_length,
- CHARSET_INFO *from_cs,
- uint *errors)
-{
- int cnvres;
- my_wc_t wc;
- const uchar *from_end= (const uchar*) from+from_length;
- char *to_start= to;
- uchar *to_end= (uchar*) to+to_length;
- my_charset_conv_mb_wc mb_wc= from_cs->cset->mb_wc;
- my_charset_conv_wc_mb wc_mb= to_cs->cset->wc_mb;
- uint error_count= 0;
-
- while (1)
- {
- if ((cnvres= (*mb_wc)(from_cs, &wc, (uchar*) from,
- from_end)) > 0)
- from+= cnvres;
- else if (cnvres == MY_CS_ILSEQ)
- {
- error_count++;
- from++;
- wc= '?';
- }
- else if (cnvres > MY_CS_TOOSMALL)
- {
- /*
- A correct multibyte sequence detected
- But it doesn't have Unicode mapping.
- */
- error_count++;
- from+= (-cnvres);
- wc= '?';
- }
- else
- break; // Not enough characters
-
-outp:
- if ((cnvres= (*wc_mb)(to_cs, wc, (uchar*) to, to_end)) > 0)
- to+= cnvres;
- else if (cnvres == MY_CS_ILUNI && wc != '?')
- {
- error_count++;
- wc= '?';
- goto outp;
- }
- else
- break;
- }
- *errors= error_count;
- return (uint32) (to - to_start);
-}
-
-
-/*
- Optimized for quick copying of ASCII characters in the range 0x00..0x7F.
-*/
-uint32
-copy_and_convert(char *to, uint32 to_length, CHARSET_INFO *to_cs,
- const char *from, uint32 from_length, CHARSET_INFO *from_cs,
- uint *errors)
-{
- /*
- If any of the character sets is not ASCII compatible,
- immediately switch to slow mb_wc->wc_mb method.
- */
- if ((to_cs->state | from_cs->state) & MY_CS_NONASCII)
- return copy_and_convert_extended(to, to_length, to_cs,
- from, from_length, from_cs, errors);
-
- uint32 length= min(to_length, from_length), length2= length;
-
-#if defined(__i386__) || defined(__x86_64__)
- /*
- Special loop for i386, it allows to refer to a
- non-aligned memory block as UINT32, which makes
- it possible to copy four bytes at once. This
- gives about 10% performance improvement comparing
- to byte-by-byte loop.
- */
- for ( ; length >= 4; length-= 4, from+= 4, to+= 4)
- {
- if ((*(uint32*)from) & 0x80808080)
- break;
- *((uint32*) to)= *((const uint32*) from);
- }
-#endif
-
- for (; ; *to++= *from++, length--)
- {
- if (!length)
- {
- *errors= 0;
- return length2;
- }
- if (*((unsigned char*) from) > 0x7F) /* A non-ASCII character */
- {
- uint32 copied_length= length2 - length;
- to_length-= copied_length;
- from_length-= copied_length;
- return copied_length + copy_and_convert_extended(to, to_length,
- to_cs,
- from, from_length,
- from_cs,
- errors);
- }
- }
-
- DBUG_ASSERT(FALSE); // Should never get to here
- return 0; // Make compiler happy
-}
-
-
/**
Copy string with HEX-encoding of "bad" characters.
@@ -1036,7 +928,7 @@ well_formed_copy_nchars(CHARSET_INFO *to_cs,
if (to_cs == &my_charset_bin)
{
- res= min(min(nchars, to_length), from_length);
+ res= MY_MIN(MY_MIN(nchars, to_length), from_length);
memmove(to, from, res);
*from_end_pos= from + res;
*well_formed_error_pos= NULL;
@@ -1130,8 +1022,15 @@ well_formed_copy_nchars(CHARSET_INFO *to_cs,
wc= '?';
}
else
- break; // Not enough characters
-
+ {
+ if ((uchar *) from >= from_end)
+ break; // End of line
+ // Incomplete byte sequence
+ if (!*well_formed_error_pos)
+ *well_formed_error_pos= from;
+ from++;
+ wc= '?';
+ }
outp:
if ((cnvres= (*wc_mb)(to_cs, wc, (uchar*) to, to_end)) > 0)
to+= cnvres;
@@ -1158,10 +1057,11 @@ outp:
/*
Append characters to a single-quoted string '...', escaping special
- characters as necessary.
+ characters with backslashes as necessary.
Does not add the enclosing quotes, this is left up to caller.
*/
-void String::append_for_single_quote(const char *st, uint len)
+#define APPEND(X) if (append(X)) return 1; else break
+bool String::append_for_single_quote(const char *st, uint len)
{
const char *end= st+len;
for (; st < end; st++)
@@ -1169,31 +1069,19 @@ void String::append_for_single_quote(const char *st, uint len)
uchar c= *st;
switch (c)
{
- case '\\':
- append(STRING_WITH_LEN("\\\\"));
- break;
- case '\0':
- append(STRING_WITH_LEN("\\0"));
- break;
- case '\'':
- append(STRING_WITH_LEN("\\'"));
- break;
- case '\n':
- append(STRING_WITH_LEN("\\n"));
- break;
- case '\r':
- append(STRING_WITH_LEN("\\r"));
- break;
- case '\032': // Ctrl-Z
- append(STRING_WITH_LEN("\\Z"));
- break;
- default:
- append(c);
+ case '\\': APPEND(STRING_WITH_LEN("\\\\"));
+ case '\0': APPEND(STRING_WITH_LEN("\\0"));
+ case '\'': APPEND(STRING_WITH_LEN("\\'"));
+ case '\n': APPEND(STRING_WITH_LEN("\\n"));
+ case '\r': APPEND(STRING_WITH_LEN("\\r"));
+ case '\032': APPEND(STRING_WITH_LEN("\\Z"));
+ default: APPEND(c);
}
}
+ return 0;
}
-void String::print(String *str)
+void String::print(String *str) const
{
str->append_for_single_quote(Ptr, str_length);
}
@@ -1248,7 +1136,7 @@ uint convert_to_printable(char *to, size_t to_len,
char *t= to;
char *t_end= to + to_len - 1; // '- 1' is for the '\0' at the end
const char *f= from;
- const char *f_end= from + (nbytes ? min(from_len, nbytes) : from_len);
+ const char *f_end= from + (nbytes ? MY_MIN(from_len, nbytes) : from_len);
char *dots= to; // last safe place to append '...'
if (!f || t == t_end)
diff --git a/sql/sql_string.h b/sql/sql_string.h
index 1fce3ae6c6f..c287f051d98 100644
--- a/sql/sql_string.h
+++ b/sql/sql_string.h
@@ -34,9 +34,13 @@ typedef struct st_mem_root MEM_ROOT;
int sortcmp(const String *a,const String *b, CHARSET_INFO *cs);
String *copy_if_not_alloced(String *a,String *b,uint32 arg_length);
-uint32 copy_and_convert(char *to, uint32 to_length, CHARSET_INFO *to_cs,
- const char *from, uint32 from_length,
- CHARSET_INFO *from_cs, uint *errors);
+inline uint32 copy_and_convert(char *to, uint32 to_length,
+ const CHARSET_INFO *to_cs,
+ const char *from, uint32 from_length,
+ const CHARSET_INFO *from_cs, uint *errors)
+{
+ return my_convert(to, to_length, to_cs, from, from_length, from_cs, errors);
+}
uint32 well_formed_copy_nchars(CHARSET_INFO *to_cs,
char *to, uint to_length,
CHARSET_INFO *from_cs,
@@ -56,23 +60,26 @@ class String
{
char *Ptr;
uint32 str_length,Alloced_length, extra_alloc;
- bool alloced;
+ bool alloced,thread_specific;
CHARSET_INFO *str_charset;
public:
String()
{
- Ptr=0; str_length=Alloced_length=extra_alloc=0; alloced=0;
+ Ptr=0; str_length=Alloced_length=extra_alloc=0;
+ alloced= thread_specific= 0;
str_charset= &my_charset_bin;
}
String(uint32 length_arg)
{
- alloced=0; Alloced_length= extra_alloc= 0; (void) real_alloc(length_arg);
+ alloced= thread_specific= 0;
+ Alloced_length= extra_alloc= 0; (void) real_alloc(length_arg);
str_charset= &my_charset_bin;
}
String(const char *str, CHARSET_INFO *cs)
{
Ptr=(char*) str; str_length= (uint32) strlen(str);
- Alloced_length= extra_alloc= 0; alloced=0;
+ Alloced_length= extra_alloc= 0;
+ alloced= thread_specific= 0;
str_charset=cs;
}
/*
@@ -82,18 +89,21 @@ public:
*/
String(const char *str,uint32 len, CHARSET_INFO *cs)
{
- Ptr=(char*) str; str_length=len; Alloced_length= extra_alloc=0; alloced=0;
+ Ptr=(char*) str; str_length=len; Alloced_length= extra_alloc=0;
+ alloced= thread_specific= 0;
str_charset=cs;
}
String(char *str,uint32 len, CHARSET_INFO *cs)
{
- Ptr=(char*) str; Alloced_length=str_length=len; extra_alloc= 0; alloced=0;
+ Ptr=(char*) str; Alloced_length=str_length=len; extra_alloc= 0;
+ alloced= thread_specific= 0;
str_charset=cs;
}
String(const String &str)
{
Ptr=str.Ptr ; str_length=str.str_length ;
- Alloced_length=str.Alloced_length; extra_alloc= 0; alloced=0;
+ Alloced_length=str.Alloced_length; extra_alloc= 0;
+ alloced= thread_specific= 0;
str_charset=str.str_charset;
}
static void *operator new(size_t size, MEM_ROOT *mem_root) throw ()
@@ -108,6 +118,12 @@ public:
{ /* never called */ }
~String() { free(); }
+ /* Mark variable thread specific it it's not allocated already */
+ inline void set_thread_specific()
+ {
+ if (!alloced)
+ thread_specific= 1;
+ }
inline void set_charset(CHARSET_INFO *charset_arg)
{ str_charset= charset_arg; }
inline CHARSET_INFO *charset() const { return str_charset; }
@@ -148,6 +164,11 @@ public:
LEX_STRING lex_string = { (char*) ptr(), length() };
return lex_string;
}
+ LEX_CSTRING lex_cstring() const
+ {
+ LEX_CSTRING lex_cstring = { ptr(), length() };
+ return lex_cstring;
+ }
void set(String &str,uint32 offset,uint32 arg_length)
{
@@ -332,17 +353,22 @@ public:
bool set_or_copy_aligned(const char *s, uint32 arg_length, CHARSET_INFO *cs);
bool copy(const char*s,uint32 arg_length, CHARSET_INFO *csfrom,
CHARSET_INFO *csto, uint *errors);
+ bool copy(const String *str, CHARSET_INFO *tocs, uint *errors)
+ {
+ return copy(str->ptr(), str->length(), str->charset(), tocs, errors);
+ }
void move(String &s)
{
free();
Ptr=s.Ptr ; str_length=s.str_length ; Alloced_length=s.Alloced_length;
extra_alloc= s.extra_alloc;
alloced= s.alloced;
+ thread_specific= s.thread_specific;
s.alloced= 0;
}
bool append(const String &s);
bool append(const char *s);
- bool append(LEX_STRING *ls)
+ bool append(const LEX_STRING *ls)
{
return append(ls->str, ls->length);
}
@@ -352,6 +378,7 @@ public:
bool append(IO_CACHE* file, uint32 arg_length);
bool append_with_prefill(const char *s, uint32 arg_length,
uint32 full_length, char fill_char);
+ bool append_parenthesized(long nr, int radix= 10);
int strstr(const String &search,uint32 offset=0); // Returns offset to substring or -1
int strrstr(const String &search,uint32 offset=0); // Returns offset to substring or -1
bool replace(uint32 offset,uint32 arg_length,const char *to,uint32 length);
@@ -386,7 +413,7 @@ public:
friend int stringcmp(const String *a,const String *b);
friend String *copy_if_not_alloced(String *a,String *b,uint32 arg_length);
friend class Field;
- uint32 numchars();
+ uint32 numchars() const;
int charpos(longlong i,uint32 offset=0);
int reserve(uint32 space_needed)
@@ -477,8 +504,17 @@ public:
str_length+= arg_length;
return FALSE;
}
- void print(String *print);
- void append_for_single_quote(const char *st, uint len);
+ void print(String *print) const;
+
+ bool append_for_single_quote(const char *st, uint len);
+ bool append_for_single_quote(const String *s)
+ {
+ return append_for_single_quote(s->ptr(), s->length());
+ }
+ bool append_for_single_quote(const char *st)
+ {
+ return append_for_single_quote(st, strlen(st));
+ }
/* Swap two string objects. Efficient way to exchange data without memcpy. */
void swap(String &s);
@@ -487,6 +523,12 @@ public:
{
return (s->alloced && Ptr >= s->Ptr && Ptr < s->Ptr + s->str_length);
}
+ uint well_formed_length() const
+ {
+ int dummy_error;
+ return charset()->cset->well_formed_len(charset(), ptr(), ptr() + length(),
+ length(), &dummy_error);
+ }
bool is_ascii() const
{
if (length() == 0)
@@ -500,8 +542,49 @@ public:
}
return TRUE;
}
+ bool bin_eq(const String *other) const
+ {
+ return length() == other->length() &&
+ !memcmp(ptr(), other->ptr(), length());
+ }
+ bool eq(const String *other, CHARSET_INFO *cs) const
+ {
+ return !sortcmp(this, other, cs);
+ }
};
+
+// The following class is a backport from MySQL 5.6:
+/**
+ String class wrapper with a preallocated buffer of size buff_sz
+
+ This class allows to replace sequences of:
+ char buff[12345];
+ String str(buff, sizeof(buff));
+ str.length(0);
+ with a simple equivalent declaration:
+ StringBuffer<12345> str;
+*/
+
+template<size_t buff_sz>
+class StringBuffer : public String
+{
+ char buff[buff_sz];
+
+public:
+ StringBuffer() : String(buff, buff_sz, &my_charset_bin) { length(0); }
+ explicit StringBuffer(const CHARSET_INFO *cs) : String(buff, buff_sz, cs)
+ {
+ length(0);
+ }
+ StringBuffer(const char *str, size_t length, const CHARSET_INFO *cs)
+ : String(buff, buff_sz, cs)
+ {
+ set(str, length, cs);
+ }
+};
+
+
static inline bool check_if_only_end_space(CHARSET_INFO *cs,
const char *str,
const char *end)
@@ -509,4 +592,7 @@ static inline bool check_if_only_end_space(CHARSET_INFO *cs,
return str+ cs->cset->scan(cs, str, end, MY_SEQ_SPACES) == end;
}
+int append_query_string(CHARSET_INFO *csinfo, String *to,
+ const char *str, size_t len, bool no_backslash);
+
#endif /* SQL_STRING_INCLUDED */
diff --git a/sql/sql_table.cc b/sql/sql_table.cc
index d8e9aaf1f5f..f19b4e4cb8d 100644
--- a/sql/sql_table.cc
+++ b/sql/sql_table.cc
@@ -18,11 +18,11 @@
/* drop and alter of tables */
+#include <my_global.h>
#include "sql_priv.h"
#include "unireg.h"
#include "debug_sync.h"
#include "sql_table.h"
-#include "sql_rename.h" // do_rename
#include "sql_parse.h" // test_if_data_home_dir
#include "sql_cache.h" // query_cache_*
#include "sql_base.h" // open_table_uncached, lock_table_names
@@ -32,6 +32,7 @@
#include "sql_partition.h" // mem_alloc_error,
// generate_partition_syntax,
// partition_info
+ // NOT_A_PARTITION_ID
#include "sql_db.h" // load_db_opt_by_name
#include "sql_time.h" // make_truncated_value_warning
#include "records.h" // init_read_record, end_read_record
@@ -42,6 +43,7 @@
#include "discover.h" // readfrm
#include "my_pthread.h" // pthread_mutex_t
#include "log_event.h" // Query_log_event
+#include "sql_statistics.h"
#include <hash.h>
#include <myisam.h>
#include <my_dir.h>
@@ -52,7 +54,6 @@
#include "sql_parse.h"
#include "sql_show.h"
#include "transaction.h"
-#include "datadict.h" // dd_frm_type()
#include "sql_audit.h"
#ifdef __WIN__
@@ -63,16 +64,17 @@ const char *primary_key_name="PRIMARY";
static bool check_if_keyname_exists(const char *name,KEY *start, KEY *end);
static char *make_unique_key_name(const char *field_name,KEY *start,KEY *end);
-static int copy_data_between_tables(THD *thd, TABLE *,TABLE *,
- List<Create_field> &, bool,
- uint, ORDER *, ha_rows *,ha_rows *,
- enum enum_enable_or_disable, bool);
+static int copy_data_between_tables(THD *thd, TABLE *from,TABLE *to,
+ List<Create_field> &create, bool ignore,
+ uint order_num, ORDER *order,
+ ha_rows *copied,ha_rows *deleted,
+ Alter_info::enum_enable_or_disable keys_onoff,
+ Alter_table_ctx *alter_ctx);
static bool prepare_blob_field(THD *thd, Create_field *sql_field);
static bool check_engine(THD *, const char *, const char *, HA_CREATE_INFO *);
static int mysql_prepare_create_table(THD *, HA_CREATE_INFO *, Alter_info *,
- bool, uint *, handler *, KEY **, uint *,
- int);
+ uint *, handler *, KEY **, uint *, int);
static uint blob_length_by_type(enum_field_types type);
/**
@@ -102,7 +104,8 @@ static char* add_identifier(THD* thd, char *to_p, const char * end_p,
tmp_name[name_len]= 0;
conv_name= tmp_name;
}
- res= strconvert(&my_charset_filename, conv_name, system_charset_info,
+ res= strconvert(&my_charset_filename, conv_name, name_len,
+ system_charset_info,
conv_string, FN_REFLEN, &errors);
if (!res || errors)
{
@@ -367,42 +370,22 @@ uint explain_filename(THD* thd,
Table name length.
*/
-uint filename_to_tablename(const char *from, char *to, uint to_length
-#ifndef DBUG_OFF
- , bool stay_quiet
-#endif /* DBUG_OFF */
- )
+uint filename_to_tablename(const char *from, char *to, uint to_length,
+ bool stay_quiet)
{
uint errors;
size_t res;
DBUG_ENTER("filename_to_tablename");
DBUG_PRINT("enter", ("from '%s'", from));
- if (!strncmp(from, tmp_file_prefix, tmp_file_prefix_length))
+ res= strconvert(&my_charset_filename, from, FN_REFLEN,
+ system_charset_info, to, to_length, &errors);
+ if (errors) // Old 5.0 name
{
- /* Temporary table name. */
- res= (strnmov(to, from, to_length) - to);
- }
- else
- {
- res= strconvert(&my_charset_filename, from,
- system_charset_info, to, to_length, &errors);
- if (errors) // Old 5.0 name
- {
- res= (strxnmov(to, to_length, MYSQL50_TABLE_NAME_PREFIX, from, NullS) -
- to);
-#ifndef DBUG_OFF
- if (!stay_quiet) {
-#endif /* DBUG_OFF */
- sql_print_error("Invalid (old?) table or database name '%s'", from);
-#ifndef DBUG_OFF
- }
-#endif /* DBUG_OFF */
- /*
- TODO: add a stored procedure for fix table and database names,
- and mention its name in error log.
- */
- }
+ res= (strxnmov(to, to_length, MYSQL50_TABLE_NAME_PREFIX, from, NullS) -
+ to);
+ if (!stay_quiet)
+ sql_print_error("Invalid (old?) table or database name '%s'", from);
}
DBUG_PRINT("exit", ("to '%s'", to));
@@ -486,7 +469,7 @@ uint tablename_to_filename(const char *from, char *to, uint to_length)
}
DBUG_RETURN(length);
}
- length= strconvert(system_charset_info, from,
+ length= strconvert(system_charset_info, from, FN_REFLEN,
&my_charset_filename, to, to_length, &errors);
if (check_if_legal_tablename(to) &&
length + 4 < to_length)
@@ -542,7 +525,7 @@ uint build_table_filename(char *buff, size_t bufflen, const char *db,
db, table_name, ext, flags));
if (flags & FN_IS_TMP) // FN_FROM_IS_TMP | FN_TO_IS_TMP
- strnmov(tbbuff, table_name, sizeof(tbbuff));
+ strmake(tbbuff, table_name, sizeof(tbbuff)-1);
else
(void) tablename_to_filename(table_name, tbbuff, sizeof(tbbuff));
@@ -557,8 +540,11 @@ uint build_table_filename(char *buff, size_t bufflen, const char *db,
pos= strnmov(pos, FN_ROOTDIR, end - pos);
pos= strxnmov(pos, end - pos, dbbuff, FN_ROOTDIR, NullS);
#ifdef USE_SYMDIR
- unpack_dirname(buff, buff);
- pos= strend(buff);
+ if (!(flags & SKIP_SYMDIR_ACCESS))
+ {
+ unpack_dirname(buff, buff);
+ pos= strend(buff);
+ }
#endif
pos= strxnmov(pos, end - pos, tbbuff, ext, NullS);
@@ -567,22 +553,19 @@ uint build_table_filename(char *buff, size_t bufflen, const char *db,
}
-/*
- Creates path to a file: mysql_tmpdir/#sql1234_12_1.ext
-
- SYNOPSIS
- build_tmptable_filename()
- thd The thread handle.
- buff Where to write result in my_charset_filename.
- bufflen buff size
+/**
+ Create path to a temporary table mysql_tmpdir/#sql1234_12_1
+ (i.e. to its .FRM file but without an extension).
- NOTES
+ @param thd The thread handle.
+ @param buff Where to write result in my_charset_filename.
+ @param bufflen buff size
+ @note
Uses current_pid, thread_id, and tmp_table counter to create
a file name in mysql_tmpdir.
- RETURN
- path length
+ @return Path length.
*/
uint build_tmptable_filename(THD* thd, char *buff, size_t bufflen)
@@ -590,9 +573,9 @@ uint build_tmptable_filename(THD* thd, char *buff, size_t bufflen)
DBUG_ENTER("build_tmptable_filename");
char *p= strnmov(buff, mysql_tmpdir, bufflen);
- my_snprintf(p, bufflen - (p - buff), "/%s%lx_%lx_%x%s",
+ my_snprintf(p, bufflen - (p - buff), "/%s%lx_%lx_%x",
tmp_file_prefix, current_pid,
- thd->thread_id, thd->tmp_table++, reg_ext);
+ thd->thread_id, thd->tmp_table++);
if (lower_case_table_names)
{
@@ -631,9 +614,15 @@ uint build_tmptable_filename(THD* thd, char *buff, size_t bufflen)
--------------------------------------------------------------------------
*/
-
struct st_global_ddl_log
{
+ /*
+ We need to adjust buffer size to be able to handle downgrades/upgrades
+ where IO_SIZE has changed. We'll set the buffer size such that we can
+ handle that the buffer size was upto 4 times bigger in the version
+ that wrote the DDL log.
+ */
+ char file_entry_buf[4*IO_SIZE];
char file_name_str[FN_REFLEN];
char *file_name;
DDL_LOG_MEMORY_ENTRY *first_free;
@@ -661,31 +650,28 @@ mysql_mutex_t LOCK_gdl;
#define DDL_LOG_NUM_ENTRY_POS 0
#define DDL_LOG_NAME_LEN_POS 4
#define DDL_LOG_IO_SIZE_POS 8
-#define DDL_LOG_HEADER_SIZE 12
/**
Read one entry from ddl log file.
- @param[out] file_entry_buf Buffer to read into
- @param entry_no Entry number to read
- @param size Number of bytes of the entry to read
+
+ @param entry_no Entry number to read
@return Operation status
@retval true Error
@retval false Success
*/
-static bool read_ddl_log_file_entry(uchar *file_entry_buf,
- uint entry_no,
- uint size)
+static bool read_ddl_log_file_entry(uint entry_no)
{
bool error= FALSE;
File file_id= global_ddl_log.file_id;
+ uchar *file_entry_buf= (uchar*)global_ddl_log.file_entry_buf;
uint io_size= global_ddl_log.io_size;
DBUG_ENTER("read_ddl_log_file_entry");
- DBUG_ASSERT(io_size >= size);
- if (mysql_file_pread(file_id, file_entry_buf, size, io_size * entry_no,
- MYF(MY_WME)) != size)
+ mysql_mutex_assert_owner(&LOCK_gdl);
+ if (mysql_file_pread(file_id, file_entry_buf, io_size, io_size * entry_no,
+ MYF(MY_WME)) != io_size)
error= TRUE;
DBUG_RETURN(error);
}
@@ -694,75 +680,77 @@ static bool read_ddl_log_file_entry(uchar *file_entry_buf,
/**
Write one entry to ddl log file.
- @param file_entry_buf Buffer to write
- @param entry_no Entry number to write
- @param size Number of bytes of the entry to write
+ @param entry_no Entry number to write
@return Operation status
@retval true Error
@retval false Success
*/
-static bool write_ddl_log_file_entry(uchar *file_entry_buf,
- uint entry_no,
- uint size)
+static bool write_ddl_log_file_entry(uint entry_no)
{
bool error= FALSE;
File file_id= global_ddl_log.file_id;
- uint io_size= global_ddl_log.io_size;
+ uchar *file_entry_buf= (uchar*)global_ddl_log.file_entry_buf;
DBUG_ENTER("write_ddl_log_file_entry");
- DBUG_ASSERT(io_size >= size);
- if (mysql_file_pwrite(file_id, file_entry_buf, size,
- io_size * entry_no, MYF(MY_WME)) != size)
+ mysql_mutex_assert_owner(&LOCK_gdl);
+ if (mysql_file_pwrite(file_id, file_entry_buf,
+ IO_SIZE, IO_SIZE * entry_no, MYF(MY_WME)) != IO_SIZE)
error= TRUE;
DBUG_RETURN(error);
}
-/*
- Write ddl log header
- SYNOPSIS
- write_ddl_log_header()
- RETURN VALUES
- TRUE Error
- FALSE Success
+/**
+ Sync the ddl log file.
+
+ @return Operation status
+ @retval FALSE Success
+ @retval TRUE Error
+*/
+
+
+static bool sync_ddl_log_file()
+{
+ DBUG_ENTER("sync_ddl_log_file");
+ DBUG_RETURN(mysql_file_sync(global_ddl_log.file_id, MYF(MY_WME)));
+}
+
+
+/**
+ Write ddl log header.
+
+ @return Operation status
+ @retval TRUE Error
+ @retval FALSE Success
*/
static bool write_ddl_log_header()
{
uint16 const_var;
- bool error= FALSE;
- uchar file_entry_buf[DDL_LOG_HEADER_SIZE];
DBUG_ENTER("write_ddl_log_header");
- DBUG_ASSERT((DDL_LOG_NAME_POS + 3 * global_ddl_log.name_len)
- <= global_ddl_log.io_size);
- int4store(&file_entry_buf[DDL_LOG_NUM_ENTRY_POS],
+ int4store(&global_ddl_log.file_entry_buf[DDL_LOG_NUM_ENTRY_POS],
global_ddl_log.num_entries);
- const_var= global_ddl_log.name_len;
- int4store(&file_entry_buf[DDL_LOG_NAME_LEN_POS],
+ const_var= FN_REFLEN;
+ int4store(&global_ddl_log.file_entry_buf[DDL_LOG_NAME_LEN_POS],
(ulong) const_var);
- const_var= global_ddl_log.io_size;
- int4store(&file_entry_buf[DDL_LOG_IO_SIZE_POS],
+ const_var= IO_SIZE;
+ int4store(&global_ddl_log.file_entry_buf[DDL_LOG_IO_SIZE_POS],
(ulong) const_var);
- if (write_ddl_log_file_entry(file_entry_buf, 0UL, DDL_LOG_HEADER_SIZE))
+ if (write_ddl_log_file_entry(0UL))
{
sql_print_error("Error writing ddl log header");
DBUG_RETURN(TRUE);
}
- (void) sync_ddl_log();
- DBUG_RETURN(error);
+ DBUG_RETURN(sync_ddl_log_file());
}
-/*
- Create ddl log file name
- SYNOPSIS
- create_ddl_log_file_name()
- file_name Filename setup
- RETURN VALUES
- NONE
+/**
+ Create ddl log file name.
+ @param file_name Filename setup
*/
static inline void create_ddl_log_file_name(char *file_name)
@@ -771,35 +759,32 @@ static inline void create_ddl_log_file_name(char *file_name)
}
-/*
- Read header of ddl log file
- SYNOPSIS
- read_ddl_log_header()
- RETURN VALUES
- > 0 Last entry in ddl log
- 0 No entries in ddl log
- DESCRIPTION
- When we read the ddl log header we get information about maximum sizes
- of names in the ddl log and we also get information about the number
- of entries in the ddl log.
+/**
+ Read header of ddl log file.
+
+ When we read the ddl log header we get information about maximum sizes
+ of names in the ddl log and we also get information about the number
+ of entries in the ddl log.
+
+ @return Last entry in ddl log (0 if no entries)
*/
static uint read_ddl_log_header()
{
- char file_entry_buf[DDL_LOG_HEADER_SIZE];
+ uchar *file_entry_buf= (uchar*)global_ddl_log.file_entry_buf;
char file_name[FN_REFLEN];
uint entry_no;
bool successful_open= FALSE;
DBUG_ENTER("read_ddl_log_header");
- DBUG_ASSERT(global_ddl_log.io_size <= IO_SIZE);
+ mysql_mutex_init(key_LOCK_gdl, &LOCK_gdl, MY_MUTEX_INIT_SLOW);
+ mysql_mutex_lock(&LOCK_gdl);
create_ddl_log_file_name(file_name);
if ((global_ddl_log.file_id= mysql_file_open(key_file_global_ddl_log,
file_name,
O_RDWR | O_BINARY, MYF(0))) >= 0)
{
- if (read_ddl_log_file_entry((uchar *) file_entry_buf, 0UL,
- DDL_LOG_HEADER_SIZE))
+ if (read_ddl_log_file_entry(0UL))
{
/* Write message into error log */
sql_print_error("Failed to read ddl log file in recovery");
@@ -812,6 +797,8 @@ static uint read_ddl_log_header()
entry_no= uint4korr(&file_entry_buf[DDL_LOG_NUM_ENTRY_POS]);
global_ddl_log.name_len= uint4korr(&file_entry_buf[DDL_LOG_NAME_LEN_POS]);
global_ddl_log.io_size= uint4korr(&file_entry_buf[DDL_LOG_IO_SIZE_POS]);
+ DBUG_ASSERT(global_ddl_log.io_size <=
+ sizeof(global_ddl_log.file_entry_buf));
}
else
{
@@ -820,28 +807,72 @@ static uint read_ddl_log_header()
global_ddl_log.first_free= NULL;
global_ddl_log.first_used= NULL;
global_ddl_log.num_entries= 0;
- mysql_mutex_init(key_LOCK_gdl, &LOCK_gdl, MY_MUTEX_INIT_FAST);
global_ddl_log.do_release= true;
+ mysql_mutex_unlock(&LOCK_gdl);
DBUG_RETURN(entry_no);
}
/**
- Set ddl log entry struct from buffer
- @param read_entry Entry number
- @param file_entry_buf Buffer to use
- @param ddl_log_entry Entry to be set
+ Convert from ddl_log_entry struct to file_entry_buf binary blob.
- @note Pointers in ddl_log_entry will point into file_entry_buf!
+ @param ddl_log_entry filled in ddl_log_entry struct.
*/
-static void set_ddl_log_entry_from_buf(uint read_entry,
- uchar *file_entry_buf,
- DDL_LOG_ENTRY *ddl_log_entry)
+static void set_global_from_ddl_log_entry(const DDL_LOG_ENTRY *ddl_log_entry)
{
+ mysql_mutex_assert_owner(&LOCK_gdl);
+ global_ddl_log.file_entry_buf[DDL_LOG_ENTRY_TYPE_POS]=
+ (char)DDL_LOG_ENTRY_CODE;
+ global_ddl_log.file_entry_buf[DDL_LOG_ACTION_TYPE_POS]=
+ (char)ddl_log_entry->action_type;
+ global_ddl_log.file_entry_buf[DDL_LOG_PHASE_POS]= 0;
+ int4store(&global_ddl_log.file_entry_buf[DDL_LOG_NEXT_ENTRY_POS],
+ ddl_log_entry->next_entry);
+ DBUG_ASSERT(strlen(ddl_log_entry->name) < FN_REFLEN);
+ strmake(&global_ddl_log.file_entry_buf[DDL_LOG_NAME_POS],
+ ddl_log_entry->name, FN_REFLEN - 1);
+ if (ddl_log_entry->action_type == DDL_LOG_RENAME_ACTION ||
+ ddl_log_entry->action_type == DDL_LOG_REPLACE_ACTION ||
+ ddl_log_entry->action_type == DDL_LOG_EXCHANGE_ACTION)
+ {
+ DBUG_ASSERT(strlen(ddl_log_entry->from_name) < FN_REFLEN);
+ strmake(&global_ddl_log.file_entry_buf[DDL_LOG_NAME_POS + FN_REFLEN],
+ ddl_log_entry->from_name, FN_REFLEN - 1);
+ }
+ else
+ global_ddl_log.file_entry_buf[DDL_LOG_NAME_POS + FN_REFLEN]= 0;
+ DBUG_ASSERT(strlen(ddl_log_entry->handler_name) < FN_REFLEN);
+ strmake(&global_ddl_log.file_entry_buf[DDL_LOG_NAME_POS + (2*FN_REFLEN)],
+ ddl_log_entry->handler_name, FN_REFLEN - 1);
+ if (ddl_log_entry->action_type == DDL_LOG_EXCHANGE_ACTION)
+ {
+ DBUG_ASSERT(strlen(ddl_log_entry->tmp_name) < FN_REFLEN);
+ strmake(&global_ddl_log.file_entry_buf[DDL_LOG_NAME_POS + (3*FN_REFLEN)],
+ ddl_log_entry->tmp_name, FN_REFLEN - 1);
+ }
+ else
+ global_ddl_log.file_entry_buf[DDL_LOG_NAME_POS + (3*FN_REFLEN)]= 0;
+}
+
+
+/**
+ Convert from file_entry_buf binary blob to ddl_log_entry struct.
+
+ @param[out] ddl_log_entry struct to fill in.
+
+ @note Strings (names) are pointing to the global_ddl_log structure,
+ so LOCK_gdl needs to be hold until they are read or copied.
+*/
+
+static void set_ddl_log_entry_from_global(DDL_LOG_ENTRY *ddl_log_entry,
+ const uint read_entry)
+{
+ char *file_entry_buf= (char*) global_ddl_log.file_entry_buf;
uint inx;
uchar single_char;
- DBUG_ENTER("set_ddl_log_entry_from_buf");
+
+ mysql_mutex_assert_owner(&LOCK_gdl);
ddl_log_entry->entry_pos= read_entry;
single_char= file_entry_buf[DDL_LOG_ENTRY_TYPE_POS];
ddl_log_entry->entry_type= (enum ddl_log_entry_code)single_char;
@@ -849,27 +880,56 @@ static void set_ddl_log_entry_from_buf(uint read_entry,
ddl_log_entry->action_type= (enum ddl_log_action_code)single_char;
ddl_log_entry->phase= file_entry_buf[DDL_LOG_PHASE_POS];
ddl_log_entry->next_entry= uint4korr(&file_entry_buf[DDL_LOG_NEXT_ENTRY_POS]);
- ddl_log_entry->name= (char*) &file_entry_buf[DDL_LOG_NAME_POS];
+ ddl_log_entry->name= &file_entry_buf[DDL_LOG_NAME_POS];
inx= DDL_LOG_NAME_POS + global_ddl_log.name_len;
- ddl_log_entry->from_name= (char*) &file_entry_buf[inx];
+ ddl_log_entry->from_name= &file_entry_buf[inx];
inx+= global_ddl_log.name_len;
- ddl_log_entry->handler_name= (char*) &file_entry_buf[inx];
- DBUG_VOID_RETURN;
+ ddl_log_entry->handler_name= &file_entry_buf[inx];
+ if (ddl_log_entry->action_type == DDL_LOG_EXCHANGE_ACTION)
+ {
+ inx+= global_ddl_log.name_len;
+ ddl_log_entry->tmp_name= &file_entry_buf[inx];
+ }
+ else
+ ddl_log_entry->tmp_name= NULL;
}
-
-/*
- Initialise ddl log
- SYNOPSIS
- init_ddl_log()
- DESCRIPTION
- Write the header of the ddl log file and length of names. Also set
- number of entries to zero.
+/**
+ Read a ddl log entry.
- RETURN VALUES
- TRUE Error
- FALSE Success
+ Read a specified entry in the ddl log.
+
+ @param read_entry Number of entry to read
+ @param[out] entry_info Information from entry
+
+ @return Operation status
+ @retval TRUE Error
+ @retval FALSE Success
+*/
+
+static bool read_ddl_log_entry(uint read_entry, DDL_LOG_ENTRY *ddl_log_entry)
+{
+ DBUG_ENTER("read_ddl_log_entry");
+
+ if (read_ddl_log_file_entry(read_entry))
+ {
+ DBUG_RETURN(TRUE);
+ }
+ set_ddl_log_entry_from_global(ddl_log_entry, read_entry);
+ DBUG_RETURN(FALSE);
+}
+
+
+/**
+ Initialise ddl log.
+
+ Write the header of the ddl log file and length of names. Also set
+ number of entries to zero.
+
+ @return Operation status
+ @retval TRUE Error
+ @retval FALSE Success
*/
static bool init_ddl_log()
@@ -881,7 +941,7 @@ static bool init_ddl_log()
goto end;
global_ddl_log.io_size= IO_SIZE;
- global_ddl_log.name_len= FN_LEN;
+ global_ddl_log.name_len= FN_REFLEN;
create_ddl_log_file_name(file_name);
if ((global_ddl_log.file_id= mysql_file_create(key_file_global_ddl_log,
file_name, CREATE_MODE,
@@ -905,14 +965,116 @@ end:
}
-/*
+/**
+ Sync ddl log file.
+
+ @return Operation status
+ @retval TRUE Error
+ @retval FALSE Success
+*/
+
+static bool sync_ddl_log_no_lock()
+{
+ DBUG_ENTER("sync_ddl_log_no_lock");
+
+ mysql_mutex_assert_owner(&LOCK_gdl);
+ if ((!global_ddl_log.recovery_phase) &&
+ init_ddl_log())
+ {
+ DBUG_RETURN(TRUE);
+ }
+ DBUG_RETURN(sync_ddl_log_file());
+}
+
+
+/**
+ @brief Deactivate an individual entry.
+
+ @details For complex rename operations we need to deactivate individual
+ entries.
+
+ During replace operations where we start with an existing table called
+ t1 and a replacement table called t1#temp or something else and where
+ we want to delete t1 and rename t1#temp to t1 this is not possible to
+ do in a safe manner unless the ddl log is informed of the phases in
+ the change.
+
+ Delete actions are 1-phase actions that can be ignored immediately after
+ being executed.
+ Rename actions from x to y is also a 1-phase action since there is no
+ interaction with any other handlers named x and y.
+ Replace action where drop y and x -> y happens needs to be a two-phase
+ action. Thus the first phase will drop y and the second phase will
+ rename x -> y.
+
+ @param entry_no Entry position of record to change
+
+ @return Operation status
+ @retval TRUE Error
+ @retval FALSE Success
+*/
+
+static bool deactivate_ddl_log_entry_no_lock(uint entry_no)
+{
+ uchar *file_entry_buf= (uchar*)global_ddl_log.file_entry_buf;
+ DBUG_ENTER("deactivate_ddl_log_entry_no_lock");
+
+ mysql_mutex_assert_owner(&LOCK_gdl);
+ if (!read_ddl_log_file_entry(entry_no))
+ {
+ if (file_entry_buf[DDL_LOG_ENTRY_TYPE_POS] == DDL_LOG_ENTRY_CODE)
+ {
+ /*
+ Log entry, if complete mark it done (IGNORE).
+ Otherwise increase the phase by one.
+ */
+ if (file_entry_buf[DDL_LOG_ACTION_TYPE_POS] == DDL_LOG_DELETE_ACTION ||
+ file_entry_buf[DDL_LOG_ACTION_TYPE_POS] == DDL_LOG_RENAME_ACTION ||
+ (file_entry_buf[DDL_LOG_ACTION_TYPE_POS] == DDL_LOG_REPLACE_ACTION &&
+ file_entry_buf[DDL_LOG_PHASE_POS] == 1) ||
+ (file_entry_buf[DDL_LOG_ACTION_TYPE_POS] == DDL_LOG_EXCHANGE_ACTION &&
+ file_entry_buf[DDL_LOG_PHASE_POS] >= EXCH_PHASE_TEMP_TO_FROM))
+ file_entry_buf[DDL_LOG_ENTRY_TYPE_POS]= DDL_IGNORE_LOG_ENTRY_CODE;
+ else if (file_entry_buf[DDL_LOG_ACTION_TYPE_POS] == DDL_LOG_REPLACE_ACTION)
+ {
+ DBUG_ASSERT(file_entry_buf[DDL_LOG_PHASE_POS] == 0);
+ file_entry_buf[DDL_LOG_PHASE_POS]= 1;
+ }
+ else if (file_entry_buf[DDL_LOG_ACTION_TYPE_POS] == DDL_LOG_EXCHANGE_ACTION)
+ {
+ DBUG_ASSERT(file_entry_buf[DDL_LOG_PHASE_POS] <=
+ EXCH_PHASE_FROM_TO_NAME);
+ file_entry_buf[DDL_LOG_PHASE_POS]++;
+ }
+ else
+ {
+ DBUG_ASSERT(0);
+ }
+ if (write_ddl_log_file_entry(entry_no))
+ {
+ sql_print_error("Error in deactivating log entry. Position = %u",
+ entry_no);
+ DBUG_RETURN(TRUE);
+ }
+ }
+ }
+ else
+ {
+ sql_print_error("Failed in reading entry before deactivating it");
+ DBUG_RETURN(TRUE);
+ }
+ DBUG_RETURN(FALSE);
+}
+
+
+/**
Execute one action in a ddl log entry
- SYNOPSIS
- execute_ddl_log_action()
- ddl_log_entry Information in action entry to execute
- RETURN VALUES
- TRUE Error
- FALSE Success
+
+ @param ddl_log_entry Information in action entry to execute
+
+ @return Operation status
+ @retval TRUE Error
+ @retval FALSE Success
*/
static int execute_ddl_log_action(THD *thd, DDL_LOG_ENTRY *ddl_log_entry)
@@ -930,20 +1092,23 @@ static int execute_ddl_log_action(THD *thd, DDL_LOG_ENTRY *ddl_log_entry)
handlerton *hton;
DBUG_ENTER("execute_ddl_log_action");
+ mysql_mutex_assert_owner(&LOCK_gdl);
if (ddl_log_entry->entry_type == DDL_IGNORE_LOG_ENTRY_CODE)
{
DBUG_RETURN(FALSE);
}
DBUG_PRINT("ddl_log",
- ("execute type %c next %u name '%s' from_name '%s' handler '%s'",
+ ("execute type %c next %u name '%s' from_name '%s' handler '%s'"
+ " tmp_name '%s'",
ddl_log_entry->action_type,
ddl_log_entry->next_entry,
ddl_log_entry->name,
ddl_log_entry->from_name,
- ddl_log_entry->handler_name));
+ ddl_log_entry->handler_name,
+ ddl_log_entry->tmp_name));
handler_name.str= (char*)ddl_log_entry->handler_name;
handler_name.length= strlen(ddl_log_entry->handler_name);
- init_sql_alloc(&mem_root, TABLE_ALLOC_BLOCK_SIZE, 0);
+ init_sql_alloc(&mem_root, TABLE_ALLOC_BLOCK_SIZE, 0, MYF(MY_THREAD_SPECIFIC));
if (!strcmp(ddl_log_entry->handler_name, reg_ext))
frm_action= TRUE;
else
@@ -951,7 +1116,7 @@ static int execute_ddl_log_action(THD *thd, DDL_LOG_ENTRY *ddl_log_entry)
plugin_ref plugin= ha_resolve_by_name(thd, &handler_name);
if (!plugin)
{
- my_error(ER_ILLEGAL_HA, MYF(0), ddl_log_entry->handler_name);
+ my_error(ER_UNKNOWN_STORAGE_ENGINE, MYF(0), ddl_log_entry->handler_name);
goto error;
}
hton= plugin_data(plugin, handlerton*);
@@ -990,9 +1155,9 @@ static int execute_ddl_log_action(THD *thd, DDL_LOG_ENTRY *ddl_log_entry)
break;
}
}
- if ((deactivate_ddl_log_entry(ddl_log_entry->entry_pos)))
+ if ((deactivate_ddl_log_entry_no_lock(ddl_log_entry->entry_pos)))
break;
- (void) sync_ddl_log();
+ (void) sync_ddl_log_no_lock();
error= FALSE;
if (ddl_log_entry->action_type == DDL_LOG_DELETE_ACTION)
break;
@@ -1025,12 +1190,64 @@ static int execute_ddl_log_action(THD *thd, DDL_LOG_ENTRY *ddl_log_entry)
ddl_log_entry->name))
break;
}
- if ((deactivate_ddl_log_entry(ddl_log_entry->entry_pos)))
+ if ((deactivate_ddl_log_entry_no_lock(ddl_log_entry->entry_pos)))
break;
- (void) sync_ddl_log();
+ (void) sync_ddl_log_no_lock();
error= FALSE;
break;
}
+ case DDL_LOG_EXCHANGE_ACTION:
+ {
+ /* We hold LOCK_gdl, so we can alter global_ddl_log.file_entry_buf */
+ char *file_entry_buf= (char*)&global_ddl_log.file_entry_buf;
+ /* not yet implemented for frm */
+ DBUG_ASSERT(!frm_action);
+ /*
+ Using a case-switch here to revert all currently done phases,
+ since it will fall through until the first phase is undone.
+ */
+ switch (ddl_log_entry->phase) {
+ case EXCH_PHASE_TEMP_TO_FROM:
+ /* tmp_name -> from_name possibly done */
+ (void) file->ha_rename_table(ddl_log_entry->from_name,
+ ddl_log_entry->tmp_name);
+ /* decrease the phase and sync */
+ file_entry_buf[DDL_LOG_PHASE_POS]--;
+ if (write_ddl_log_file_entry(ddl_log_entry->entry_pos))
+ break;
+ if (sync_ddl_log_no_lock())
+ break;
+ /* fall through */
+ case EXCH_PHASE_FROM_TO_NAME:
+ /* from_name -> name possibly done */
+ (void) file->ha_rename_table(ddl_log_entry->name,
+ ddl_log_entry->from_name);
+ /* decrease the phase and sync */
+ file_entry_buf[DDL_LOG_PHASE_POS]--;
+ if (write_ddl_log_file_entry(ddl_log_entry->entry_pos))
+ break;
+ if (sync_ddl_log_no_lock())
+ break;
+ /* fall through */
+ case EXCH_PHASE_NAME_TO_TEMP:
+ /* name -> tmp_name possibly done */
+ (void) file->ha_rename_table(ddl_log_entry->tmp_name,
+ ddl_log_entry->name);
+ /* disable the entry and sync */
+ file_entry_buf[DDL_LOG_ENTRY_TYPE_POS]= DDL_IGNORE_LOG_ENTRY_CODE;
+ if (write_ddl_log_file_entry(ddl_log_entry->entry_pos))
+ break;
+ if (sync_ddl_log_no_lock())
+ break;
+ error= FALSE;
+ break;
+ default:
+ DBUG_ASSERT(0);
+ break;
+ }
+
+ break;
+ }
default:
DBUG_ASSERT(0);
break;
@@ -1042,14 +1259,14 @@ error:
}
-/*
+/**
Get a free entry in the ddl log
- SYNOPSIS
- get_free_ddl_log_entry()
- out:active_entry A ddl log memory entry returned
- RETURN VALUES
- TRUE Error
- FALSE Success
+
+ @param[out] active_entry A ddl log memory entry returned
+
+ @return Operation status
+ @retval TRUE Error
+ @retval FALSE Success
*/
static bool get_free_ddl_log_entry(DDL_LOG_MEMORY_ENTRY **active_entry,
@@ -1059,7 +1276,6 @@ static bool get_free_ddl_log_entry(DDL_LOG_MEMORY_ENTRY **active_entry,
DDL_LOG_MEMORY_ENTRY *first_used= global_ddl_log.first_used;
DBUG_ENTER("get_free_ddl_log_entry");
- mysql_mutex_assert_owner(&LOCK_gdl);
if (global_ddl_log.first_free == NULL)
{
if (!(used_entry= (DDL_LOG_MEMORY_ENTRY*)my_malloc(
@@ -1093,76 +1309,99 @@ static bool get_free_ddl_log_entry(DDL_LOG_MEMORY_ENTRY **active_entry,
}
+/**
+ Execute one entry in the ddl log.
+
+ Executing an entry means executing a linked list of actions.
+
+ @param first_entry Reference to first action in entry
+
+ @return Operation status
+ @retval TRUE Error
+ @retval FALSE Success
+*/
+
+static bool execute_ddl_log_entry_no_lock(THD *thd, uint first_entry)
+{
+ DDL_LOG_ENTRY ddl_log_entry;
+ uint read_entry= first_entry;
+ DBUG_ENTER("execute_ddl_log_entry_no_lock");
+
+ mysql_mutex_assert_owner(&LOCK_gdl);
+ do
+ {
+ if (read_ddl_log_entry(read_entry, &ddl_log_entry))
+ {
+ /* Write to error log and continue with next log entry */
+ sql_print_error("Failed to read entry = %u from ddl log",
+ read_entry);
+ break;
+ }
+ DBUG_ASSERT(ddl_log_entry.entry_type == DDL_LOG_ENTRY_CODE ||
+ ddl_log_entry.entry_type == DDL_IGNORE_LOG_ENTRY_CODE);
+
+ if (execute_ddl_log_action(thd, &ddl_log_entry))
+ {
+ /* Write to error log and continue with next log entry */
+ sql_print_error("Failed to execute action for entry = %u from ddl log",
+ read_entry);
+ break;
+ }
+ read_entry= ddl_log_entry.next_entry;
+ } while (read_entry);
+ DBUG_RETURN(FALSE);
+}
+
+
/*
External interface methods for the DDL log Module
---------------------------------------------------
*/
-/*
- SYNOPSIS
- write_ddl_log_entry()
- ddl_log_entry Information about log entry
- out:entry_written Entry information written into
+/**
+ Write a ddl log entry.
- RETURN VALUES
- TRUE Error
- FALSE Success
+ A careful write of the ddl log is performed to ensure that we can
+ handle crashes occurring during CREATE and ALTER TABLE processing.
- DESCRIPTION
- A careful write of the ddl log is performed to ensure that we can
- handle crashes occurring during CREATE and ALTER TABLE processing.
+ @param ddl_log_entry Information about log entry
+ @param[out] entry_written Entry information written into
+
+ @return Operation status
+ @retval TRUE Error
+ @retval FALSE Success
*/
bool write_ddl_log_entry(DDL_LOG_ENTRY *ddl_log_entry,
DDL_LOG_MEMORY_ENTRY **active_entry)
{
bool error, write_header;
- char file_entry_buf[IO_SIZE];
DBUG_ENTER("write_ddl_log_entry");
+ mysql_mutex_assert_owner(&LOCK_gdl);
if (init_ddl_log())
{
DBUG_RETURN(TRUE);
}
- memset(file_entry_buf, 0, sizeof(file_entry_buf));
- file_entry_buf[DDL_LOG_ENTRY_TYPE_POS]=
- (char)DDL_LOG_ENTRY_CODE;
- file_entry_buf[DDL_LOG_ACTION_TYPE_POS]=
- (char)ddl_log_entry->action_type;
- file_entry_buf[DDL_LOG_PHASE_POS]= 0;
- int4store(&file_entry_buf[DDL_LOG_NEXT_ENTRY_POS],
- ddl_log_entry->next_entry);
- DBUG_ASSERT(strlen(ddl_log_entry->name) < global_ddl_log.name_len);
- strmake(&file_entry_buf[DDL_LOG_NAME_POS], ddl_log_entry->name,
- global_ddl_log.name_len - 1);
- if (ddl_log_entry->action_type == DDL_LOG_RENAME_ACTION ||
- ddl_log_entry->action_type == DDL_LOG_REPLACE_ACTION)
- {
- DBUG_ASSERT(strlen(ddl_log_entry->from_name) < global_ddl_log.name_len);
- strmake(&file_entry_buf[DDL_LOG_NAME_POS + global_ddl_log.name_len],
- ddl_log_entry->from_name, global_ddl_log.name_len - 1);
- }
- else
- file_entry_buf[DDL_LOG_NAME_POS + global_ddl_log.name_len]= 0;
- DBUG_ASSERT(strlen(ddl_log_entry->handler_name) < global_ddl_log.name_len);
- strmake(&file_entry_buf[DDL_LOG_NAME_POS + (2*global_ddl_log.name_len)],
- ddl_log_entry->handler_name, global_ddl_log.name_len - 1);
+ set_global_from_ddl_log_entry(ddl_log_entry);
if (get_free_ddl_log_entry(active_entry, &write_header))
{
DBUG_RETURN(TRUE);
}
error= FALSE;
DBUG_PRINT("ddl_log",
- ("write type %c next %u name '%s' from_name '%s' handler '%s'",
- (char) file_entry_buf[DDL_LOG_ACTION_TYPE_POS],
+ ("write type %c next %u name '%s' from_name '%s' handler '%s'"
+ " tmp_name '%s'",
+ (char) global_ddl_log.file_entry_buf[DDL_LOG_ACTION_TYPE_POS],
ddl_log_entry->next_entry,
- (char*) &file_entry_buf[DDL_LOG_NAME_POS],
- (char*) &file_entry_buf[DDL_LOG_NAME_POS +
- global_ddl_log.name_len],
- (char*) &file_entry_buf[DDL_LOG_NAME_POS +
- (2*global_ddl_log.name_len)]));
- if (write_ddl_log_file_entry((uchar*) file_entry_buf,
- (*active_entry)->entry_pos, IO_SIZE))
+ (char*) &global_ddl_log.file_entry_buf[DDL_LOG_NAME_POS],
+ (char*) &global_ddl_log.file_entry_buf[DDL_LOG_NAME_POS
+ + FN_REFLEN],
+ (char*) &global_ddl_log.file_entry_buf[DDL_LOG_NAME_POS
+ + (2*FN_REFLEN)],
+ (char*) &global_ddl_log.file_entry_buf[DDL_LOG_NAME_POS
+ + (3*FN_REFLEN)]));
+ if (write_ddl_log_file_entry((*active_entry)->entry_pos))
{
error= TRUE;
sql_print_error("Failed to write entry_no = %u",
@@ -1170,7 +1409,7 @@ bool write_ddl_log_entry(DDL_LOG_ENTRY *ddl_log_entry,
}
if (write_header && !error)
{
- (void) sync_ddl_log();
+ (void) sync_ddl_log_no_lock();
if (write_ddl_log_header())
error= TRUE;
}
@@ -1180,31 +1419,30 @@ bool write_ddl_log_entry(DDL_LOG_ENTRY *ddl_log_entry,
}
-/*
- Write final entry in the ddl log
- SYNOPSIS
- write_execute_ddl_log_entry()
- first_entry First entry in linked list of entries
+/**
+ @brief Write final entry in the ddl log.
+
+ @details This is the last write in the ddl log. The previous log entries
+ have already been written but not yet synched to disk.
+ We write a couple of log entries that describes action to perform.
+ This entries are set-up in a linked list, however only when a first
+ execute entry is put as the first entry these will be executed.
+ This routine writes this first.
+
+ @param first_entry First entry in linked list of entries
to execute, if 0 = NULL it means that
the entry is removed and the entries
are put into the free list.
- complete Flag indicating we are simply writing
+ @param complete Flag indicating we are simply writing
info about that entry has been completed
- in:out:active_entry Entry to execute, 0 = NULL if the entry
+ @param[in,out] active_entry Entry to execute, 0 = NULL if the entry
is written first time and needs to be
returned. In this case the entry written
is returned in this parameter
- RETURN VALUES
- TRUE Error
- FALSE Success
- DESCRIPTION
- This is the last write in the ddl log. The previous log entries have
- already been written but not yet synched to disk.
- We write a couple of log entries that describes action to perform.
- This entries are set-up in a linked list, however only when a first
- execute entry is put as the first entry these will be executed.
- This routine writes this first
+ @return Operation status
+ @retval TRUE Error
+ @retval FALSE Success
*/
bool write_execute_ddl_log_entry(uint first_entry,
@@ -1212,14 +1450,14 @@ bool write_execute_ddl_log_entry(uint first_entry,
DDL_LOG_MEMORY_ENTRY **active_entry)
{
bool write_header= FALSE;
- char file_entry_buf[IO_SIZE];
+ char *file_entry_buf= (char*)global_ddl_log.file_entry_buf;
DBUG_ENTER("write_execute_ddl_log_entry");
+ mysql_mutex_assert_owner(&LOCK_gdl);
if (init_ddl_log())
{
DBUG_RETURN(TRUE);
}
- memset(file_entry_buf, 0, sizeof(file_entry_buf));
if (!complete)
{
/*
@@ -1228,28 +1466,32 @@ bool write_execute_ddl_log_entry(uint first_entry,
any log entries before, we are only here to write the execute
entry to indicate it is done.
*/
- (void) sync_ddl_log();
+ (void) sync_ddl_log_no_lock();
file_entry_buf[DDL_LOG_ENTRY_TYPE_POS]= (char)DDL_LOG_EXECUTE_CODE;
}
else
file_entry_buf[DDL_LOG_ENTRY_TYPE_POS]= (char)DDL_IGNORE_LOG_ENTRY_CODE;
+ file_entry_buf[DDL_LOG_ACTION_TYPE_POS]= 0; /* Ignored for execute entries */
+ file_entry_buf[DDL_LOG_PHASE_POS]= 0;
int4store(&file_entry_buf[DDL_LOG_NEXT_ENTRY_POS], first_entry);
+ file_entry_buf[DDL_LOG_NAME_POS]= 0;
+ file_entry_buf[DDL_LOG_NAME_POS + FN_REFLEN]= 0;
+ file_entry_buf[DDL_LOG_NAME_POS + 2*FN_REFLEN]= 0;
if (!(*active_entry))
{
if (get_free_ddl_log_entry(active_entry, &write_header))
{
DBUG_RETURN(TRUE);
}
+ write_header= TRUE;
}
- if (write_ddl_log_file_entry((uchar*) file_entry_buf,
- (*active_entry)->entry_pos,
- IO_SIZE))
+ if (write_ddl_log_file_entry((*active_entry)->entry_pos))
{
sql_print_error("Error writing execute entry in ddl log");
release_ddl_log_memory_entry(*active_entry);
DBUG_RETURN(TRUE);
}
- (void) sync_ddl_log();
+ (void) sync_ddl_log_no_lock();
if (write_header)
{
if (write_ddl_log_header())
@@ -1262,112 +1504,54 @@ bool write_execute_ddl_log_entry(uint first_entry,
}
-/*
- For complex rename operations we need to deactivate individual entries.
- SYNOPSIS
- deactivate_ddl_log_entry()
- entry_no Entry position of record to change
- RETURN VALUES
- TRUE Error
- FALSE Success
- DESCRIPTION
- During replace operations where we start with an existing table called
- t1 and a replacement table called t1#temp or something else and where
- we want to delete t1 and rename t1#temp to t1 this is not possible to
- do in a safe manner unless the ddl log is informed of the phases in
- the change.
-
- Delete actions are 1-phase actions that can be ignored immediately after
- being executed.
- Rename actions from x to y is also a 1-phase action since there is no
- interaction with any other handlers named x and y.
- Replace action where drop y and x -> y happens needs to be a two-phase
- action. Thus the first phase will drop y and the second phase will
- rename x -> y.
+/**
+ Deactivate an individual entry.
+
+ @details see deactivate_ddl_log_entry_no_lock.
+
+ @param entry_no Entry position of record to change
+
+ @return Operation status
+ @retval TRUE Error
+ @retval FALSE Success
*/
bool deactivate_ddl_log_entry(uint entry_no)
{
- uchar file_entry_buf[DDL_LOG_NAME_POS];
+ bool error;
DBUG_ENTER("deactivate_ddl_log_entry");
-
- /*
- Only need to read and write the first bytes of the entry, where
- ENTRY_TYPE, ACTION_TYPE and PHASE reside. Using DDL_LOG_NAME_POS
- to include all info except for the names.
- */
- if (!read_ddl_log_file_entry(file_entry_buf, entry_no, DDL_LOG_NAME_POS))
- {
- if (file_entry_buf[DDL_LOG_ENTRY_TYPE_POS] == DDL_LOG_ENTRY_CODE)
- {
- if (file_entry_buf[DDL_LOG_ACTION_TYPE_POS] == DDL_LOG_DELETE_ACTION ||
- file_entry_buf[DDL_LOG_ACTION_TYPE_POS] == DDL_LOG_RENAME_ACTION ||
- (file_entry_buf[DDL_LOG_ACTION_TYPE_POS] == DDL_LOG_REPLACE_ACTION &&
- file_entry_buf[DDL_LOG_PHASE_POS] == 1))
- file_entry_buf[DDL_LOG_ENTRY_TYPE_POS]= DDL_IGNORE_LOG_ENTRY_CODE;
- else if (file_entry_buf[DDL_LOG_ACTION_TYPE_POS] == DDL_LOG_REPLACE_ACTION)
- {
- DBUG_ASSERT(file_entry_buf[DDL_LOG_PHASE_POS] == 0);
- file_entry_buf[DDL_LOG_PHASE_POS]= 1;
- }
- else
- {
- DBUG_ASSERT(0);
- }
- if (write_ddl_log_file_entry(file_entry_buf, entry_no, DDL_LOG_NAME_POS))
- {
- sql_print_error("Error in deactivating log entry. Position = %u",
- entry_no);
- DBUG_RETURN(TRUE);
- }
- }
- }
- else
- {
- sql_print_error("Failed in reading entry before deactivating it");
- DBUG_RETURN(TRUE);
- }
- DBUG_RETURN(FALSE);
+ mysql_mutex_lock(&LOCK_gdl);
+ error= deactivate_ddl_log_entry_no_lock(entry_no);
+ mysql_mutex_unlock(&LOCK_gdl);
+ DBUG_RETURN(error);
}
-/*
- Sync ddl log file
- SYNOPSIS
- sync_ddl_log()
- RETURN VALUES
- TRUE Error
- FALSE Success
+/**
+ Sync ddl log file.
+
+ @return Operation status
+ @retval TRUE Error
+ @retval FALSE Success
*/
bool sync_ddl_log()
{
- bool error= FALSE;
+ bool error;
DBUG_ENTER("sync_ddl_log");
- if ((!global_ddl_log.recovery_phase) &&
- init_ddl_log())
- {
- DBUG_RETURN(TRUE);
- }
- if (mysql_file_sync(global_ddl_log.file_id, MYF(0)))
- {
- /* Write to error log */
- sql_print_error("Failed to sync ddl log");
- error= TRUE;
- }
+ mysql_mutex_lock(&LOCK_gdl);
+ error= sync_ddl_log_no_lock();
+ mysql_mutex_unlock(&LOCK_gdl);
+
DBUG_RETURN(error);
}
-/*
- Release a log memory entry
- SYNOPSIS
- release_ddl_log_memory_entry()
- log_memory_entry Log memory entry to release
- RETURN VALUES
- NONE
+/**
+ Release a log memory entry.
+ @param log_memory_entry Log memory entry to release
*/
void release_ddl_log_memory_entry(DDL_LOG_MEMORY_ENTRY *log_entry)
@@ -1376,8 +1560,8 @@ void release_ddl_log_memory_entry(DDL_LOG_MEMORY_ENTRY *log_entry)
DDL_LOG_MEMORY_ENTRY *next_log_entry= log_entry->next_log_entry;
DDL_LOG_MEMORY_ENTRY *prev_log_entry= log_entry->prev_log_entry;
DBUG_ENTER("release_ddl_log_memory_entry");
- mysql_mutex_assert_owner(&LOCK_gdl);
+ mysql_mutex_assert_owner(&LOCK_gdl);
global_ddl_log.first_free= log_entry;
log_entry->next_log_entry= first_free;
@@ -1391,58 +1575,32 @@ void release_ddl_log_memory_entry(DDL_LOG_MEMORY_ENTRY *log_entry)
}
-/*
- Execute one entry in the ddl log. Executing an entry means executing
- a linked list of actions.
- SYNOPSIS
- execute_ddl_log_entry()
- first_entry Reference to first action in entry
- RETURN VALUES
- TRUE Error
- FALSE Success
+/**
+ Execute one entry in the ddl log.
+
+ Executing an entry means executing a linked list of actions.
+
+ @param first_entry Reference to first action in entry
+
+ @return Operation status
+ @retval TRUE Error
+ @retval FALSE Success
*/
bool execute_ddl_log_entry(THD *thd, uint first_entry)
{
- DDL_LOG_ENTRY ddl_log_entry;
- uint read_entry= first_entry;
- uchar file_entry_buf[IO_SIZE];
+ bool error;
DBUG_ENTER("execute_ddl_log_entry");
mysql_mutex_lock(&LOCK_gdl);
- do
- {
- if (read_ddl_log_file_entry(file_entry_buf, read_entry, IO_SIZE))
- {
- /* Print the error to the log and continue with next log entry */
- sql_print_error("Failed to read entry = %u from ddl log",
- read_entry);
- break;
- }
- set_ddl_log_entry_from_buf(read_entry, file_entry_buf, &ddl_log_entry);
- DBUG_ASSERT(ddl_log_entry.entry_type == DDL_LOG_ENTRY_CODE ||
- ddl_log_entry.entry_type == DDL_IGNORE_LOG_ENTRY_CODE);
-
- if (execute_ddl_log_action(thd, &ddl_log_entry))
- {
- /* Print the error to the log and continue with next log entry */
- sql_print_error("Failed to execute action for entry = %u from ddl log",
- read_entry);
- break;
- }
- read_entry= ddl_log_entry.next_entry;
- } while (read_entry);
+ error= execute_ddl_log_entry_no_lock(thd, first_entry);
mysql_mutex_unlock(&LOCK_gdl);
- DBUG_RETURN(FALSE);
+ DBUG_RETURN(error);
}
-/*
- Close the ddl log
- SYNOPSIS
- close_ddl_log()
- RETURN VALUES
- NONE
+/**
+ Close the ddl log.
*/
static void close_ddl_log()
@@ -1457,12 +1615,8 @@ static void close_ddl_log()
}
-/*
- Execute the ddl log at recovery of MySQL Server
- SYNOPSIS
- execute_ddl_log_recovery()
- RETURN VALUES
- NONE
+/**
+ Execute the ddl log at recovery of MySQL Server.
*/
void execute_ddl_log_recovery()
@@ -1470,14 +1624,14 @@ void execute_ddl_log_recovery()
uint num_entries, i;
THD *thd;
DDL_LOG_ENTRY ddl_log_entry;
- uchar *file_entry_buf;
- uint io_size;
char file_name[FN_REFLEN];
+ static char recover_query_string[]= "INTERNAL DDL LOG RECOVER IN PROGRESS";
DBUG_ENTER("execute_ddl_log_recovery");
/*
Initialise global_ddl_log struct
*/
+ bzero(global_ddl_log.file_entry_buf, sizeof(global_ddl_log.file_entry_buf));
global_ddl_log.inited= FALSE;
global_ddl_log.recovery_phase= TRUE;
global_ddl_log.io_size= IO_SIZE;
@@ -1491,27 +1645,22 @@ void execute_ddl_log_recovery()
thd->thread_stack= (char*) &thd;
thd->store_globals();
+ thd->set_query(recover_query_string, strlen(recover_query_string));
+
+ /* this also initialize LOCK_gdl */
num_entries= read_ddl_log_header();
- io_size= global_ddl_log.io_size;
- file_entry_buf= (uchar*) my_malloc(io_size, MYF(0));
- if (!file_entry_buf)
- {
- sql_print_error("Failed to allocate buffer for recover ddl log");
- DBUG_VOID_RETURN;
- }
+ mysql_mutex_lock(&LOCK_gdl);
for (i= 1; i < num_entries + 1; i++)
{
- if (read_ddl_log_file_entry(file_entry_buf, i, io_size))
+ if (read_ddl_log_entry(i, &ddl_log_entry))
{
sql_print_error("Failed to read entry no = %u from ddl log",
i);
continue;
}
-
- set_ddl_log_entry_from_buf(i, file_entry_buf, &ddl_log_entry);
if (ddl_log_entry.entry_type == DDL_LOG_EXECUTE_CODE)
{
- if (execute_ddl_log_entry(thd, ddl_log_entry.next_entry))
+ if (execute_ddl_log_entry_no_lock(thd, ddl_log_entry.next_entry))
{
/* Real unpleasant scenario but we continue anyways. */
continue;
@@ -1522,20 +1671,15 @@ void execute_ddl_log_recovery()
create_ddl_log_file_name(file_name);
(void) mysql_file_delete(key_file_global_ddl_log, file_name, MYF(0));
global_ddl_log.recovery_phase= FALSE;
+ mysql_mutex_unlock(&LOCK_gdl);
+ thd->reset_query();
delete thd;
- my_free(file_entry_buf);
- /* Remember that we don't have a THD */
- my_pthread_setspecific_ptr(THR_THD, 0);
DBUG_VOID_RETURN;
}
-/*
- Release all memory allocated to the ddl log
- SYNOPSIS
- release_ddl_log()
- RETURN VALUES
- NONE
+/**
+ Release all memory allocated to the ddl log.
*/
void release_ddl_log()
@@ -1658,14 +1802,10 @@ bool mysql_write_frm(ALTER_PARTITION_PARAM_TYPE *lpt, uint flags)
strxmov(shadow_frm_name, shadow_path, reg_ext, NullS);
if (flags & WFRM_WRITE_SHADOW)
{
- if (mysql_prepare_create_table(lpt->thd, lpt->create_info,
- lpt->alter_info,
- /*tmp_table*/ 1,
- &lpt->db_options,
- lpt->table->file,
- &lpt->key_info_buffer,
- &lpt->key_count,
- /*select_field_count*/ 0))
+ if (mysql_prepare_create_table(lpt->thd, lpt->create_info, lpt->alter_info,
+ &lpt->db_options, lpt->table->file,
+ &lpt->key_info_buffer, &lpt->key_count,
+ C_ALTER_TABLE))
{
DBUG_RETURN(TRUE);
}
@@ -1690,13 +1830,23 @@ bool mysql_write_frm(ALTER_PARTITION_PARAM_TYPE *lpt, uint flags)
#endif
/* Write shadow frm file */
lpt->create_info->table_options= lpt->db_options;
- if ((mysql_create_frm(lpt->thd, shadow_frm_name, lpt->db,
- lpt->table_name, lpt->create_info,
- lpt->alter_info->create_list, lpt->key_count,
- lpt->key_info_buffer, lpt->table->file)) ||
- lpt->table->file->ha_create_handler_files(shadow_path, NULL,
- CHF_CREATE_FLAG,
- lpt->create_info))
+ LEX_CUSTRING frm= build_frm_image(lpt->thd, lpt->table_name,
+ lpt->create_info,
+ lpt->alter_info->create_list,
+ lpt->key_count, lpt->key_info_buffer,
+ lpt->table->file);
+ if (!frm.str)
+ {
+ error= 1;
+ goto end;
+ }
+
+ int error= writefrm(shadow_path, lpt->db, lpt->table_name,
+ lpt->create_info->tmp_table(), frm.str, frm.length);
+ my_free(const_cast<uchar*>(frm.str));
+
+ if (error || lpt->table->file->ha_create_partitioning_metadata(shadow_path,
+ NULL, CHF_CREATE_FLAG))
{
mysql_file_delete(key_file_frm, shadow_frm_name, MYF(0));
error= 1;
@@ -1711,12 +1861,12 @@ bool mysql_write_frm(ALTER_PARTITION_PARAM_TYPE *lpt, uint flags)
handlers that have the main version of the frm file stored in the
handler.
*/
- uchar *data;
+ const uchar *data;
size_t length;
if (readfrm(shadow_path, &data, &length) ||
packfrm(data, length, &lpt->pack_frm_data, &lpt->pack_frm_len))
{
- my_free(data);
+ my_free(const_cast<uchar*>(data));
my_free(lpt->pack_frm_data);
mem_alloc_error(length);
error= 1;
@@ -1734,7 +1884,7 @@ bool mysql_write_frm(ALTER_PARTITION_PARAM_TYPE *lpt, uint flags)
*/
build_table_filename(path, sizeof(path) - 1, lpt->db,
lpt->table_name, "", 0);
- strxmov(frm_name, path, reg_ext, NullS);
+ strxnmov(frm_name, sizeof(frm_name), path, reg_ext, NullS);
/*
When we are changing to use new frm file we need to ensure that we
don't collide with another thread in process to open the frm file.
@@ -1747,14 +1897,14 @@ bool mysql_write_frm(ALTER_PARTITION_PARAM_TYPE *lpt, uint flags)
*/
if (mysql_file_delete(key_file_frm, frm_name, MYF(MY_WME)) ||
#ifdef WITH_PARTITION_STORAGE_ENGINE
- lpt->table->file->ha_create_handler_files(path, shadow_path,
- CHF_DELETE_FLAG, NULL) ||
+ lpt->table->file->ha_create_partitioning_metadata(path, shadow_path,
+ CHF_DELETE_FLAG) ||
deactivate_ddl_log_entry(part_info->frm_log_entry->entry_pos) ||
(sync_ddl_log(), FALSE) ||
mysql_file_rename(key_file_frm,
shadow_frm_name, frm_name, MYF(MY_WME)) ||
- lpt->table->file->ha_create_handler_files(path, shadow_path,
- CHF_RENAME_FLAG, NULL))
+ lpt->table->file->ha_create_partitioning_metadata(path, shadow_path,
+ CHF_RENAME_FLAG))
#else
mysql_file_rename(key_file_frm,
shadow_frm_name, frm_name, MYF(MY_WME)))
@@ -1881,38 +2031,40 @@ bool mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists,
bool error;
Drop_table_error_handler err_handler;
TABLE_LIST *table;
-
DBUG_ENTER("mysql_rm_table");
/* Disable drop of enabled log tables, must be done before name locking */
for (table= tables; table; table= table->next_local)
{
- if (check_if_log_table(table->db_length, table->db,
- table->table_name_length, table->table_name, true))
- {
- my_error(ER_BAD_LOG_STATEMENT, MYF(0), "DROP");
+ if (check_if_log_table(table, TRUE, "DROP"))
DBUG_RETURN(true);
- }
}
- mysql_ha_rm_tables(thd, tables);
-
if (!drop_temporary)
{
+ if (!in_bootstrap)
+ {
+ for (table= tables; table; table= table->next_local)
+ {
+ LEX_STRING db_name= { table->db, table->db_length };
+ LEX_STRING table_name= { table->table_name, table->table_name_length };
+ if (table->open_type == OT_BASE_ONLY ||
+ !find_temporary_table(thd, table))
+ (void) delete_statistics_for_table(thd, &db_name, &table_name);
+ }
+ }
+
if (!thd->locked_tables_mode)
{
- if (lock_table_names(thd, tables, NULL, thd->variables.lock_wait_timeout,
- MYSQL_OPEN_SKIP_TEMPORARY))
+ if (lock_table_names(thd, tables, NULL,
+ thd->variables.lock_wait_timeout, 0))
DBUG_RETURN(true);
- for (table= tables; table; table= table->next_local)
- tdc_remove_table(thd, TDC_RT_REMOVE_ALL, table->db, table->table_name,
- false);
}
else
{
for (table= tables; table; table= table->next_local)
- if (table->open_type != OT_BASE_ONLY &&
- find_temporary_table(thd, table))
+ {
+ if (is_temporary_table(table))
{
/*
A temporary table.
@@ -1944,13 +2096,14 @@ bool mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists,
DBUG_RETURN(true);
table->mdl_request.ticket= table->table->mdl_ticket;
}
+ }
}
}
/* mark for close and remove all cached entries */
thd->push_internal_handler(&err_handler);
error= mysql_rm_table_no_locks(thd, tables, if_exists, drop_temporary,
- false, false);
+ false, false, false);
thd->pop_internal_handler();
if (error)
@@ -2015,6 +2168,8 @@ static uint32 comment_length(THD *thd, uint32 comment_pos,
@param drop_view Allow to delete VIEW .frm
@param dont_log_query Don't write query to log files. This will also not
generate warnings if the handler files doesn't exists
+ @param dont_free_locks Don't do automatic UNLOCK TABLE if no more locked
+ tables
@retval 0 ok
@retval 1 Error
@@ -2023,6 +2178,9 @@ static uint32 comment_length(THD *thd, uint32 comment_pos,
@note This function assumes that metadata locks have already been taken.
It is also assumed that the tables have been removed from TDC.
+ @note This function assumes that temporary tables to be dropped have
+ been pre-opened using corresponding table list elements.
+
@todo When logging to the binary log, we should log
tmp_tables and transactional tables as separate statements if we
are in a transaction; This is needed to get these tables into the
@@ -2034,23 +2192,26 @@ static uint32 comment_length(THD *thd, uint32 comment_pos,
int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists,
bool drop_temporary, bool drop_view,
- bool dont_log_query)
+ bool dont_log_query,
+ bool dont_free_locks)
{
TABLE_LIST *table;
- char path[FN_REFLEN + 1], *alias= NULL;
- uint path_length= 0;
- String wrong_tables;
+ char path[FN_REFLEN + 1], wrong_tables_buff[160], *alias= NULL;
+ String wrong_tables(wrong_tables_buff, sizeof(wrong_tables_buff)-1,
+ system_charset_info);
+ uint path_length= 0, errors= 0;
int error= 0;
int non_temp_tables_count= 0;
- bool foreign_key_error=0;
bool non_tmp_error= 0;
bool trans_tmp_table_deleted= 0, non_trans_tmp_table_deleted= 0;
bool non_tmp_table_deleted= 0;
bool is_drop_tmp_if_exists_added= 0;
+ bool was_view= 0;
String built_query;
String built_trans_tmp_query, built_non_trans_tmp_query;
DBUG_ENTER("mysql_rm_table_no_locks");
+ wrong_tables.length(0);
/*
Prepares the drop statements that will be written into the binary
log as follows:
@@ -2124,11 +2285,11 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists,
for (table= tables; table; table= table->next_local)
{
- bool is_trans;
+ bool is_trans= 0;
+ bool table_creation_was_logged= 1;
char *db=table->db;
size_t db_length= table->db_length;
- handlerton *table_type;
- enum legacy_db_type frm_db_type= DB_TYPE_UNKNOWN;
+ handlerton *table_type= 0;
DBUG_PRINT("table", ("table_l: '%s'.'%s' table: 0x%lx s: 0x%lx",
table->db, table->table_name, (long) table->table,
@@ -2150,12 +2311,17 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists,
. 1 - a temporary table was not found.
. -1 - a temporary table is used by an outer statement.
*/
- if (table->open_type == OT_BASE_ONLY)
+ if (table->open_type == OT_BASE_ONLY || !is_temporary_table(table))
error= 1;
- else if ((error= drop_temporary_table(thd, table, &is_trans)) == -1)
+ else
{
- DBUG_ASSERT(thd->in_sub_stmt);
- goto err;
+ table_creation_was_logged= table->table->s->table_creation_was_logged;
+ if ((error= drop_temporary_table(thd, table->table, &is_trans)) == -1)
+ {
+ DBUG_ASSERT(thd->in_sub_stmt);
+ goto err;
+ }
+ table->table= 0;
}
if ((drop_temporary && if_exists) || !error)
@@ -2170,7 +2336,10 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists,
. "DROP" was executed but a temporary table was affected (.i.e
!error).
*/
- if (!dont_log_query)
+#ifndef DONT_LOG_DROP_OF_TEMPORARY_TABLES
+ table_creation_was_logged= 1;
+#endif
+ if (!dont_log_query && table_creation_was_logged)
{
/*
If there is an error, we don't know the type of the engine
@@ -2210,36 +2379,20 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists,
{
non_temp_tables_count++;
- if (thd->locked_tables_mode)
- {
- if (wait_while_table_is_used(thd, table->table, HA_EXTRA_NOT_USED,
- TDC_RT_REMOVE_NOT_OWN_AND_MARK_NOT_USABLE))
- {
- error= -1;
- goto err;
- }
- close_all_tables_for_name(thd, table->table->s,
- HA_EXTRA_PREPARE_FOR_DROP);
- table->table= 0;
- }
-
- /* Check that we have an exclusive lock on the table to be dropped. */
DBUG_ASSERT(thd->mdl_context.is_lock_owner(MDL_key::TABLE, table->db,
table->table_name,
- MDL_EXCLUSIVE));
+ MDL_SHARED));
alias= (lower_case_table_names == 2) ? table->alias : table->table_name;
/* remove .frm file and engine files */
path_length= build_table_filename(path, sizeof(path) - 1, db, alias,
- reg_ext,
- table->internal_tmp_table ?
- FN_IS_TMP : 0);
+ reg_ext, 0);
/*
This handles the case where a "DROP" was executed and a regular
table "may be" dropped as drop_temporary is FALSE and error is
TRUE. If the error was FALSE a temporary table was dropped and
- regardless of the status of drop_tempoary a "DROP TEMPORARY"
+ regardless of the status of drop_temporary a "DROP TEMPORARY"
must be used.
*/
if (!dont_log_query)
@@ -2266,26 +2419,29 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists,
}
}
DEBUG_SYNC(thd, "rm_table_no_locks_before_delete_table");
- DBUG_EXECUTE_IF("sleep_before_no_locks_delete_table",
- my_sleep(100000););
error= 0;
if (drop_temporary ||
- ((access(path, F_OK) &&
- ha_create_table_from_engine(thd, db, alias)) ||
- (!drop_view &&
- dd_frm_type(thd, path, &frm_db_type) != FRMTYPE_TABLE)))
+ (ha_table_exists(thd, db, alias, &table_type) == 0 && table_type == 0) ||
+ (!drop_view && (was_view= (table_type == view_pseudo_hton))))
{
/*
One of the following cases happened:
. "DROP TEMPORARY" but a temporary table was not found.
- . "DROP" but table was not found on disk and table can't be
- created from engine.
- . ./sql/datadict.cc +32 /Alfranio - TODO: We need to test this.
+ . "DROP" but table was not found
+ . "DROP TABLE" statement, but it's a view.
*/
if (if_exists)
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
- ER_BAD_TABLE_ERROR, ER(ER_BAD_TABLE_ERROR),
- table->table_name);
+ {
+ char buff[FN_REFLEN];
+ String tbl_name(buff, sizeof(buff), system_charset_info);
+ tbl_name.length(0);
+ tbl_name.append(db);
+ tbl_name.append('.');
+ tbl_name.append(table->table_name);
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
+ ER_BAD_TABLE_ERROR, ER(ER_BAD_TABLE_ERROR),
+ tbl_name.c_ptr_safe());
+ }
else
{
non_tmp_error = (drop_temporary ? non_tmp_error : TRUE);
@@ -2296,59 +2452,84 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists,
{
char *end;
/*
- Cannot use the db_type from the table, since that might have changed
- while waiting for the exclusive name lock.
+ It could happen that table's share in the table definition cache
+ is the only thing that keeps the engine plugin loaded
+ (if it is uninstalled and waits for the ref counter to drop to 0).
+
+ In this case, the tdc_remove_table() below will release and unload
+ the plugin. And ha_delete_table() will get a dangling pointer.
+
+ Let's lock the plugin till the end of the statement.
*/
- if (frm_db_type == DB_TYPE_UNKNOWN)
+ if (table_type && table_type != view_pseudo_hton)
+ ha_lock_engine(thd, table_type);
+
+ if (thd->locked_tables_mode)
{
- dd_frm_type(thd, path, &frm_db_type);
- DBUG_PRINT("info", ("frm_db_type %d from %s", frm_db_type, path));
+ if (wait_while_table_is_used(thd, table->table, HA_EXTRA_NOT_USED))
+ {
+ error= -1;
+ goto err;
+ }
+ /* the following internally does TDC_RT_REMOVE_ALL */
+ close_all_tables_for_name(thd, table->table->s,
+ HA_EXTRA_PREPARE_FOR_DROP, NULL);
+ table->table= 0;
}
- table_type= ha_resolve_by_legacy_type(thd, frm_db_type);
+ else
+ tdc_remove_table(thd, TDC_RT_REMOVE_ALL, table->db, table->table_name,
+ false);
+
+ /* Check that we have an exclusive lock on the table to be dropped. */
+ DBUG_ASSERT(thd->mdl_context.is_lock_owner(MDL_key::TABLE, table->db,
+ table->table_name,
+ MDL_EXCLUSIVE));
+
// Remove extension for delete
*(end= path + path_length - reg_ext_length)= '\0';
- DBUG_PRINT("info", ("deleting table of type %d",
- (table_type ? table_type->db_type : 0)));
+
error= ha_delete_table(thd, table_type, path, db, table->table_name,
!dont_log_query);
- /* No error if non existent table and 'IF EXIST' clause or view */
- if ((error == ENOENT || error == HA_ERR_NO_SUCH_TABLE) &&
- (if_exists || table_type == NULL))
- {
- error= 0;
- thd->clear_error();
- }
- if (error == HA_ERR_ROW_IS_REFERENCED)
- {
- /* the table is referenced by a foreign key constraint */
- foreign_key_error= 1;
- }
- if (!error || error == ENOENT || error == HA_ERR_NO_SUCH_TABLE)
+ if (!error)
{
- int new_error;
+ int frm_delete_error, trigger_drop_error= 0;
/* Delete the table definition file */
strmov(end,reg_ext);
- if (!(new_error= mysql_file_delete(key_file_frm, path, MYF(MY_WME))))
+ frm_delete_error= mysql_file_delete(key_file_frm, path, MYF(MY_WME));
+ if (frm_delete_error)
+ frm_delete_error= my_errno;
+ else
{
non_tmp_table_deleted= TRUE;
- new_error= Table_triggers_list::drop_all_triggers(thd, db,
- table->table_name);
+ trigger_drop_error=
+ Table_triggers_list::drop_all_triggers(thd, db, table->table_name);
}
- error|= new_error;
+
+ if (trigger_drop_error ||
+ (frm_delete_error && frm_delete_error != ENOENT))
+ error= 1;
+ else if (frm_delete_error && if_exists)
+ thd->clear_error();
}
- non_tmp_error= error ? TRUE : non_tmp_error;
+ non_tmp_error= error ? TRUE : non_tmp_error;
}
if (error)
{
if (wrong_tables.length())
wrong_tables.append(',');
- wrong_tables.append(String(table->table_name,system_charset_info));
+ wrong_tables.append(db);
+ wrong_tables.append('.');
+ wrong_tables.append(table->table_name);
+ errors++;
}
else
{
+ PSI_CALL_drop_table_share(false, table->db, table->db_length,
+ table->table_name, table->table_name_length);
mysql_audit_drop_table(thd, table);
}
+
DBUG_PRINT("table", ("table: 0x%lx s: 0x%lx", (long) table->table,
table->table ? (long) table->table->s : (long) -1));
@@ -2364,14 +2545,28 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists,
err:
if (wrong_tables.length())
{
- if (!foreign_key_error)
+ DBUG_ASSERT(errors);
+ if (errors == 1 && was_view)
+ my_printf_error(ER_IT_IS_A_VIEW, ER(ER_IT_IS_A_VIEW), MYF(0),
+ wrong_tables.c_ptr_safe());
+ else if (errors > 1 || !thd->is_error())
my_printf_error(ER_BAD_TABLE_ERROR, ER(ER_BAD_TABLE_ERROR), MYF(0),
wrong_tables.c_ptr_safe());
- else
- my_message(ER_ROW_IS_REFERENCED, ER(ER_ROW_IS_REFERENCED), MYF(0));
error= 1;
}
+ /*
+ We are always logging drop of temporary tables.
+ The reason is to handle the following case:
+ - Use statement based replication
+ - CREATE TEMPORARY TABLE foo (logged)
+ - set row based replication
+ - DROP TEMPORAY TABLE foo (needs to be logged)
+ This should be fixed so that we remember if creation of the
+ temporary table was logged and only log it if the creation was
+ logged.
+ */
+
if (non_trans_tmp_table_deleted ||
trans_tmp_table_deleted || non_tmp_table_deleted)
{
@@ -2407,8 +2602,8 @@ err:
/* Chop of the last comma */
built_query.chop();
built_query.append(" /* generated by server */");
- int error_code = (non_tmp_error ?
- (foreign_key_error ? ER_ROW_IS_REFERENCED : ER_BAD_TABLE_ERROR) : 0);
+ int error_code = non_tmp_error ? thd->get_stmt_da()->sql_errno()
+ : 0;
error |= thd->binlog_query(THD::STMT_QUERY_TYPE,
built_query.ptr(),
built_query.length(),
@@ -2430,7 +2625,8 @@ err:
*/
if (thd->locked_tables_mode)
{
- if (thd->lock && thd->lock->table_count == 0 && non_temp_tables_count > 0)
+ if (thd->lock && thd->lock->table_count == 0 &&
+ non_temp_tables_count > 0 && !dont_free_locks)
{
thd->locked_tables_list.unlock_locked_tables(thd);
goto end;
@@ -2459,23 +2655,61 @@ end:
DBUG_RETURN(error);
}
+/**
+ Log the drop of a table.
-/*
+ @param thd Thread handler
+ @param db_name Database name
+ @param table_name Table name
+ @param temporary_table 1 if table was a temporary table
+
+ This code is only used in the case of failed CREATE OR REPLACE TABLE
+ when the original table was dropped but we could not create the new one.
+*/
+
+bool log_drop_table(THD *thd, const char *db_name, size_t db_name_length,
+ const char *table_name, size_t table_name_length,
+ bool temporary_table)
+{
+ char buff[NAME_LEN*2 + 80];
+ String query(buff, sizeof(buff), system_charset_info);
+ bool error;
+ DBUG_ENTER("log_drop_table");
+
+ if (!mysql_bin_log.is_open())
+ DBUG_RETURN(0);
+
+ query.length(0);
+ query.append(STRING_WITH_LEN("DROP "));
+ if (temporary_table)
+ query.append(STRING_WITH_LEN("TEMPORARY "));
+ query.append(STRING_WITH_LEN("TABLE IF EXISTS "));
+ append_identifier(thd, &query, db_name, db_name_length);
+ query.append(".");
+ append_identifier(thd, &query, table_name, table_name_length);
+ query.append(STRING_WITH_LEN("/* Generated to handle "
+ "failed CREATE OR REPLACE */"));
+ error= thd->binlog_query(THD::STMT_QUERY_TYPE,
+ query.ptr(), query.length(),
+ FALSE, FALSE, temporary_table, 0);
+ DBUG_RETURN(error);
+}
+
+
+/**
Quickly remove a table.
- SYNOPSIS
- quick_rm_table()
- base The handlerton handle.
- db The database name.
- table_name The table name.
- flags flags for build_table_filename().
+ @param thd Thread context.
+ @param base The handlerton handle.
+ @param db The database name.
+ @param table_name The table name.
+ @param flags Flags for build_table_filename() as well as describing
+ if handler files / .FRM should be deleted as well.
- RETURN
- 0 OK
- != 0 Error
+ @return False in case of success, True otherwise.
*/
-bool quick_rm_table(handlerton *base,const char *db,
+bool quick_rm_table(THD *thd, handlerton *base, const char *db,
const char *table_name, uint flags)
{
char path[FN_REFLEN + 1];
@@ -2487,11 +2721,27 @@ bool quick_rm_table(handlerton *base,const char *db,
if (mysql_file_delete(key_file_frm, path, MYF(0)))
error= 1; /* purecov: inspected */
path[path_length - reg_ext_length]= '\0'; // Remove reg_ext
- if (!(flags & FRM_ONLY))
+ if (flags & NO_HA_TABLE)
+ {
+ handler *file= get_new_handler((TABLE_SHARE*) 0, thd->mem_root, base);
+ if (!file)
+ DBUG_RETURN(true);
+ (void) file->ha_create_partitioning_metadata(path, NULL, CHF_DELETE_FLAG);
+ delete file;
+ }
+ if (!(flags & (FRM_ONLY|NO_HA_TABLE)))
error|= ha_delete_table(current_thd, base, path, db, table_name, 0);
+
+ if (likely(error == 0))
+ {
+ PSI_CALL_drop_table_share(flags & FN_IS_TMP, db, strlen(db),
+ table_name, strlen(table_name));
+ }
+
DBUG_RETURN(error);
}
+
/*
Sort keys in the following order:
- PRIMARY KEY
@@ -2579,14 +2829,13 @@ bool check_duplicates_in_interval(const char *set_or_name,
{
THD *thd= current_thd;
ErrConvString err(*cur_value, *cur_length, cs);
- if ((current_thd->variables.sql_mode &
- (MODE_STRICT_TRANS_TABLES | MODE_STRICT_ALL_TABLES)))
+ if (current_thd->is_strict_mode())
{
my_error(ER_DUPLICATED_VALUE_IN_TYPE, MYF(0),
name, err.ptr(), set_or_name);
return 1;
}
- push_warning_printf(thd,MYSQL_ERROR::WARN_LEVEL_NOTE,
+ push_warning_printf(thd,Sql_condition::WARN_LEVEL_NOTE,
ER_DUPLICATED_VALUE_IN_TYPE,
ER(ER_DUPLICATED_VALUE_IN_TYPE),
name, err.ptr(), set_or_name);
@@ -2638,7 +2887,6 @@ void calculate_interval_lengths(CHARSET_INFO *cs, TYPELIB *interval,
prepare_create_field()
sql_field field to prepare for packing
blob_columns count for BLOBs
- timestamps count for timestamps
table_flags table flags
DESCRIPTION
@@ -2652,11 +2900,10 @@ void calculate_interval_lengths(CHARSET_INFO *cs, TYPELIB *interval,
int prepare_create_field(Create_field *sql_field,
uint *blob_columns,
- int *timestamps, int *timestamps_with_niladic,
longlong table_flags)
{
unsigned int dup_val_count;
- DBUG_ENTER("prepare_field");
+ DBUG_ENTER("prepare_create_field");
/*
This code came from mysql_prepare_create_table.
@@ -2756,6 +3003,8 @@ int prepare_create_field(Create_field *sql_field,
case MYSQL_TYPE_NEWDATE:
case MYSQL_TYPE_TIME:
case MYSQL_TYPE_DATETIME:
+ case MYSQL_TYPE_TIME2:
+ case MYSQL_TYPE_DATETIME2:
case MYSQL_TYPE_NULL:
sql_field->pack_flag=f_settype((uint) sql_field->sql_type);
break;
@@ -2774,21 +3023,7 @@ int prepare_create_field(Create_field *sql_field,
(sql_field->decimals << FIELDFLAG_DEC_SHIFT));
break;
case MYSQL_TYPE_TIMESTAMP:
- /* We should replace old TIMESTAMP fields with their newer analogs */
- if (sql_field->unireg_check == Field::TIMESTAMP_OLD_FIELD)
- {
- if (!*timestamps)
- {
- sql_field->unireg_check= Field::TIMESTAMP_DNUN_FIELD;
- (*timestamps_with_niladic)++;
- }
- else
- sql_field->unireg_check= Field::NONE;
- }
- else if (sql_field->unireg_check != Field::NONE)
- (*timestamps_with_niladic)++;
-
- (*timestamps)++;
+ case MYSQL_TYPE_TIMESTAMP2:
/* fall-through */
default:
sql_field->pack_flag=(FIELDFLAG_NUMBER |
@@ -2841,18 +3076,122 @@ CHARSET_INFO* get_sql_field_charset(Create_field *sql_field,
}
-bool check_duplicate_warning(THD *thd, char *msg, ulong length)
+/**
+ Modifies the first column definition whose SQL type is TIMESTAMP
+ by adding the features DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP.
+
+ @param column_definitions The list of column definitions, in the physical
+ order in which they appear in the table.
+ */
+void promote_first_timestamp_column(List<Create_field> *column_definitions)
{
- List_iterator_fast<MYSQL_ERROR> it(thd->warning_info->warn_list());
- MYSQL_ERROR *err;
- while ((err= it++))
+ List_iterator<Create_field> it(*column_definitions);
+ Create_field *column_definition;
+
+ while ((column_definition= it++) != NULL)
{
- if (strncmp(msg, err->get_message_text(), length) == 0)
+ if (is_timestamp_type(column_definition->sql_type) || // TIMESTAMP
+ column_definition->unireg_check == Field::TIMESTAMP_OLD_FIELD) // Legacy
{
- return true;
+ if ((column_definition->flags & NOT_NULL_FLAG) != 0 && // NOT NULL,
+ column_definition->def == NULL && // no constant default,
+ column_definition->unireg_check == Field::NONE) // no function default
+ {
+ DBUG_PRINT("info", ("First TIMESTAMP column '%s' was promoted to "
+ "DEFAULT CURRENT_TIMESTAMP ON UPDATE "
+ "CURRENT_TIMESTAMP",
+ column_definition->field_name
+ ));
+ column_definition->unireg_check= Field::TIMESTAMP_DNUN_FIELD;
+ }
+ return;
+ }
+ }
+}
+
+
+/**
+ Check if there is a duplicate key. Report a warning for every duplicate key.
+
+ @param thd Thread context.
+ @param key Key to be checked.
+ @param key_info Key meta-data info.
+ @param key_list List of existing keys.
+*/
+static void check_duplicate_key(THD *thd,
+ Key *key, KEY *key_info,
+ List<Key> *key_list)
+{
+ /*
+ We only check for duplicate indexes if it is requested and the
+ key is not auto-generated.
+
+ Check is requested if the key was explicitly created or altered
+ by the user (unless it's a foreign key).
+ */
+ if (!key->key_create_info.check_for_duplicate_indexes || key->generated)
+ return;
+
+ List_iterator<Key> key_list_iterator(*key_list);
+ List_iterator<Key_part_spec> key_column_iterator(key->columns);
+ Key *k;
+
+ while ((k= key_list_iterator++))
+ {
+ // Looking for a similar key...
+
+ if (k == key)
+ break;
+
+ if (k->generated ||
+ (key->type != k->type) ||
+ (key->key_create_info.algorithm != k->key_create_info.algorithm) ||
+ (key->columns.elements != k->columns.elements))
+ {
+ // Keys are different.
+ continue;
+ }
+
+ /*
+ Keys 'key' and 'k' might be identical.
+ Check that the keys have identical columns in the same order.
+ */
+
+ List_iterator<Key_part_spec> k_column_iterator(k->columns);
+
+ bool all_columns_are_identical= true;
+
+ key_column_iterator.rewind();
+
+ for (uint i= 0; i < key->columns.elements; ++i)
+ {
+ Key_part_spec *c1= key_column_iterator++;
+ Key_part_spec *c2= k_column_iterator++;
+
+ DBUG_ASSERT(c1 && c2);
+
+ if (my_strcasecmp(system_charset_info,
+ c1->field_name.str, c2->field_name.str) ||
+ (c1->length != c2->length))
+ {
+ all_columns_are_identical= false;
+ break;
+ }
+ }
+
+ // Report a warning if we have two identical keys.
+
+ DBUG_ASSERT(thd->lex->query_tables->alias);
+ if (all_columns_are_identical)
+ {
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
+ ER_DUP_INDEX, ER(ER_DUP_INDEX),
+ key_info->name,
+ thd->lex->query_tables->db,
+ thd->lex->query_tables->alias);
+ break;
}
}
- return false;
}
@@ -2864,12 +3203,12 @@ bool check_duplicate_warning(THD *thd, char *msg, ulong length)
thd Thread object.
create_info Create information (like MAX_ROWS).
alter_info List of columns and indexes to create
- tmp_table If a temporary table is to be created.
db_options INOUT Table options (like HA_OPTION_PACK_RECORD).
file The handler for the new table.
key_info_buffer OUT An array of KEY structs for the indexes.
key_count OUT The number of elements in the array.
- select_field_count The number of fields coming from a select table.
+ create_table_mode C_ORDINARY_CREATE, C_ALTER_TABLE,
+ C_CREATE_SELECT, C_ASSISTED_DISCOVERY
DESCRIPTION
Prepares the table and key structures for table creation.
@@ -2884,11 +3223,9 @@ bool check_duplicate_warning(THD *thd, char *msg, ulong length)
static int
mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
- Alter_info *alter_info,
- bool tmp_table,
- uint *db_options,
+ Alter_info *alter_info, uint *db_options,
handler *file, KEY **key_info_buffer,
- uint *key_count, int select_field_count)
+ uint *key_count, int create_table_mode)
{
const char *key_name;
Create_field *sql_field,*dup_field;
@@ -2896,12 +3233,13 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
ulong record_offset= 0;
KEY *key_info;
KEY_PART_INFO *key_part_info;
- int timestamps= 0, timestamps_with_niladic= 0;
int field_no,dup_no;
int select_field_pos,auto_increment=0;
List_iterator<Create_field> it(alter_info->create_list);
List_iterator<Create_field> it2(alter_info->create_list);
uint total_uneven_bit_length= 0;
+ int select_field_count= C_CREATE_SELECT(create_table_mode);
+ bool tmp_table= create_table_mode == C_ALTER_TABLE;
DBUG_ENTER("mysql_prepare_create_table");
select_field_pos= alter_info->create_list.elements - select_field_count;
@@ -2919,6 +3257,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
executing a prepared statement for the second time.
*/
sql_field->length= sql_field->char_length;
+ /* Set field charset. */
save_cs= sql_field->charset= get_sql_field_charset(sql_field,
create_info);
if ((sql_field->flags & BINCMP_FLAG) &&
@@ -3204,7 +3543,6 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
DBUG_ASSERT(sql_field->charset != 0);
if (prepare_create_field(sql_field, &blob_columns,
- &timestamps, &timestamps_with_niladic,
file->ha_table_flags()))
DBUG_RETURN(TRUE);
if (sql_field->sql_type == MYSQL_TYPE_VARCHAR)
@@ -3212,8 +3550,8 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
sql_field->offset= record_offset;
if (MTYP_TYPENR(sql_field->unireg_check) == Field::NEXT_NUMBER)
auto_increment++;
- if (parse_option_list(thd, &sql_field->option_struct,
- sql_field->option_list,
+ if (parse_option_list(thd, create_info->db_type, &sql_field->option_struct,
+ &sql_field->option_list,
create_info->db_type->field_options, FALSE,
thd->mem_root))
DBUG_RETURN(TRUE);
@@ -3235,12 +3573,6 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
record_offset+= sql_field->pack_length;
}
}
- if (timestamps_with_niladic > 1)
- {
- my_message(ER_TOO_MUCH_AUTO_TIMESTAMP_COLS,
- ER(ER_TOO_MUCH_AUTO_TIMESTAMP_COLS), MYF(0));
- DBUG_RETURN(TRUE);
- }
if (auto_increment > 1)
{
my_message(ER_WRONG_AUTO_KEY, ER(ER_WRONG_AUTO_KEY), MYF(0));
@@ -3249,15 +3581,13 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
if (auto_increment &&
(file->ha_table_flags() & HA_NO_AUTO_INCREMENT))
{
- my_message(ER_TABLE_CANT_HANDLE_AUTO_INCREMENT,
- ER(ER_TABLE_CANT_HANDLE_AUTO_INCREMENT), MYF(0));
+ my_error(ER_TABLE_CANT_HANDLE_AUTO_INCREMENT, MYF(0), file->table_type());
DBUG_RETURN(TRUE);
}
if (blob_columns && (file->ha_table_flags() & HA_NO_BLOBS))
{
- my_message(ER_TABLE_CANT_HANDLE_BLOB, ER(ER_TABLE_CANT_HANDLE_BLOB),
- MYF(0));
+ my_error(ER_TABLE_CANT_HANDLE_BLOB, MYF(0), file->table_type());
DBUG_RETURN(TRUE);
}
@@ -3418,13 +3748,13 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
if (key->generated)
key_info->flags|= HA_GENERATED_KEY;
- key_info->key_parts=(uint8) key->columns.elements;
+ key_info->user_defined_key_parts=(uint8) key->columns.elements;
key_info->key_part=key_part_info;
key_info->usable_key_parts= key_number;
key_info->algorithm= key->key_create_info.algorithm;
key_info->option_list= key->option_list;
- if (parse_option_list(thd, &key_info->option_struct,
- key_info->option_list,
+ if (parse_option_list(thd, create_info->db_type, &key_info->option_struct,
+ &key_info->option_list,
create_info->db_type->index_options, FALSE,
thd->mem_root))
DBUG_RETURN(TRUE);
@@ -3433,8 +3763,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
{
if (!(file->ha_table_flags() & HA_CAN_FULLTEXT))
{
- my_message(ER_TABLE_CANT_HANDLE_FT, ER(ER_TABLE_CANT_HANDLE_FT),
- MYF(0));
+ my_error(ER_TABLE_CANT_HANDLE_FT, MYF(0), file->table_type());
DBUG_RETURN(TRUE);
}
}
@@ -3451,11 +3780,10 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
{
if (!(file->ha_table_flags() & HA_CAN_RTREEKEYS))
{
- my_message(ER_TABLE_CANT_HANDLE_SPKEYS, ER(ER_TABLE_CANT_HANDLE_SPKEYS),
- MYF(0));
+ my_error(ER_TABLE_CANT_HANDLE_SPKEYS, MYF(0), file->table_type());
DBUG_RETURN(TRUE);
}
- if (key_info->key_parts != 1)
+ if (key_info->user_defined_key_parts != 1)
{
my_error(ER_WRONG_ARGUMENTS, MYF(0), "SPATIAL INDEX");
DBUG_RETURN(TRUE);
@@ -3464,7 +3792,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
else if (key_info->algorithm == HA_KEY_ALG_RTREE)
{
#ifdef HAVE_RTREE_KEYS
- if ((key_info->key_parts & 1) == 1)
+ if ((key_info->user_defined_key_parts & 1) == 1)
{
my_error(ER_WRONG_ARGUMENTS, MYF(0), "RTREE INDEX");
DBUG_RETURN(TRUE);
@@ -3541,7 +3869,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
with length (unlike blobs, where ft code takes data length from a
data prefix, ignoring column->length).
*/
- column->length=test(f_is_blob(sql_field->pack_flag));
+ column->length= MY_TEST(f_is_blob(sql_field->pack_flag));
}
else
{
@@ -3566,7 +3894,8 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
{
if (!(file->ha_table_flags() & HA_CAN_INDEX_BLOBS))
{
- my_error(ER_BLOB_USED_AS_KEY, MYF(0), column->field_name.str);
+ my_error(ER_BLOB_USED_AS_KEY, MYF(0), column->field_name.str,
+ file->table_type());
DBUG_RETURN(TRUE);
}
if (f_is_geom(sql_field->pack_flag) && sql_field->geom_type ==
@@ -3643,17 +3972,17 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
{
if (f_is_blob(sql_field->pack_flag))
{
- key_part_length= min(column->length,
+ key_part_length= MY_MIN(column->length,
blob_length_by_type(sql_field->sql_type)
* sql_field->charset->mbmaxlen);
if (key_part_length > max_key_length ||
key_part_length > file->max_key_part_length())
{
- key_part_length= min(max_key_length, file->max_key_part_length());
+ key_part_length= MY_MIN(max_key_length, file->max_key_part_length());
if (key->type == Key::MULTIPLE)
{
/* not a critical problem */
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_TOO_LONG_KEY, ER(ER_TOO_LONG_KEY),
key_part_length);
/* Align key length to multibyte char boundary */
@@ -3689,7 +4018,8 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
}
else if (key_part_length == 0 && (sql_field->flags & NOT_NULL_FLAG))
{
- my_error(ER_WRONG_KEY_COLUMN, MYF(0), column->field_name.str);
+ my_error(ER_WRONG_KEY_COLUMN, MYF(0), file->table_type(),
+ column->field_name.str);
DBUG_RETURN(TRUE);
}
if (key_part_length > file->max_key_part_length() &&
@@ -3699,7 +4029,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
if (key->type == Key::MULTIPLE)
{
/* not a critical problem */
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_TOO_LONG_KEY, ER(ER_TOO_LONG_KEY),
key_part_length);
/* Align key length to multibyte char boundary */
@@ -3772,31 +4102,10 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
DBUG_RETURN(TRUE);
}
- uint tmp_len= system_charset_info->cset->charpos(system_charset_info,
- key->key_create_info.comment.str,
- key->key_create_info.comment.str +
- key->key_create_info.comment.length,
- INDEX_COMMENT_MAXLEN);
-
- if (tmp_len < key->key_create_info.comment.length)
- {
- if ((thd->variables.sql_mode &
- (MODE_STRICT_TRANS_TABLES | MODE_STRICT_ALL_TABLES)))
- {
- my_error(ER_TOO_LONG_INDEX_COMMENT, MYF(0),
- key_info->name, static_cast<ulong>(INDEX_COMMENT_MAXLEN));
- DBUG_RETURN(-1);
- }
- char warn_buff[MYSQL_ERRMSG_SIZE];
- my_snprintf(warn_buff, sizeof(warn_buff), ER(ER_TOO_LONG_INDEX_COMMENT),
- key_info->name, static_cast<ulong>(INDEX_COMMENT_MAXLEN));
- /* do not push duplicate warnings */
- if (!check_duplicate_warning(thd, warn_buff, strlen(warn_buff)))
- push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
- ER_TOO_LONG_INDEX_COMMENT, warn_buff);
-
- key->key_create_info.comment.length= tmp_len;
- }
+ if (validate_comment_length(thd, &key->key_create_info.comment,
+ INDEX_COMMENT_MAXLEN, ER_TOO_LONG_INDEX_COMMENT,
+ key_info->name))
+ DBUG_RETURN(TRUE);
key_info->comment.length= key->key_create_info.comment.length;
if (key_info->comment.length > 0)
@@ -3805,8 +4114,12 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
key_info->comment.str= key->key_create_info.comment.str;
}
+ // Check if a duplicate index is defined.
+ check_duplicate_key(thd, key, key_info, &alter_info->key_list);
+
key_info++;
}
+
if (!unique_key && !primary_key &&
(file->ha_table_flags() & HA_REQUIRE_PRIMARY_KEY))
{
@@ -3831,7 +4144,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
if (thd->variables.sql_mode & MODE_NO_ZERO_DATE &&
!sql_field->def &&
- sql_field->sql_type == MYSQL_TYPE_TIMESTAMP &&
+ is_timestamp_type(sql_field->sql_type) &&
(sql_field->flags & NOT_NULL_FLAG) &&
(type == Field::NONE || type == Field::TIMESTAMP_UN_FIELD))
{
@@ -3854,8 +4167,20 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
}
}
- if (parse_option_list(thd, &create_info->option_struct,
- create_info->option_list,
+ /* Give warnings for not supported table options */
+#if defined(WITH_ARIA_STORAGE_ENGINE)
+ extern handlerton *maria_hton;
+ if (file->ht != maria_hton)
+#endif
+ if (create_info->transactional)
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
+ ER_ILLEGAL_HA_CREATE_OPTION,
+ ER(ER_ILLEGAL_HA_CREATE_OPTION),
+ file->engine_name()->str,
+ "TRANSACTIONAL=1");
+
+ if (parse_option_list(thd, file->partition_ht(), &create_info->option_struct,
+ &create_info->option_list,
file->partition_ht()->table_options, FALSE,
thd->mem_root))
DBUG_RETURN(TRUE);
@@ -3863,6 +4188,43 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
DBUG_RETURN(FALSE);
}
+/**
+ check comment length of table, column, index and partition
+
+ If comment lenght is more than the standard length
+ truncate it and store the comment lenght upto the standard
+ comment length size
+
+ @param thd Thread handle
+ @param[in,out] comment Comment
+ @param max_len Maximum allowed comment length
+ @param err_code Error message
+ @param name Name of commented object
+
+ @return Operation status
+ @retval true Error found
+ @retval false On Success
+*/
+bool validate_comment_length(THD *thd, LEX_STRING *comment, size_t max_len,
+ uint err_code, const char *name)
+{
+ DBUG_ENTER("validate_comment_length");
+ uint tmp_len= my_charpos(system_charset_info, comment->str,
+ comment->str + comment->length, max_len);
+ if (tmp_len < comment->length)
+ {
+ if (thd->is_strict_mode())
+ {
+ my_error(err_code, MYF(0), name, static_cast<ulong>(max_len));
+ DBUG_RETURN(true);
+ }
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, err_code,
+ ER(err_code), name, static_cast<ulong>(max_len));
+ comment->length= tmp_len;
+ }
+ DBUG_RETURN(false);
+}
+
/*
Set table default charset, if not set
@@ -3919,8 +4281,7 @@ static bool prepare_blob_field(THD *thd, Create_field *sql_field)
/* Convert long VARCHAR columns to TEXT or BLOB */
char warn_buff[MYSQL_ERRMSG_SIZE];
- if (sql_field->def || (thd->variables.sql_mode & (MODE_STRICT_TRANS_TABLES |
- MODE_STRICT_ALL_TABLES)))
+ if (sql_field->def || thd->is_strict_mode())
{
my_error(ER_TOO_BIG_FIELDLENGTH, MYF(0), sql_field->field_name,
static_cast<ulong>(MAX_FIELD_VARCHARLENGTH /
@@ -3932,7 +4293,7 @@ static bool prepare_blob_field(THD *thd, Create_field *sql_field)
my_snprintf(warn_buff, sizeof(warn_buff), ER(ER_AUTO_CONVERT), sql_field->field_name,
(sql_field->charset == &my_charset_bin) ? "VARBINARY" : "VARCHAR",
(sql_field->charset == &my_charset_bin) ? "BLOB" : "TEXT");
- push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, ER_AUTO_CONVERT,
+ push_warning(thd, Sql_condition::WARN_LEVEL_NOTE, ER_AUTO_CONVERT,
warn_buff);
}
@@ -4003,193 +4364,36 @@ void sp_prepare_create_field(THD *thd, Create_field *sql_field)
}
-#ifdef WITH_PARTITION_STORAGE_ENGINE
-/**
- Auxiliary function which allows to check if freshly created .FRM
- file for table can be opened.
-
- @retval FALSE - Success.
- @retval TRUE - Failure.
-*/
-
-static bool check_if_created_table_can_be_opened(THD *thd,
- const char *path,
- const char *db,
- const char *table_name,
- HA_CREATE_INFO *create_info,
- handler *file)
-{
- TABLE table;
- TABLE_SHARE share;
- bool result;
-
- /*
- It is impossible to open definition of partitioned table without .par file.
- */
- if (file->ha_create_handler_files(path, NULL, CHF_CREATE_FLAG, create_info))
- return TRUE;
-
- init_tmp_table_share(thd, &share, db, 0, table_name, path);
-
- result= (open_table_def(thd, &share, 0) ||
- open_table_from_share(thd, &share, "", 0, (uint) READ_ALL,
- 0, &table, TRUE));
- /*
- Assert that the change list is empty as no partition function currently
- needs to modify item tree. May need call THD::rollback_item_tree_changes
- later before calling closefrm if the change list is not empty.
- */
- DBUG_ASSERT(thd->change_list.is_empty());
- if (! result)
- (void) closefrm(&table, 0);
-
- free_table_share(&share);
- (void) file->ha_create_handler_files(path, NULL, CHF_DELETE_FLAG, create_info);
- return result;
-}
-#endif
-
-
-/**
- Check that there is no frm file for given table
-
- @param old_path path to the old frm file
- @param path path to the frm file in new encoding
- @param db database name
- @param table_name table name
- @param alias table name for error message (for new encoding)
- @param issue_error should we issue error messages
-
- @retval FALSE there is no frm file
- @retval TRUE there is frm file
-*/
-
-bool check_table_file_presence(char *old_path,
- char *path,
- const char *db,
- const char *table_name,
- const char *alias,
- bool issue_error)
-{
- if (!access(path,F_OK))
- {
- if (issue_error)
- my_error(ER_TABLE_EXISTS_ERROR,MYF(0),alias);
- return TRUE;
- }
- {
- /*
- Check if file of the table in 5.0 file name encoding exists.
-
- Except case when it is the same table.
- */
- char tbl50[FN_REFLEN];
-#ifdef _WIN32
- if (check_if_legal_tablename(table_name) != 0)
- {
- /*
- Check for reserved device names for which access() returns 0
- (CON, AUX etc).
- */
- return FALSE;
- }
-#endif
- strxmov(tbl50, mysql_data_home, "/", db, "/", table_name, NullS);
- fn_format(tbl50, tbl50, "", reg_ext, MY_UNPACK_FILENAME);
- if (!access(tbl50, F_OK) &&
- (old_path == NULL ||
- strcmp(old_path, tbl50) != 0))
- {
- if (issue_error)
- {
- strxmov(tbl50, MYSQL50_TABLE_NAME_PREFIX, table_name, NullS);
- my_error(ER_TABLE_EXISTS_ERROR, MYF(0), tbl50);
- }
- return TRUE;
- }
- }
- return FALSE;
-}
-
-
-/*
- Create a table
-
- SYNOPSIS
- mysql_create_table_no_lock()
- thd Thread object
- db Database
- table_name Table name
- create_info Create information (like MAX_ROWS)
- fields List of fields to create
- keys List of keys to create
- internal_tmp_table Set to 1 if this is an internal temporary table
- (From ALTER TABLE)
- select_field_count
- is_trans identifies the type of engine where the table
- was created: either trans or non-trans.
-
- DESCRIPTION
- If one creates a temporary table, this is automatically opened
-
- Note that this function assumes that caller already have taken
- exclusive metadata lock on table being created or used some other
- way to ensure that concurrent operations won't intervene.
- mysql_create_table() is a wrapper that can be used for this.
-
- no_log is needed for the case of CREATE ... SELECT,
- as the logging will be done later in sql_insert.cc
- select_field_count is also used for CREATE ... SELECT,
- and must be zero for standard create of table.
-
- RETURN VALUES
- FALSE OK
- TRUE error
-*/
-
-bool mysql_create_table_no_lock(THD *thd,
+handler *mysql_create_frm_image(THD *thd,
const char *db, const char *table_name,
HA_CREATE_INFO *create_info,
- Alter_info *alter_info,
- bool internal_tmp_table,
- uint select_field_count,
- bool *is_trans)
+ Alter_info *alter_info, int create_table_mode,
+ KEY **key_info,
+ uint *key_count,
+ LEX_CUSTRING *frm)
{
- char path[FN_REFLEN + 1];
- uint path_length;
- const char *alias;
- uint db_options, key_count;
- KEY *key_info_buffer;
- handler *file;
- bool error= TRUE;
- DBUG_ENTER("mysql_create_table_no_lock");
- DBUG_PRINT("enter", ("db: '%s' table: '%s' tmp: %d",
- db, table_name, internal_tmp_table));
-
+ uint db_options;
+ handler *file;
+ DBUG_ENTER("mysql_create_frm_image");
- /* Check for duplicate fields and check type of table to create */
if (!alter_info->create_list.elements)
{
- my_message(ER_TABLE_MUST_HAVE_COLUMNS, ER(ER_TABLE_MUST_HAVE_COLUMNS),
- MYF(0));
- DBUG_RETURN(TRUE);
+ my_error(ER_TABLE_MUST_HAVE_COLUMNS, MYF(0));
+ DBUG_RETURN(NULL);
}
- if (check_engine(thd, db, table_name, create_info))
- DBUG_RETURN(TRUE);
set_table_default_charset(thd, create_info, (char*) db);
db_options= create_info->table_options;
- if (!create_info->frm_only &&
- create_info->row_type != ROW_TYPE_FIXED &&
- create_info->row_type != ROW_TYPE_DEFAULT)
+ if (create_info->row_type == ROW_TYPE_DYNAMIC ||
+ create_info->row_type == ROW_TYPE_PAGE)
db_options|= HA_OPTION_PACK_RECORD;
- alias= table_case_name(create_info, table_name);
+
if (!(file= get_new_handler((TABLE_SHARE*) 0, thd->mem_root,
create_info->db_type)))
{
mem_alloc_error(sizeof(handler));
- DBUG_RETURN(TRUE);
+ DBUG_RETURN(NULL);
}
#ifdef WITH_PARTITION_STORAGE_ENGINE
partition_info *part_info= thd->work_part_info;
@@ -4206,7 +4410,7 @@ bool mysql_create_table_no_lock(THD *thd,
if (!part_info)
{
mem_alloc_error(sizeof(partition_info));
- DBUG_RETURN(TRUE);
+ goto err;
}
file->set_auto_partitions(part_info);
part_info->default_engine_type= create_info->db_type;
@@ -4221,29 +4425,54 @@ bool mysql_create_table_no_lock(THD *thd,
partitions also in the call to check_partition_info. We transport
this information in the default_db_type variable, it is either
DB_TYPE_DEFAULT or the engine set in the ALTER TABLE command.
-
- Check that we don't use foreign keys in the table since it won't
- work even with InnoDB beneath it.
*/
- List_iterator<Key> key_iterator(alter_info->key_list);
- Key *key;
handlerton *part_engine_type= create_info->db_type;
char *part_syntax_buf;
uint syntax_len;
handlerton *engine_type;
- if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
- {
- my_error(ER_PARTITION_NO_TEMPORARY, MYF(0));
- goto err;
- }
- while ((key= key_iterator++))
+ List_iterator<partition_element> part_it(part_info->partitions);
+ partition_element *part_elem;
+
+ while ((part_elem= part_it++))
{
- if (key->type == Key::FOREIGN_KEY &&
- !part_info->is_auto_partitioned)
+ if (part_elem->part_comment)
{
- my_error(ER_FOREIGN_KEY_ON_PARTITIONED, MYF(0));
- goto err;
+ LEX_STRING comment= {
+ part_elem->part_comment, strlen(part_elem->part_comment)
+ };
+ if (validate_comment_length(thd, &comment,
+ TABLE_PARTITION_COMMENT_MAXLEN,
+ ER_TOO_LONG_TABLE_PARTITION_COMMENT,
+ part_elem->partition_name))
+ DBUG_RETURN(NULL);
+ part_elem->part_comment[comment.length]= '\0';
}
+ if (part_elem->subpartitions.elements)
+ {
+ List_iterator<partition_element> sub_it(part_elem->subpartitions);
+ partition_element *subpart_elem;
+ while ((subpart_elem= sub_it++))
+ {
+ if (subpart_elem->part_comment)
+ {
+ LEX_STRING comment= {
+ subpart_elem->part_comment, strlen(subpart_elem->part_comment)
+ };
+ if (validate_comment_length(thd, &comment,
+ TABLE_PARTITION_COMMENT_MAXLEN,
+ ER_TOO_LONG_TABLE_PARTITION_COMMENT,
+ subpart_elem->partition_name))
+ DBUG_RETURN(NULL);
+ subpart_elem->part_comment[comment.length]= '\0';
+ }
+ }
+ }
+ }
+
+ if (create_info->tmp_table())
+ {
+ my_error(ER_PARTITION_NO_TEMPORARY, MYF(0));
+ goto err;
}
if ((part_engine_type == partition_hton) &&
part_info->default_engine_type)
@@ -4304,9 +4533,8 @@ bool mysql_create_table_no_lock(THD *thd,
delete file;
create_info->db_type= partition_hton;
if (!(file= get_ha_partition(part_info)))
- {
- DBUG_RETURN(TRUE);
- }
+ DBUG_RETURN(NULL);
+
/*
If we have default number of partitions or subpartitions we
might require to set-up the part_info object such that it
@@ -4348,193 +4576,291 @@ bool mysql_create_table_no_lock(THD *thd,
engine_type)))
{
mem_alloc_error(sizeof(handler));
- DBUG_RETURN(TRUE);
+ DBUG_RETURN(NULL);
+ }
+ }
+ }
+ /*
+ Unless table's storage engine supports partitioning natively
+ don't allow foreign keys on partitioned tables (they won't
+ work work even with InnoDB beneath of partitioning engine).
+ If storage engine handles partitioning natively (like NDB)
+ foreign keys support is possible, so we let the engine decide.
+ */
+ if (create_info->db_type == partition_hton)
+ {
+ List_iterator_fast<Key> key_iterator(alter_info->key_list);
+ Key *key;
+ while ((key= key_iterator++))
+ {
+ if (key->type == Key::FOREIGN_KEY)
+ {
+ my_error(ER_FOREIGN_KEY_ON_PARTITIONED, MYF(0));
+ goto err;
}
}
}
#endif
- if (mysql_prepare_create_table(thd, create_info, alter_info,
- internal_tmp_table,
- &db_options, file,
- &key_info_buffer, &key_count,
- select_field_count))
+ if (mysql_prepare_create_table(thd, create_info, alter_info, &db_options,
+ file, key_info, key_count,
+ create_table_mode))
goto err;
+ create_info->table_options=db_options;
- /* Check if table exists */
- if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
- {
- path_length= build_tmptable_filename(thd, path, sizeof(path));
- create_info->table_options|=HA_CREATE_DELAY_KEY_WRITE;
- }
- else
- {
- path_length= build_table_filename(path, sizeof(path) - 1, db, alias, reg_ext,
- internal_tmp_table ? FN_IS_TMP : 0);
- }
+ *frm= build_frm_image(thd, table_name, create_info,
+ alter_info->create_list, *key_count,
+ *key_info, file);
+
+ if (frm->str)
+ DBUG_RETURN(file);
+
+err:
+ delete file;
+ DBUG_RETURN(NULL);
+}
+
+
+/**
+ Create a table
+
+ @param thd Thread object
+ @param orig_db Database for error messages
+ @param orig_table_name Table name for error messages
+ (it's different from table_name for ALTER TABLE)
+ @param db Database
+ @param table_name Table name
+ @param path Path to table (i.e. to its .FRM file without
+ the extension).
+ @param create_info Create information (like MAX_ROWS)
+ @param alter_info Description of fields and keys for new table
+ @param create_table_mode C_ORDINARY_CREATE, C_ALTER_TABLE, C_ASSISTED_DISCOVERY
+ or any positive number (for C_CREATE_SELECT).
+ @param[out] is_trans Identifies the type of engine where the table
+ was created: either trans or non-trans.
+ @param[out] key_info Array of KEY objects describing keys in table
+ which was created.
+ @param[out] key_count Number of keys in table which was created.
+
+ If one creates a temporary table, this is automatically opened
+
+ Note that this function assumes that caller already have taken
+ exclusive metadata lock on table being created or used some other
+ way to ensure that concurrent operations won't intervene.
+ mysql_create_table() is a wrapper that can be used for this.
+
+ @retval 0 OK
+ @retval 1 error
+ @retval -1 table existed but IF EXISTS was used
+*/
+
+static
+int create_table_impl(THD *thd,
+ const char *orig_db, const char *orig_table_name,
+ const char *db, const char *table_name,
+ const char *path,
+ HA_CREATE_INFO *create_info,
+ Alter_info *alter_info,
+ int create_table_mode,
+ bool *is_trans,
+ KEY **key_info,
+ uint *key_count,
+ LEX_CUSTRING *frm)
+{
+ const char *alias;
+ handler *file= 0;
+ int error= 1;
+ bool frm_only= create_table_mode == C_ALTER_TABLE_FRM_ONLY;
+ bool internal_tmp_table= create_table_mode == C_ALTER_TABLE || frm_only;
+ DBUG_ENTER("mysql_create_table_no_lock");
+ DBUG_PRINT("enter", ("db: '%s' table: '%s' tmp: %d path: %s",
+ db, table_name, internal_tmp_table, path));
- /* Check if table already exists */
- if ((create_info->options & HA_LEX_CREATE_TMP_TABLE) &&
- find_temporary_table(thd, db, table_name))
+ if (thd->variables.sql_mode & MODE_NO_DIR_IN_CREATE)
{
- if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS)
- goto warn;
- my_error(ER_TABLE_EXISTS_ERROR, MYF(0), alias);
- goto err;
+ if (create_info->data_file_name)
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
+ WARN_OPTION_IGNORED, ER(WARN_OPTION_IGNORED),
+ "DATA DIRECTORY");
+ if (create_info->index_file_name)
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
+ WARN_OPTION_IGNORED, ER(WARN_OPTION_IGNORED),
+ "INDEX DIRECTORY");
+ create_info->data_file_name= create_info->index_file_name= 0;
}
+ else
+ if (error_if_data_home_dir(create_info->data_file_name, "DATA DIRECTORY") ||
+ error_if_data_home_dir(create_info->index_file_name, "INDEX DIRECTORY")||
+ check_partition_dirs(thd->lex->part_info))
+ goto err;
- /* Give warnings for not supported table options */
-#if defined(WITH_ARIA_STORAGE_ENGINE)
- extern handlerton *maria_hton;
- if (file->ht != maria_hton)
-#endif
- if (create_info->transactional)
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
- ER_ILLEGAL_HA_CREATE_OPTION,
- ER(ER_ILLEGAL_HA_CREATE_OPTION),
- file->engine_name()->str,
- "TRANSACTIONAL=1");
+ alias= table_case_name(create_info, table_name);
- if (!internal_tmp_table && !(create_info->options & HA_LEX_CREATE_TMP_TABLE))
+ /* Check if table exists */
+ if (create_info->tmp_table())
{
- if (check_table_file_presence(NULL, path, db, table_name, table_name,
- !(create_info->options &
- HA_LEX_CREATE_IF_NOT_EXISTS)))
- {
- if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS)
- goto warn;
+ TABLE *tmp_table;
+ if (find_and_use_temporary_table(thd, db, table_name, &tmp_table))
goto err;
- }
- /*
- We don't assert here, but check the result, because the table could be
- in the table definition cache and in the same time the .frm could be
- missing from the disk, in case of manual intervention which deletes
- the .frm file. The user has to use FLUSH TABLES; to clear the cache.
- Then she could create the table. This case is pretty obscure and
- therefore we don't introduce a new error message only for it.
- */
- mysql_mutex_lock(&LOCK_open);
- if (get_cached_table_share(db, table_name))
+ if (tmp_table)
{
- mysql_mutex_unlock(&LOCK_open);
- my_error(ER_TABLE_EXISTS_ERROR, MYF(0), table_name);
- goto err;
+ bool table_creation_was_logged= tmp_table->s->table_creation_was_logged;
+ if (create_info->options & HA_LEX_CREATE_REPLACE)
+ {
+ bool tmp;
+ /*
+ We are using CREATE OR REPLACE on an existing temporary table
+ Remove the old table so that we can re-create it.
+ */
+ if (drop_temporary_table(thd, tmp_table, &tmp))
+ goto err;
+ }
+ else if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS)
+ goto warn;
+ else
+ {
+ my_error(ER_TABLE_EXISTS_ERROR, MYF(0), alias);
+ goto err;
+ }
+ /*
+ We have to log this query, even if it failed later to ensure the
+ drop is done.
+ */
+#ifndef DONT_LOG_DROP_OF_TEMPORARY_TABLES
+ table_creation_was_logged= 1;
+#endif
+ if (table_creation_was_logged)
+ {
+ thd->variables.option_bits|= OPTION_KEEP_LOG;
+ thd->log_current_statement= 1;
+ create_info->table_was_deleted= 1;
+ }
}
- mysql_mutex_unlock(&LOCK_open);
}
-
- /*
- Check that table with given name does not already
- exist in any storage engine. In such a case it should
- be discovered and the error ER_TABLE_EXISTS_ERROR be returned
- unless user specified CREATE TABLE IF EXISTS
- An exclusive metadata lock ensures that no
- one else is attempting to discover the table. Since
- it's not on disk as a frm file, no one could be using it!
- */
- if (!(create_info->options & HA_LEX_CREATE_TMP_TABLE))
+ else
{
- bool create_if_not_exists =
- create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS;
- int retcode = ha_table_exists_in_engine(thd, db, table_name);
- DBUG_PRINT("info", ("exists_in_engine: %u",retcode));
- switch (retcode)
+ if (!internal_tmp_table && ha_table_exists(thd, db, table_name))
{
- case HA_ERR_NO_SUCH_TABLE:
- /* Normal case, no table exists. we can go and create it */
- break;
- case HA_ERR_TABLE_EXIST:
- DBUG_PRINT("info", ("Table existed in handler"));
+ if (create_info->options & HA_LEX_CREATE_REPLACE)
+ {
+ TABLE_LIST table_list;
+ table_list.init_one_table(db, strlen(db), table_name,
+ strlen(table_name), table_name,
+ TL_WRITE_ALLOW_WRITE);
+ table_list.table= create_info->table;
- if (create_if_not_exists)
- goto warn;
- my_error(ER_TABLE_EXISTS_ERROR,MYF(0),table_name);
- goto err;
- default:
- DBUG_PRINT("info", ("error: %u from storage engine", retcode));
- my_error(retcode, MYF(0),table_name);
+ if (check_if_log_table(&table_list, TRUE, "CREATE OR REPLACE"))
+ goto err;
+
+ /*
+ Rollback the empty transaction started in mysql_create_table()
+ call to open_and_lock_tables() when we are using LOCK TABLES.
+ */
+ (void) trans_rollback_stmt(thd);
+ /* Remove normal table without logging. Keep tables locked */
+ if (mysql_rm_table_no_locks(thd, &table_list, 0, 0, 0, 1, 1))
+ goto err;
+
+ /*
+ We have to log this query, even if it failed later to ensure the
+ drop is done.
+ */
+ thd->variables.option_bits|= OPTION_KEEP_LOG;
+ thd->log_current_statement= 1;
+ create_info->table_was_deleted= 1;
+ DBUG_EXECUTE_IF("send_kill_after_delete", thd->killed= KILL_QUERY; );
+
+ /*
+ Restart statement transactions for the case of CREATE ... SELECT.
+ */
+ if (thd->lex->select_lex.item_list.elements &&
+ restart_trans_for_tables(thd, thd->lex->query_tables))
+ goto err;
+ }
+ else if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS)
+ goto warn;
+ else
+ {
+ my_error(ER_TABLE_EXISTS_ERROR, MYF(0), table_name);
goto err;
+ }
}
}
- thd_proc_info(thd, "creating table");
+ THD_STAGE_INFO(thd, stage_creating_table);
+
+ if (check_engine(thd, orig_db, orig_table_name, create_info))
+ goto err;
-#ifdef HAVE_READLINK
+ if (create_table_mode == C_ASSISTED_DISCOVERY)
{
- size_t dirlen;
- char dirpath[FN_REFLEN];
+ /* check that it's used correctly */
+ DBUG_ASSERT(alter_info->create_list.elements == 0);
+ DBUG_ASSERT(alter_info->key_list.elements == 0);
- /*
- data_file_name and index_file_name include the table name without
- extension. Mostly this does not refer to an existing file. When
- comparing data_file_name or index_file_name against the data
- directory, we try to resolve all symbolic links. On some systems,
- we use realpath(3) for the resolution. This returns ENOENT if the
- resolved path does not refer to an existing file. my_realpath()
- does then copy the requested path verbatim, without symlink
- resolution. Thereafter the comparison can fail even if the
- requested path is within the data directory. E.g. if symlinks to
- another file system are used. To make realpath(3) return the
- resolved path, we strip the table name and compare the directory
- path only. If the directory doesn't exist either, table creation
- will fail anyway.
- */
- if (create_info->data_file_name)
+ TABLE_SHARE share;
+ handlerton *hton= create_info->db_type;
+ int ha_err;
+ Field *no_fields= 0;
+
+ if (!hton->discover_table_structure)
{
- dirname_part(dirpath, create_info->data_file_name, &dirlen);
- if (test_if_data_home_dir(dirpath))
- {
- my_error(ER_WRONG_ARGUMENTS, MYF(0), "DATA DIRECTORY");
- goto err;
- }
+ my_error(ER_TABLE_MUST_HAVE_COLUMNS, MYF(0));
+ goto err;
}
- if (create_info->index_file_name)
+
+ init_tmp_table_share(thd, &share, db, 0, table_name, path);
+
+ /* prepare everything for discovery */
+ share.field= &no_fields;
+ share.db_plugin= ha_lock_engine(thd, hton);
+ share.option_list= create_info->option_list;
+ share.connect_string= create_info->connect_string;
+
+ if (parse_engine_table_options(thd, hton, &share))
+ goto err;
+
+ ha_err= hton->discover_table_structure(hton, thd, &share, create_info);
+
+ /*
+ if discovery failed, the plugin will be auto-unlocked, as it
+ was locked on the THD, see above.
+ if discovery succeeded, the plugin was replaced by a globally
+ locked plugin, that will be unlocked by free_table_share()
+ */
+ if (ha_err)
+ share.db_plugin= 0; // will be auto-freed, locked above on the THD
+
+ free_table_share(&share);
+
+ if (ha_err)
{
- dirname_part(dirpath, create_info->index_file_name, &dirlen);
- if (test_if_data_home_dir(dirpath))
- {
- my_error(ER_WRONG_ARGUMENTS, MYF(0), "INDEX DIRECTORY");
- goto err;
- }
+ my_error(ER_GET_ERRNO, MYF(0), ha_err, hton_name(hton)->str);
+ goto err;
}
}
-
-#ifdef WITH_PARTITION_STORAGE_ENGINE
- if (check_partition_dirs(thd->lex->part_info))
- {
- goto err;
- }
-#endif /* WITH_PARTITION_STORAGE_ENGINE */
-
- if (!my_use_symdir || (thd->variables.sql_mode & MODE_NO_DIR_IN_CREATE))
-#endif /* HAVE_READLINK */
+ else
{
- if (create_info->data_file_name)
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
- WARN_OPTION_IGNORED, ER(WARN_OPTION_IGNORED),
- "DATA DIRECTORY");
- if (create_info->index_file_name)
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
- WARN_OPTION_IGNORED, ER(WARN_OPTION_IGNORED),
- "INDEX DIRECTORY");
- create_info->data_file_name= create_info->index_file_name= 0;
+ file= mysql_create_frm_image(thd, orig_db, orig_table_name, create_info,
+ alter_info, create_table_mode, key_info,
+ key_count, frm);
+ if (!file)
+ goto err;
+ if (rea_create_table(thd, frm, path, db, table_name, create_info,
+ file, frm_only))
+ goto err;
}
- create_info->table_options=db_options;
- path[path_length - reg_ext_length]= '\0'; // Remove .frm extension
- if (rea_create_table(thd, path, db, table_name,
- create_info, alter_info->create_list,
- key_count, key_info_buffer, file))
- goto err;
-
- if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
+ create_info->table= 0;
+ if (!frm_only && create_info->tmp_table())
{
/*
Open a table (skipping table cache) and add it into
THD::temporary_tables list.
*/
- TABLE *table= open_table_uncached(thd, path, db, table_name, TRUE);
+ TABLE *table= open_table_uncached(thd, create_info->db_type, path,
+ db, table_name, true, true);
if (!table)
{
@@ -4546,9 +4872,10 @@ bool mysql_create_table_no_lock(THD *thd,
*is_trans= table->file->has_transactions();
thd->thread_specific_used= TRUE;
+ create_info->table= table; // Store pointer to table
}
#ifdef WITH_PARTITION_STORAGE_ENGINE
- else if (part_info && create_info->frm_only)
+ else if (thd->work_part_info && frm_only)
{
/*
For partitioned tables we can't find some problems with table
@@ -4560,31 +4887,84 @@ bool mysql_create_table_no_lock(THD *thd,
In cases when we create .FRM without SE part we have to open
table explicitly.
*/
- if (check_if_created_table_can_be_opened(thd, path, db, table_name,
- create_info, file))
+ TABLE table;
+ TABLE_SHARE share;
+
+ init_tmp_table_share(thd, &share, db, 0, table_name, path);
+
+ bool result= (open_table_def(thd, &share, GTS_TABLE) ||
+ open_table_from_share(thd, &share, "", 0, (uint) READ_ALL,
+ 0, &table, true));
+ if (!result)
+ (void) closefrm(&table, 0);
+
+ free_table_share(&share);
+
+ if (result)
{
char frm_name[FN_REFLEN];
- strxmov(frm_name, path, reg_ext, NullS);
+ strxnmov(frm_name, sizeof(frm_name), path, reg_ext, NullS);
(void) mysql_file_delete(key_file_frm, frm_name, MYF(0));
+ (void) file->ha_create_partitioning_metadata(path, NULL, CHF_DELETE_FLAG);
goto err;
}
}
#endif
- error= FALSE;
+ error= 0;
err:
- thd_proc_info(thd, "After create");
+ THD_STAGE_INFO(thd, stage_after_create);
delete file;
+ DBUG_PRINT("exit", ("return: %d", error));
DBUG_RETURN(error);
warn:
- error= FALSE;
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
+ error= -1;
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
ER_TABLE_EXISTS_ERROR, ER(ER_TABLE_EXISTS_ERROR),
alias);
goto err;
}
+/**
+ Simple wrapper around create_table_impl() to be used
+ in various version of CREATE TABLE statement.
+*/
+
+int mysql_create_table_no_lock(THD *thd,
+ const char *db, const char *table_name,
+ HA_CREATE_INFO *create_info,
+ Alter_info *alter_info, bool *is_trans,
+ int create_table_mode)
+{
+ KEY *not_used_1;
+ uint not_used_2;
+ int res;
+ char path[FN_REFLEN + 1];
+ LEX_CUSTRING frm= {0,0};
+
+ if (create_info->tmp_table())
+ build_tmptable_filename(thd, path, sizeof(path));
+ else
+ {
+ int length;
+ const char *alias= table_case_name(create_info, table_name);
+ length= build_table_filename(path, sizeof(path) - 1, db, alias,
+ "", 0);
+ // Check if we hit FN_REFLEN bytes along with file extension.
+ if (length+reg_ext_length > FN_REFLEN)
+ {
+ my_error(ER_IDENT_CAUSES_TOO_LONG_PATH, MYF(0), sizeof(path)-1, path);
+ return true;
+ }
+ }
+
+ res= create_table_impl(thd, db, table_name, db, table_name, path,
+ create_info, alter_info, create_table_mode,
+ is_trans, &not_used_1, &not_used_2, &frm);
+ my_free(const_cast<uchar*>(frm.str));
+ return res;
+}
/**
Implementation of SQLCOM_CREATE_TABLE.
@@ -4600,40 +4980,106 @@ bool mysql_create_table(THD *thd, TABLE_LIST *create_table,
HA_CREATE_INFO *create_info,
Alter_info *alter_info)
{
- bool result;
+ const char *db= create_table->db;
+ const char *table_name= create_table->table_name;
bool is_trans= FALSE;
+ bool result;
+ int create_table_mode;
+ TABLE_LIST *pos_in_locked_tables= 0;
+ MDL_ticket *mdl_ticket= 0;
DBUG_ENTER("mysql_create_table");
- /*
- Open or obtain an exclusive metadata lock on table being created.
- */
- if (open_and_lock_tables(thd, thd->lex->query_tables, FALSE, 0))
+ DBUG_ASSERT(create_table == thd->lex->query_tables);
+
+ /* Copy temporarily the statement flags to thd for lock_table_names() */
+ uint save_thd_create_info_options= thd->lex->create_info.options;
+ thd->lex->create_info.options|= create_info->options;
+
+ /* Open or obtain an exclusive metadata lock on table being created */
+ result= open_and_lock_tables(thd, create_table, FALSE, 0);
+
+ thd->lex->create_info.options= save_thd_create_info_options;
+
+ if (result)
{
/* is_error() may be 0 if table existed and we generated a warning */
- result= thd->is_error();
- goto end;
+ DBUG_RETURN(thd->is_error());
}
-
+ /* The following is needed only in case of lock tables */
+ if ((create_info->table= create_table->table))
+ {
+ pos_in_locked_tables= create_info->table->pos_in_locked_tables;
+ mdl_ticket= create_table->table->mdl_ticket;
+ }
+
/* Got lock. */
DEBUG_SYNC(thd, "locked_table_name");
- result= mysql_create_table_no_lock(thd, create_table->db,
- create_table->table_name, create_info,
- alter_info, FALSE, 0, &is_trans);
+ if (alter_info->create_list.elements || alter_info->key_list.elements)
+ create_table_mode= C_ORDINARY_CREATE;
+ else
+ create_table_mode= C_ASSISTED_DISCOVERY;
+
+ promote_first_timestamp_column(&alter_info->create_list);
+ if (mysql_create_table_no_lock(thd, db, table_name, create_info, alter_info,
+ &is_trans, create_table_mode) > 0)
+ {
+ result= 1;
+ goto err;
+ }
/*
- Don't write statement if:
- - Table creation has failed
- - Row-based logging is used and we are creating a temporary table
- Otherwise, the statement shall be binlogged.
+ Check if we are doing CREATE OR REPLACE TABLE under LOCK TABLES
+ on a non temporary table
*/
- if (!result &&
- (!thd->is_current_stmt_binlog_format_row() ||
- (thd->is_current_stmt_binlog_format_row() &&
- !(create_info->options & HA_LEX_CREATE_TMP_TABLE))))
- result= write_bin_log(thd, TRUE, thd->query(), thd->query_length(), is_trans);
+ if (thd->locked_tables_mode && pos_in_locked_tables &&
+ (create_info->options & HA_LEX_CREATE_REPLACE))
+ {
+ /*
+ Add back the deleted table and re-created table as a locked table
+ This should always work as we have a meta lock on the table.
+ */
+ thd->locked_tables_list.add_back_last_deleted_lock(pos_in_locked_tables);
+ if (thd->locked_tables_list.reopen_tables(thd))
+ {
+ thd->locked_tables_list.unlink_all_closed_tables(thd, NULL, 0);
+ result= 1;
+ }
+ else
+ {
+ TABLE *table= pos_in_locked_tables->table;
+ table->mdl_ticket->downgrade_lock(MDL_SHARED_NO_READ_WRITE);
+ }
+ }
-end:
+err:
+ /* In RBR we don't need to log CREATE TEMPORARY TABLE */
+ if (thd->is_current_stmt_binlog_format_row() && create_info->tmp_table())
+ DBUG_RETURN(result);
+
+ /* Write log if no error or if we already deleted a table */
+ if (!result || thd->log_current_statement)
+ {
+ if (result && create_info->table_was_deleted)
+ {
+ /*
+ Possible locked table was dropped. We should remove meta data locks
+ associated with it and do UNLOCK_TABLES if no more locked tables.
+ */
+ thd->locked_tables_list.unlock_locked_table(thd, mdl_ticket);
+ }
+ else if (!result && create_info->tmp_table() && create_info->table)
+ {
+ /*
+ Remember that tmp table creation was logged so that we know if
+ we should log a delete of it.
+ */
+ create_info->table->s->table_creation_was_logged= 1;
+ }
+ if (write_bin_log(thd, result ? FALSE : TRUE, thd->query(),
+ thd->query_length(), is_trans))
+ result= 1;
+ }
DBUG_RETURN(result);
}
@@ -4682,27 +5128,24 @@ make_unique_key_name(const char *field_name,KEY *start,KEY *end)
****************************************************************************/
-/*
+/**
Rename a table.
- SYNOPSIS
- mysql_rename_table()
- base The handlerton handle.
- old_db The old database name.
- old_name The old table name.
- new_db The new database name.
- new_name The new table name.
- flags flags for build_table_filename().
- FN_FROM_IS_TMP old_name is temporary.
- FN_TO_IS_TMP new_name is temporary.
- NO_FRM_RENAME Don't rename the FRM file
- but only the table in the storage engine.
- NO_FK_CHECKS Don't check FK constraints
- during rename.
-
- RETURN
- FALSE OK
- TRUE Error
+ @param base The handlerton handle.
+ @param old_db The old database name.
+ @param old_name The old table name.
+ @param new_db The new database name.
+ @param new_name The new table name.
+ @param flags flags
+ FN_FROM_IS_TMP old_name is temporary.
+ FN_TO_IS_TMP new_name is temporary.
+ NO_FRM_RENAME Don't rename the FRM file
+ but only the table in the storage engine.
+ NO_HA_TABLE Don't rename table in engine.
+ NO_FK_CHECKS Don't check FK constraints during rename.
+
+ @return false OK
+ @return true Error
*/
bool
@@ -4714,25 +5157,32 @@ mysql_rename_table(handlerton *base, const char *old_db,
char from[FN_REFLEN + 1], to[FN_REFLEN + 1],
lc_from[FN_REFLEN + 1], lc_to[FN_REFLEN + 1];
char *from_base= from, *to_base= to;
- char tmp_name[SAFE_NAME_LEN+1];
+ char tmp_name[SAFE_NAME_LEN+1], tmp_db_name[SAFE_NAME_LEN+1];
handler *file;
int error=0;
ulonglong save_bits= thd->variables.option_bits;
+ int length;
DBUG_ENTER("mysql_rename_table");
+ DBUG_ASSERT(base);
DBUG_PRINT("enter", ("old: '%s'.'%s' new: '%s'.'%s'",
old_db, old_name, new_db, new_name));
-
+
// Temporarily disable foreign key checks
if (flags & NO_FK_CHECKS)
thd->variables.option_bits|= OPTION_NO_FOREIGN_KEY_CHECKS;
- file= (base == NULL ? 0 :
- get_new_handler((TABLE_SHARE*) 0, thd->mem_root, base));
+ file= get_new_handler((TABLE_SHARE*) 0, thd->mem_root, base);
build_table_filename(from, sizeof(from) - 1, old_db, old_name, "",
flags & FN_FROM_IS_TMP);
- build_table_filename(to, sizeof(to) - 1, new_db, new_name, "",
- flags & FN_TO_IS_TMP);
+ length= build_table_filename(to, sizeof(to) - 1, new_db, new_name, "",
+ flags & FN_TO_IS_TMP);
+ // Check if we hit FN_REFLEN bytes along with file extension.
+ if (length+reg_ext_length > FN_REFLEN)
+ {
+ my_error(ER_IDENT_CAUSES_TOO_LONG_PATH, MYF(0), sizeof(to)-1, to);
+ DBUG_RETURN(TRUE);
+ }
/*
If lower_case_table_names == 2 (case-preserving but case-insensitive
@@ -4744,25 +5194,41 @@ mysql_rename_table(handlerton *base, const char *old_db,
{
strmov(tmp_name, old_name);
my_casedn_str(files_charset_info, tmp_name);
- build_table_filename(lc_from, sizeof(lc_from) - 1, old_db, tmp_name, "",
- flags & FN_FROM_IS_TMP);
+ strmov(tmp_db_name, old_db);
+ my_casedn_str(files_charset_info, tmp_db_name);
+
+ build_table_filename(lc_from, sizeof(lc_from) - 1, tmp_db_name, tmp_name,
+ "", flags & FN_FROM_IS_TMP);
from_base= lc_from;
strmov(tmp_name, new_name);
my_casedn_str(files_charset_info, tmp_name);
- build_table_filename(lc_to, sizeof(lc_to) - 1, new_db, tmp_name, "",
+ strmov(tmp_db_name, new_db);
+ my_casedn_str(files_charset_info, tmp_db_name);
+
+ build_table_filename(lc_to, sizeof(lc_to) - 1, tmp_db_name, tmp_name, "",
flags & FN_TO_IS_TMP);
to_base= lc_to;
}
- if (!file || !(error=file->ha_rename_table(from_base, to_base)))
+ if (flags & NO_HA_TABLE)
+ {
+ if (rename_file_ext(from,to,reg_ext))
+ error= my_errno;
+ (void) file->ha_create_partitioning_metadata(to, from, CHF_RENAME_FLAG);
+ }
+ else if (!file || !(error=file->ha_rename_table(from_base, to_base)))
{
if (!(flags & NO_FRM_RENAME) && rename_file_ext(from,to,reg_ext))
{
error=my_errno;
- /* Restore old file name */
if (file)
- file->ha_rename_table(to_base, from_base);
+ {
+ if (error == ENOENT)
+ error= 0; // this is ok if file->ha_rename_table() succeeded
+ else
+ file->ha_rename_table(to_base, from_base); // Restore old file name
+ }
}
}
delete file;
@@ -4772,7 +5238,18 @@ mysql_rename_table(handlerton *base, const char *old_db,
my_error(ER_ERROR_ON_RENAME, MYF(0), from, to, error);
else if (!(flags & FN_IS_TMP))
mysql_audit_rename_table(thd, old_db, old_name, new_db, new_name);
-
+
+ /*
+ Remove the old table share from the pfs table share array. The new table
+ share will be created when the renamed table is first accessed.
+ */
+ if (likely(error == 0))
+ {
+ PSI_CALL_drop_table_share(flags & FN_FROM_IS_TMP,
+ old_db, strlen(old_db),
+ old_name, strlen(old_name));
+ }
+
// Restore options bits to the original value
thd->variables.option_bits= save_bits;
@@ -4795,14 +5272,20 @@ mysql_rename_table(handlerton *base, const char *old_db,
TRUE error
*/
-bool mysql_create_like_table(THD* thd, TABLE_LIST* table, TABLE_LIST* src_table,
+bool mysql_create_like_table(THD* thd, TABLE_LIST* table,
+ TABLE_LIST* src_table,
HA_CREATE_INFO *create_info)
{
HA_CREATE_INFO local_create_info;
+ TABLE_LIST *pos_in_locked_tables= 0;
Alter_info local_alter_info;
+ Alter_table_ctx local_alter_ctx; // Not used
bool res= TRUE;
bool is_trans= FALSE;
+ bool do_logging= FALSE;
uint not_used;
+ int create_res;
+ uint save_thd_create_info_options;
DBUG_ENTER("mysql_create_like_table");
#ifdef WITH_WSREP
@@ -4846,7 +5329,7 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, TABLE_LIST* src_table,
String query(buf, sizeof(buf), system_charset_info);
query.length(0); // Have to zero it since constructor doesn't
- (void) store_create_info(thd, &tbl, &query, NULL, TRUE);
+ (void) show_create_table(thd, &tbl, &query, NULL, WITH_DB_NAME);
WSREP_DEBUG("TMP TABLE: %s", query.ptr());
thd->wsrep_TOI_pre_query= query.ptr();
@@ -4871,11 +5354,31 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, TABLE_LIST* src_table,
Thus by holding both these locks we ensure that our statement is
properly isolated from all concurrent operations which matter.
*/
- if (open_tables(thd, &thd->lex->query_tables, &not_used, 0))
+
+ /* Copy temporarily the statement flags to thd for lock_table_names() */
+ save_thd_create_info_options= thd->lex->create_info.options;
+ thd->lex->create_info.options|= create_info->options;
+ res= open_tables(thd, &thd->lex->query_tables, &not_used, 0);
+ thd->lex->create_info.options= save_thd_create_info_options;
+
+ if (res)
{
+ /* is_error() may be 0 if table existed and we generated a warning */
res= thd->is_error();
goto err;
}
+ /* Ensure we don't try to create something from which we select from */
+ if ((create_info->options & HA_LEX_CREATE_REPLACE) &&
+ !create_info->tmp_table())
+ {
+ TABLE_LIST *duplicate;
+ if ((duplicate= unique_table(thd, table, src_table, 0)))
+ {
+ update_non_unique_table_error(src_table, "CREATE", duplicate);
+ goto err;
+ }
+ }
+
src_table->table->use_all_columns();
DEBUG_SYNC(thd, "create_table_like_after_open");
@@ -4885,7 +5388,7 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, TABLE_LIST* src_table,
local_create_info.db_type= src_table->table->s->db_type();
local_create_info.row_type= src_table->table->s->row_type;
if (mysql_prepare_alter_table(thd, src_table->table, &local_create_info,
- &local_alter_info))
+ &local_alter_info, &local_alter_ctx))
goto err;
#ifdef WITH_PARTITION_STORAGE_ENGINE
/* Partition info is not handled by mysql_prepare_alter_table() call. */
@@ -4903,10 +5406,12 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, TABLE_LIST* src_table,
if (src_table->schema_table)
local_create_info.max_rows= 0;
/* Set IF NOT EXISTS option as in the CREATE TABLE LIKE statement. */
- local_create_info.options|= create_info->options&HA_LEX_CREATE_IF_NOT_EXISTS;
+ local_create_info.options|= (create_info->options &
+ (HA_LEX_CREATE_IF_NOT_EXISTS |
+ HA_LEX_CREATE_REPLACE));
/* Replace type of source table with one specified in the statement. */
local_create_info.options&= ~HA_LEX_CREATE_TMP_TABLE;
- local_create_info.options|= create_info->options & HA_LEX_CREATE_TMP_TABLE;
+ local_create_info.options|= create_info->tmp_table();
/* Reset auto-increment counter for the new table. */
local_create_info.auto_increment_value= 0;
/*
@@ -4915,19 +5420,57 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, TABLE_LIST* src_table,
*/
local_create_info.data_file_name= local_create_info.index_file_name= NULL;
- if ((res= mysql_create_table_no_lock(thd, table->db, table->table_name,
- &local_create_info, &local_alter_info,
- FALSE, 0, &is_trans)))
+ /* The following is needed only in case of lock tables */
+ if ((local_create_info.table= thd->lex->query_tables->table))
+ pos_in_locked_tables= local_create_info.table->pos_in_locked_tables;
+
+ res= ((create_res=
+ mysql_create_table_no_lock(thd, table->db, table->table_name,
+ &local_create_info, &local_alter_info,
+ &is_trans, C_ORDINARY_CREATE)) > 0);
+ /* Remember to log if we deleted something */
+ do_logging= thd->log_current_statement;
+ if (res)
goto err;
/*
- Ensure that we have an exclusive lock on target table if we are creating
- non-temporary table.
+ Check if we are doing CREATE OR REPLACE TABLE under LOCK TABLES
+ on a non temporary table
*/
- DBUG_ASSERT((create_info->options & HA_LEX_CREATE_TMP_TABLE) ||
- thd->mdl_context.is_lock_owner(MDL_key::TABLE, table->db,
- table->table_name,
- MDL_EXCLUSIVE));
+ if (thd->locked_tables_mode && pos_in_locked_tables &&
+ (create_info->options & HA_LEX_CREATE_REPLACE))
+ {
+ /*
+ Add back the deleted table and re-created table as a locked table
+ This should always work as we have a meta lock on the table.
+ */
+ thd->locked_tables_list.add_back_last_deleted_lock(pos_in_locked_tables);
+ if (thd->locked_tables_list.reopen_tables(thd))
+ {
+ thd->locked_tables_list.unlink_all_closed_tables(thd, NULL, 0);
+ res= 1; // We got an error
+ }
+ else
+ {
+ /*
+ Get pointer to the newly opened table. We need this to ensure we
+ don't reopen the table when doing statment logging below.
+ */
+ table->table= pos_in_locked_tables->table;
+ table->table->mdl_ticket->downgrade_lock(MDL_SHARED_NO_READ_WRITE);
+ }
+ }
+ else
+ {
+ /*
+ Ensure that we have an exclusive lock on target table if we are creating
+ non-temporary table.
+ */
+ DBUG_ASSERT((create_info->tmp_table()) ||
+ thd->mdl_context.is_lock_owner(MDL_key::TABLE, table->db,
+ table->table_name,
+ MDL_EXCLUSIVE));
+ }
DEBUG_SYNC(thd, "create_table_like_before_binlog");
@@ -4946,66 +5489,130 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, TABLE_LIST* src_table,
Case Target Source Write to binary log
==== ========= ========= ==============================
1 normal normal Original statement
- 2 normal temporary Generated statement
+ 2 normal temporary Generated statement if the table
+ was created.
3 temporary normal Nothing
4 temporary temporary Nothing
==== ========= ========= ==============================
*/
- if (!(create_info->options & HA_LEX_CREATE_TMP_TABLE))
+ if (!(create_info->tmp_table()))
{
if (src_table->table->s->tmp_table) // Case 2
{
char buf[2048];
String query(buf, sizeof(buf), system_charset_info);
query.length(0); // Have to zero it since constructor doesn't
- Open_table_context ot_ctx(thd, MYSQL_OPEN_REOPEN);
+ Open_table_context ot_ctx(thd, MYSQL_OPEN_REOPEN |
+ MYSQL_OPEN_IGNORE_KILLED);
+ bool new_table= FALSE; // Whether newly created table is open.
- /*
- The condition avoids a crash as described in BUG#48506. Other
- binlogging problems related to CREATE TABLE IF NOT EXISTS LIKE
- when the existing object is a view will be solved by BUG 47442.
- */
- if (!table->view)
+ if (create_res != 0)
{
/*
- Here we open the destination table, on which we already have
- exclusive metadata lock. This is needed for store_create_info()
- to work. The table will be closed by close_thread_table() at
- the end of this branch.
+ Table or view with same name already existed and we where using
+ IF EXISTS. Continue without logging anything.
*/
- if (open_table(thd, table, thd->mem_root, &ot_ctx))
- goto err;
+ do_logging= 0;
+ goto err;
+ }
+ if (!table->table)
+ {
+ TABLE_LIST::enum_open_strategy save_open_strategy;
+ int open_res;
+ /* Force the newly created table to be opened */
+ save_open_strategy= table->open_strategy;
+ table->open_strategy= TABLE_LIST::OPEN_NORMAL;
+ /*
+ In order for show_create_table() to work we need to open
+ destination table if it is not already open (i.e. if it
+ has not existed before). We don't need acquire metadata
+ lock in order to do this as we already hold exclusive
+ lock on this table. The table will be closed by
+ close_thread_table() at the end of this branch.
+ */
+ open_res= open_table(thd, table, thd->mem_root, &ot_ctx);
+ /* Restore */
+ table->open_strategy= save_open_strategy;
+ if (open_res)
+ {
+ res= 1;
+ goto err;
+ }
+ new_table= TRUE;
+ }
+ /*
+ We have to re-test if the table was a view as the view may not
+ have been opened until just above.
+ */
+ if (!table->view)
+ {
int result __attribute__((unused))=
- store_create_info(thd, table, &query,
- create_info, TRUE /* show_database */);
+ show_create_table(thd, table, &query, create_info, WITH_DB_NAME);
- DBUG_ASSERT(result == 0); // store_create_info() always return 0
+ DBUG_ASSERT(result == 0); // show_create_table() always return 0
+ do_logging= FALSE;
if (write_bin_log(thd, TRUE, query.ptr(), query.length()))
+ {
+ res= 1;
+ do_logging= 0;
goto err;
+ }
- DBUG_ASSERT(thd->open_tables == table->table);
- /*
- When opening the table, we ignored the locked tables
- (MYSQL_OPEN_GET_NEW_TABLE). Now we can close the table without
- risking to close some locked table.
- */
- close_thread_table(thd, &thd->open_tables);
+ if (new_table)
+ {
+ DBUG_ASSERT(thd->open_tables == table->table);
+ /*
+ When opening the table, we ignored the locked tables
+ (MYSQL_OPEN_GET_NEW_TABLE). Now we can close the table
+ without risking to close some locked table.
+ */
+ close_thread_table(thd, &thd->open_tables);
+ }
}
}
else // Case 1
- if (write_bin_log(thd, TRUE, thd->query(), thd->query_length()))
- goto err;
+ do_logging= TRUE;
}
/*
Case 3 and 4 does nothing under RBR
*/
}
- else if (write_bin_log(thd, TRUE, thd->query(), thd->query_length(), is_trans))
- goto err;
+ else
+ {
+ DBUG_PRINT("info",
+ ("res: %d tmp_table: %d create_info->table: %p",
+ res, create_info->tmp_table(), local_create_info.table));
+ if (!res && create_info->tmp_table() && local_create_info.table)
+ {
+ /*
+ Remember that tmp table creation was logged so that we know if
+ we should log a delete of it.
+ */
+ local_create_info.table->s->table_creation_was_logged= 1;
+ }
+ do_logging= TRUE;
+ }
err:
+ if (do_logging)
+ {
+ if (res && create_info->table_was_deleted)
+ {
+ /*
+ Table was not deleted. Original table was deleted.
+ We have to log it.
+ */
+ log_drop_table(thd, table->db, table->db_length,
+ table->table_name, table->table_name_length,
+ create_info->tmp_table());
+ }
+ else if (write_bin_log(thd, res ? FALSE : TRUE, thd->query(),
+ thd->query_length(), is_trans))
+ res= 1;
+ }
DBUG_RETURN(res);
+
#ifdef WITH_WSREP
error:
thd->wsrep_TOI_pre_query= NULL;
@@ -5016,40 +5623,47 @@ err:
/* table_list should contain just one table */
-static int
-mysql_discard_or_import_tablespace(THD *thd,
- TABLE_LIST *table_list,
- enum tablespace_op_type tablespace_op)
+int mysql_discard_or_import_tablespace(THD *thd,
+ TABLE_LIST *table_list,
+ bool discard)
{
- TABLE *table;
- my_bool discard;
+ Alter_table_prelocking_strategy alter_prelocking_strategy;
int error;
DBUG_ENTER("mysql_discard_or_import_tablespace");
+ mysql_audit_alter_table(thd, table_list);
+
/*
Note that DISCARD/IMPORT TABLESPACE always is the only operation in an
ALTER TABLE
*/
- thd_proc_info(thd, "discard_or_import_tablespace");
-
- discard= test(tablespace_op == DISCARD_TABLESPACE);
+ THD_STAGE_INFO(thd, stage_discard_or_import_tablespace);
/*
We set this flag so that ha_innobase::open and ::external_lock() do
not complain when we lock the table
*/
thd->tablespace_op= TRUE;
- table_list->mdl_request.set_type(MDL_SHARED_WRITE);
- if (!(table=open_ltable(thd, table_list, TL_WRITE, 0)))
+ /*
+ Adjust values of table-level and metadata which was set in parser
+ for the case general ALTER TABLE.
+ */
+ table_list->mdl_request.set_type(MDL_EXCLUSIVE);
+ table_list->lock_type= TL_WRITE;
+ /* Do not open views. */
+ table_list->required_type= FRMTYPE_TABLE;
+
+ if (open_and_lock_tables(thd, table_list, FALSE, 0,
+ &alter_prelocking_strategy))
{
thd->tablespace_op=FALSE;
DBUG_RETURN(-1);
}
- error= table->file->ha_discard_or_import_tablespace(discard);
+ error= table_list->table->file->ha_discard_or_import_tablespace(discard);
- thd_proc_info(thd, "end");
+ THD_STAGE_INFO(thd, stage_end);
if (error)
goto err;
@@ -5077,279 +5691,734 @@ err:
DBUG_RETURN(0);
}
- table->file->print_error(error, MYF(0));
+ table_list->table->file->print_error(error, MYF(0));
DBUG_RETURN(-1);
}
+
/**
- @brief Check if both DROP and CREATE are present for an index in ALTER TABLE
-
- @details Checks if any index is being modified (present as both DROP INDEX
- and ADD INDEX) in the current ALTER TABLE statement. Needed for disabling
- in-place ALTER TABLE.
-
- @param table The table being altered
- @param alter_info The ALTER TABLE structure
- @return presence of index being altered
- @retval FALSE No such index
- @retval TRUE Have at least 1 index modified
+ Check if key is a candidate key, i.e. a unique index with no index
+ fields partial or nullable.
*/
-static bool
-is_index_maintenance_unique (TABLE *table, Alter_info *alter_info)
+static bool is_candidate_key(KEY *key)
{
- List_iterator<Key> key_it(alter_info->key_list);
- List_iterator<Alter_drop> drop_it(alter_info->drop_list);
- Key *key;
+ KEY_PART_INFO *key_part;
+ KEY_PART_INFO *key_part_end= key->key_part + key->user_defined_key_parts;
+
+ if (!(key->flags & HA_NOSAME) || (key->flags & HA_NULL_PART_KEY))
+ return false;
+
+ for (key_part= key->key_part; key_part < key_part_end; key_part++)
+ {
+ if (key_part->key_part_flag & HA_PART_KEY_SEG)
+ return false;
+ }
+ return true;
+}
+
+
+/*
+ Preparation for table creation
+
+ SYNOPSIS
+ handle_if_exists_option()
+ thd Thread object.
+ table The altered table.
+ alter_info List of columns and indexes to create
- while ((key= key_it++))
+ DESCRIPTION
+ Looks for the IF [NOT] EXISTS options, checks the states and remove items
+ from the list if existing found.
+
+ RETURN VALUES
+ NONE
+*/
+
+static void
+handle_if_exists_options(THD *thd, TABLE *table, Alter_info *alter_info)
+{
+ Field **f_ptr;
+ DBUG_ENTER("handle_if_exists_option");
+
+ /* Handle ADD COLUMN IF NOT EXISTS. */
{
- if (key->name.str)
+ List_iterator<Create_field> it(alter_info->create_list);
+ Create_field *sql_field;
+
+ while ((sql_field=it++))
{
- Alter_drop *drop;
+ if (!sql_field->create_if_not_exists || sql_field->change)
+ continue;
+ /*
+ If there is a field with the same name in the table already,
+ remove the sql_field from the list.
+ */
+ for (f_ptr=table->field; *f_ptr; f_ptr++)
+ {
+ if (my_strcasecmp(system_charset_info,
+ sql_field->field_name, (*f_ptr)->field_name) == 0)
+ goto drop_create_field;
+ }
+ {
+ /*
+ If in the ADD list there is a field with the same name,
+ remove the sql_field from the list.
+ */
+ List_iterator<Create_field> chk_it(alter_info->create_list);
+ Create_field *chk_field;
+ while ((chk_field= chk_it++) && chk_field != sql_field)
+ {
+ if (my_strcasecmp(system_charset_info,
+ sql_field->field_name, chk_field->field_name) == 0)
+ goto drop_create_field;
+ }
+ }
+ continue;
+drop_create_field:
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
+ ER_DUP_FIELDNAME, ER(ER_DUP_FIELDNAME),
+ sql_field->field_name);
+ it.remove();
+ if (alter_info->create_list.is_empty())
+ {
+ alter_info->flags&= ~Alter_info::ALTER_ADD_COLUMN;
+ if (alter_info->key_list.is_empty())
+ alter_info->flags&= ~(Alter_info::ALTER_ADD_INDEX |
+ Alter_info::ADD_FOREIGN_KEY);
+ }
+ }
+ }
+
+ /* Handle MODIFY COLUMN IF EXISTS. */
+ {
+ List_iterator<Create_field> it(alter_info->create_list);
+ Create_field *sql_field;
- drop_it.rewind();
- while ((drop= drop_it++))
+ while ((sql_field=it++))
+ {
+ if (!sql_field->create_if_not_exists || !sql_field->change)
+ continue;
+ /*
+ If there is NO field with the same name in the table already,
+ remove the sql_field from the list.
+ */
+ for (f_ptr=table->field; *f_ptr; f_ptr++)
{
- if (drop->type == Alter_drop::KEY &&
- !my_strcasecmp(system_charset_info, key->name.str, drop->name))
- return TRUE;
+ if (my_strcasecmp(system_charset_info,
+ sql_field->change, (*f_ptr)->field_name) == 0)
+ {
+ break;
+ }
+ }
+ if (*f_ptr == NULL)
+ {
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
+ ER_BAD_FIELD_ERROR, ER(ER_BAD_FIELD_ERROR),
+ sql_field->change, table->s->table_name.str);
+ it.remove();
+ if (alter_info->create_list.is_empty())
+ {
+ alter_info->flags&= ~(Alter_info::ALTER_ADD_COLUMN |
+ Alter_info::ALTER_CHANGE_COLUMN);
+ if (alter_info->key_list.is_empty())
+ alter_info->flags&= ~Alter_info::ALTER_ADD_INDEX;
+ }
}
}
}
- return FALSE;
+
+ /* Handle DROP COLUMN/KEY IF EXISTS. */
+ {
+ List_iterator<Alter_drop> drop_it(alter_info->drop_list);
+ Alter_drop *drop;
+ bool remove_drop;
+ while ((drop= drop_it++))
+ {
+ if (!drop->drop_if_exists)
+ continue;
+ remove_drop= TRUE;
+ if (drop->type == Alter_drop::COLUMN)
+ {
+ /*
+ If there is NO field with that name in the table,
+ remove the 'drop' from the list.
+ */
+ for (f_ptr=table->field; *f_ptr; f_ptr++)
+ {
+ if (my_strcasecmp(system_charset_info,
+ drop->name, (*f_ptr)->field_name) == 0)
+ {
+ remove_drop= FALSE;
+ break;
+ }
+ }
+ }
+ else /* Alter_drop::KEY */
+ {
+ uint n_key;
+ if (drop->type != Alter_drop::FOREIGN_KEY)
+ {
+ for (n_key=0; n_key < table->s->keys; n_key++)
+ {
+ if (my_strcasecmp(system_charset_info,
+ drop->name, table->key_info[n_key].name) == 0)
+ {
+ remove_drop= FALSE;
+ break;
+ }
+ }
+ }
+ else
+ {
+ List <FOREIGN_KEY_INFO> fk_child_key_list;
+ FOREIGN_KEY_INFO *f_key;
+ table->file->get_foreign_key_list(thd, &fk_child_key_list);
+ List_iterator<FOREIGN_KEY_INFO> fk_key_it(fk_child_key_list);
+ while ((f_key= fk_key_it++))
+ {
+ if (my_strcasecmp(system_charset_info, f_key->foreign_id->str,
+ drop->name) == 0)
+ {
+ remove_drop= FALSE;
+ break;
+ }
+ }
+ }
+ }
+
+ if (!remove_drop)
+ {
+ /*
+ Check if the name appears twice in the DROP list.
+ */
+ List_iterator<Alter_drop> chk_it(alter_info->drop_list);
+ Alter_drop *chk_drop;
+ while ((chk_drop= chk_it++) && chk_drop != drop)
+ {
+ if (drop->type == chk_drop->type &&
+ my_strcasecmp(system_charset_info,
+ drop->name, chk_drop->name) == 0)
+ {
+ remove_drop= TRUE;
+ break;
+ }
+ }
+ }
+
+ if (remove_drop)
+ {
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
+ ER_CANT_DROP_FIELD_OR_KEY, ER(ER_CANT_DROP_FIELD_OR_KEY),
+ drop->name);
+ drop_it.remove();
+ if (alter_info->drop_list.is_empty())
+ alter_info->flags&= ~(Alter_info::ALTER_DROP_COLUMN |
+ Alter_info::ALTER_DROP_INDEX |
+ Alter_info::DROP_FOREIGN_KEY);
+ }
+ }
+ }
+
+ /* ALTER TABLE ADD KEY IF NOT EXISTS */
+ /* ALTER TABLE ADD FOREIGN KEY IF NOT EXISTS */
+ {
+ Key *key;
+ List_iterator<Key> key_it(alter_info->key_list);
+ uint n_key;
+ const char *keyname;
+ while ((key=key_it++))
+ {
+ if (!key->create_if_not_exists)
+ continue;
+
+ /* Check if the table already has a PRIMARY KEY */
+ if (key->type == Key::PRIMARY &&
+ table->s->primary_key != MAX_KEY)
+ {
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
+ ER_DUP_KEYNAME, ER(ER_MULTIPLE_PRI_KEY));
+ goto remove_key_no_warn;
+ }
+
+ /* If the name of the key is not specified, */
+ /* let us check the name of the first key part. */
+ if ((keyname= key->name.str) == NULL)
+ {
+ if (key->type == Key::PRIMARY)
+ keyname= primary_key_name;
+ else
+ {
+ List_iterator<Key_part_spec> part_it(key->columns);
+ Key_part_spec *kp;
+ if ((kp= part_it++))
+ keyname= kp->field_name.str;
+ if (keyname == NULL)
+ continue;
+ }
+ }
+ if (key->type != Key::FOREIGN_KEY)
+ {
+ for (n_key=0; n_key < table->s->keys; n_key++)
+ {
+ if (my_strcasecmp(system_charset_info,
+ keyname, table->key_info[n_key].name) == 0)
+ {
+ goto remove_key;
+ }
+ }
+ }
+ else
+ {
+ List <FOREIGN_KEY_INFO> fk_child_key_list;
+ FOREIGN_KEY_INFO *f_key;
+ table->file->get_foreign_key_list(thd, &fk_child_key_list);
+ List_iterator<FOREIGN_KEY_INFO> fk_key_it(fk_child_key_list);
+ while ((f_key= fk_key_it++))
+ {
+ if (my_strcasecmp(system_charset_info, f_key->foreign_id->str,
+ key->name.str) == 0)
+ goto remove_key;
+ }
+ }
+
+ {
+ Key *chk_key;
+ List_iterator<Key> chk_it(alter_info->key_list);
+ const char *chkname;
+ while ((chk_key=chk_it++) && chk_key != key)
+ {
+ if ((chkname= chk_key->name.str) == NULL)
+ {
+ List_iterator<Key_part_spec> part_it(chk_key->columns);
+ Key_part_spec *kp;
+ if ((kp= part_it++))
+ chkname= kp->field_name.str;
+ if (keyname == NULL)
+ continue;
+ }
+ if (key->type == chk_key->type &&
+ my_strcasecmp(system_charset_info, keyname, chkname) == 0)
+ goto remove_key;
+ }
+ }
+ continue;
+
+remove_key:
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
+ ER_DUP_KEYNAME, ER(ER_DUP_KEYNAME), keyname);
+remove_key_no_warn:
+ key_it.remove();
+ if (key->type == Key::FOREIGN_KEY)
+ {
+ /* ADD FOREIGN KEY appends two items. */
+ key_it.remove();
+ }
+ if (alter_info->key_list.is_empty())
+ alter_info->flags&= ~(Alter_info::ALTER_ADD_INDEX |
+ Alter_info::ADD_FOREIGN_KEY);
+ }
+ }
+
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+ partition_info *tab_part_info= table->part_info;
+ if (tab_part_info && thd->lex->check_exists)
+ {
+ /* ALTER TABLE ADD PARTITION IF NOT EXISTS */
+ if (alter_info->flags & Alter_info::ALTER_ADD_PARTITION)
+ {
+ partition_info *alt_part_info= thd->lex->part_info;
+ if (alt_part_info)
+ {
+ List_iterator<partition_element> new_part_it(alt_part_info->partitions);
+ partition_element *pe;
+ while ((pe= new_part_it++))
+ {
+ if (!tab_part_info->has_unique_name(pe))
+ {
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
+ ER_SAME_NAME_PARTITION, ER(ER_SAME_NAME_PARTITION),
+ pe->partition_name);
+ alter_info->flags&= ~Alter_info::ALTER_ADD_PARTITION;
+ thd->lex->part_info= NULL;
+ break;
+ }
+ }
+ }
+ }
+ /* ALTER TABLE DROP PARTITION IF EXISTS */
+ if (alter_info->flags & Alter_info::ALTER_DROP_PARTITION)
+ {
+ List_iterator<char> names_it(alter_info->partition_names);
+ char *name;
+
+ while ((name= names_it++))
+ {
+ List_iterator<partition_element> part_it(tab_part_info->partitions);
+ partition_element *part_elem;
+ while ((part_elem= part_it++))
+ {
+ if (my_strcasecmp(system_charset_info,
+ part_elem->partition_name, name) == 0)
+ break;
+ }
+ if (!part_elem)
+ {
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
+ ER_DROP_PARTITION_NON_EXISTENT,
+ ER(ER_DROP_PARTITION_NON_EXISTENT), "DROP");
+ names_it.remove();
+ }
+ }
+ if (alter_info->partition_names.elements == 0)
+ alter_info->flags&= ~Alter_info::ALTER_DROP_PARTITION;
+ }
+ }
+#endif /*WITH_PARTITION_STORAGE_ENGINE*/
+
+ DBUG_VOID_RETURN;
}
-/*
- SYNOPSIS
- mysql_compare_tables()
- table The original table.
- alter_info Alter options, fields and keys for the new
- table.
- create_info Create options for the new table.
- order_num Number of order list elements.
- need_copy_table OUT Result of the comparison. Undefined if error.
- Otherwise is one of:
- ALTER_TABLE_METADATA_ONLY No copy needed
- ALTER_TABLE_DATA_CHANGED Data changes,
- copy needed
- ALTER_TABLE_INDEX_CHANGED Index changes,
- copy might be needed
- key_info_buffer OUT An array of KEY structs for new indexes
- index_drop_buffer OUT An array of offsets into table->key_info.
- index_drop_count OUT The number of elements in the array.
- index_add_buffer OUT An array of offsets into key_info_buffer.
- index_add_count OUT The number of elements in the array.
- candidate_key_count OUT The number of candidate keys in original table.
+/**
+ Get Create_field object for newly created table by field index.
- DESCRIPTION
- 'table' (first argument) contains information of the original
- table, which includes all corresponding parts that the new
- table has in arguments create_list, key_list and create_info.
+ @param alter_info Alter_info describing newly created table.
+ @param idx Field index.
+*/
- By comparing the changes between the original and new table
- we can determine how much it has changed after ALTER TABLE
- and whether we need to make a copy of the table, or just change
- the .frm file.
+static Create_field *get_field_by_index(Alter_info *alter_info, uint idx)
+{
+ List_iterator_fast<Create_field> field_it(alter_info->create_list);
+ uint field_idx= 0;
+ Create_field *field;
- If there are no data changes, but index changes, 'index_drop_buffer'
- and/or 'index_add_buffer' are populated with offsets into
- table->key_info or key_info_buffer respectively for the indexes
- that need to be dropped and/or (re-)created.
+ while ((field= field_it++) && field_idx < idx)
+ { field_idx++; }
- RETURN VALUES
- TRUE The tables are not compatible; We have to do a full alter table
- FALSE The tables are compatible; We only have to modify the .frm
+ return field;
+}
+
+
+static int compare_uint(const uint *s, const uint *t)
+{
+ return (*s < *t) ? -1 : ((*s > *t) ? 1 : 0);
+}
+
+
+/**
+ Compare original and new versions of a table and fill Alter_inplace_info
+ describing differences between those versions.
+
+ @param thd Thread
+ @param table The original table.
+ @param varchar Indicates that new definition has new
+ VARCHAR column.
+ @param[in/out] ha_alter_info Data structure which already contains
+ basic information about create options,
+ field and keys for the new version of
+ table and which should be completed with
+ more detailed information needed for
+ in-place ALTER.
+
+ First argument 'table' contains information of the original
+ table, which includes all corresponding parts that the new
+ table has in arguments create_list, key_list and create_info.
+
+ Compare the changes between the original and new table definitions.
+ The result of this comparison is then passed to SE which determines
+ whether it can carry out these changes in-place.
+
+ Mark any changes detected in the ha_alter_flags.
+ We generally try to specify handler flags only if there are real
+ changes. But in cases when it is cumbersome to determine if some
+ attribute has really changed we might choose to set flag
+ pessimistically, for example, relying on parser output only.
+
+ If there are no data changes, but index changes, 'index_drop_buffer'
+ and/or 'index_add_buffer' are populated with offsets into
+ table->key_info or key_info_buffer respectively for the indexes
+ that need to be dropped and/or (re-)created.
+
+ Note that this function assumes that it is OK to change Alter_info
+ and HA_CREATE_INFO which it gets. It is caller who is responsible
+ for creating copies for this structures if he needs them unchanged.
+
+ @retval true error
+ @retval false success
*/
-bool
-mysql_compare_tables(TABLE *table,
- Alter_info *alter_info,
- HA_CREATE_INFO *create_info,
- uint order_num,
- enum_alter_table_change_level *need_copy_table,
- KEY **key_info_buffer,
- uint **index_drop_buffer, uint *index_drop_count,
- uint **index_add_buffer, uint *index_add_count,
- uint *candidate_key_count)
+static bool fill_alter_inplace_info(THD *thd,
+ TABLE *table,
+ bool varchar,
+ Alter_inplace_info *ha_alter_info)
{
Field **f_ptr, *field;
- uint changes= 0, tmp;
- uint key_count;
- List_iterator_fast<Create_field> new_field_it, tmp_new_field_it;
- Create_field *new_field, *tmp_new_field;
- KEY_PART_INFO *key_part;
+ List_iterator_fast<Create_field> new_field_it;
+ Create_field *new_field;
+ KEY_PART_INFO *key_part, *new_part;
KEY_PART_INFO *end;
- THD *thd= table->in_use;
- uint i;
+ uint candidate_key_count= 0;
+ Alter_info *alter_info= ha_alter_info->alter_info;
+ DBUG_ENTER("fill_alter_inplace_info");
+
+ /* Allocate result buffers. */
+ if (! (ha_alter_info->index_drop_buffer=
+ (KEY**) thd->alloc(sizeof(KEY*) * table->s->keys)) ||
+ ! (ha_alter_info->index_add_buffer=
+ (uint*) thd->alloc(sizeof(uint) *
+ alter_info->key_list.elements)))
+ DBUG_RETURN(true);
+
+ /* First we setup ha_alter_flags based on what was detected by parser. */
+ if (alter_info->flags & Alter_info::ALTER_ADD_COLUMN)
+ ha_alter_info->handler_flags|= Alter_inplace_info::ADD_COLUMN;
+ if (alter_info->flags & Alter_info::ALTER_DROP_COLUMN)
+ ha_alter_info->handler_flags|= Alter_inplace_info::DROP_COLUMN;
/*
- Remember if the new definition has new VARCHAR column;
- create_info->varchar will be reset in mysql_prepare_create_table.
+ Comparing new and old default values of column is cumbersome.
+ So instead of using such a comparison for detecting if default
+ has really changed we rely on flags set by parser to get an
+ approximate value for storage engine flag.
*/
- bool varchar= create_info->varchar;
- bool not_nullable= true;
- DBUG_ENTER("mysql_compare_tables");
+ if (alter_info->flags & (Alter_info::ALTER_CHANGE_COLUMN |
+ Alter_info::ALTER_CHANGE_COLUMN_DEFAULT))
+ ha_alter_info->handler_flags|= Alter_inplace_info::ALTER_COLUMN_DEFAULT;
+ if (alter_info->flags & Alter_info::ADD_FOREIGN_KEY)
+ ha_alter_info->handler_flags|= Alter_inplace_info::ADD_FOREIGN_KEY;
+ if (alter_info->flags & Alter_info::DROP_FOREIGN_KEY)
+ ha_alter_info->handler_flags|= Alter_inplace_info::DROP_FOREIGN_KEY;
+ if (alter_info->flags & Alter_info::ALTER_OPTIONS)
+ ha_alter_info->handler_flags|= Alter_inplace_info::CHANGE_CREATE_OPTION;
+ if (alter_info->flags & Alter_info::ALTER_RENAME)
+ ha_alter_info->handler_flags|= Alter_inplace_info::ALTER_RENAME;
+ /* Check partition changes */
+ if (alter_info->flags & Alter_info::ALTER_ADD_PARTITION)
+ ha_alter_info->handler_flags|= Alter_inplace_info::ADD_PARTITION;
+ if (alter_info->flags & Alter_info::ALTER_DROP_PARTITION)
+ ha_alter_info->handler_flags|= Alter_inplace_info::DROP_PARTITION;
+ if (alter_info->flags & Alter_info::ALTER_PARTITION)
+ ha_alter_info->handler_flags|= Alter_inplace_info::ALTER_PARTITION;
+ if (alter_info->flags & Alter_info::ALTER_COALESCE_PARTITION)
+ ha_alter_info->handler_flags|= Alter_inplace_info::COALESCE_PARTITION;
+ if (alter_info->flags & Alter_info::ALTER_REORGANIZE_PARTITION)
+ ha_alter_info->handler_flags|= Alter_inplace_info::REORGANIZE_PARTITION;
+ if (alter_info->flags & Alter_info::ALTER_TABLE_REORG)
+ ha_alter_info->handler_flags|= Alter_inplace_info::ALTER_TABLE_REORG;
+ if (alter_info->flags & Alter_info::ALTER_REMOVE_PARTITIONING)
+ ha_alter_info->handler_flags|= Alter_inplace_info::ALTER_REMOVE_PARTITIONING;
+ if (alter_info->flags & Alter_info::ALTER_ALL_PARTITION)
+ ha_alter_info->handler_flags|= Alter_inplace_info::ALTER_ALL_PARTITION;
+ /* Check for: ALTER TABLE FORCE, ALTER TABLE ENGINE and OPTIMIZE TABLE. */
+ if (alter_info->flags & Alter_info::ALTER_RECREATE)
+ ha_alter_info->handler_flags|= Alter_inplace_info::RECREATE_TABLE;
/*
- Create a copy of alter_info.
- To compare the new and old table definitions, we need to "prepare"
- the new definition - transform it from parser output to a format
- that describes the final table layout (all column defaults are
- initialized, duplicate columns are removed). This is done by
- mysql_prepare_create_table. Unfortunately,
- mysql_prepare_create_table performs its transformations
- "in-place", that is, modifies the argument. Since we would
- like to keep mysql_compare_tables() idempotent (not altering any
- of the arguments) we create a copy of alter_info here and
- pass it to mysql_prepare_create_table, then use the result
- to evaluate possibility of in-place ALTER TABLE, and then
- destroy the copy.
+ If we altering table with old VARCHAR fields we will be automatically
+ upgrading VARCHAR column types.
*/
- Alter_info tmp_alter_info(*alter_info, thd->mem_root);
- uint db_options= 0; /* not used */
-
- /* Set default value for return value (to ensure it's always set) */
- *need_copy_table= ALTER_TABLE_DATA_CHANGED;
+ if (table->s->frm_version < FRM_VER_TRUE_VARCHAR && varchar)
+ ha_alter_info->handler_flags|= Alter_inplace_info::ALTER_COLUMN_TYPE;
- /* Create the prepared information. */
- if (mysql_prepare_create_table(thd, create_info,
- &tmp_alter_info,
- (table->s->tmp_table != NO_TMP_TABLE),
- &db_options,
- table->file, key_info_buffer,
- &key_count, 0))
- DBUG_RETURN(1);
- /* Allocate result buffers. */
- if (! (*index_drop_buffer=
- (uint*) thd->alloc(sizeof(uint) * table->s->keys)) ||
- ! (*index_add_buffer=
- (uint*) thd->alloc(sizeof(uint) * tmp_alter_info.key_list.elements)))
- DBUG_RETURN(1);
-
/*
- Some very basic checks. If number of fields changes, or the
- handler, we need to run full ALTER TABLE. In the future
- new fields can be added and old dropped without copy, but
- not yet.
+ Go through fields in old version of table and detect changes to them.
+ We don't want to rely solely on Alter_info flags for this since:
+ a) new definition of column can be fully identical to the old one
+ despite the fact that this column is mentioned in MODIFY clause.
+ b) even if new column type differs from its old column from metadata
+ point of view, it might be identical from storage engine point
+ of view (e.g. when ENUM('a','b') is changed to ENUM('a','b',c')).
+ c) flags passed to storage engine contain more detailed information
+ about nature of changes than those provided from parser.
+ */
+ bool maybe_alter_vcol= false;
+ for (f_ptr= table->field; (field= *f_ptr); f_ptr++)
+ {
+ /* Clear marker for renamed or dropped field
+ which we are going to set later. */
+ field->flags&= ~(FIELD_IS_RENAMED | FIELD_IS_DROPPED);
- Test also that engine was not given during ALTER TABLE, or
- we are force to run regular alter table (copy).
- E.g. ALTER TABLE tbl_name ENGINE=MyISAM.
+ /* Use transformed info to evaluate flags for storage engine. */
+ uint new_field_index= 0;
+ new_field_it.init(alter_info->create_list);
+ while ((new_field= new_field_it++))
+ {
+ if (new_field->field == field)
+ break;
+ new_field_index++;
+ }
- For the following ones we also want to run regular alter table:
- ALTER TABLE tbl_name ORDER BY ..
- ALTER TABLE tbl_name CONVERT TO CHARACTER SET ..
+ if (new_field)
+ {
+ /* Field is not dropped. Evaluate changes bitmap for it. */
- At the moment we can't handle altering temporary tables without a copy.
- We also test if OPTIMIZE TABLE was given and was mapped to alter table.
- In that case we always do full copy.
+ /*
+ Check if type of column has changed to some incompatible type.
+ */
+ uint is_equal= field->is_equal(new_field);
+ switch (is_equal)
+ {
+ case IS_EQUAL_NO:
+ /* New column type is incompatible with old one. */
+ ha_alter_info->handler_flags|= Alter_inplace_info::ALTER_COLUMN_TYPE;
+ if (table->s->tmp_table == NO_TMP_TABLE)
+ {
+ delete_statistics_for_column(thd, table, field);
+ KEY *key_info= table->key_info;
+ for (uint i=0; i < table->s->keys; i++, key_info++)
+ {
+ if (field->part_of_key.is_set(i))
+ {
+ uint key_parts= table->actual_n_key_parts(key_info);
+ for (uint j= 0; j < key_parts; j++)
+ {
+ if (key_info->key_part[j].fieldnr-1 == field->field_index)
+ {
+ delete_statistics_for_index(thd, table, key_info,
+ j >= key_info->user_defined_key_parts);
+ break;
+ }
+ }
+ }
+ }
+ }
+ break;
+ case IS_EQUAL_YES:
+ /*
+ New column is the same as the old one or the fully compatible with
+ it (for example, ENUM('a','b') was changed to ENUM('a','b','c')).
+ Such a change if any can ALWAYS be carried out by simply updating
+ data-dictionary without even informing storage engine.
+ No flag is set in this case.
+ */
+ break;
+ case IS_EQUAL_PACK_LENGTH:
+ /*
+ New column type differs from the old one, but has compatible packed
+ data representation. Depending on storage engine, such a change can
+ be carried out by simply updating data dictionary without changing
+ actual data (for example, VARCHAR(300) is changed to VARCHAR(400)).
+ */
+ ha_alter_info->handler_flags|= Alter_inplace_info::
+ ALTER_COLUMN_EQUAL_PACK_LENGTH;
+ break;
+ default:
+ DBUG_ASSERT(0);
+ /* Safety. */
+ ha_alter_info->handler_flags|= Alter_inplace_info::ALTER_COLUMN_TYPE;
+ }
- There was a bug prior to mysql-4.0.25. Number of null fields was
- calculated incorrectly. As a result frm and data files gets out of
- sync after in-place alter table. There is no way to determine by which
- mysql version (in 4.0 and 4.1 branches) table was created, thus we
- disable in-place alter table for all tables created by mysql versions
- prior to 5.0 branch.
- See BUG#6236.
- */
- if (table->s->fields != alter_info->create_list.elements ||
- table->s->db_type() != create_info->db_type ||
- table->s->tmp_table ||
- create_info->used_fields & HA_CREATE_USED_ENGINE ||
- create_info->used_fields & HA_CREATE_USED_CHARSET ||
- create_info->used_fields & HA_CREATE_USED_DEFAULT_CHARSET ||
- (table->s->row_type != create_info->row_type) ||
- create_info->used_fields & HA_CREATE_USED_PAGE_CHECKSUM ||
- create_info->used_fields & HA_CREATE_USED_TRANSACTIONAL ||
- create_info->used_fields & HA_CREATE_USED_PACK_KEYS ||
- create_info->used_fields & HA_CREATE_USED_MAX_ROWS ||
- (alter_info->flags & (ALTER_RECREATE | ALTER_FOREIGN_KEY)) ||
- order_num ||
- !table->s->mysql_version ||
- (table->s->frm_version < FRM_VER_TRUE_VARCHAR && varchar))
- {
- DBUG_PRINT("info", ("Basic checks -> ALTER_TABLE_DATA_CHANGED"));
- DBUG_RETURN(0);
- }
+ /*
+ Check if the column is computed and either
+ is stored or is used in the partitioning expression.
+ */
+ if (field->vcol_info &&
+ (field->stored_in_db || field->vcol_info->is_in_partitioning_expr()))
+ {
+ if (is_equal == IS_EQUAL_NO ||
+ !field->vcol_info->is_equal(new_field->vcol_info))
+ ha_alter_info->handler_flags|= Alter_inplace_info::ALTER_COLUMN_VCOL;
+ else
+ maybe_alter_vcol= true;
+ }
- if ((create_info->fields_option_struct= (ha_field_option_struct**)
- thd->calloc(sizeof(void*) * table->s->fields)) == NULL ||
- (create_info->indexes_option_struct= (ha_index_option_struct**)
- thd->calloc(sizeof(void*) * table->s->keys)) == NULL)
- DBUG_RETURN(1);
+ /* Check if field was renamed */
+ if (my_strcasecmp(system_charset_info, field->field_name,
+ new_field->field_name))
+ {
+ field->flags|= FIELD_IS_RENAMED;
+ ha_alter_info->handler_flags|= Alter_inplace_info::ALTER_COLUMN_NAME;
+ rename_column_in_stat_tables(thd, table, field,
+ new_field->field_name);
+ }
- /*
- Use transformed info to evaluate possibility of in-place ALTER TABLE
- but use the preserved field to persist modifications.
- */
- new_field_it.init(alter_info->create_list);
- tmp_new_field_it.init(tmp_alter_info.create_list);
+ /* Check that NULL behavior is same for old and new fields */
+ if ((new_field->flags & NOT_NULL_FLAG) !=
+ (uint) (field->flags & NOT_NULL_FLAG))
+ {
+ if (new_field->flags & NOT_NULL_FLAG)
+ ha_alter_info->handler_flags|=
+ Alter_inplace_info::ALTER_COLUMN_NOT_NULLABLE;
+ else
+ ha_alter_info->handler_flags|=
+ Alter_inplace_info::ALTER_COLUMN_NULLABLE;
+ }
- /*
- Go through fields and check if the original ones are compatible
- with new table.
- */
- for (i= 0, f_ptr= table->field, new_field= new_field_it++,
- tmp_new_field= tmp_new_field_it++;
- (field= *f_ptr);
- i++, f_ptr++, new_field= new_field_it++,
- tmp_new_field= tmp_new_field_it++)
- {
- DBUG_ASSERT(i < table->s->fields);
- create_info->fields_option_struct[i]= tmp_new_field->option_struct;
+ /*
+ We do not detect changes to default values in this loop.
+ See comment above for more details.
+ */
- /* reset common markers of how field changed */
- field->flags&= ~(FIELD_IS_RENAMED | FIELD_IN_ADD_INDEX);
+ /*
+ Detect changes in column order.
+ */
+ if (field->field_index != new_field_index)
+ ha_alter_info->handler_flags|= Alter_inplace_info::ALTER_COLUMN_ORDER;
- /* Make sure we have at least the default charset in use. */
- if (!new_field->charset)
- new_field->charset= create_info->default_table_charset;
+ /* Detect changes in storage type of column */
+ if (new_field->field_storage_type() != field->field_storage_type())
+ ha_alter_info->handler_flags|=
+ Alter_inplace_info::ALTER_COLUMN_STORAGE_TYPE;
- /* Check that NULL behavior is same for old and new fields */
- if ((tmp_new_field->flags & NOT_NULL_FLAG) !=
- (uint) (field->flags & NOT_NULL_FLAG))
+ /* Detect changes in column format of column */
+ if (new_field->column_format() != field->column_format())
+ ha_alter_info->handler_flags|=
+ Alter_inplace_info::ALTER_COLUMN_COLUMN_FORMAT;
+
+ if (engine_options_differ(field->option_struct, new_field->option_struct,
+ table->file->ht->field_options))
+ {
+ ha_alter_info->handler_flags|= Alter_inplace_info::ALTER_COLUMN_OPTION;
+ ha_alter_info->create_info->fields_option_struct[f_ptr - table->field]=
+ new_field->option_struct;
+ }
+
+ }
+ else
{
- DBUG_PRINT("info", ("NULL behaviour difference in field '%s' -> "
- "ALTER_TABLE_DATA_CHANGED", new_field->field_name));
- DBUG_RETURN(0);
+ /*
+ Field is not present in new version of table and therefore was dropped.
+ Corresponding storage engine flag should be already set.
+ */
+ DBUG_ASSERT(ha_alter_info->handler_flags & Alter_inplace_info::DROP_COLUMN);
+ field->flags|= FIELD_IS_DROPPED;
}
+ }
+ if (maybe_alter_vcol)
+ {
/*
- Check if the altered column is computed and either
- is stored or is used in the partitioning expression.
- TODO: Mark such a column with an alter flag only if
- the defining expression has changed.
+ No virtual column was altered, but perhaps one of the other columns was,
+ and that column was part of the vcol expression?
+ We don't detect this correctly (FIXME), so let's just say that a vcol
+ *might* be affected if any other column was altered.
*/
- if (field->vcol_info &&
- (field->stored_in_db || field->vcol_info->is_in_partitioning_expr()))
- {
- *need_copy_table= ALTER_TABLE_DATA_CHANGED;
- DBUG_RETURN(0);
- }
-
- /* Don't pack rows in old tables if the user has requested this. */
- if (create_info->row_type == ROW_TYPE_DYNAMIC ||
- (tmp_new_field->flags & BLOB_FLAG) ||
- (tmp_new_field->sql_type == MYSQL_TYPE_VARCHAR &&
- create_info->row_type != ROW_TYPE_FIXED))
- create_info->table_options|= HA_OPTION_PACK_RECORD;
-
- /* Check if field was renamed */
- if (my_strcasecmp(system_charset_info,
- field->field_name,
- tmp_new_field->field_name))
- field->flags|= FIELD_IS_RENAMED;
+ if (ha_alter_info->handler_flags &
+ ( Alter_inplace_info::ALTER_COLUMN_TYPE
+ | Alter_inplace_info::ALTER_COLUMN_NOT_NULLABLE
+ | Alter_inplace_info::ALTER_COLUMN_OPTION ))
+ ha_alter_info->handler_flags|= Alter_inplace_info::ALTER_COLUMN_VCOL;
+ }
- /* Evaluate changes bitmap and send to check_if_incompatible_data() */
- if (!(tmp= field->is_equal(tmp_new_field)))
+ new_field_it.init(alter_info->create_list);
+ while ((new_field= new_field_it++))
+ {
+ if (! new_field->field)
{
- DBUG_PRINT("info", ("!field_is_equal('%s') -> ALTER_TABLE_DATA_CHANGED",
- new_field->field_name));
- DBUG_RETURN(0);
+ /*
+ Field is not present in old version of table and therefore was added.
+ Again corresponding storage engine flag should be already set.
+ */
+ DBUG_ASSERT(ha_alter_info->handler_flags & Alter_inplace_info::ADD_COLUMN);
+
+ if (new_field->vcol_info &&
+ (new_field->stored_in_db || new_field->vcol_info->is_in_partitioning_expr()))
+ {
+ ha_alter_info->handler_flags|= Alter_inplace_info::ALTER_COLUMN_VCOL;
+ }
+ break;
}
- changes|= tmp;
}
/*
@@ -5359,90 +6428,90 @@ mysql_compare_tables(TABLE *table,
KEY *table_key;
KEY *table_key_end= table->key_info + table->s->keys;
KEY *new_key;
- KEY *new_key_end= *key_info_buffer + key_count;
+ KEY *new_key_end=
+ ha_alter_info->key_info_buffer + ha_alter_info->key_count;
DBUG_PRINT("info", ("index count old: %d new: %d",
- table->s->keys, key_count));
+ table->s->keys, ha_alter_info->key_count));
+
/*
Step through all keys of the old table and search matching new keys.
*/
- *index_drop_count= 0;
- *index_add_count= 0;
- *candidate_key_count= 0;
+ ha_alter_info->index_drop_count= 0;
+ ha_alter_info->index_add_count= 0;
for (table_key= table->key_info; table_key < table_key_end; table_key++)
{
- KEY_PART_INFO *table_part;
- KEY_PART_INFO *table_part_end= table_key->key_part + table_key->key_parts;
- KEY_PART_INFO *new_part;
-
- /*
- Check if key is a candidate key, i.e. a unique index with no index
- fields nullable, then key is either already primary key or could
- be promoted to primary key if the original primary key is dropped.
- Count all candidate keys.
- */
- not_nullable= true;
- for (table_part= table_key->key_part;
- table_part < table_part_end;
- table_part++)
- {
- not_nullable= not_nullable && (! table_part->field->maybe_null());
- }
- if ((table_key->flags & HA_NOSAME) && not_nullable)
- (*candidate_key_count)++;
-
/* Search a new key with the same name. */
- for (new_key= *key_info_buffer; new_key < new_key_end; new_key++)
+ for (new_key= ha_alter_info->key_info_buffer;
+ new_key < new_key_end;
+ new_key++)
{
if (! strcmp(table_key->name, new_key->name))
break;
}
if (new_key >= new_key_end)
{
- /* Key not found. Add the offset of the key to the drop buffer. */
- (*index_drop_buffer)[(*index_drop_count)++]= table_key - table->key_info;
+ /* Key not found. Add the key to the drop buffer. */
+ ha_alter_info->index_drop_buffer
+ [ha_alter_info->index_drop_count++]=
+ table_key;
DBUG_PRINT("info", ("index dropped: '%s'", table_key->name));
continue;
}
/* Check that the key types are compatible between old and new tables. */
if ((table_key->algorithm != new_key->algorithm) ||
- ((table_key->flags & HA_KEYFLAG_MASK) !=
+ ((table_key->flags & HA_KEYFLAG_MASK) !=
(new_key->flags & HA_KEYFLAG_MASK)) ||
- (table_key->key_parts != new_key->key_parts))
+ (table_key->user_defined_key_parts !=
+ new_key->user_defined_key_parts))
+ goto index_changed;
+
+ if (engine_options_differ(table_key->option_struct, new_key->option_struct,
+ table->file->ht->index_options))
goto index_changed;
/*
Check that the key parts remain compatible between the old and
new tables.
*/
- for (table_part= table_key->key_part, new_part= new_key->key_part;
- table_part < table_part_end;
- table_part++, new_part++)
+ end= table_key->key_part + table_key->user_defined_key_parts;
+ for (key_part= table_key->key_part, new_part= new_key->key_part;
+ key_part < end;
+ key_part++, new_part++)
{
/*
- Key definition has changed if we are using a different field or
- if the used key part length is different. We know that the fields
- did not change. Comparing field numbers is sufficient.
+ Key definition has changed if we are using a different field or
+ if the used key part length is different. It makes sense to
+ check lengths first as in case when fields differ it is likely
+ that lengths differ too and checking fields is more expensive
+ in general case.
*/
- if ((table_part->length != new_part->length) ||
- (table_part->fieldnr - 1 != new_part->fieldnr))
- goto index_changed;
+ if (key_part->length != new_part->length)
+ goto index_changed;
+
+ new_field= get_field_by_index(alter_info, new_part->fieldnr);
+
+ /*
+ For prefix keys KEY_PART_INFO::field points to cloned Field
+ object with adjusted length. So below we have to check field
+ indexes instead of simply comparing pointers to Field objects.
+ */
+ if (! new_field->field ||
+ new_field->field->field_index != key_part->fieldnr - 1)
+ goto index_changed;
}
continue;
index_changed:
- /* Key modified. Add the offset of the key to both buffers. */
- (*index_drop_buffer)[(*index_drop_count)++]= table_key - table->key_info;
- (*index_add_buffer)[(*index_add_count)++]= new_key - *key_info_buffer;
- key_part= new_key->key_part;
- end= key_part + new_key->key_parts;
- for(; key_part != end; key_part++)
- {
- // Mark field to be part of new key
- field= table->field[key_part->fieldnr];
- field->flags|= FIELD_IN_ADD_INDEX;
- }
+ /* Key modified. Add the key / key offset to both buffers. */
+ ha_alter_info->index_drop_buffer
+ [ha_alter_info->index_drop_count++]=
+ table_key;
+ ha_alter_info->index_add_buffer
+ [ha_alter_info->index_add_count++]=
+ new_key - ha_alter_info->key_info_buffer;
+ /* Mark all old fields which are used in newly created index. */
DBUG_PRINT("info", ("index changed: '%s'", table_key->name));
}
/*end of for (; table_key < table_key_end;) */
@@ -5450,12 +6519,12 @@ mysql_compare_tables(TABLE *table,
/*
Step through all keys of the new table and find matching old keys.
*/
- for (new_key= *key_info_buffer; new_key < new_key_end; new_key++)
+ for (new_key= ha_alter_info->key_info_buffer;
+ new_key < new_key_end;
+ new_key++)
{
/* Search an old key with the same name. */
- for (i= 0, table_key= table->key_info;
- table_key < table_key_end;
- i++, table_key++)
+ for (table_key= table->key_info; table_key < table_key_end; table_key++)
{
if (! strcmp(table_key->name, new_key->name))
break;
@@ -5463,44 +6532,310 @@ mysql_compare_tables(TABLE *table,
if (table_key >= table_key_end)
{
/* Key not found. Add the offset of the key to the add buffer. */
- (*index_add_buffer)[(*index_add_count)++]= new_key - *key_info_buffer;
- key_part= new_key->key_part;
- end= key_part + new_key->key_parts;
- for(; key_part != end; key_part++)
+ ha_alter_info->index_add_buffer
+ [ha_alter_info->index_add_count++]=
+ new_key - ha_alter_info->key_info_buffer;
+ DBUG_PRINT("info", ("index added: '%s'", new_key->name));
+ }
+ else
+ ha_alter_info->create_info->indexes_option_struct[table_key - table->key_info]=
+ new_key->option_struct;
+ }
+
+ /*
+ Sort index_add_buffer according to how key_info_buffer is sorted.
+ I.e. with primary keys first - see sort_keys().
+ */
+ my_qsort(ha_alter_info->index_add_buffer,
+ ha_alter_info->index_add_count,
+ sizeof(uint), (qsort_cmp) compare_uint);
+
+ /* Now let us calculate flags for storage engine API. */
+
+ /* Count all existing candidate keys. */
+ for (table_key= table->key_info; table_key < table_key_end; table_key++)
+ {
+ /*
+ Check if key is a candidate key, This key is either already primary key
+ or could be promoted to primary key if the original primary key is
+ dropped.
+ In MySQL one is allowed to create primary key with partial fields (i.e.
+ primary key which is not considered candidate). For simplicity we count
+ such key as a candidate key here.
+ */
+ if (((uint) (table_key - table->key_info) == table->s->primary_key) ||
+ is_candidate_key(table_key))
+ candidate_key_count++;
+ }
+
+ /* Figure out what kind of indexes we are dropping. */
+ KEY **dropped_key;
+ KEY **dropped_key_end= ha_alter_info->index_drop_buffer +
+ ha_alter_info->index_drop_count;
+
+ for (dropped_key= ha_alter_info->index_drop_buffer;
+ dropped_key < dropped_key_end; dropped_key++)
+ {
+ table_key= *dropped_key;
+
+ if (table_key->flags & HA_NOSAME)
+ {
+ /*
+ Unique key. Check for PRIMARY KEY. Also see comment about primary
+ and candidate keys above.
+ */
+ if ((uint) (table_key - table->key_info) == table->s->primary_key)
{
- // Mark field to be part of new key
- field= table->field[key_part->fieldnr];
- field->flags|= FIELD_IN_ADD_INDEX;
+ ha_alter_info->handler_flags|= Alter_inplace_info::DROP_PK_INDEX;
+ candidate_key_count--;
+ }
+ else
+ {
+ ha_alter_info->handler_flags|= Alter_inplace_info::DROP_UNIQUE_INDEX;
+ if (is_candidate_key(table_key))
+ candidate_key_count--;
}
- DBUG_PRINT("info", ("index added: '%s'", new_key->name));
}
else
+ ha_alter_info->handler_flags|= Alter_inplace_info::DROP_INDEX;
+ }
+
+ /* Now figure out what kind of indexes we are adding. */
+ for (uint add_key_idx= 0; add_key_idx < ha_alter_info->index_add_count; add_key_idx++)
+ {
+ new_key= ha_alter_info->key_info_buffer + ha_alter_info->index_add_buffer[add_key_idx];
+
+ if (new_key->flags & HA_NOSAME)
{
- DBUG_ASSERT(i < table->s->keys);
- create_info->indexes_option_struct[i]= new_key->option_struct;
+ bool is_pk= !my_strcasecmp(system_charset_info, new_key->name, primary_key_name);
+
+ if ((!(new_key->flags & HA_KEY_HAS_PART_KEY_SEG) &&
+ !(new_key->flags & HA_NULL_PART_KEY)) ||
+ is_pk)
+ {
+ /* Candidate key or primary key! */
+ if (candidate_key_count == 0 || is_pk)
+ ha_alter_info->handler_flags|= Alter_inplace_info::ADD_PK_INDEX;
+ else
+ ha_alter_info->handler_flags|= Alter_inplace_info::ADD_UNIQUE_INDEX;
+ candidate_key_count++;
+ }
+ else
+ {
+ ha_alter_info->handler_flags|= Alter_inplace_info::ADD_UNIQUE_INDEX;
+ }
}
+ else
+ ha_alter_info->handler_flags|= Alter_inplace_info::ADD_INDEX;
+ }
+
+ DBUG_RETURN(false);
+}
+
+
+/**
+ Mark fields participating in newly added indexes in TABLE object which
+ corresponds to new version of altered table.
+
+ @param ha_alter_info Alter_inplace_info describing in-place ALTER.
+ @param altered_table TABLE object for new version of TABLE in which
+ fields should be marked.
+*/
+
+static void update_altered_table(const Alter_inplace_info &ha_alter_info,
+ TABLE *altered_table)
+{
+ uint field_idx, add_key_idx;
+ KEY *key;
+ KEY_PART_INFO *end, *key_part;
+
+ /*
+ Clear marker for all fields, as we are going to set it only
+ for fields which participate in new indexes.
+ */
+ for (field_idx= 0; field_idx < altered_table->s->fields; ++field_idx)
+ altered_table->field[field_idx]->flags&= ~FIELD_IN_ADD_INDEX;
+
+ /*
+ Go through array of newly added indexes and mark fields
+ participating in them.
+ */
+ for (add_key_idx= 0; add_key_idx < ha_alter_info.index_add_count;
+ add_key_idx++)
+ {
+ key= ha_alter_info.key_info_buffer +
+ ha_alter_info.index_add_buffer[add_key_idx];
+
+ end= key->key_part + key->user_defined_key_parts;
+ for (key_part= key->key_part; key_part < end; key_part++)
+ altered_table->field[key_part->fieldnr]->flags|= FIELD_IN_ADD_INDEX;
+ }
+}
+
+
+/**
+ Compare two tables to see if their metadata are compatible.
+ One table specified by a TABLE instance, the other using Alter_info
+ and HA_CREATE_INFO.
+
+ @param[in] table The first table.
+ @param[in] alter_info Alter options, fields and keys for the
+ second table.
+ @param[in] create_info Create options for the second table.
+ @param[out] metadata_equal Result of comparison.
+
+ @retval true error
+ @retval false success
+*/
+
+bool mysql_compare_tables(TABLE *table,
+ Alter_info *alter_info,
+ HA_CREATE_INFO *create_info,
+ bool *metadata_equal)
+{
+ DBUG_ENTER("mysql_compare_tables");
+
+ uint changes= IS_EQUAL_NO;
+ uint key_count;
+ List_iterator_fast<Create_field> tmp_new_field_it;
+ THD *thd= table->in_use;
+ *metadata_equal= false;
+
+ /*
+ Create a copy of alter_info.
+ To compare definitions, we need to "prepare" the definition - transform it
+ from parser output to a format that describes the table layout (all column
+ defaults are initialized, duplicate columns are removed). This is done by
+ mysql_prepare_create_table. Unfortunately, mysql_prepare_create_table
+ performs its transformations "in-place", that is, modifies the argument.
+ Since we would like to keep mysql_compare_tables() idempotent (not altering
+ any of the arguments) we create a copy of alter_info here and pass it to
+ mysql_prepare_create_table, then use the result to compare the tables, and
+ then destroy the copy.
+ */
+ Alter_info tmp_alter_info(*alter_info, thd->mem_root);
+ uint db_options= 0; /* not used */
+ KEY *key_info_buffer= NULL;
+
+ /* Create the prepared information. */
+ int create_table_mode= table->s->tmp_table == NO_TMP_TABLE ?
+ C_ORDINARY_CREATE : C_ALTER_TABLE;
+ if (mysql_prepare_create_table(thd, create_info, &tmp_alter_info,
+ &db_options, table->file, &key_info_buffer,
+ &key_count, create_table_mode))
+ DBUG_RETURN(1);
+
+ /* Some very basic checks. */
+ if (table->s->fields != alter_info->create_list.elements ||
+ table->s->db_type() != create_info->db_type ||
+ table->s->tmp_table ||
+ (table->s->row_type != create_info->row_type))
+ DBUG_RETURN(false);
+
+ /* Go through fields and check if they are compatible. */
+ tmp_new_field_it.init(tmp_alter_info.create_list);
+ for (Field **f_ptr= table->field; *f_ptr; f_ptr++)
+ {
+ Field *field= *f_ptr;
+ Create_field *tmp_new_field= tmp_new_field_it++;
+
+ /* Check that NULL behavior is the same. */
+ if ((tmp_new_field->flags & NOT_NULL_FLAG) !=
+ (uint) (field->flags & NOT_NULL_FLAG))
+ DBUG_RETURN(false);
+
+ /*
+ mysql_prepare_alter_table() clears HA_OPTION_PACK_RECORD bit when
+ preparing description of existing table. In ALTER TABLE it is later
+ updated to correct value by create_table_impl() call.
+ So to get correct value of this bit in this function we have to
+ mimic behavior of create_table_impl().
+ */
+ if (create_info->row_type == ROW_TYPE_DYNAMIC ||
+ create_info->row_type == ROW_TYPE_PAGE ||
+ (tmp_new_field->flags & BLOB_FLAG) ||
+ (tmp_new_field->sql_type == MYSQL_TYPE_VARCHAR &&
+ create_info->row_type != ROW_TYPE_FIXED))
+ create_info->table_options|= HA_OPTION_PACK_RECORD;
+
+ /* Check if field was renamed */
+ if (my_strcasecmp(system_charset_info,
+ field->field_name,
+ tmp_new_field->field_name))
+ DBUG_RETURN(false);
+
+ /* Evaluate changes bitmap and send to check_if_incompatible_data() */
+ uint field_changes= field->is_equal(tmp_new_field);
+ if (field_changes != IS_EQUAL_YES)
+ DBUG_RETURN(false);
+
+ changes|= field_changes;
}
- /* Check if changes are compatible with current handler without a copy */
+ /* Check if changes are compatible with current handler. */
if (table->file->check_if_incompatible_data(create_info, changes))
+ DBUG_RETURN(false);
+
+ /* Go through keys and check if they are compatible. */
+ KEY *table_key;
+ KEY *table_key_end= table->key_info + table->s->keys;
+ KEY *new_key;
+ KEY *new_key_end= key_info_buffer + key_count;
+
+ /* Step through all keys of the first table and search matching keys. */
+ for (table_key= table->key_info; table_key < table_key_end; table_key++)
{
- DBUG_PRINT("info", ("check_if_incompatible_data() -> "
- "ALTER_TABLE_DATA_CHANGED"));
- DBUG_RETURN(0);
+ /* Search a key with the same name. */
+ for (new_key= key_info_buffer; new_key < new_key_end; new_key++)
+ {
+ if (! strcmp(table_key->name, new_key->name))
+ break;
+ }
+ if (new_key >= new_key_end)
+ DBUG_RETURN(false);
+
+ /* Check that the key types are compatible. */
+ if ((table_key->algorithm != new_key->algorithm) ||
+ ((table_key->flags & HA_KEYFLAG_MASK) !=
+ (new_key->flags & HA_KEYFLAG_MASK)) ||
+ (table_key->user_defined_key_parts !=
+ new_key->user_defined_key_parts))
+ DBUG_RETURN(false);
+
+ /* Check that the key parts remain compatible. */
+ KEY_PART_INFO *table_part;
+ KEY_PART_INFO *table_part_end= table_key->key_part + table_key->user_defined_key_parts;
+ KEY_PART_INFO *new_part;
+ for (table_part= table_key->key_part, new_part= new_key->key_part;
+ table_part < table_part_end;
+ table_part++, new_part++)
+ {
+ /*
+ Key definition is different if we are using a different field or
+ if the used key part length is different. We know that the fields
+ are equal. Comparing field numbers is sufficient.
+ */
+ if ((table_part->length != new_part->length) ||
+ (table_part->fieldnr - 1 != new_part->fieldnr))
+ DBUG_RETURN(false);
+ }
}
- if (*index_drop_count || *index_add_count)
+ /* Step through all keys of the second table and find matching keys. */
+ for (new_key= key_info_buffer; new_key < new_key_end; new_key++)
{
- DBUG_PRINT("info", ("Index dropped=%u added=%u -> "
- "ALTER_TABLE_INDEX_CHANGED",
- *index_drop_count, *index_add_count));
- *need_copy_table= ALTER_TABLE_INDEX_CHANGED;
- DBUG_RETURN(0);
+ /* Search a key with the same name. */
+ for (table_key= table->key_info; table_key < table_key_end; table_key++)
+ {
+ if (! strcmp(table_key->name, new_key->name))
+ break;
+ }
+ if (table_key >= table_key_end)
+ DBUG_RETURN(false);
}
- DBUG_PRINT("info", (" -> ALTER_TABLE_METADATA_ONLY"));
- *need_copy_table= ALTER_TABLE_METADATA_ONLY; // Tables are compatible
- DBUG_RETURN(0);
+ *metadata_equal= true; // Tables are compatible
+ DBUG_RETURN(false);
}
@@ -5521,7 +6856,7 @@ mysql_compare_tables(TABLE *table,
static
bool alter_table_manage_keys(TABLE *table, int indexes_were_disabled,
- enum enum_enable_or_disable keys_onoff)
+ Alter_info::enum_enable_or_disable keys_onoff)
{
int error= 0;
DBUG_ENTER("alter_table_manage_keys");
@@ -5529,22 +6864,24 @@ bool alter_table_manage_keys(TABLE *table, int indexes_were_disabled,
table, indexes_were_disabled, keys_onoff));
switch (keys_onoff) {
- case ENABLE:
+ case Alter_info::ENABLE:
+ DEBUG_SYNC(table->in_use, "alter_table_enable_indexes");
error= table->file->ha_enable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE);
break;
- case LEAVE_AS_IS:
+ case Alter_info::LEAVE_AS_IS:
if (!indexes_were_disabled)
break;
/* fall-through: disabled indexes */
- case DISABLE:
+ case Alter_info::DISABLE:
error= table->file->ha_disable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE);
}
if (error == HA_ERR_WRONG_COMMAND)
{
- push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
+ push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_NOTE,
ER_ILLEGAL_HA, ER(ER_ILLEGAL_HA),
- table->s->table_name.str);
+ table->file->table_type(),
+ table->s->db.str, table->s->table_name.str);
error= 0;
} else if (error)
table->file->print_error(error, MYF(0));
@@ -5552,6 +6889,388 @@ bool alter_table_manage_keys(TABLE *table, int indexes_were_disabled,
DBUG_RETURN(error);
}
+
+/**
+ Check if the pending ALTER TABLE operations support the in-place
+ algorithm based on restrictions in the SQL layer or given the
+ nature of the operations themselves. If in-place isn't supported,
+ it won't be necessary to check with the storage engine.
+
+ @param table The original TABLE.
+ @param create_info Information from the parsing phase about new
+ table properties.
+ @param alter_info Data related to detected changes.
+
+ @return false In-place is possible, check with storage engine.
+ @return true Incompatible operations, must use table copy.
+*/
+
+static bool is_inplace_alter_impossible(TABLE *table,
+ HA_CREATE_INFO *create_info,
+ const Alter_info *alter_info)
+{
+ DBUG_ENTER("is_inplace_alter_impossible");
+
+ /* At the moment we can't handle altering temporary tables without a copy. */
+ if (table->s->tmp_table)
+ DBUG_RETURN(true);
+
+ /*
+ For the ALTER TABLE tbl_name ORDER BY ... we always use copy
+ algorithm. In theory, this operation can be done in-place by some
+ engine, but since a) no current engine does this and b) our current
+ API lacks infrastructure for passing information about table ordering
+ to storage engine we simply always do copy now.
+
+ ENABLE/DISABLE KEYS is a MyISAM/Heap specific operation that is
+ not supported for in-place in combination with other operations.
+ Alone, it will be done by simple_rename_or_index_change().
+ */
+ if (alter_info->flags & (Alter_info::ALTER_ORDER |
+ Alter_info::ALTER_KEYS_ONOFF))
+ DBUG_RETURN(true);
+
+ /*
+ If the table engine is changed explicitly (using ENGINE clause)
+ or implicitly (e.g. when non-partitioned table becomes
+ partitioned) a regular alter table (copy) needs to be
+ performed.
+ */
+ if (create_info->db_type != table->s->db_type())
+ DBUG_RETURN(true);
+
+ /*
+ There was a bug prior to mysql-4.0.25. Number of null fields was
+ calculated incorrectly. As a result frm and data files gets out of
+ sync after fast alter table. There is no way to determine by which
+ mysql version (in 4.0 and 4.1 branches) table was created, thus we
+ disable fast alter table for all tables created by mysql versions
+ prior to 5.0 branch.
+ See BUG#6236.
+ */
+ if (!table->s->mysql_version)
+ DBUG_RETURN(true);
+
+ DBUG_RETURN(false);
+}
+
+
+/**
+ Perform in-place alter table.
+
+ @param thd Thread handle.
+ @param table_list TABLE_LIST for the table to change.
+ @param table The original TABLE.
+ @param altered_table TABLE object for new version of the table.
+ @param ha_alter_info Structure describing ALTER TABLE to be carried
+ out and serving as a storage place for data
+ used during different phases.
+ @param inplace_supported Enum describing the locking requirements.
+ @param target_mdl_request Metadata request/lock on the target table name.
+ @param alter_ctx ALTER TABLE runtime context.
+
+ @retval true Error
+ @retval false Success
+
+ @note
+ If mysql_alter_table does not need to copy the table, it is
+ either an alter table where the storage engine does not
+ need to know about the change, only the frm will change,
+ or the storage engine supports performing the alter table
+ operation directly, in-place without mysql having to copy
+ the table.
+
+ @note This function frees the TABLE object associated with the new version of
+ the table and removes the .FRM file for it in case of both success and
+ failure.
+*/
+
+static bool mysql_inplace_alter_table(THD *thd,
+ TABLE_LIST *table_list,
+ TABLE *table,
+ TABLE *altered_table,
+ Alter_inplace_info *ha_alter_info,
+ enum_alter_inplace_result inplace_supported,
+ MDL_request *target_mdl_request,
+ Alter_table_ctx *alter_ctx)
+{
+ Open_table_context ot_ctx(thd, MYSQL_OPEN_REOPEN | MYSQL_OPEN_IGNORE_KILLED);
+ handlerton *db_type= table->s->db_type();
+ MDL_ticket *mdl_ticket= table->mdl_ticket;
+ HA_CREATE_INFO *create_info= ha_alter_info->create_info;
+ Alter_info *alter_info= ha_alter_info->alter_info;
+ bool reopen_tables= false;
+
+ DBUG_ENTER("mysql_inplace_alter_table");
+
+ /*
+ Upgrade to EXCLUSIVE lock if:
+ - This is requested by the storage engine
+ - Or the storage engine needs exclusive lock for just the prepare
+ phase
+ - Or requested by the user
+
+ Note that we handle situation when storage engine needs exclusive
+ lock for prepare phase under LOCK TABLES in the same way as when
+ exclusive lock is required for duration of the whole statement.
+ */
+ if (inplace_supported == HA_ALTER_INPLACE_EXCLUSIVE_LOCK ||
+ ((inplace_supported == HA_ALTER_INPLACE_SHARED_LOCK_AFTER_PREPARE ||
+ inplace_supported == HA_ALTER_INPLACE_NO_LOCK_AFTER_PREPARE) &&
+ (thd->locked_tables_mode == LTM_LOCK_TABLES ||
+ thd->locked_tables_mode == LTM_PRELOCKED_UNDER_LOCK_TABLES)) ||
+ alter_info->requested_lock == Alter_info::ALTER_TABLE_LOCK_EXCLUSIVE)
+ {
+ if (wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN))
+ goto cleanup;
+ /*
+ Get rid of all TABLE instances belonging to this thread
+ except one to be used for in-place ALTER TABLE.
+
+ This is mostly needed to satisfy InnoDB assumptions/asserts.
+ */
+ close_all_tables_for_name(thd, table->s,
+ alter_ctx->is_table_renamed() ?
+ HA_EXTRA_PREPARE_FOR_RENAME :
+ HA_EXTRA_NOT_USED,
+ table);
+ /*
+ If we are under LOCK TABLES we will need to reopen tables which we
+ just have closed in case of error.
+ */
+ reopen_tables= true;
+ }
+ else if (inplace_supported == HA_ALTER_INPLACE_SHARED_LOCK_AFTER_PREPARE ||
+ inplace_supported == HA_ALTER_INPLACE_NO_LOCK_AFTER_PREPARE)
+ {
+ /*
+ Storage engine has requested exclusive lock only for prepare phase
+ and we are not under LOCK TABLES.
+ Don't mark TABLE_SHARE as old in this case, as this won't allow opening
+ of table by other threads during main phase of in-place ALTER TABLE.
+ */
+ if (thd->mdl_context.upgrade_shared_lock(table->mdl_ticket, MDL_EXCLUSIVE,
+ thd->variables.lock_wait_timeout))
+ goto cleanup;
+
+ tdc_remove_table(thd, TDC_RT_REMOVE_NOT_OWN_KEEP_SHARE,
+ table->s->db.str, table->s->table_name.str,
+ false);
+ }
+
+ /*
+ Upgrade to SHARED_NO_WRITE lock if:
+ - The storage engine needs writes blocked for the whole duration
+ - Or this is requested by the user
+ Note that under LOCK TABLES, we will already have SHARED_NO_READ_WRITE.
+ */
+ if ((inplace_supported == HA_ALTER_INPLACE_SHARED_LOCK ||
+ alter_info->requested_lock == Alter_info::ALTER_TABLE_LOCK_SHARED) &&
+ thd->mdl_context.upgrade_shared_lock(table->mdl_ticket,
+ MDL_SHARED_NO_WRITE,
+ thd->variables.lock_wait_timeout))
+ {
+ goto cleanup;
+ }
+
+ // It's now safe to take the table level lock.
+ if (lock_tables(thd, table_list, alter_ctx->tables_opened, 0))
+ goto cleanup;
+
+ DEBUG_SYNC(thd, "alter_table_inplace_after_lock_upgrade");
+ THD_STAGE_INFO(thd, stage_alter_inplace_prepare);
+
+ switch (inplace_supported) {
+ case HA_ALTER_ERROR:
+ case HA_ALTER_INPLACE_NOT_SUPPORTED:
+ DBUG_ASSERT(0);
+ // fall through
+ case HA_ALTER_INPLACE_NO_LOCK:
+ case HA_ALTER_INPLACE_NO_LOCK_AFTER_PREPARE:
+ switch (alter_info->requested_lock) {
+ case Alter_info::ALTER_TABLE_LOCK_DEFAULT:
+ case Alter_info::ALTER_TABLE_LOCK_NONE:
+ ha_alter_info->online= true;
+ break;
+ case Alter_info::ALTER_TABLE_LOCK_SHARED:
+ case Alter_info::ALTER_TABLE_LOCK_EXCLUSIVE:
+ break;
+ }
+ break;
+ case HA_ALTER_INPLACE_EXCLUSIVE_LOCK:
+ case HA_ALTER_INPLACE_SHARED_LOCK_AFTER_PREPARE:
+ case HA_ALTER_INPLACE_SHARED_LOCK:
+ break;
+ }
+
+ if (table->file->ha_prepare_inplace_alter_table(altered_table,
+ ha_alter_info))
+ {
+ goto rollback;
+ }
+
+ /*
+ Downgrade the lock if storage engine has told us that exclusive lock was
+ necessary only for prepare phase (unless we are not under LOCK TABLES) and
+ user has not explicitly requested exclusive lock.
+ */
+ if ((inplace_supported == HA_ALTER_INPLACE_SHARED_LOCK_AFTER_PREPARE ||
+ inplace_supported == HA_ALTER_INPLACE_NO_LOCK_AFTER_PREPARE) &&
+ !(thd->locked_tables_mode == LTM_LOCK_TABLES ||
+ thd->locked_tables_mode == LTM_PRELOCKED_UNDER_LOCK_TABLES) &&
+ (alter_info->requested_lock != Alter_info::ALTER_TABLE_LOCK_EXCLUSIVE))
+ {
+ /* If storage engine or user requested shared lock downgrade to SNW. */
+ if (inplace_supported == HA_ALTER_INPLACE_SHARED_LOCK_AFTER_PREPARE ||
+ alter_info->requested_lock == Alter_info::ALTER_TABLE_LOCK_SHARED)
+ table->mdl_ticket->downgrade_lock(MDL_SHARED_NO_WRITE);
+ else
+ {
+ DBUG_ASSERT(inplace_supported == HA_ALTER_INPLACE_NO_LOCK_AFTER_PREPARE);
+ table->mdl_ticket->downgrade_lock(MDL_SHARED_UPGRADABLE);
+ }
+ }
+
+ DEBUG_SYNC(thd, "alter_table_inplace_after_lock_downgrade");
+ THD_STAGE_INFO(thd, stage_alter_inplace);
+
+ if (table->file->ha_inplace_alter_table(altered_table,
+ ha_alter_info))
+ {
+ goto rollback;
+ }
+
+ // Upgrade to EXCLUSIVE before commit.
+ if (wait_while_table_is_used(thd, table, HA_EXTRA_PREPARE_FOR_RENAME))
+ goto rollback;
+
+ /*
+ If we are killed after this point, we should ignore and continue.
+ We have mostly completed the operation at this point, there should
+ be no long waits left.
+ */
+
+ DBUG_EXECUTE_IF("alter_table_rollback_new_index", {
+ table->file->ha_commit_inplace_alter_table(altered_table,
+ ha_alter_info,
+ false);
+ my_error(ER_UNKNOWN_ERROR, MYF(0));
+ goto cleanup;
+ });
+
+ DEBUG_SYNC(thd, "alter_table_inplace_before_commit");
+ THD_STAGE_INFO(thd, stage_alter_inplace_commit);
+
+ if (table->file->ha_commit_inplace_alter_table(altered_table,
+ ha_alter_info,
+ true))
+ {
+ goto rollback;
+ }
+
+ close_all_tables_for_name(thd, table->s,
+ alter_ctx->is_table_renamed() ?
+ HA_EXTRA_PREPARE_FOR_RENAME :
+ HA_EXTRA_NOT_USED,
+ NULL);
+ table_list->table= table= NULL;
+ close_temporary_table(thd, altered_table, true, false);
+
+ /*
+ Replace the old .FRM with the new .FRM, but keep the old name for now.
+ Rename to the new name (if needed) will be handled separately below.
+ */
+ if (mysql_rename_table(db_type, alter_ctx->new_db, alter_ctx->tmp_name,
+ alter_ctx->db, alter_ctx->alias,
+ FN_FROM_IS_TMP | NO_HA_TABLE))
+ {
+ // Since changes were done in-place, we can't revert them.
+ (void) quick_rm_table(thd, db_type,
+ alter_ctx->new_db, alter_ctx->tmp_name,
+ FN_IS_TMP | NO_HA_TABLE);
+ DBUG_RETURN(true);
+ }
+
+ table_list->mdl_request.ticket= mdl_ticket;
+ if (open_table(thd, table_list, thd->mem_root, &ot_ctx))
+ DBUG_RETURN(true);
+
+ /*
+ Tell the handler that the changed frm is on disk and table
+ has been re-opened
+ */
+ table_list->table->file->ha_notify_table_changed();
+
+ /*
+ We might be going to reopen table down on the road, so we have to
+ restore state of the TABLE object which we used for obtaining of
+ handler object to make it usable for later reopening.
+ */
+ close_thread_table(thd, &thd->open_tables);
+ table_list->table= NULL;
+
+ // Rename altered table if requested.
+ if (alter_ctx->is_table_renamed())
+ {
+ // Remove TABLE and TABLE_SHARE for old name from TDC.
+ tdc_remove_table(thd, TDC_RT_REMOVE_ALL,
+ alter_ctx->db, alter_ctx->table_name, false);
+
+ if (mysql_rename_table(db_type, alter_ctx->db, alter_ctx->table_name,
+ alter_ctx->new_db, alter_ctx->new_alias, 0))
+ {
+ /*
+ If the rename fails we will still have a working table
+ with the old name, but with other changes applied.
+ */
+ DBUG_RETURN(true);
+ }
+ if (Table_triggers_list::change_table_name(thd,
+ alter_ctx->db,
+ alter_ctx->alias,
+ alter_ctx->table_name,
+ alter_ctx->new_db,
+ alter_ctx->new_alias))
+ {
+ /*
+ If the rename of trigger files fails, try to rename the table
+ back so we at least have matching table and trigger files.
+ */
+ (void) mysql_rename_table(db_type,
+ alter_ctx->new_db, alter_ctx->new_alias,
+ alter_ctx->db, alter_ctx->alias, NO_FK_CHECKS);
+ DBUG_RETURN(true);
+ }
+ rename_table_in_stat_tables(thd, alter_ctx->db,alter_ctx->alias,
+ alter_ctx->new_db, alter_ctx->new_alias);
+ }
+
+ DBUG_RETURN(false);
+
+ rollback:
+ table->file->ha_commit_inplace_alter_table(altered_table,
+ ha_alter_info,
+ false);
+ cleanup:
+ if (reopen_tables)
+ {
+ /* Close the only table instance which is still around. */
+ close_all_tables_for_name(thd, table->s,
+ alter_ctx->is_table_renamed() ?
+ HA_EXTRA_PREPARE_FOR_RENAME :
+ HA_EXTRA_NOT_USED,
+ NULL);
+ if (thd->locked_tables_list.reopen_tables(thd))
+ thd->locked_tables_list.unlink_all_closed_tables(thd, NULL, 0);
+ /* QQ; do something about metadata locks ? */
+ }
+ close_temporary_table(thd, altered_table, true, false);
+ // Delete temporary .frm/.par
+ (void) quick_rm_table(thd, create_info->db_type, alter_ctx->new_db,
+ alter_ctx->tmp_name, FN_IS_TMP | NO_HA_TABLE);
+ DBUG_RETURN(true);
+}
+
/**
maximum possible length for certain blob types.
@@ -5610,6 +7329,7 @@ blob_length_by_type(enum_field_types type)
But since ALTER might end-up doing CREATE,
this distinction is gone and we just carry
around two structures.
+ @param[in,out] alter_ctx Runtime context for ALTER TABLE.
@return
Fills various create_info members based on information retrieved
@@ -5626,7 +7346,8 @@ blob_length_by_type(enum_field_types type)
bool
mysql_prepare_alter_table(THD *thd, TABLE *table,
HA_CREATE_INFO *create_info,
- Alter_info *alter_info)
+ Alter_info *alter_info,
+ Alter_table_ctx *alter_ctx)
{
/* New column definitions are added here */
List<Create_field> new_create_list;
@@ -5641,13 +7362,22 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
List<Key_part_spec> key_parts;
uint db_create_options= (table->s->db_create_options
& ~(HA_OPTION_PACK_RECORD));
- uint used_fields= create_info->used_fields;
+ uint used_fields;
KEY *key_info=table->key_info;
bool rc= TRUE;
+ bool modified_primary_key= FALSE;
Create_field *def;
Field **f_ptr,*field;
DBUG_ENTER("mysql_prepare_alter_table");
+ /*
+ Merge incompatible changes flag in case of upgrade of a table from an
+ old MariaDB or MySQL version. This ensures that we don't try to do an
+ online alter table if field packing or character set changes are required.
+ */
+ create_info->used_fields|= table->s->incompatible_version;
+ used_fields= create_info->used_fields;
+
create_info->varchar= FALSE;
/* Let new create options override the old ones */
if (!(used_fields & HA_CREATE_USED_MIN_ROWS))
@@ -5664,19 +7394,33 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
table->file->info(HA_STATUS_AUTO);
create_info->auto_increment_value= table->file->stats.auto_increment_value;
}
+
if (!(used_fields & HA_CREATE_USED_KEY_BLOCK_SIZE))
create_info->key_block_size= table->s->key_block_size;
+
+ if (!(used_fields & HA_CREATE_USED_STATS_SAMPLE_PAGES))
+ create_info->stats_sample_pages= table->s->stats_sample_pages;
+
+ if (!(used_fields & HA_CREATE_USED_STATS_AUTO_RECALC))
+ create_info->stats_auto_recalc= table->s->stats_auto_recalc;
+
if (!(used_fields & HA_CREATE_USED_TRANSACTIONAL))
create_info->transactional= table->s->transactional;
- /* Creation of federated table with LIKE clause needs connection string */
if (!(used_fields & HA_CREATE_USED_CONNECTION))
create_info->connect_string= table->s->connect_string;
restore_record(table, s->default_values); // Empty record for DEFAULT
+ if ((create_info->fields_option_struct= (ha_field_option_struct**)
+ thd->calloc(sizeof(void*) * table->s->fields)) == NULL ||
+ (create_info->indexes_option_struct= (ha_index_option_struct**)
+ thd->calloc(sizeof(void*) * table->s->keys)) == NULL)
+ DBUG_RETURN(1);
+
create_info->option_list= merge_engine_table_options(table->s->option_list,
create_info->option_list, thd->mem_root);
+
/*
First collect all fields from table which isn't in drop_list
*/
@@ -5704,13 +7448,9 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
}
if (drop)
{
+ if (table->s->tmp_table == NO_TMP_TABLE)
+ (void) delete_statistics_for_column(thd, table, field);
drop_it.remove();
- /*
- ALTER TABLE DROP COLUMN always changes table data even in cases
- when new version of the table has the same structure as the old
- one.
- */
- alter_info->change_level= ALTER_TABLE_DATA_CHANGED;
continue;
}
/* Check if field is changed */
@@ -5724,6 +7464,12 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
if (def)
{ // Field is changed
def->field=field;
+ /*
+ Add column being updated to the list of new columns.
+ Note that columns with AFTER clauses are added to the end
+ of the list for now. Their positions will be corrected later.
+ */
+ new_create_list.push_back(def);
if (field->stored_in_db != def->stored_in_db)
{
my_error(ER_UNSUPPORTED_ACTION_ON_VIRTUAL_COLUMN, MYF(0));
@@ -5731,7 +7477,13 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
}
if (!def->after)
{
- new_create_list.push_back(def);
+ /*
+ If this ALTER TABLE doesn't have an AFTER clause for the modified
+ column then remove this column from the list of columns to be
+ processed. So later we can iterate over the columns remaining
+ in this list and process modified columns with AFTER clause or
+ add new columns.
+ */
def_it.remove();
}
}
@@ -5783,46 +7535,60 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
*/
if ((def->sql_type == MYSQL_TYPE_DATE ||
def->sql_type == MYSQL_TYPE_NEWDATE ||
- def->sql_type == MYSQL_TYPE_DATETIME) &&
- !alter_info->datetime_field &&
+ def->sql_type == MYSQL_TYPE_DATETIME ||
+ def->sql_type == MYSQL_TYPE_DATETIME2) &&
+ !alter_ctx->datetime_field &&
!(~def->flags & (NO_DEFAULT_VALUE_FLAG | NOT_NULL_FLAG)) &&
thd->variables.sql_mode & MODE_NO_ZERO_DATE)
{
- alter_info->datetime_field= def;
- alter_info->error_if_not_empty= TRUE;
+ alter_ctx->datetime_field= def;
+ alter_ctx->error_if_not_empty= TRUE;
}
if (!def->after)
new_create_list.push_back(def);
- else if (def->after == first_keyword)
- {
- new_create_list.push_front(def);
- /*
- Re-ordering columns in table can't be done using in-place algorithm
- as it always changes table data.
- */
- alter_info->change_level= ALTER_TABLE_DATA_CHANGED;
- }
else
{
Create_field *find;
- find_it.rewind();
- while ((find=find_it++)) // Add new columns
+ if (def->change)
{
- if (!my_strcasecmp(system_charset_info,def->after, find->field_name))
- break;
+ find_it.rewind();
+ /*
+ For columns being modified with AFTER clause we should first remove
+ these columns from the list and then add them back at their correct
+ positions.
+ */
+ while ((find=find_it++))
+ {
+ /*
+ Create_fields representing changed columns are added directly
+ from Alter_info::create_list to new_create_list. We can therefore
+ safely use pointer equality rather than name matching here.
+ This prevents removing the wrong column in case of column rename.
+ */
+ if (find == def)
+ {
+ find_it.remove();
+ break;
+ }
+ }
}
- if (!find)
+ if (def->after == first_keyword)
+ new_create_list.push_front(def);
+ else
{
- my_error(ER_BAD_FIELD_ERROR, MYF(0), def->after,
- table->s->table_name.str);
- goto err;
+ find_it.rewind();
+ while ((find=find_it++))
+ {
+ if (!my_strcasecmp(system_charset_info, def->after, find->field_name))
+ break;
+ }
+ if (!find)
+ {
+ my_error(ER_BAD_FIELD_ERROR, MYF(0), def->after, table->s->table_name.str);
+ goto err;
+ }
+ find_it.after(def); // Put column after this
}
- find_it.after(def); // Put element after this
- /*
- Re-ordering columns in table can't be done using in-place algorithm
- as it always changes table data.
- */
- alter_info->change_level= ALTER_TABLE_DATA_CHANGED;
}
}
if (alter_info->alter_list.elements)
@@ -5842,7 +7608,7 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
Collect all keys which isn't in drop list. Add only those
for which some fields exists.
*/
-
+
for (uint i=0 ; i < table->s->keys ; i++,key_info++)
{
char *key_name= key_info->name;
@@ -5856,13 +7622,29 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
}
if (drop)
{
+ if (table->s->tmp_table == NO_TMP_TABLE)
+ {
+ (void) delete_statistics_for_index(thd, table, key_info, FALSE);
+ if (i == table->s->primary_key)
+ {
+ KEY *tab_key_info= table->key_info;
+ for (uint j=0; j < table->s->keys; j++, tab_key_info++)
+ {
+ if (tab_key_info->user_defined_key_parts !=
+ tab_key_info->ext_key_parts)
+ (void) delete_statistics_for_index(thd, table, tab_key_info,
+ TRUE);
+ }
+ }
+ }
drop_it.remove();
continue;
}
KEY_PART_INFO *key_part= key_info->key_part;
key_parts.empty();
- for (uint j=0 ; j < key_info->key_parts ; j++,key_part++)
+ bool delete_index_stat= FALSE;
+ for (uint j=0 ; j < key_info->user_defined_key_parts ; j++,key_part++)
{
if (!key_part->field)
continue; // Wrong field (from UNIREG)
@@ -5884,7 +7666,12 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
break;
}
if (!cfield)
+ {
+ if (table->s->primary_key == i)
+ modified_primary_key= TRUE;
+ delete_index_stat= TRUE;
continue; // Field is removed
+ }
key_part_length= key_part->length;
if (cfield->field) // Not new field
{
@@ -5926,6 +7713,15 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
strlen(cfield->field_name),
key_part_length));
}
+ if (table->s->tmp_table == NO_TMP_TABLE)
+ {
+ if (delete_index_stat)
+ (void) delete_statistics_for_index(thd, table, key_info, FALSE);
+ else if (modified_primary_key &&
+ key_info->user_defined_key_parts != key_info->ext_key_parts)
+ (void) delete_statistics_for_index(thd, table, key_info, TRUE);
+ }
+
if (key_parts.elements)
{
KEY_CREATE_INFO key_create_info;
@@ -5941,6 +7737,12 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
if (key_info->flags & HA_USES_COMMENT)
key_create_info.comment= key_info->comment;
+ /*
+ We're refreshing an already existing index. Since the index is not
+ modified, there is no need to check for duplicate indexes again.
+ */
+ key_create_info.check_for_duplicate_indexes= false;
+
if (key_info->flags & HA_SPATIAL)
key_type= Key::SPATIAL;
else if (key_info->flags & HA_NOSAME)
@@ -5957,8 +7759,8 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
key= new Key(key_type, key_name, strlen(key_name),
&key_create_info,
- test(key_info->flags & HA_GENERATED_KEY),
- key_parts, key_info->option_list);
+ MY_TEST(key_info->flags & HA_GENERATED_KEY),
+ key_parts, key_info->option_list, FALSE);
new_key_list.push_back(key);
}
}
@@ -5969,8 +7771,7 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
if (key->type == Key::FOREIGN_KEY &&
((Foreign_key *)key)->validate(new_create_list))
goto err;
- if (key->type != Key::FOREIGN_KEY)
- new_key_list.push_back(key);
+ new_key_list.push_back(key);
if (key->name.str &&
!my_strcasecmp(system_charset_info, key->name.str, primary_key_name))
{
@@ -5982,9 +7783,20 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
if (alter_info->drop_list.elements)
{
- my_error(ER_CANT_DROP_FIELD_OR_KEY, MYF(0),
- alter_info->drop_list.head()->name);
- goto err;
+ Alter_drop *drop;
+ drop_it.rewind();
+ while ((drop=drop_it++)) {
+ switch (drop->type) {
+ case Alter_drop::KEY:
+ case Alter_drop::COLUMN:
+ my_error(ER_CANT_DROP_FIELD_OR_KEY, MYF(0),
+ alter_info->drop_list.head()->name);
+ goto err;
+ case Alter_drop::FOREIGN_KEY:
+ // Leave the DROP FOREIGN KEY names in the alter_info->drop_list.
+ break;
+ }
+ }
}
if (alter_info->alter_list.elements)
{
@@ -6004,6 +7816,11 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
(HA_OPTION_PACK_KEYS | HA_OPTION_NO_PACK_KEYS)) ||
(used_fields & HA_CREATE_USED_PACK_KEYS))
db_create_options&= ~(HA_OPTION_PACK_KEYS | HA_OPTION_NO_PACK_KEYS);
+ if ((create_info->table_options &
+ (HA_OPTION_STATS_PERSISTENT | HA_OPTION_NO_STATS_PERSISTENT)) ||
+ (used_fields & HA_CREATE_USED_STATS_PERSISTENT))
+ db_create_options&= ~(HA_OPTION_STATS_PERSISTENT | HA_OPTION_NO_STATS_PERSISTENT);
+
if (create_info->table_options &
(HA_OPTION_CHECKSUM | HA_OPTION_NO_CHECKSUM))
db_create_options&= ~(HA_OPTION_CHECKSUM | HA_OPTION_NO_CHECKSUM);
@@ -6024,157 +7841,527 @@ err:
}
-/*
- Alter table
-
- SYNOPSIS
- mysql_alter_table()
- thd Thread handle
- new_db If there is a RENAME clause
- new_name If there is a RENAME clause
- create_info Information from the parsing phase about new
- table properties.
- table_list The table to change.
- alter_info Lists of fields, keys to be changed, added
- or dropped.
- order_num How many ORDER BY fields has been specified.
- order List of fields to ORDER BY.
- ignore Whether we have ALTER IGNORE TABLE
- require_online Give an error if we can't do operation online
+/**
+ Get Create_field object for newly created table by its name
+ in the old version of table.
- DESCRIPTION
- This is a veery long function and is everything but the kitchen sink :)
- It is used to alter a table and not only by ALTER TABLE but also
- CREATE|DROP INDEX are mapped on this function.
-
- When the ALTER TABLE statement just does a RENAME or ENABLE|DISABLE KEYS,
- or both, then this function short cuts its operation by renaming
- the table and/or enabling/disabling the keys. In this case, the FRM is
- not changed, directly by mysql_alter_table. However, if there is a
- RENAME + change of a field, or an index, the short cut is not used.
- See how `create_list` is used to generate the new FRM regarding the
- structure of the fields. The same is done for the indices of the table.
-
- Important is the fact, that this function tries to do as little work as
- possible, by finding out whether a intermediate table is needed to copy
- data into and when finishing the altering to use it as the original table.
- For this reason the function mysql_compare_tables() is called, which decides
- based on all kind of data how similar are the new and the original
- tables.
+ @param alter_info Alter_info describing newly created table.
+ @param old_name Name of field in old table.
- RETURN VALUES
- FALSE OK
- TRUE Error
+ @returns Pointer to Create_field object, NULL - if field is
+ not present in new version of table.
*/
-bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
- HA_CREATE_INFO *create_info,
- TABLE_LIST *table_list,
- Alter_info *alter_info,
- uint order_num, ORDER *order, bool ignore,
- bool require_online)
+static Create_field *get_field_by_old_name(Alter_info *alter_info,
+ const char *old_name)
{
- TABLE *table, *new_table= 0;
- MDL_ticket *mdl_ticket;
- MDL_request target_mdl_request;
- int error= 0;
- char tmp_name[80],old_name[32],new_name_buff[FN_REFLEN + 1];
- char old_name_buff[FN_REFLEN + 1];
- char new_alias_buff[FN_REFLEN], *table_name, *db, *new_alias, *alias;
- char index_file[FN_REFLEN], data_file[FN_REFLEN];
- char path[FN_REFLEN + 1];
- char reg_path[FN_REFLEN+1];
- ha_rows copied,deleted;
- handlerton *old_db_type, *new_db_type, *save_old_db_type;
- enum_alter_table_change_level need_copy_table= ALTER_TABLE_METADATA_ONLY;
-#ifdef WITH_PARTITION_STORAGE_ENGINE
- TABLE *table_for_fast_alter_partition= NULL;
- bool partition_changed= FALSE;
-#endif
- bool need_lock_for_indexes __attribute__((unused)) = TRUE;
- KEY *key_info_buffer;
- uint index_drop_count= 0;
- uint *index_drop_buffer= NULL;
- uint index_add_count= 0;
- handler_add_index *add= NULL;
- bool pending_inplace_add_index= false;
- uint *index_add_buffer= NULL;
- uint candidate_key_count= 0;
- bool no_pk;
- ulong explicit_used_fields= 0;
- enum ha_extra_function extra_func= thd->locked_tables_mode
- ? HA_EXTRA_NOT_USED
- : HA_EXTRA_FORCE_REOPEN;
- DBUG_ENTER("mysql_alter_table");
+ List_iterator_fast<Create_field> new_field_it(alter_info->create_list);
+ Create_field *new_field;
- /*
- Check if we attempt to alter mysql.slow_log or
- mysql.general_log table and return an error if
- it is the case.
- TODO: this design is obsolete and will be removed.
- */
- if (table_list && table_list->db && table_list->table_name)
+ while ((new_field= new_field_it++))
{
- int table_kind= 0;
+ if (new_field->field &&
+ (my_strcasecmp(system_charset_info,
+ new_field->field->field_name,
+ old_name) == 0))
+ break;
+ }
+ return new_field;
+}
+
- table_kind= check_if_log_table(table_list->db_length, table_list->db,
- table_list->table_name_length,
- table_list->table_name, 0);
+/** Type of change to foreign key column, */
- if (table_kind)
+enum fk_column_change_type
+{
+ FK_COLUMN_NO_CHANGE, FK_COLUMN_DATA_CHANGE,
+ FK_COLUMN_RENAMED, FK_COLUMN_DROPPED
+};
+
+/**
+ Check that ALTER TABLE's changes on columns of a foreign key are allowed.
+
+ @param[in] thd Thread context.
+ @param[in] alter_info Alter_info describing changes to be done
+ by ALTER TABLE.
+ @param[in] fk_columns List of columns of the foreign key to check.
+ @param[out] bad_column_name Name of field on which ALTER TABLE tries to
+ do prohibited operation.
+
+ @note This function takes into account value of @@foreign_key_checks
+ setting.
+
+ @retval FK_COLUMN_NO_CHANGE No significant changes are to be done on
+ foreign key columns.
+ @retval FK_COLUMN_DATA_CHANGE ALTER TABLE might result in value
+ change in foreign key column (and
+ foreign_key_checks is on).
+ @retval FK_COLUMN_RENAMED Foreign key column is renamed.
+ @retval FK_COLUMN_DROPPED Foreign key column is dropped.
+*/
+
+static enum fk_column_change_type
+fk_check_column_changes(THD *thd, Alter_info *alter_info,
+ List<LEX_STRING> &fk_columns,
+ const char **bad_column_name)
+{
+ List_iterator_fast<LEX_STRING> column_it(fk_columns);
+ LEX_STRING *column;
+
+ *bad_column_name= NULL;
+
+ while ((column= column_it++))
+ {
+ Create_field *new_field= get_field_by_old_name(alter_info, column->str);
+
+ if (new_field)
{
- /* Disable alter of enabled log tables */
- if (logger.is_log_table_enabled(table_kind))
- {
- my_error(ER_BAD_LOG_STATEMENT, MYF(0), "ALTER");
- DBUG_RETURN(TRUE);
- }
+ Field *old_field= new_field->field;
- /* Disable alter of log tables to unsupported engine */
- if ((create_info->used_fields & HA_CREATE_USED_ENGINE) &&
- (!create_info->db_type || /* unknown engine */
- !(create_info->db_type->flags & HTON_SUPPORT_LOG_TABLES)))
+ if (my_strcasecmp(system_charset_info, old_field->field_name,
+ new_field->field_name))
{
- my_error(ER_UNSUPORTED_LOG_ENGINE, MYF(0));
- DBUG_RETURN(TRUE);
+ /*
+ Copy algorithm doesn't support proper renaming of columns in
+ the foreign key yet. At the moment we lack API which will tell
+ SE that foreign keys should be updated to use new name of column
+ like it happens in case of in-place algorithm.
+ */
+ *bad_column_name= column->str;
+ return FK_COLUMN_RENAMED;
}
-#ifdef WITH_PARTITION_STORAGE_ENGINE
- if (alter_info->flags & ALTER_PARTITION)
+ if ((old_field->is_equal(new_field) == IS_EQUAL_NO) ||
+ ((new_field->flags & NOT_NULL_FLAG) &&
+ !(old_field->flags & NOT_NULL_FLAG)))
{
- my_error(ER_WRONG_USAGE, MYF(0), "PARTITION", "log table");
- DBUG_RETURN(TRUE);
+ if (!(thd->variables.option_bits & OPTION_NO_FOREIGN_KEY_CHECKS))
+ {
+ /*
+ Column in a FK has changed significantly. Unless
+ foreign_key_checks are off we prohibit this since this
+ means values in this column might be changed by ALTER
+ and thus referential integrity might be broken,
+ */
+ *bad_column_name= column->str;
+ return FK_COLUMN_DATA_CHANGE;
+ }
}
-#endif
+ }
+ else
+ {
+ /*
+ Column in FK was dropped. Most likely this will break
+ integrity constraints of InnoDB data-dictionary (and thus
+ InnoDB will emit an error), so we prohibit this right away
+ even if foreign_key_checks are off.
+ This also includes a rare case when another field replaces
+ field being dropped since it is easy to break referential
+ integrity in this case.
+ */
+ *bad_column_name= column->str;
+ return FK_COLUMN_DROPPED;
+ }
+ }
+
+ return FK_COLUMN_NO_CHANGE;
+}
+
+
+/**
+ Check if ALTER TABLE we are about to execute using COPY algorithm
+ is not supported as it might break referential integrity.
+
+ @note If foreign_key_checks is disabled (=0), we allow to break
+ referential integrity. But we still disallow some operations
+ like dropping or renaming columns in foreign key since they
+ are likely to break consistency of InnoDB data-dictionary
+ and thus will end-up in error anyway.
+
+ @param[in] thd Thread context.
+ @param[in] table Table to be altered.
+ @param[in] alter_info Lists of fields, keys to be changed, added
+ or dropped.
+ @param[out] alter_ctx ALTER TABLE runtime context.
+ Alter_table_ctx::fk_error_if_delete flag
+ is set if deletion during alter can break
+ foreign key integrity.
+
+ @retval false Success.
+ @retval true Error, ALTER - tries to do change which is not compatible
+ with foreign key definitions on the table.
+*/
+
+static bool fk_prepare_copy_alter_table(THD *thd, TABLE *table,
+ Alter_info *alter_info,
+ Alter_table_ctx *alter_ctx)
+{
+ List <FOREIGN_KEY_INFO> fk_parent_key_list;
+ List <FOREIGN_KEY_INFO> fk_child_key_list;
+ FOREIGN_KEY_INFO *f_key;
+
+ DBUG_ENTER("fk_prepare_copy_alter_table");
+
+ table->file->get_parent_foreign_key_list(thd, &fk_parent_key_list);
+
+ /* OOM when building list. */
+ if (thd->is_error())
+ DBUG_RETURN(true);
+
+ /*
+ Remove from the list all foreign keys in which table participates as
+ parent which are to be dropped by this ALTER TABLE. This is possible
+ when a foreign key has the same table as child and parent.
+ */
+ List_iterator<FOREIGN_KEY_INFO> fk_parent_key_it(fk_parent_key_list);
+
+ while ((f_key= fk_parent_key_it++))
+ {
+ Alter_drop *drop;
+ List_iterator_fast<Alter_drop> drop_it(alter_info->drop_list);
+
+ while ((drop= drop_it++))
+ {
+ /*
+ InnoDB treats foreign key names in case-insensitive fashion.
+ So we do it here too. For database and table name type of
+ comparison used depends on lower-case-table-names setting.
+ For l_c_t_n = 0 we use case-sensitive comparison, for
+ l_c_t_n > 0 modes case-insensitive comparison is used.
+ */
+ if ((drop->type == Alter_drop::FOREIGN_KEY) &&
+ (my_strcasecmp(system_charset_info, f_key->foreign_id->str,
+ drop->name) == 0) &&
+ (my_strcasecmp(table_alias_charset, f_key->foreign_db->str,
+ table->s->db.str) == 0) &&
+ (my_strcasecmp(table_alias_charset, f_key->foreign_table->str,
+ table->s->table_name.str) == 0))
+ fk_parent_key_it.remove();
}
}
/*
- Assign variables table_name, new_name, db, new_db, path, reg_path
- to simplify further comparisions: we want to see if it's a RENAME
- later just by comparing the pointers, avoiding the need for strcmp.
+ If there are FKs in which this table is parent which were not
+ dropped we need to prevent ALTER deleting rows from the table,
+ as it might break referential integrity. OTOH it is OK to do
+ so if foreign_key_checks are disabled.
+ */
+ if (!fk_parent_key_list.is_empty() &&
+ !(thd->variables.option_bits & OPTION_NO_FOREIGN_KEY_CHECKS))
+ alter_ctx->set_fk_error_if_delete_row(fk_parent_key_list.head());
+
+ fk_parent_key_it.rewind();
+ while ((f_key= fk_parent_key_it++))
+ {
+ enum fk_column_change_type changes;
+ const char *bad_column_name;
+
+ changes= fk_check_column_changes(thd, alter_info,
+ f_key->referenced_fields,
+ &bad_column_name);
+
+ switch(changes)
+ {
+ case FK_COLUMN_NO_CHANGE:
+ /* No significant changes. We can proceed with ALTER! */
+ break;
+ case FK_COLUMN_DATA_CHANGE:
+ {
+ char buff[NAME_LEN*2+2];
+ strxnmov(buff, sizeof(buff)-1, f_key->foreign_db->str, ".",
+ f_key->foreign_table->str, NullS);
+ my_error(ER_FK_COLUMN_CANNOT_CHANGE_CHILD, MYF(0), bad_column_name,
+ f_key->foreign_id->str, buff);
+ DBUG_RETURN(true);
+ }
+ case FK_COLUMN_RENAMED:
+ my_error(ER_ALTER_OPERATION_NOT_SUPPORTED_REASON, MYF(0),
+ "ALGORITHM=COPY",
+ ER(ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_FK_RENAME),
+ "ALGORITHM=INPLACE");
+ DBUG_RETURN(true);
+ case FK_COLUMN_DROPPED:
+ {
+ char buff[NAME_LEN*2+2];
+ strxnmov(buff, sizeof(buff)-1, f_key->foreign_db->str, ".",
+ f_key->foreign_table->str, NullS);
+ my_error(ER_FK_COLUMN_CANNOT_DROP_CHILD, MYF(0), bad_column_name,
+ f_key->foreign_id->str, buff);
+ DBUG_RETURN(true);
+ }
+ default:
+ DBUG_ASSERT(0);
+ }
+ }
+
+ table->file->get_foreign_key_list(thd, &fk_child_key_list);
+
+ /* OOM when building list. */
+ if (thd->is_error())
+ DBUG_RETURN(true);
+
+ /*
+ Remove from the list all foreign keys which are to be dropped
+ by this ALTER TABLE.
*/
- thd_proc_info(thd, "init");
- table_name=table_list->table_name;
- alias= (lower_case_table_names == 2) ? table_list->alias : table_name;
- db=table_list->db;
- if (!new_db || !my_strcasecmp(table_alias_charset, new_db, db))
- new_db= db;
- build_table_filename(reg_path, sizeof(reg_path) - 1, db, table_name, reg_ext, 0);
- build_table_filename(path, sizeof(path) - 1, db, table_name, "", 0);
+ List_iterator<FOREIGN_KEY_INFO> fk_key_it(fk_child_key_list);
+
+ while ((f_key= fk_key_it++))
+ {
+ Alter_drop *drop;
+ List_iterator_fast<Alter_drop> drop_it(alter_info->drop_list);
- mysql_ha_rm_tables(thd, table_list);
+ while ((drop= drop_it++))
+ {
+ /* Names of foreign keys in InnoDB are case-insensitive. */
+ if ((drop->type == Alter_drop::FOREIGN_KEY) &&
+ (my_strcasecmp(system_charset_info, f_key->foreign_id->str,
+ drop->name) == 0))
+ fk_key_it.remove();
+ }
+ }
- /* DISCARD/IMPORT TABLESPACE is always alone in an ALTER TABLE */
- if (alter_info->tablespace_op != NO_TABLESPACE_OP)
+ fk_key_it.rewind();
+ while ((f_key= fk_key_it++))
{
- mysql_audit_alter_table(thd, table_list);
+ enum fk_column_change_type changes;
+ const char *bad_column_name;
+
+ changes= fk_check_column_changes(thd, alter_info,
+ f_key->foreign_fields,
+ &bad_column_name);
+
+ switch(changes)
+ {
+ case FK_COLUMN_NO_CHANGE:
+ /* No significant changes. We can proceed with ALTER! */
+ break;
+ case FK_COLUMN_DATA_CHANGE:
+ my_error(ER_FK_COLUMN_CANNOT_CHANGE, MYF(0), bad_column_name,
+ f_key->foreign_id->str);
+ DBUG_RETURN(true);
+ case FK_COLUMN_RENAMED:
+ my_error(ER_ALTER_OPERATION_NOT_SUPPORTED_REASON, MYF(0),
+ "ALGORITHM=COPY",
+ ER(ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_FK_RENAME),
+ "ALGORITHM=INPLACE");
+ DBUG_RETURN(true);
+ case FK_COLUMN_DROPPED:
+ my_error(ER_FK_COLUMN_CANNOT_DROP, MYF(0), bad_column_name,
+ f_key->foreign_id->str);
+ DBUG_RETURN(true);
+ default:
+ DBUG_ASSERT(0);
+ }
+ }
+
+ DBUG_RETURN(false);
+}
+
+
+/**
+ Rename table and/or turn indexes on/off without touching .FRM
+
+ @param thd Thread handler
+ @param table_list TABLE_LIST for the table to change
+ @param keys_onoff ENABLE or DISABLE KEYS?
+ @param alter_ctx ALTER TABLE runtime context.
+
+ @return Operation status
+ @retval false Success
+ @retval true Failure
+*/
+
+static bool
+simple_rename_or_index_change(THD *thd, TABLE_LIST *table_list,
+ Alter_info::enum_enable_or_disable keys_onoff,
+ Alter_table_ctx *alter_ctx)
+{
+ TABLE *table= table_list->table;
+ MDL_ticket *mdl_ticket= table->mdl_ticket;
+ int error= 0;
+ enum ha_extra_function extra_func= thd->locked_tables_mode
+ ? HA_EXTRA_NOT_USED
+ : HA_EXTRA_FORCE_REOPEN;
+ DBUG_ENTER("simple_rename_or_index_change");
- /* Conditionally writes to binlog. */
- bool ret= mysql_discard_or_import_tablespace(thd,table_list,
- alter_info->tablespace_op);
- DBUG_RETURN(ret);
+#ifdef WITH_WSREP
+ bool do_log_write(true);
+#endif /* WITH_WSREP */
+
+ if (keys_onoff != Alter_info::LEAVE_AS_IS)
+ {
+ if (wait_while_table_is_used(thd, table, extra_func))
+ DBUG_RETURN(true);
+
+ // It's now safe to take the table level lock.
+ if (lock_tables(thd, table_list, alter_ctx->tables_opened, 0))
+ DBUG_RETURN(true);
+
+ error= alter_table_manage_keys(table,
+ table->file->indexes_are_disabled(),
+ keys_onoff);
+ }
+
+ if (!error && alter_ctx->is_table_renamed())
+ {
+ THD_STAGE_INFO(thd, stage_rename);
+ handlerton *old_db_type= table->s->db_type();
+ /*
+ Then do a 'simple' rename of the table. First we need to close all
+ instances of 'source' table.
+ Note that if wait_while_table_is_used() returns error here (i.e. if
+ this thread was killed) then it must be that previous step of
+ simple rename did nothing and therefore we can safely return
+ without additional clean-up.
+ */
+ if (wait_while_table_is_used(thd, table, extra_func))
+ DBUG_RETURN(true);
+ close_all_tables_for_name(thd, table->s, HA_EXTRA_PREPARE_FOR_RENAME, NULL);
+
+ LEX_STRING old_db_name= { alter_ctx->db, strlen(alter_ctx->db) };
+ LEX_STRING old_table_name=
+ { alter_ctx->table_name, strlen(alter_ctx->table_name) };
+ LEX_STRING new_db_name= { alter_ctx->new_db, strlen(alter_ctx->new_db) };
+ LEX_STRING new_table_name=
+ { alter_ctx->new_alias, strlen(alter_ctx->new_alias) };
+ (void) rename_table_in_stat_tables(thd, &old_db_name, &old_table_name,
+ &new_db_name, &new_table_name);
+
+ if (mysql_rename_table(old_db_type, alter_ctx->db, alter_ctx->table_name,
+ alter_ctx->new_db, alter_ctx->new_alias, 0))
+ error= -1;
+ else if (Table_triggers_list::change_table_name(thd,
+ alter_ctx->db,
+ alter_ctx->alias,
+ alter_ctx->table_name,
+ alter_ctx->new_db,
+ alter_ctx->new_alias))
+ {
+ (void) mysql_rename_table(old_db_type,
+ alter_ctx->new_db, alter_ctx->new_alias,
+ alter_ctx->db, alter_ctx->table_name,
+ NO_FK_CHECKS);
+ error= -1;
+ }
+ }
+
+ if (!error)
+ {
+#ifdef WITH_WSREP
+ if (!WSREP(thd) || do_log_write) {
+#endif /* WITH_WSREP */
+ error= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
+#ifdef WITH_WSREP
+ }
+#endif /* !WITH_WSREP */
+
+ if (!error)
+ my_ok(thd);
}
+ table_list->table= NULL; // For query cache
+ query_cache_invalidate3(thd, table_list, 0);
+
+ if ((thd->locked_tables_mode == LTM_LOCK_TABLES ||
+ thd->locked_tables_mode == LTM_PRELOCKED_UNDER_LOCK_TABLES))
+ {
+ /*
+ Under LOCK TABLES we should adjust meta-data locks before finishing
+ statement. Otherwise we can rely on them being released
+ along with the implicit commit.
+ */
+ if (alter_ctx->is_table_renamed())
+ thd->mdl_context.release_all_locks_for_name(mdl_ticket);
+ else
+ mdl_ticket->downgrade_lock(MDL_SHARED_NO_READ_WRITE);
+ }
+ DBUG_RETURN(error != 0);
+}
+
+
+/**
+ Alter table
+
+ @param thd Thread handle
+ @param new_db If there is a RENAME clause
+ @param new_name If there is a RENAME clause
+ @param create_info Information from the parsing phase about new
+ table properties.
+ @param table_list The table to change.
+ @param alter_info Lists of fields, keys to be changed, added
+ or dropped.
+ @param order_num How many ORDER BY fields has been specified.
+ @param order List of fields to ORDER BY.
+ @param ignore Whether we have ALTER IGNORE TABLE
+
+ @retval true Error
+ @retval false Success
+
+ This is a veery long function and is everything but the kitchen sink :)
+ It is used to alter a table and not only by ALTER TABLE but also
+ CREATE|DROP INDEX are mapped on this function.
+
+ When the ALTER TABLE statement just does a RENAME or ENABLE|DISABLE KEYS,
+ or both, then this function short cuts its operation by renaming
+ the table and/or enabling/disabling the keys. In this case, the FRM is
+ not changed, directly by mysql_alter_table. However, if there is a
+ RENAME + change of a field, or an index, the short cut is not used.
+ See how `create_list` is used to generate the new FRM regarding the
+ structure of the fields. The same is done for the indices of the table.
+
+ Altering a table can be done in two ways. The table can be modified
+ directly using an in-place algorithm, or the changes can be done using
+ an intermediate temporary table (copy). In-place is the preferred
+ algorithm as it avoids copying table data. The storage engine
+ selects which algorithm to use in check_if_supported_inplace_alter()
+ based on information about the table changes from fill_alter_inplace_info().
+*/
+
+bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
+ HA_CREATE_INFO *create_info,
+ TABLE_LIST *table_list,
+ Alter_info *alter_info,
+ uint order_num, ORDER *order, bool ignore)
+{
+ DBUG_ENTER("mysql_alter_table");
+
+ /*
+ Check if we attempt to alter mysql.slow_log or
+ mysql.general_log table and return an error if
+ it is the case.
+ TODO: this design is obsolete and will be removed.
+ */
+ int table_kind= check_if_log_table(table_list, FALSE, NullS);
+
+ if (table_kind)
+ {
+ /* Disable alter of enabled log tables */
+ if (logger.is_log_table_enabled(table_kind))
+ {
+ my_error(ER_BAD_LOG_STATEMENT, MYF(0), "ALTER");
+ DBUG_RETURN(true);
+ }
+
+ /* Disable alter of log tables to unsupported engine */
+ if ((create_info->used_fields & HA_CREATE_USED_ENGINE) &&
+ (!create_info->db_type || /* unknown engine */
+ !(create_info->db_type->flags & HTON_SUPPORT_LOG_TABLES)))
+ {
+ my_error(ER_UNSUPORTED_LOG_ENGINE, MYF(0),
+ hton_name(create_info->db_type)->str);
+ DBUG_RETURN(true);
+ }
+
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+ if (alter_info->flags & Alter_info::ALTER_PARTITION)
+ {
+ my_error(ER_WRONG_USAGE, MYF(0), "PARTITION", "log table");
+ DBUG_RETURN(true);
+ }
+#endif
+ }
+
+ THD_STAGE_INFO(thd, stage_init);
/*
Code below can handle only base tables so ensure that we won't open a view.
@@ -6183,20 +8370,35 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
*/
table_list->required_type= FRMTYPE_TABLE;
- Alter_table_prelocking_strategy alter_prelocking_strategy(alter_info);
+ Alter_table_prelocking_strategy alter_prelocking_strategy;
DEBUG_SYNC(thd, "alter_table_before_open_tables");
- error= open_and_lock_tables(thd, table_list, FALSE, 0,
- &alter_prelocking_strategy);
+ uint tables_opened;
+
+ thd->open_options|= HA_OPEN_FOR_ALTER;
+ bool error= open_tables(thd, &table_list, &tables_opened, 0,
+ &alter_prelocking_strategy);
+ thd->open_options&= ~HA_OPEN_FOR_ALTER;
+
+ DEBUG_SYNC(thd, "alter_opened_table");
+
+#ifdef WITH_WSREP
+ DBUG_EXECUTE_IF("sync.alter_opened_table",
+ {
+ const char act[]=
+ "now "
+ "wait_for signal.alter_opened_table";
+ DBUG_ASSERT(!debug_sync_set_action(thd,
+ STRING_WITH_LEN(act)));
+ };);
+#endif // WITH_WSREP
if (error)
- {
- DBUG_RETURN(TRUE);
- }
+ DBUG_RETURN(true);
- table= table_list->table;
+ TABLE *table= table_list->table;
table->use_all_columns();
- mdl_ticket= table->mdl_ticket;
+ MDL_ticket *mdl_ticket= table->mdl_ticket;
/*
Prohibit changing of the UNION list of a non-temporary MERGE table
@@ -6210,102 +8412,73 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
(table->s->tmp_table == NO_TMP_TABLE))
{
my_error(ER_LOCK_OR_ACTIVE_TRANSACTION, MYF(0));
- DBUG_RETURN(TRUE);
+ DBUG_RETURN(true);
}
+ Alter_table_ctx alter_ctx(thd, table_list, tables_opened, new_db, new_name);
+
+ MDL_request target_mdl_request;
+
/* Check that we are not trying to rename to an existing table */
- if (new_name)
+ if (alter_ctx.is_table_renamed())
{
- DBUG_PRINT("info", ("new_db.new_name: '%s'.'%s'", new_db, new_name));
- strmov(new_name_buff,new_name);
- strmov(new_alias= new_alias_buff, new_name);
- if (lower_case_table_names)
+ if (table->s->tmp_table != NO_TMP_TABLE)
{
- if (lower_case_table_names != 2)
+ if (find_temporary_table(thd, alter_ctx.new_db, alter_ctx.new_name))
{
- my_casedn_str(files_charset_info, new_name_buff);
- new_alias= new_name; // Create lower case table name
+ my_error(ER_TABLE_EXISTS_ERROR, MYF(0), alter_ctx.new_alias);
+ DBUG_RETURN(true);
}
- my_casedn_str(files_charset_info, new_name);
}
- if (new_db == db &&
- !my_strcasecmp(table_alias_charset, new_name_buff, table_name))
+ else
{
+ MDL_request_list mdl_requests;
+ MDL_request target_db_mdl_request;
+
+ target_mdl_request.init(MDL_key::TABLE,
+ alter_ctx.new_db, alter_ctx.new_name,
+ MDL_EXCLUSIVE, MDL_TRANSACTION);
+ mdl_requests.push_front(&target_mdl_request);
+
/*
- Source and destination table names are equal: make later check
- easier.
+ If we are moving the table to a different database, we also
+ need IX lock on the database name so that the target database
+ is protected by MDL while the table is moved.
*/
- new_alias= new_name= table_name;
- }
- else
- {
- if (table->s->tmp_table != NO_TMP_TABLE)
+ if (alter_ctx.is_database_changed())
{
- if (find_temporary_table(thd,new_db,new_name_buff))
- {
- my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_name_buff);
- DBUG_RETURN(TRUE);
- }
+ target_db_mdl_request.init(MDL_key::SCHEMA, alter_ctx.new_db, "",
+ MDL_INTENTION_EXCLUSIVE,
+ MDL_TRANSACTION);
+ mdl_requests.push_front(&target_db_mdl_request);
}
- else
- {
- MDL_request_list mdl_requests;
- MDL_request target_db_mdl_request;
-
- target_mdl_request.init(MDL_key::TABLE, new_db, new_name,
- MDL_EXCLUSIVE, MDL_TRANSACTION);
- mdl_requests.push_front(&target_mdl_request);
-
- /*
- If we are moving the table to a different database, we also
- need IX lock on the database name so that the target database
- is protected by MDL while the table is moved.
- */
- if (new_db != db)
- {
- target_db_mdl_request.init(MDL_key::SCHEMA, new_db, "",
- MDL_INTENTION_EXCLUSIVE,
- MDL_TRANSACTION);
- mdl_requests.push_front(&target_db_mdl_request);
- }
- /*
- Global intention exclusive lock must have been already acquired when
- table to be altered was open, so there is no need to do it here.
- */
- DBUG_ASSERT(thd->mdl_context.is_lock_owner(MDL_key::GLOBAL,
- "", "",
- MDL_INTENTION_EXCLUSIVE));
+ /*
+ Global intention exclusive lock must have been already acquired when
+ table to be altered was open, so there is no need to do it here.
+ */
+ DBUG_ASSERT(thd->mdl_context.is_lock_owner(MDL_key::GLOBAL,
+ "", "",
+ MDL_INTENTION_EXCLUSIVE));
- if (thd->mdl_context.acquire_locks(&mdl_requests,
- thd->variables.lock_wait_timeout))
- DBUG_RETURN(TRUE);
+ if (thd->mdl_context.acquire_locks(&mdl_requests,
+ thd->variables.lock_wait_timeout))
+ DBUG_RETURN(true);
- DEBUG_SYNC(thd, "locked_table_name");
- /*
- Table maybe does not exist, but we got an exclusive lock
- on the name, now we can safely try to find out for sure.
- */
- build_table_filename(new_name_buff, sizeof(new_name_buff) - 1,
- new_db, new_name_buff, reg_ext, 0);
- build_table_filename(old_name_buff, sizeof(old_name_buff) - 1,
- db, table_name, reg_ext, 0);
- if (check_table_file_presence(old_name_buff, new_name_buff, new_db,
- new_name, new_alias, TRUE))
- {
- /* Table will be closed in do_command() */
- goto err;
- }
+ DEBUG_SYNC(thd, "locked_table_name");
+ /*
+ Table maybe does not exist, but we got an exclusive lock
+ on the name, now we can safely try to find out for sure.
+ */
+ if (ha_table_exists(thd, alter_ctx.new_db, alter_ctx.new_name, 0))
+ {
+ /* Table will be closed in do_command() */
+ my_error(ER_TABLE_EXISTS_ERROR, MYF(0), alter_ctx.new_alias);
+ DBUG_RETURN(true);
}
}
}
- else
- {
- new_alias= (lower_case_table_names == 2) ? alias : table_name;
- new_name= table_name;
- }
- old_db_type= table->s->db_type();
if (!create_info->db_type)
{
#ifdef WITH_PARTITION_STORAGE_ENGINE
@@ -6323,19 +8496,18 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
}
else
#endif
- create_info->db_type= old_db_type;
+ create_info->db_type= table->s->db_type();
}
- if (check_engine(thd, new_db, new_name, create_info))
- goto err;
- new_db_type= create_info->db_type;
+ if (check_engine(thd, alter_ctx.new_db, alter_ctx.new_name, create_info))
+ DBUG_RETURN(true);
- if ((new_db_type != old_db_type ||
- alter_info->flags & ALTER_PARTITION) &&
+ if ((create_info->db_type != table->s->db_type() ||
+ alter_info->flags & Alter_info::ALTER_PARTITION) &&
!table->file->can_switch_engines())
{
my_error(ER_ROW_IS_REFERENCED, MYF(0));
- goto err;
+ DBUG_RETURN(true);
}
/*
@@ -6346,448 +8518,211 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
create_info->db_type is set here, check to parent table access is delayed
till this point for the alter operation.
*/
- if ((alter_info->flags & ALTER_FOREIGN_KEY) &&
+ if ((alter_info->flags & Alter_info::ADD_FOREIGN_KEY) &&
check_fk_parent_table_access(thd, create_info, alter_info))
- goto err;
+ DBUG_RETURN(true);
/*
If this is an ALTER TABLE and no explicit row type specified reuse
the table's row type.
- Note: this is the same as if the row type was specified explicitly and
- we must thus set HA_CREATE_USED_ROW_FORMAT!
+ Note: this is the same as if the row type was specified explicitly.
*/
if (create_info->row_type == ROW_TYPE_NOT_USED)
{
/* ALTER TABLE without explicit row type */
create_info->row_type= table->s->row_type;
- /*
- We have to mark the row type as used, as otherwise the engine may
- change the row format in update_create_info().
- */
- create_info->used_fields|= HA_CREATE_USED_ROW_FORMAT;
- explicit_used_fields|= HA_CREATE_USED_ROW_FORMAT;
+ }
+ else
+ {
+ /* ALTER TABLE with specific row type */
+ create_info->used_fields |= HA_CREATE_USED_ROW_FORMAT;
}
DBUG_PRINT("info", ("old type: %s new type: %s",
- ha_resolve_storage_engine_name(old_db_type),
- ha_resolve_storage_engine_name(new_db_type)));
- if (ha_check_storage_engine_flag(old_db_type, HTON_ALTER_NOT_SUPPORTED) ||
- ha_check_storage_engine_flag(new_db_type, HTON_ALTER_NOT_SUPPORTED))
+ ha_resolve_storage_engine_name(table->s->db_type()),
+ ha_resolve_storage_engine_name(create_info->db_type)));
+ if (ha_check_storage_engine_flag(table->s->db_type(), HTON_ALTER_NOT_SUPPORTED))
{
DBUG_PRINT("info", ("doesn't support alter"));
- my_error(ER_ILLEGAL_HA, MYF(0), table_name);
- goto err;
+ my_error(ER_ILLEGAL_HA, MYF(0), hton_name(table->s->db_type())->str,
+ alter_ctx.db, alter_ctx.table_name);
+ DBUG_RETURN(true);
}
- if (table->s->tmp_table == NO_TMP_TABLE)
- mysql_audit_alter_table(thd, table_list);
-
- thd_proc_info(thd, "setup");
- if (!(alter_info->flags & ~(ALTER_RENAME | ALTER_KEYS_ONOFF)) &&
- !table->s->tmp_table) // no need to touch frm
+ if (ha_check_storage_engine_flag(create_info->db_type,
+ HTON_ALTER_NOT_SUPPORTED))
{
- switch (alter_info->keys_onoff) {
- case LEAVE_AS_IS:
- break;
- case ENABLE:
- if (wait_while_table_is_used(thd, table, extra_func,
- TDC_RT_REMOVE_NOT_OWN_AND_MARK_NOT_USABLE))
- goto err;
- DEBUG_SYNC(thd,"alter_table_enable_indexes");
- error= table->file->ha_enable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE);
- table->s->allow_access_to_protected_table();
- break;
- case DISABLE:
- if (wait_while_table_is_used(thd, table, extra_func,
- TDC_RT_REMOVE_NOT_OWN_AND_MARK_NOT_USABLE))
- goto err;
- error=table->file->ha_disable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE);
- table->s->allow_access_to_protected_table();
- break;
- default:
- DBUG_ASSERT(FALSE);
- error= 0;
- break;
- }
+ DBUG_PRINT("info", ("doesn't support alter"));
+ my_error(ER_ILLEGAL_HA, MYF(0), hton_name(create_info->db_type)->str,
+ alter_ctx.new_db, alter_ctx.new_name);
+ DBUG_RETURN(true);
+ }
- if (error == HA_ERR_WRONG_COMMAND)
- {
- error= 0;
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
- ER_ILLEGAL_HA, ER(ER_ILLEGAL_HA),
- table->alias.c_ptr());
-#ifdef WITH_WSREP
- WSREP_DEBUG("ignoring DDL failure: %d %s", error, thd->query());
-#endif /* WITH_WSREP */
- }
+ if (table->s->tmp_table == NO_TMP_TABLE)
+ mysql_audit_alter_table(thd, table_list);
- if (!error && (new_name != table_name || new_db != db))
- {
- thd_proc_info(thd, "rename");
- /*
- Then do a 'simple' rename of the table. First we need to close all
- instances of 'source' table.
- Note that if wait_while_table_is_used() returns error here (i.e. if
- this thread was killed) then it must be that previous step of
- simple rename did nothing and therefore we can safely return
- without additional clean-up.
- */
- if (wait_while_table_is_used(thd, table, extra_func,
- TDC_RT_REMOVE_NOT_OWN_AND_MARK_NOT_USABLE))
- goto err;
- close_all_tables_for_name(thd, table->s, HA_EXTRA_PREPARE_FOR_RENAME);
- /*
- Then, we want check once again that target table does not exist.
- Actually the order of these two steps does not matter since
- earlier we took exclusive metadata lock on the target table, so
- we do them in this particular order only to be consistent with 5.0,
- in which we don't take this lock and where this order really matters.
- TODO: Investigate if we need this access() check at all.
- */
- if (!access(new_name_buff,F_OK))
- {
- my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_name);
- error= -1;
- }
- else
- {
- *fn_ext(new_name)=0;
- if (mysql_rename_table(old_db_type,db,table_name,new_db,new_alias, 0))
- error= -1;
- else if (Table_triggers_list::change_table_name(thd, db,
- alias, table_name,
- new_db, new_alias))
- {
- (void) mysql_rename_table(old_db_type, new_db, new_alias, db,
- table_name, NO_FK_CHECKS);
- error= -1;
- }
- }
- }
+ THD_STAGE_INFO(thd, stage_setup);
- if (error == HA_ERR_WRONG_COMMAND)
- {
- error= 0;
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
- ER_ILLEGAL_HA, ER(ER_ILLEGAL_HA),
- table->alias.c_ptr());
-#ifdef WITH_WSREP
- WSREP_DEBUG("ignoring DDL failure: %d %s", error, thd->query());
-#endif /* WITH_WSREP */
- }
+ handle_if_exists_options(thd, table, alter_info);
- if (!error)
- {
- error= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
- if (!error)
- my_ok(thd);
- }
- else if (error > 0)
- {
- table->file->print_error(error, MYF(0));
- error= -1;
- }
- table_list->table= NULL; // For query cache
- query_cache_invalidate3(thd, table_list, 0);
+ /*
+ Look if we have to do anything at all.
+ ALTER can become NOOP after handling
+ the IF (NOT) EXISTS options.
+ */
+ if (alter_info->flags == 0)
+ {
+ my_snprintf(alter_ctx.tmp_name, sizeof(alter_ctx.tmp_name),
+ ER(ER_INSERT_INFO), 0L, 0L,
+ thd->get_stmt_da()->current_statement_warn_count());
+ my_ok(thd, 0L, 0L, alter_ctx.tmp_name);
+ DBUG_RETURN(false);
+ }
- if ((thd->locked_tables_mode == LTM_LOCK_TABLES ||
- thd->locked_tables_mode == LTM_PRELOCKED_UNDER_LOCK_TABLES))
+ if (!(alter_info->flags & ~(Alter_info::ALTER_RENAME |
+ Alter_info::ALTER_KEYS_ONOFF)) &&
+ alter_info->requested_algorithm !=
+ Alter_info::ALTER_TABLE_ALGORITHM_COPY &&
+ !table->s->tmp_table) // no need to touch frm
+ {
+ // This requires X-lock, no other lock levels supported.
+ if (alter_info->requested_lock != Alter_info::ALTER_TABLE_LOCK_DEFAULT &&
+ alter_info->requested_lock != Alter_info::ALTER_TABLE_LOCK_EXCLUSIVE)
{
- /*
- Under LOCK TABLES we should adjust meta-data locks before finishing
- statement. Otherwise we can rely on them being released
- along with the implicit commit.
- */
- if (new_name != table_name || new_db != db)
- thd->mdl_context.release_all_locks_for_name(mdl_ticket);
- else
- mdl_ticket->downgrade_exclusive_lock(MDL_SHARED_NO_READ_WRITE);
+ my_error(ER_ALTER_OPERATION_NOT_SUPPORTED, MYF(0),
+ "LOCK=NONE/SHARED", "LOCK=EXCLUSIVE");
+ DBUG_RETURN(true);
}
- DBUG_RETURN(error);
+ bool res= simple_rename_or_index_change(thd, table_list,
+ alter_info->keys_onoff,
+ &alter_ctx);
+ DBUG_RETURN(res);
}
/* We have to do full alter table. */
#ifdef WITH_PARTITION_STORAGE_ENGINE
- if (prep_alter_part_table(thd, table, alter_info, create_info, old_db_type,
- &partition_changed,
- db, table_name, path,
- &table_for_fast_alter_partition))
- goto err;
+ bool partition_changed= false;
+ bool fast_alter_partition= false;
+ {
+ if (prep_alter_part_table(thd, table, alter_info, create_info,
+ &alter_ctx, &partition_changed,
+ &fast_alter_partition))
+ {
+ DBUG_RETURN(true);
+ }
+ }
#endif
- /*
- If the old table had partitions and we are doing ALTER TABLE ...
- engine= <new_engine>, the new table must preserve the original
- partitioning. That means that the new engine is still the
- partitioning engine, not the engine specified in the parser.
- This is discovered in prep_alter_part_table, which in such case
- updates create_info->db_type.
- Now we need to update the stack copy of create_info->db_type,
- as otherwise we won't be able to correctly move the files of the
- temporary table to the result table files.
- */
- new_db_type= create_info->db_type;
-
- if (is_index_maintenance_unique (table, alter_info))
- need_copy_table= ALTER_TABLE_DATA_CHANGED;
-
- if (mysql_prepare_alter_table(thd, table, create_info, alter_info))
- goto err;
-
- /* Remove markers set for update_create_info */
- create_info->used_fields&= ~explicit_used_fields;
-
- if (need_copy_table == ALTER_TABLE_METADATA_ONLY)
- need_copy_table= alter_info->change_level;
-
- set_table_default_charset(thd, create_info, db);
- if (thd->variables.old_alter_table
- || (table->s->db_type() != create_info->db_type)
-#ifdef WITH_PARTITION_STORAGE_ENGINE
- || partition_changed
-#endif
- )
- need_copy_table= ALTER_TABLE_DATA_CHANGED;
- else
+ if (mysql_prepare_alter_table(thd, table, create_info, alter_info,
+ &alter_ctx))
{
- enum_alter_table_change_level need_copy_table_res;
- /* Check how much the tables differ. */
- if (mysql_compare_tables(table, alter_info,
- create_info, order_num,
- &need_copy_table_res,
- &key_info_buffer,
- &index_drop_buffer, &index_drop_count,
- &index_add_buffer, &index_add_count,
- &candidate_key_count))
- goto err;
-
- DBUG_EXECUTE_IF("alter_table_only_metadata_change", {
- if (need_copy_table_res != ALTER_TABLE_METADATA_ONLY)
- goto err; });
- DBUG_EXECUTE_IF("alter_table_only_index_change", {
- if (need_copy_table_res != ALTER_TABLE_INDEX_CHANGED)
- goto err; });
-
- if (need_copy_table == ALTER_TABLE_METADATA_ONLY)
- need_copy_table= need_copy_table_res;
+ DBUG_RETURN(true);
}
- /*
- If there are index changes only, try to do them in-place. "Index
- changes only" means also that the handler for the table does not
- change. The table is open and locked. The handler can be accessed.
- */
- if (need_copy_table == ALTER_TABLE_INDEX_CHANGED)
- {
- int pk_changed= 0;
- ulong alter_flags= 0;
- ulong needed_inplace_with_read_flags= 0;
- ulong needed_inplace_flags= 0;
- KEY *key;
- uint *idx_p;
- uint *idx_end_p;
-
- alter_flags= table->file->alter_table_flags(alter_info->flags);
- DBUG_PRINT("info", ("alter_flags: %lu", alter_flags));
- /* Check dropped indexes. */
- for (idx_p= index_drop_buffer, idx_end_p= idx_p + index_drop_count;
- idx_p < idx_end_p;
- idx_p++)
- {
- key= table->key_info + *idx_p;
- DBUG_PRINT("info", ("index dropped: '%s'", key->name));
- if (key->flags & HA_NOSAME)
- {
- /*
- Unique key. Check for "PRIMARY".
- or if dropping last unique key
- */
- if ((uint) (key - table->key_info) == table->s->primary_key)
- {
- DBUG_PRINT("info", ("Dropping primary key"));
- /* Primary key. */
- needed_inplace_with_read_flags|= HA_INPLACE_DROP_PK_INDEX_NO_WRITE;
- needed_inplace_flags|= HA_INPLACE_DROP_PK_INDEX_NO_READ_WRITE;
- pk_changed++;
- candidate_key_count--;
- }
- else
- {
- KEY_PART_INFO *part_end= key->key_part + key->key_parts;
- bool is_candidate_key= true;
-
- /* Non-primary unique key. */
- needed_inplace_with_read_flags|=
- HA_INPLACE_DROP_UNIQUE_INDEX_NO_WRITE;
- needed_inplace_flags|= HA_INPLACE_DROP_UNIQUE_INDEX_NO_READ_WRITE;
+ set_table_default_charset(thd, create_info, alter_ctx.db);
+ promote_first_timestamp_column(&alter_info->create_list);
- /*
- Check if all fields in key are declared
- NOT NULL and adjust candidate_key_count
- */
- for (KEY_PART_INFO *key_part= key->key_part;
- key_part < part_end;
- key_part++)
- is_candidate_key=
- (is_candidate_key &&
- (! table->field[key_part->fieldnr-1]->maybe_null()));
- if (is_candidate_key)
- candidate_key_count--;
- }
- }
- else
- {
- /* Non-unique key. */
- needed_inplace_with_read_flags|= HA_INPLACE_DROP_INDEX_NO_WRITE;
- needed_inplace_flags|= HA_INPLACE_DROP_INDEX_NO_READ_WRITE;
- }
- }
- no_pk= ((table->s->primary_key == MAX_KEY) ||
- (needed_inplace_with_read_flags &
- HA_INPLACE_DROP_PK_INDEX_NO_WRITE));
- /* Check added indexes. */
- for (idx_p= index_add_buffer, idx_end_p= idx_p + index_add_count;
- idx_p < idx_end_p;
- idx_p++)
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+ if (fast_alter_partition)
+ {
+ /*
+ ALGORITHM and LOCK clauses are generally not allowed by the
+ parser for operations related to partitioning.
+ The exceptions are ALTER_PARTITION and ALTER_REMOVE_PARTITIONING.
+ For consistency, we report ER_ALTER_OPERATION_NOT_SUPPORTED here.
+ */
+ if (alter_info->requested_lock !=
+ Alter_info::ALTER_TABLE_LOCK_DEFAULT)
{
- key= key_info_buffer + *idx_p;
- DBUG_PRINT("info", ("index added: '%s'", key->name));
- if (key->flags & HA_NOSAME)
- {
- /* Unique key */
-
- KEY_PART_INFO *part_end= key->key_part + key->key_parts;
- bool is_candidate_key= true;
-
- /*
- Check if all fields in key are declared
- NOT NULL
- */
- for (KEY_PART_INFO *key_part= key->key_part;
- key_part < part_end;
- key_part++)
- is_candidate_key=
- (is_candidate_key &&
- (! table->field[key_part->fieldnr]->maybe_null()));
-
- /*
- Check for "PRIMARY"
- or if adding first unique key
- defined on non-nullable fields
- */
-
- if ((!my_strcasecmp(system_charset_info,
- key->name, primary_key_name)) ||
- (no_pk && candidate_key_count == 0 && is_candidate_key))
- {
- DBUG_PRINT("info", ("Adding primary key"));
- /* Primary key. */
- needed_inplace_with_read_flags|= HA_INPLACE_ADD_PK_INDEX_NO_WRITE;
- needed_inplace_flags|= HA_INPLACE_ADD_PK_INDEX_NO_READ_WRITE;
- pk_changed++;
- no_pk= false;
- }
- else
- {
- /* Non-primary unique key. */
- needed_inplace_with_read_flags|= HA_INPLACE_ADD_UNIQUE_INDEX_NO_WRITE;
- needed_inplace_flags|= HA_INPLACE_ADD_UNIQUE_INDEX_NO_READ_WRITE;
- if (ignore)
- {
- /*
- If ignore is used, we have to remove all duplicate rows,
- which require a full table copy.
- */
- need_copy_table= ALTER_TABLE_DATA_CHANGED;
- pk_changed= 2; // Don't change need_copy_table
- break;
- }
- }
- }
- else
- {
- /* Non-unique key. */
- needed_inplace_with_read_flags|= HA_INPLACE_ADD_INDEX_NO_WRITE;
- needed_inplace_flags|= HA_INPLACE_ADD_INDEX_NO_READ_WRITE;
- }
+ my_error(ER_ALTER_OPERATION_NOT_SUPPORTED_REASON, MYF(0),
+ "LOCK=NONE/SHARED/EXCLUSIVE",
+ ER(ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_PARTITION),
+ "LOCK=DEFAULT");
+ DBUG_RETURN(true);
}
-
- if ((candidate_key_count > 0) &&
- (needed_inplace_with_read_flags & HA_INPLACE_DROP_PK_INDEX_NO_WRITE))
+ else if (alter_info->requested_algorithm !=
+ Alter_info::ALTER_TABLE_ALGORITHM_DEFAULT)
{
- /*
- Dropped primary key when there is some other unique
- not null key that should be converted to primary key
- */
- needed_inplace_with_read_flags|= HA_INPLACE_ADD_PK_INDEX_NO_WRITE;
- needed_inplace_flags|= HA_INPLACE_ADD_PK_INDEX_NO_READ_WRITE;
- pk_changed= 2;
+ my_error(ER_ALTER_OPERATION_NOT_SUPPORTED_REASON, MYF(0),
+ "ALGORITHM=COPY/INPLACE",
+ ER(ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_PARTITION),
+ "ALGORITHM=DEFAULT");
+ DBUG_RETURN(true);
}
- DBUG_PRINT("info",
- ("needed_inplace_with_read_flags: 0x%lx, needed_inplace_flags: 0x%lx",
- needed_inplace_with_read_flags, needed_inplace_flags));
/*
- In-place add/drop index is possible only if
- the primary key is not added and dropped in the same statement.
- Otherwise we have to recreate the table.
- need_copy_table is no-zero at this place.
-
- Also, in-place is not possible if we add a primary key
- and drop another key in the same statement. If the drop fails,
- we will not be able to revert adding of primary key.
+ Upgrade from MDL_SHARED_UPGRADABLE to MDL_SHARED_NO_WRITE.
+ Afterwards it's safe to take the table level lock.
*/
- if ( pk_changed < 2 )
+ if (thd->mdl_context.upgrade_shared_lock(mdl_ticket, MDL_SHARED_NO_WRITE,
+ thd->variables.lock_wait_timeout)
+ || lock_tables(thd, table_list, alter_ctx.tables_opened, 0))
{
- if ((needed_inplace_with_read_flags & HA_INPLACE_ADD_PK_INDEX_NO_WRITE) &&
- index_drop_count > 0)
- {
- /*
- Do copy, not in-place ALTER.
- Avoid setting ALTER_TABLE_METADATA_ONLY.
- */
- }
- else if ((alter_flags & needed_inplace_with_read_flags) ==
- needed_inplace_with_read_flags)
- {
- /* All required in-place flags to allow concurrent reads are present. */
- need_copy_table= ALTER_TABLE_METADATA_ONLY;
- need_lock_for_indexes= FALSE;
- }
- else if ((alter_flags & needed_inplace_flags) == needed_inplace_flags)
- {
- /* All required in-place flags are present. */
- need_copy_table= ALTER_TABLE_METADATA_ONLY;
- }
+ DBUG_RETURN(true);
}
- DBUG_PRINT("info", ("need_copy_table: %u need_lock: %d",
- need_copy_table, need_lock_for_indexes));
+
+ // In-place execution of ALTER TABLE for partitioning.
+ DBUG_RETURN(fast_alter_partition_table(thd, table, alter_info,
+ create_info, table_list,
+ alter_ctx.db,
+ alter_ctx.table_name));
}
+#endif
/*
- better have a negative test here, instead of positive, like
- alter_info->flags & ALTER_ADD_COLUMN|ALTER_ADD_INDEX|...
- so that ALTER TABLE won't break when somebody will add new flag
+ Use copy algorithm if:
+ - old_alter_table system variable is set without in-place requested using
+ the ALGORITHM clause.
+ - Or if in-place is impossible for given operation.
+ - Changes to partitioning which were not handled by fast_alter_part_table()
+ needs to be handled using table copying algorithm unless the engine
+ supports auto-partitioning as such engines can do some changes
+ using in-place API.
*/
- if (need_copy_table == ALTER_TABLE_METADATA_ONLY)
- create_info->frm_only= 1;
-
+ if ((thd->variables.old_alter_table &&
+ alter_info->requested_algorithm !=
+ Alter_info::ALTER_TABLE_ALGORITHM_INPLACE)
+ || is_inplace_alter_impossible(table, create_info, alter_info)
#ifdef WITH_PARTITION_STORAGE_ENGINE
- if (table_for_fast_alter_partition)
+ || (partition_changed &&
+ !(table->s->db_type()->partition_flags() & HA_USE_AUTO_PARTITION))
+#endif
+ )
{
- DBUG_RETURN(fast_alter_partition_table(thd, table, alter_info,
- create_info, table_list,
- db, table_name,
- table_for_fast_alter_partition));
+ if (alter_info->requested_algorithm ==
+ Alter_info::ALTER_TABLE_ALGORITHM_INPLACE)
+ {
+ my_error(ER_ALTER_OPERATION_NOT_SUPPORTED, MYF(0),
+ "ALGORITHM=INPLACE", "ALGORITHM=COPY");
+ DBUG_RETURN(true);
+ }
+ alter_info->requested_algorithm= Alter_info::ALTER_TABLE_ALGORITHM_COPY;
}
-#endif
- my_snprintf(tmp_name, sizeof(tmp_name), "%s-%lx_%lx", tmp_file_prefix,
- current_pid, thd->thread_id);
- /* Safety fix for innodb */
- if (lower_case_table_names)
- my_casedn_str(files_charset_info, tmp_name);
+ /*
+ ALTER TABLE ... ENGINE to the same engine is a common way to
+ request table rebuild. Set ALTER_RECREATE flag to force table
+ rebuild.
+ */
+ if (create_info->db_type == table->s->db_type() &&
+ create_info->used_fields & HA_CREATE_USED_ENGINE)
+ alter_info->flags|= Alter_info::ALTER_RECREATE;
+
+ /*
+ If the old table had partitions and we are doing ALTER TABLE ...
+ engine= <new_engine>, the new table must preserve the original
+ partitioning. This means that the new engine is still the
+ partitioning engine, not the engine specified in the parser.
+ This is discovered in prep_alter_part_table, which in such case
+ updates create_info->db_type.
+ It's therefore important that the assignment below is done
+ after prep_alter_part_table.
+ */
+ handlerton *new_db_type= create_info->db_type;
+ handlerton *old_db_type= table->s->db_type();
+ TABLE *new_table= NULL;
+ ha_rows copied=0,deleted=0;
/*
Handling of symlinked tables:
@@ -6813,318 +8748,340 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
Copy data.
Remove old table and symlinks.
*/
- if (!strcmp(db, new_db)) // Ignore symlink if db changed
+ char index_file[FN_REFLEN], data_file[FN_REFLEN];
+
+ if (!alter_ctx.is_database_changed())
{
if (create_info->index_file_name)
{
/* Fix index_file_name to have 'tmp_name' as basename */
- strmov(index_file, tmp_name);
+ strmov(index_file, alter_ctx.tmp_name);
create_info->index_file_name=fn_same(index_file,
- create_info->index_file_name,
- 1);
+ create_info->index_file_name,
+ 1);
}
if (create_info->data_file_name)
{
/* Fix data_file_name to have 'tmp_name' as basename */
- strmov(data_file, tmp_name);
+ strmov(data_file, alter_ctx.tmp_name);
create_info->data_file_name=fn_same(data_file,
- create_info->data_file_name,
- 1);
+ create_info->data_file_name,
+ 1);
}
}
else
+ {
+ /* Ignore symlink if db is changed. */
create_info->data_file_name=create_info->index_file_name=0;
+ }
DEBUG_SYNC(thd, "alter_table_before_create_table_no_lock");
- DBUG_EXECUTE_IF("sleep_before_create_table_no_lock",
- my_sleep(100000););
+ /* We can abort alter table for any table type */
+ thd->abort_on_warning= !ignore && thd->is_strict_mode();
+
/*
- Create a table with a temporary name.
- With create_info->frm_only == 1 this creates a .frm file only and
- we keep the original row format.
+ Create .FRM for new version of table with a temporary name.
We don't log the statement, it will be logged later.
+
+ Keep information about keys in newly created table as it
+ will be used later to construct Alter_inplace_info object
+ and by fill_alter_inplace_info() call.
*/
- if (need_copy_table == ALTER_TABLE_METADATA_ONLY)
- {
- DBUG_ASSERT(create_info->frm_only);
- /* Ensure we keep the original table format */
- create_info->table_options= ((create_info->table_options &
- ~HA_OPTION_PACK_RECORD) |
- (table->s->db_create_options &
- HA_OPTION_PACK_RECORD));
- }
+ KEY *key_info;
+ uint key_count;
+ /*
+ Remember if the new definition has new VARCHAR column;
+ create_info->varchar will be reset in create_table_impl()/
+ mysql_prepare_create_table().
+ */
+ bool varchar= create_info->varchar;
+ LEX_CUSTRING frm= {0,0};
+
tmp_disable_binlog(thd);
create_info->options|=HA_CREATE_TMP_ALTER;
- error= mysql_create_table_no_lock(thd, new_db, tmp_name,
- create_info,
- alter_info,
- 1, 0, NULL);
+ error= create_table_impl(thd,
+ alter_ctx.db, alter_ctx.table_name,
+ alter_ctx.new_db, alter_ctx.tmp_name,
+ alter_ctx.get_tmp_path(),
+ create_info, alter_info,
+ C_ALTER_TABLE_FRM_ONLY, NULL,
+ &key_info, &key_count, &frm);
reenable_binlog(thd);
+ thd->abort_on_warning= false;
if (error)
- goto err;
+ {
+ my_free(const_cast<uchar*>(frm.str));
+ DBUG_RETURN(true);
+ }
+
+ /* Remember that we have not created table in storage engine yet. */
+ bool no_ha_table= true;
- /* Open the table if we need to copy the data. */
- DBUG_PRINT("info", ("need_copy_table: %u", need_copy_table));
- if (need_copy_table != ALTER_TABLE_METADATA_ONLY)
+ if (alter_info->requested_algorithm != Alter_info::ALTER_TABLE_ALGORITHM_COPY)
{
- if (table->s->tmp_table)
+ Alter_inplace_info ha_alter_info(create_info, alter_info,
+ key_info, key_count,
+ IF_PARTITIONING(thd->work_part_info, NULL),
+ ignore);
+ TABLE *altered_table= NULL;
+ bool use_inplace= true;
+
+ /* Fill the Alter_inplace_info structure. */
+ if (fill_alter_inplace_info(thd, table, varchar, &ha_alter_info))
+ goto err_new_table_cleanup;
+
+ if (ha_alter_info.handler_flags == 0)
{
- Open_table_context ot_ctx(thd, (MYSQL_OPEN_IGNORE_FLUSH |
- MYSQL_OPEN_FOR_REPAIR |
- MYSQL_LOCK_IGNORE_TIMEOUT));
- TABLE_LIST tbl;
- bzero((void*) &tbl, sizeof(tbl));
- tbl.db= new_db;
- tbl.table_name= tbl.alias= tmp_name;
- /* Table is in thd->temporary_tables */
- (void) open_table(thd, &tbl, thd->mem_root, &ot_ctx);
- new_table= tbl.table;
+ /*
+ No-op ALTER, no need to call handler API functions.
+
+ If this code path is entered for an ALTER statement that
+ should not be a real no-op, new handler flags should be added
+ and fill_alter_inplace_info() adjusted.
+
+ Note that we can end up here if an ALTER statement has clauses
+ that cancel each other out (e.g. ADD/DROP identically index).
+
+ Also note that we ignore the LOCK clause here.
+
+ TODO don't create the frm in the first place
+ */
+ deletefrm(alter_ctx.get_tmp_path());
+ my_free(const_cast<uchar*>(frm.str));
+ goto end_inplace;
+ }
+
+ // We assume that the table is non-temporary.
+ DBUG_ASSERT(!table->s->tmp_table);
+
+ if (!(altered_table= open_table_uncached(thd, new_db_type,
+ alter_ctx.get_tmp_path(),
+ alter_ctx.new_db,
+ alter_ctx.tmp_name,
+ true, false)))
+ goto err_new_table_cleanup;
+
+ /* Set markers for fields in TABLE object for altered table. */
+ update_altered_table(ha_alter_info, altered_table);
+
+ /*
+ Mark all columns in 'altered_table' as used to allow usage
+ of its record[0] buffer and Field objects during in-place
+ ALTER TABLE.
+ */
+ altered_table->column_bitmaps_set_no_signal(&altered_table->s->all_set,
+ &altered_table->s->all_set);
+ restore_record(altered_table, s->default_values); // Create empty record
+ if (altered_table->default_field && altered_table->update_default_fields())
+ goto err_new_table_cleanup;
+
+ // Ask storage engine whether to use copy or in-place
+ enum_alter_inplace_result inplace_supported=
+ table->file->check_if_supported_inplace_alter(altered_table,
+ &ha_alter_info);
+
+ switch (inplace_supported) {
+ case HA_ALTER_INPLACE_EXCLUSIVE_LOCK:
+ // If SHARED lock and no particular algorithm was requested, use COPY.
+ if (alter_info->requested_lock ==
+ Alter_info::ALTER_TABLE_LOCK_SHARED &&
+ alter_info->requested_algorithm ==
+ Alter_info::ALTER_TABLE_ALGORITHM_DEFAULT)
+ {
+ use_inplace= false;
+ }
+ // Otherwise, if weaker lock was requested, report errror.
+ else if (alter_info->requested_lock ==
+ Alter_info::ALTER_TABLE_LOCK_NONE ||
+ alter_info->requested_lock ==
+ Alter_info::ALTER_TABLE_LOCK_SHARED)
+ {
+ ha_alter_info.report_unsupported_error("LOCK=NONE/SHARED",
+ "LOCK=EXCLUSIVE");
+ close_temporary_table(thd, altered_table, true, false);
+ goto err_new_table_cleanup;
+ }
+ break;
+ case HA_ALTER_INPLACE_SHARED_LOCK_AFTER_PREPARE:
+ case HA_ALTER_INPLACE_SHARED_LOCK:
+ // If weaker lock was requested, report errror.
+ if (alter_info->requested_lock ==
+ Alter_info::ALTER_TABLE_LOCK_NONE)
+ {
+ ha_alter_info.report_unsupported_error("LOCK=NONE", "LOCK=SHARED");
+ close_temporary_table(thd, altered_table, true, false);
+ goto err_new_table_cleanup;
+ }
+ break;
+ case HA_ALTER_INPLACE_NO_LOCK_AFTER_PREPARE:
+ case HA_ALTER_INPLACE_NO_LOCK:
+ break;
+ case HA_ALTER_INPLACE_NOT_SUPPORTED:
+ // If INPLACE was requested, report error.
+ if (alter_info->requested_algorithm ==
+ Alter_info::ALTER_TABLE_ALGORITHM_INPLACE)
+ {
+ ha_alter_info.report_unsupported_error("ALGORITHM=INPLACE",
+ "ALGORITHM=COPY");
+ close_temporary_table(thd, altered_table, true, false);
+ goto err_new_table_cleanup;
+ }
+ // COPY with LOCK=NONE is not supported, no point in trying.
+ if (alter_info->requested_lock ==
+ Alter_info::ALTER_TABLE_LOCK_NONE)
+ {
+ ha_alter_info.report_unsupported_error("LOCK=NONE", "LOCK=SHARED");
+ close_temporary_table(thd, altered_table, true, false);
+ goto err_new_table_cleanup;
+ }
+ // Otherwise use COPY
+ use_inplace= false;
+ break;
+ case HA_ALTER_ERROR:
+ default:
+ close_temporary_table(thd, altered_table, true, false);
+ goto err_new_table_cleanup;
+ }
+
+ if (use_inplace)
+ {
+ table->s->frm_image= &frm;
+ int res= mysql_inplace_alter_table(thd, table_list, table, altered_table,
+ &ha_alter_info, inplace_supported,
+ &target_mdl_request, &alter_ctx);
+ my_free(const_cast<uchar*>(frm.str));
+
+ if (res)
+ DBUG_RETURN(true);
+
+ goto end_inplace;
}
else
{
- char path[FN_REFLEN + 1];
- /* table is a normal table: Create temporary table in same directory */
- build_table_filename(path, sizeof(path) - 1, new_db, tmp_name, "",
- FN_IS_TMP);
- /* Open our intermediate table. */
- new_table= open_table_uncached(thd, path, new_db, tmp_name, TRUE);
+ close_temporary_table(thd, altered_table, true, false);
}
- if (!new_table)
+ }
+
+ /* ALTER TABLE using copy algorithm. */
+
+ /* Check if ALTER TABLE is compatible with foreign key definitions. */
+ if (fk_prepare_copy_alter_table(thd, table, alter_info, &alter_ctx))
+ goto err_new_table_cleanup;
+
+ if (!table->s->tmp_table)
+ {
+ // COPY algorithm doesn't work with concurrent writes.
+ if (alter_info->requested_lock == Alter_info::ALTER_TABLE_LOCK_NONE)
+ {
+ my_error(ER_ALTER_OPERATION_NOT_SUPPORTED_REASON, MYF(0),
+ "LOCK=NONE",
+ ER(ER_ALTER_OPERATION_NOT_SUPPORTED_REASON_COPY),
+ "LOCK=SHARED");
goto err_new_table_cleanup;
+ }
+
+ // If EXCLUSIVE lock is requested, upgrade already.
+ if (alter_info->requested_lock == Alter_info::ALTER_TABLE_LOCK_EXCLUSIVE &&
+ wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN))
+ goto err_new_table_cleanup;
+
/*
- Note: In case of MERGE table, we do not attach children. We do not
- copy data for MERGE tables. Only the children have data.
+ Otherwise upgrade to SHARED_NO_WRITE.
+ Note that under LOCK TABLES, we will already have SHARED_NO_READ_WRITE.
*/
+ if (alter_info->requested_lock != Alter_info::ALTER_TABLE_LOCK_EXCLUSIVE &&
+ thd->mdl_context.upgrade_shared_lock(mdl_ticket, MDL_SHARED_NO_WRITE,
+ thd->variables.lock_wait_timeout))
+ goto err_new_table_cleanup;
+
+ DEBUG_SYNC(thd, "alter_table_copy_after_lock_upgrade");
}
- /* Check if we can do the ALTER TABLE as online */
- if (require_online)
+ // It's now safe to take the table level lock.
+ if (lock_tables(thd, table_list, alter_ctx.tables_opened, 0))
+ goto err_new_table_cleanup;
+
+ if (ha_create_table(thd, alter_ctx.get_tmp_path(),
+ alter_ctx.new_db, alter_ctx.tmp_name,
+ create_info, &frm))
+ goto err_new_table_cleanup;
+
+ /* Mark that we have created table in storage engine. */
+ no_ha_table= false;
+
+ if (create_info->tmp_table())
{
- if (index_add_count || index_drop_count ||
- (new_table &&
- !(new_table->file->ha_table_flags() & HA_NO_COPY_ON_ALTER)))
- {
- my_error(ER_CANT_DO_ONLINE, MYF(0), "ALTER");
+ if (!open_table_uncached(thd, new_db_type,
+ alter_ctx.get_tmp_path(),
+ alter_ctx.new_db, alter_ctx.tmp_name,
+ true, true))
goto err_new_table_cleanup;
- }
}
+ /* Open the table since we need to copy the data. */
+ if (table->s->tmp_table != NO_TMP_TABLE)
+ {
+ TABLE_LIST tbl;
+ tbl.init_one_table(alter_ctx.new_db, strlen(alter_ctx.new_db),
+ alter_ctx.tmp_name, strlen(alter_ctx.tmp_name),
+ alter_ctx.tmp_name, TL_READ_NO_INSERT);
+ /* Table is in thd->temporary_tables */
+ (void) open_temporary_table(thd, &tbl);
+ new_table= tbl.table;
+ }
+ else
+ {
+ /* table is a normal table: Create temporary table in same directory */
+ /* Open our intermediate table. */
+ new_table= open_table_uncached(thd, new_db_type, alter_ctx.get_tmp_path(),
+ alter_ctx.new_db, alter_ctx.tmp_name,
+ true, true);
+ }
+ if (!new_table)
+ goto err_new_table_cleanup;
+ /*
+ Note: In case of MERGE table, we do not attach children. We do not
+ copy data for MERGE tables. Only the children have data.
+ */
+
/* Copy the data if necessary. */
thd->count_cuted_fields= CHECK_FIELD_WARN; // calc cuted fields
thd->cuted_fields=0L;
- copied=deleted=0;
/*
We do not copy data for MERGE tables. Only the children have data.
MERGE tables have HA_NO_COPY_ON_ALTER set.
*/
- if (new_table && !(new_table->file->ha_table_flags() & HA_NO_COPY_ON_ALTER))
+ if (!(new_table->file->ha_table_flags() & HA_NO_COPY_ON_ALTER))
{
- /* We don't want update TIMESTAMP fields during ALTER TABLE. */
- new_table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
new_table->next_number_field=new_table->found_next_number_field;
+ THD_STAGE_INFO(thd, stage_copy_to_tmp_table);
DBUG_EXECUTE_IF("abort_copy_table", {
my_error(ER_LOCK_WAIT_TIMEOUT, MYF(0));
goto err_new_table_cleanup;
});
- error= copy_data_between_tables(thd, table, new_table,
- alter_info->create_list, ignore,
- order_num, order, &copied, &deleted,
- alter_info->keys_onoff,
- alter_info->error_if_not_empty);
+ if (copy_data_between_tables(thd, table, new_table,
+ alter_info->create_list, ignore,
+ order_num, order, &copied, &deleted,
+ alter_info->keys_onoff,
+ &alter_ctx))
+ goto err_new_table_cleanup;
}
else
{
- /*
- Ensure that we will upgrade the metadata lock if
- handler::enable/disable_indexes() will be called.
- */
- if (alter_info->keys_onoff != LEAVE_AS_IS ||
- table->file->indexes_are_disabled())
- need_lock_for_indexes= true;
- if (!table->s->tmp_table && need_lock_for_indexes &&
- wait_while_table_is_used(thd, table, extra_func,
- TDC_RT_REMOVE_NOT_OWN_AND_MARK_NOT_USABLE))
+ if (!table->s->tmp_table &&
+ wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN))
goto err_new_table_cleanup;
- thd_proc_info(thd, "manage keys");
- DEBUG_SYNC(thd, "alter_table_manage_keys");
+ THD_STAGE_INFO(thd, stage_manage_keys);
alter_table_manage_keys(table, table->file->indexes_are_disabled(),
alter_info->keys_onoff);
- error= trans_commit_stmt(thd);
- if (trans_commit_implicit(thd))
- error= 1;
- /*
- If the table was locked, allow one to still run SHOW commands against it
- */
- if (table->s->protected_against_usage())
- table->s->allow_access_to_protected_table();
- }
- thd->count_cuted_fields= CHECK_FIELD_IGNORE;
-
- if (error)
- goto err_new_table_cleanup;
-
- /* If we did not need to copy, we might still need to add/drop indexes. */
- if (! new_table)
- {
- uint *key_numbers;
- uint *keyno_p;
- KEY *key_info;
- KEY *key;
- uint *idx_p;
- uint *idx_end_p;
- KEY_PART_INFO *key_part;
- KEY_PART_INFO *part_end;
- DBUG_PRINT("info", ("No new_table, checking add/drop index"));
-
- table->file->ha_prepare_for_alter();
- if (index_add_count)
- {
- /* The add_index() method takes an array of KEY structs. */
- key_info= (KEY*) thd->alloc(sizeof(KEY) * index_add_count);
- key= key_info;
- for (idx_p= index_add_buffer, idx_end_p= idx_p + index_add_count;
- idx_p < idx_end_p;
- idx_p++, key++)
- {
- /* Copy the KEY struct. */
- *key= key_info_buffer[*idx_p];
- /* Fix the key parts. */
- part_end= key->key_part + key->key_parts;
- for (key_part= key->key_part; key_part < part_end; key_part++)
- key_part->field= table->field[key_part->fieldnr];
- }
- /* Add the indexes. */
- if ((error= table->file->add_index(table, key_info, index_add_count,
- &add)))
- {
- /* Only report error if handler has not already reported an error */
- if (!thd->is_error())
- {
- /*
- HACK HACK HACK
- Prepare the list of keys for an error message.
- It must match what the engine does internally in ::add_index().
- Here we emulate what innobase_create_key_def() does.
- Luckily, in 10.0 this will go away.
- */
- KEY *save_key_info= table->key_info;
- uint add_cnt= index_add_count, old_cnt= table->s->keys;
- KEY *merged= (KEY*)thd->alloc((old_cnt + add_cnt) * sizeof(KEY));
-#define is_PK(K) (!my_strcasecmp(system_charset_info, (K)->name, "PRIMARY"))
-
- if (is_PK(key_info))
- {
- merged[0]= key_info[0];
- if (table->key_info && is_PK(table->key_info))
- {
- old_cnt--;
- table->key_info++;
- }
- memcpy(merged + 1, table->key_info, old_cnt * sizeof(KEY));
- memcpy(merged + old_cnt + 1, key_info + 1, (add_cnt - 1) * sizeof(KEY));
- }
- else
- merged= key_info;
-
- table->key_info= merged;
- table->file->print_error(error, MYF(0));
- table->key_info= save_key_info;
- }
- goto err_new_table_cleanup;
- }
- pending_inplace_add_index= true;
- }
- /*end of if (index_add_count)*/
-
- if (index_drop_count)
- {
- /* Currently we must finalize add index if we also drop indexes */
- if (pending_inplace_add_index)
- {
- /* Committing index changes needs exclusive metadata lock. */
- DBUG_ASSERT(thd->mdl_context.is_lock_owner(MDL_key::TABLE,
- table_list->db,
- table_list->table_name,
- MDL_EXCLUSIVE));
- if ((error= table->file->final_add_index(add, true)))
- {
- table->file->print_error(error, MYF(0));
- goto err_new_table_cleanup;
- }
- pending_inplace_add_index= false;
- }
- /* The prepare_drop_index() method takes an array of key numbers. */
- key_numbers= (uint*) thd->alloc(sizeof(uint) * index_drop_count);
- keyno_p= key_numbers;
- /* Get the number of each key. */
- for (idx_p= index_drop_buffer, idx_end_p= idx_p + index_drop_count;
- idx_p < idx_end_p;
- idx_p++, keyno_p++)
- *keyno_p= *idx_p;
- /*
- Tell the handler to prepare for drop indexes.
- This re-numbers the indexes to get rid of gaps.
- */
- error= table->file->prepare_drop_index(table, key_numbers,
- index_drop_count);
- if (!error)
- {
- /* Tell the handler to finally drop the indexes. */
- error= table->file->final_drop_index(table);
- }
-
- if (error)
- {
- table->file->print_error(error, MYF(0));
- if (index_add_count) // Drop any new indexes added.
- {
- /*
- Temporarily set table-key_info to include information about the
- indexes added above that we now need to drop.
- */
- KEY *save_key_info= table->key_info;
- table->key_info= key_info_buffer;
- if ((error= table->file->prepare_drop_index(table, index_add_buffer,
- index_add_count)))
- table->file->print_error(error, MYF(0));
- else if ((error= table->file->final_drop_index(table)))
- table->file->print_error(error, MYF(0));
- table->key_info= save_key_info;
- }
-
- /*
- Mark this TABLE instance as stale to avoid
- out-of-sync index information.
- */
- table->m_needs_reopen= true;
- goto err_new_table_cleanup;
- }
- }
- /*end of if (index_drop_count)*/
-
- /*
- The final .frm file is already created as a temporary file
- and will be renamed to the original table name later.
- */
-
- /* Need to commit before a table is unlocked (NDB requirement). */
- DBUG_PRINT("info", ("Committing before unlocking table"));
if (trans_commit_stmt(thd) || trans_commit_implicit(thd))
goto err_new_table_cleanup;
}
- /*end of if (! new_table) for add/drop index*/
-
- DBUG_ASSERT(error == 0);
+ thd->count_cuted_fields= CHECK_FIELD_IGNORE;
if (table->s->tmp_table != NO_TMP_TABLE)
{
- /*
- In-place operations are not supported for temporary tables, so
- we don't have to call final_add_index() in this case. The assert
- verifies that in-place add index has not been done.
- */
- DBUG_ASSERT(!pending_inplace_add_index);
/* Close lock if this is a transactional table */
if (thd->lock)
{
@@ -7132,7 +9089,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
thd->locked_tables_mode != LTM_PRELOCKED_UNDER_LOCK_TABLES)
{
mysql_unlock_tables(thd, thd->lock);
- thd->lock=0;
+ thd->lock= NULL;
}
else
{
@@ -7143,15 +9100,19 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
mysql_lock_remove(thd, thd->lock, table);
}
}
+ new_table->s->table_creation_was_logged=
+ table->s->table_creation_was_logged;
/* Remove link to old table and rename the new one */
- close_temporary_table(thd, table, 1, 1);
+ close_temporary_table(thd, table, true, true);
/* Should pass the 'new_name' as we store table name in the cache */
- if (rename_temporary_table(thd, new_table, new_db, new_name))
+ if (rename_temporary_table(thd, new_table,
+ alter_ctx.new_db, alter_ctx.new_name))
goto err_new_table_cleanup;
/* We don't replicate alter table statement on temporary tables */
if (!thd->is_current_stmt_binlog_format_row() &&
- write_bin_log(thd, TRUE, thd->query(), thd->query_length()))
- DBUG_RETURN(TRUE);
+ write_bin_log(thd, true, thd->query(), thd->query_length()))
+ DBUG_RETURN(true);
+ my_free(const_cast<uchar*>(frm.str));
goto end_temporary;
}
@@ -7160,11 +9121,9 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
not delete it! Even altough MERGE tables do not have their children
attached here it is safe to call close_temporary_table().
*/
- if (new_table)
- {
- close_temporary_table(thd, new_table, 1, 0);
- new_table= 0;
- }
+ close_temporary_table(thd, new_table, true, false);
+ new_table= NULL;
+
DEBUG_SYNC(thd, "alter_table_before_rename_result_table");
/*
@@ -7184,236 +9143,164 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
(mysql_execute_command()) to release metadata locks.
*/
- thd_proc_info(thd, "rename result table");
- my_snprintf(old_name, sizeof(old_name), "%s2-%lx-%lx", tmp_file_prefix,
- current_pid, thd->thread_id);
- if (lower_case_table_names)
- my_casedn_str(files_charset_info, old_name);
+ THD_STAGE_INFO(thd, stage_rename_result_table);
- if (wait_while_table_is_used(thd, table, HA_EXTRA_PREPARE_FOR_RENAME,
- TDC_RT_REMOVE_NOT_OWN_AND_MARK_NOT_USABLE))
- {
- if (pending_inplace_add_index)
- {
- pending_inplace_add_index= false;
- table->file->final_add_index(add, false);
- }
- // Mark this TABLE instance as stale to avoid out-of-sync index information.
- table->m_needs_reopen= true;
+ if (wait_while_table_is_used(thd, table, HA_EXTRA_PREPARE_FOR_RENAME))
goto err_new_table_cleanup;
- }
- if (pending_inplace_add_index)
- {
- pending_inplace_add_index= false;
- DBUG_EXECUTE_IF("alter_table_rollback_new_index", {
- table->file->final_add_index(add, false);
- my_error(ER_UNKNOWN_ERROR, MYF(0));
- goto err_new_table_cleanup;
- });
- if ((error= table->file->final_add_index(add, true)))
- {
- table->file->print_error(error, MYF(0));
- goto err_new_table_cleanup;
- }
- }
close_all_tables_for_name(thd, table->s,
- new_name != table_name || new_db != db ?
- HA_EXTRA_PREPARE_FOR_RENAME :
- HA_EXTRA_NOT_USED);
-
- error=0;
- table_list->table= table= 0; /* Safety */
- save_old_db_type= old_db_type;
+ alter_ctx.is_table_renamed() ?
+ HA_EXTRA_PREPARE_FOR_RENAME:
+ HA_EXTRA_NOT_USED,
+ NULL);
+ table_list->table= table= NULL; /* Safety */
+ my_free(const_cast<uchar*>(frm.str));
/*
- This leads to the storage engine (SE) not being notified for renames in
- mysql_rename_table(), because we just juggle with the FRM and nothing
- more. If we have an intermediate table, then we notify the SE that
- it should become the actual table. Later, we will recycle the old table.
- However, in case of ALTER TABLE RENAME there might be no intermediate
- table. This is when the old and new tables are compatible, according to
- mysql_compare_table(). Then, we need one additional call to
- mysql_rename_table() with flag NO_FRM_RENAME, which does nothing else but
- actual rename in the SE and the FRM is not touched. Note that, if the
- table is renamed and the SE is also changed, then an intermediate table
- is created and the additional call will not take place.
+ Rename the old table to temporary name to have a backup in case
+ anything goes wrong while renaming the new table.
*/
- if (need_copy_table == ALTER_TABLE_METADATA_ONLY)
- {
- DBUG_ASSERT(new_db_type == old_db_type);
- /* This type cannot happen in regular ALTER. */
- new_db_type= old_db_type= NULL;
- }
- if (mysql_rename_table(old_db_type, db, table_name, db, old_name,
- FN_TO_IS_TMP))
+ char backup_name[32];
+ my_snprintf(backup_name, sizeof(backup_name), "%s2-%lx-%lx", tmp_file_prefix,
+ current_pid, thd->thread_id);
+ if (lower_case_table_names)
+ my_casedn_str(files_charset_info, backup_name);
+ if (mysql_rename_table(old_db_type, alter_ctx.db, alter_ctx.table_name,
+ alter_ctx.db, backup_name, FN_TO_IS_TMP))
{
- error=1;
- (void) quick_rm_table(new_db_type, new_db, tmp_name, FN_IS_TMP);
+ // Rename to temporary name failed, delete the new table, abort ALTER.
+ (void) quick_rm_table(thd, new_db_type, alter_ctx.new_db,
+ alter_ctx.tmp_name, FN_IS_TMP);
+ goto err_with_mdl;
}
- else if (mysql_rename_table(new_db_type, new_db, tmp_name, new_db,
- new_alias, FN_FROM_IS_TMP))
+
+ // Rename the new table to the correct name.
+ if (mysql_rename_table(new_db_type, alter_ctx.new_db, alter_ctx.tmp_name,
+ alter_ctx.new_db, alter_ctx.new_alias,
+ FN_FROM_IS_TMP))
{
- /* Try to get everything back. */
- error= 1;
- (void) quick_rm_table(new_db_type, new_db, tmp_name, FN_IS_TMP);
- (void) mysql_rename_table(old_db_type, db, old_name, db, alias,
- FN_FROM_IS_TMP | NO_FK_CHECKS);
+ // Rename failed, delete the temporary table.
+ (void) quick_rm_table(thd, new_db_type, alter_ctx.new_db,
+ alter_ctx.tmp_name, FN_IS_TMP);
+
+ // Restore the backup of the original table to the old name.
+ (void) mysql_rename_table(old_db_type, alter_ctx.db, backup_name,
+ alter_ctx.db, alter_ctx.alias,
+ FN_FROM_IS_TMP | NO_FK_CHECKS);
+ goto err_with_mdl;
}
- else if (new_name != table_name || new_db != db)
+
+ // Check if we renamed the table and if so update trigger files.
+ if (alter_ctx.is_table_renamed())
{
- if (need_copy_table == ALTER_TABLE_METADATA_ONLY &&
- mysql_rename_table(save_old_db_type, db, table_name, new_db,
- new_alias, NO_FRM_RENAME))
+ if (Table_triggers_list::change_table_name(thd,
+ alter_ctx.db,
+ alter_ctx.alias,
+ alter_ctx.table_name,
+ alter_ctx.new_db,
+ alter_ctx.new_alias))
{
- /* Try to get everything back. */
- error= 1;
- (void) quick_rm_table(new_db_type, new_db, new_alias, 0);
- (void) mysql_rename_table(old_db_type, db, old_name, db, alias,
+ // Rename succeeded, delete the new table.
+ (void) quick_rm_table(thd, new_db_type,
+ alter_ctx.new_db, alter_ctx.new_alias, 0);
+ // Restore the backup of the original table to the old name.
+ (void) mysql_rename_table(old_db_type, alter_ctx.db, backup_name,
+ alter_ctx.db, alter_ctx.alias,
FN_FROM_IS_TMP | NO_FK_CHECKS);
+ goto err_with_mdl;
}
- else if (Table_triggers_list::change_table_name(thd, db, alias,
- table_name, new_db,
- new_alias))
- {
- /* Try to get everything back. */
- error= 1;
- (void) quick_rm_table(new_db_type, new_db, new_alias, 0);
- (void) mysql_rename_table(old_db_type, db, old_name, db,
- alias, FN_FROM_IS_TMP | NO_FK_CHECKS);
- /*
- If we were performing "fast"/in-place ALTER TABLE we also need
- to restore old name of table in storage engine as a separate
- step, as the above rename affects .FRM only.
- */
- if (need_copy_table == ALTER_TABLE_METADATA_ONLY)
- {
- (void) mysql_rename_table(save_old_db_type, new_db, new_alias,
- db, table_name,
- NO_FRM_RENAME | NO_FK_CHECKS);
- }
- }
- }
-
- if (! error)
- (void) quick_rm_table(old_db_type, db, old_name, FN_IS_TMP);
-
- if (error)
- {
- /* This shouldn't happen. But let us play it safe. */
- goto err_with_mdl;
+ rename_table_in_stat_tables(thd, alter_ctx.db,alter_ctx.alias,
+ alter_ctx.new_db, alter_ctx.new_alias);
}
- if (need_copy_table == ALTER_TABLE_METADATA_ONLY)
+ // ALTER TABLE succeeded, delete the backup of the old table.
+ if (quick_rm_table(thd, old_db_type, alter_ctx.db, backup_name, FN_IS_TMP))
{
/*
- Now we have to inform handler that new .FRM file is in place.
- To do this we need to obtain a handler object for it.
- NO need to tamper with MERGE tables. The real open is done later.
+ The fact that deletion of the backup failed is not critical
+ error, but still worth reporting as it might indicate serious
+ problem with server.
*/
- Open_table_context ot_ctx(thd, MYSQL_OPEN_REOPEN);
- TABLE_LIST temp_table_list;
- TABLE_LIST *t_table_list;
- if (new_name != table_name || new_db != db)
- {
- temp_table_list.init_one_table(new_db, strlen(new_db),
- new_name, strlen(new_name),
- new_name, TL_READ_NO_INSERT);
- temp_table_list.mdl_request.ticket= target_mdl_request.ticket;
- t_table_list= &temp_table_list;
- }
- else
- {
- /*
- Under LOCK TABLES, we have a different mdl_lock_ticket
- points to a different instance than the one set initially
- to request the lock.
- */
- table_list->mdl_request.ticket= mdl_ticket;
- t_table_list= table_list;
- }
- if (open_table(thd, t_table_list, thd->mem_root, &ot_ctx))
- {
- goto err_with_mdl;
- }
-
- /* Tell the handler that a new frm file is in place. */
- error= t_table_list->table->file->ha_create_handler_files(path, NULL,
- CHF_INDEX_FLAG,
- create_info);
+ goto err_with_mdl_after_alter;
+ }
- DBUG_ASSERT(thd->open_tables == t_table_list->table);
- close_thread_table(thd, &thd->open_tables);
- t_table_list->table= NULL;
+end_inplace:
- if (error)
- goto err_with_mdl;
- }
if (thd->locked_tables_list.reopen_tables(thd))
- goto err_with_mdl;
+ goto err_with_mdl_after_alter;
- thd_proc_info(thd, "end");
+ THD_STAGE_INFO(thd, stage_end);
- DBUG_EXECUTE_IF("sleep_alter_before_main_binlog", my_sleep(6000000););
DEBUG_SYNC(thd, "alter_table_before_main_binlog");
- ha_binlog_log_query(thd, create_info->db_type, LOGCOM_ALTER_TABLE,
- thd->query(), thd->query_length(),
- db, table_name);
-
DBUG_ASSERT(!(mysql_bin_log.is_open() &&
thd->is_current_stmt_binlog_format_row() &&
- (create_info->options & HA_LEX_CREATE_TMP_TABLE)));
- if (write_bin_log(thd, TRUE, thd->query(), thd->query_length()))
- DBUG_RETURN(TRUE);
+ (create_info->tmp_table())));
+ if (write_bin_log(thd, true, thd->query(), thd->query_length()))
+ DBUG_RETURN(true);
- table_list->table=0; // For query cache
- query_cache_invalidate3(thd, table_list, 0);
+ if (ha_check_storage_engine_flag(old_db_type, HTON_FLUSH_AFTER_RENAME))
+ {
+ /*
+ For the alter table to be properly flushed to the logs, we
+ have to open the new table. If not, we get a problem on server
+ shutdown. But we do not need to attach MERGE children.
+ */
+ TABLE *t_table;
+ t_table= open_table_uncached(thd, new_db_type, alter_ctx.get_new_path(),
+ alter_ctx.new_db, alter_ctx.new_name,
+ false, true);
+ if (t_table)
+ intern_close_table(t_table);
+ else
+ sql_print_warning("Could not open table %s.%s after rename\n",
+ alter_ctx.new_db, alter_ctx.table_name);
+ ha_flush_logs(old_db_type);
+ }
+ table_list->table= NULL; // For query cache
+ query_cache_invalidate3(thd, table_list, false);
if (thd->locked_tables_mode == LTM_LOCK_TABLES ||
thd->locked_tables_mode == LTM_PRELOCKED_UNDER_LOCK_TABLES)
{
- if ((new_name != table_name || new_db != db))
+ if (alter_ctx.is_table_renamed())
thd->mdl_context.release_all_locks_for_name(mdl_ticket);
else
- mdl_ticket->downgrade_exclusive_lock(MDL_SHARED_NO_READ_WRITE);
+ mdl_ticket->downgrade_lock(MDL_SHARED_NO_READ_WRITE);
}
end_temporary:
- my_snprintf(tmp_name, sizeof(tmp_name), ER(ER_INSERT_INFO),
+ my_snprintf(alter_ctx.tmp_name, sizeof(alter_ctx.tmp_name),
+ ER(ER_INSERT_INFO),
(ulong) (copied + deleted), (ulong) deleted,
- (ulong) thd->warning_info->statement_warn_count());
- my_ok(thd, copied + deleted, 0L, tmp_name);
- DBUG_RETURN(FALSE);
+ (ulong) thd->get_stmt_da()->current_statement_warn_count());
+ my_ok(thd, copied + deleted, 0L, alter_ctx.tmp_name);
+ DBUG_RETURN(false);
err_new_table_cleanup:
+ my_free(const_cast<uchar*>(frm.str));
if (new_table)
{
/* close_temporary_table() frees the new_table pointer. */
- close_temporary_table(thd, new_table, 1, 1);
+ close_temporary_table(thd, new_table, true, true);
}
else
- (void) quick_rm_table(new_db_type, new_db, tmp_name,
- create_info->frm_only ? FN_IS_TMP | FRM_ONLY : FN_IS_TMP);
+ (void) quick_rm_table(thd, new_db_type,
+ alter_ctx.new_db, alter_ctx.tmp_name,
+ (FN_IS_TMP | (no_ha_table ? NO_HA_TABLE : 0)));
-err:
-#ifdef WITH_PARTITION_STORAGE_ENGINE
- /* If prep_alter_part_table created an intermediate table, destroy it. */
- if (table_for_fast_alter_partition)
- close_temporary(table_for_fast_alter_partition, 1, 0);
-#endif /* WITH_PARTITION_STORAGE_ENGINE */
/*
No default value was provided for a DATE/DATETIME field, the
current sql_mode doesn't allow the '0000-00-00' value and
the table to be altered isn't empty.
Report error here.
*/
- if (alter_info->error_if_not_empty &&
- thd->warning_info->current_row_for_warning())
+ if (alter_ctx.error_if_not_empty &&
+ thd->get_stmt_da()->current_row_for_warning())
{
const char *f_val= 0;
enum enum_mysql_timestamp_type t_type= MYSQL_TIMESTAMP_DATE;
- switch (alter_info->datetime_field->sql_type)
+ switch (alter_ctx.datetime_field->sql_type)
{
case MYSQL_TYPE_DATE:
case MYSQL_TYPE_NEWDATE:
@@ -7421,6 +9308,7 @@ err:
t_type= MYSQL_TIMESTAMP_DATE;
break;
case MYSQL_TYPE_DATETIME:
+ case MYSQL_TYPE_DATETIME2:
f_val= "0000-00-00 00:00:00";
t_type= MYSQL_TIMESTAMP_DATETIME;
break;
@@ -7429,14 +9317,18 @@ err:
DBUG_ASSERT(0);
}
bool save_abort_on_warning= thd->abort_on_warning;
- thd->abort_on_warning= TRUE;
- make_truncated_value_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ thd->abort_on_warning= true;
+ make_truncated_value_warning(thd, Sql_condition::WARN_LEVEL_WARN,
f_val, strlength(f_val), t_type,
- alter_info->datetime_field->field_name);
+ alter_ctx.datetime_field->field_name);
thd->abort_on_warning= save_abort_on_warning;
}
- DBUG_RETURN(TRUE);
+ DBUG_RETURN(true);
+
+err_with_mdl_after_alter:
+ /* the table was altered. binlog the operation */
+ write_bin_log(thd, true, thd->query(), thd->query_length());
err_with_mdl:
/*
@@ -7447,13 +9339,10 @@ err_with_mdl:
*/
thd->locked_tables_list.unlink_all_closed_tables(thd, NULL, 0);
thd->mdl_context.release_all_locks_for_name(mdl_ticket);
- DBUG_RETURN(TRUE);
+ DBUG_RETURN(true);
}
-/* Copy all rows from one table to another */
-
-
/**
Prepare the transaction for the alter table's copy phase.
@@ -7502,16 +9391,14 @@ bool mysql_trans_commit_alter_copy_data(THD *thd)
static int
-copy_data_between_tables(THD *thd, TABLE *from,TABLE *to,
- List<Create_field> &create,
- bool ignore,
+copy_data_between_tables(THD *thd, TABLE *from, TABLE *to,
+ List<Create_field> &create, bool ignore,
uint order_num, ORDER *order,
- ha_rows *copied,
- ha_rows *deleted,
- enum enum_enable_or_disable keys_onoff,
- bool error_if_not_empty)
+ ha_rows *copied, ha_rows *deleted,
+ Alter_info::enum_enable_or_disable keys_onoff,
+ Alter_table_ctx *alter_ctx)
{
- int error= 1, errpos= 0;
+ int error= 1;
Copy_field *copy= NULL, *copy_end;
ha_rows found_count= 0, delete_count= 0;
uint length= 0;
@@ -7521,40 +9408,39 @@ copy_data_between_tables(THD *thd, TABLE *from,TABLE *to,
List<Item> fields;
List<Item> all_fields;
ha_rows examined_rows;
+ ha_rows found_rows;
bool auto_increment_field_copied= 0;
ulonglong save_sql_mode= thd->variables.sql_mode;
ulonglong prev_insert_id, time_to_report_progress;
- List_iterator<Create_field> it(create);
- Create_field *def;
+ Field **dfield_ptr= to->default_field;
DBUG_ENTER("copy_data_between_tables");
/* Two or 3 stages; Sorting, copying data and update indexes */
- thd_progress_init(thd, 2 + test(order));
+ thd_progress_init(thd, 2 + MY_TEST(order));
if (mysql_trans_prepare_alter_copy_data(thd))
- goto err;
- errpos=1;
+ DBUG_RETURN(-1);
if (!(copy= new Copy_field[to->s->fields]))
- goto err; /* purecov: inspected */
+ DBUG_RETURN(-1); /* purecov: inspected */
+ /* We need external lock before we can disable/enable keys */
if (to->file->ha_external_lock(thd, F_WRLCK))
- goto err;
- errpos= 2;
+ DBUG_RETURN(-1);
- /* We need external lock before we can disable/enable keys */
alter_table_manage_keys(to, from->file->indexes_are_disabled(), keys_onoff);
/* We can abort alter table for any table type */
- thd->abort_on_warning= !ignore && test(thd->variables.sql_mode &
- (MODE_STRICT_TRANS_TABLES |
- MODE_STRICT_ALL_TABLES));
+ thd->abort_on_warning= !ignore && thd->is_strict_mode();
from->file->info(HA_STATUS_VARIABLE);
- to->file->ha_start_bulk_insert(from->file->stats.records);
- errpos= 3;
+ to->file->ha_start_bulk_insert(from->file->stats.records,
+ ignore ? 0 : HA_CREATE_UNIQUE_INDEX_BY_SORT);
+ List_iterator<Create_field> it(create);
+ Create_field *def;
copy_end=copy;
+ to->s->default_fields= 0;
for (Field **ptr=to->field ; *ptr ; ptr++)
{
def=it++;
@@ -7574,8 +9460,23 @@ copy_data_between_tables(THD *thd, TABLE *from,TABLE *to,
}
(copy_end++)->set(*ptr,def->field,0);
}
-
+ else
+ {
+ /*
+ Update the set of auto-update fields to contain only the new fields
+ added to the table. Only these fields should be updated automatically.
+ Old fields keep their current values, and therefore should not be
+ present in the set of autoupdate fields.
+ */
+ if ((*ptr)->has_insert_default_function())
+ {
+ *(dfield_ptr++)= *ptr;
+ ++to->s->default_fields;
+ }
+ }
}
+ if (dfield_ptr)
+ *dfield_ptr= NULL;
if (order)
{
@@ -7586,43 +9487,47 @@ copy_data_between_tables(THD *thd, TABLE *from,TABLE *to,
my_snprintf(warn_buff, sizeof(warn_buff),
"ORDER BY ignored as there is a user-defined clustered index"
" in the table '%-.192s'", from->s->table_name.str);
- push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_UNKNOWN_ERROR,
+ push_warning(thd, Sql_condition::WARN_LEVEL_WARN, ER_UNKNOWN_ERROR,
warn_buff);
}
else
{
from->sort.io_cache=(IO_CACHE*) my_malloc(sizeof(IO_CACHE),
- MYF(MY_FAE | MY_ZEROFILL));
+ MYF(MY_FAE | MY_ZEROFILL |
+ MY_THREAD_SPECIFIC));
bzero((char *) &tables, sizeof(tables));
tables.table= from;
tables.alias= tables.table_name= from->s->table_name.str;
tables.db= from->s->db.str;
- thd_proc_info(thd, "Sorting");
+ THD_STAGE_INFO(thd, stage_sorting);
if (thd->lex->select_lex.setup_ref_array(thd, order_num) ||
setup_order(thd, thd->lex->select_lex.ref_pointer_array,
&tables, fields, all_fields, order) ||
!(sortorder= make_unireg_sortorder(order, &length, NULL)) ||
(from->sort.found_records= filesort(thd, from, sortorder, length,
- (SQL_SELECT *) 0, HA_POS_ERROR,
- 1, &examined_rows)) ==
+ NULL, HA_POS_ERROR,
+ true,
+ &examined_rows, &found_rows)) ==
HA_POS_ERROR)
goto err;
}
thd_progress_next_stage(thd);
}
- thd_proc_info(thd, "copy to tmp table");
+ THD_STAGE_INFO(thd, stage_copy_to_tmp_table);
/* Tell handler that we have values for all columns in the to table */
to->use_all_columns();
to->mark_virtual_columns_for_write(TRUE);
if (init_read_record(&info, thd, from, (SQL_SELECT *) 0, 1, 1, FALSE))
goto err;
- errpos= 4;
- if (ignore)
+
+ if (ignore && !alter_ctx->fk_error_if_delete_row)
to->file->extra(HA_EXTRA_IGNORE_DUP_KEY);
- thd->warning_info->reset_current_row_for_warning();
+ thd->get_stmt_da()->reset_current_row_for_warning();
restore_record(to, s->default_values); // Create empty record
+ if (to->default_field && to->update_default_fields())
+ goto err;
thd->progress.max_counter= from->file->records();
time_to_report_progress= MY_HOW_OFTEN_TO_WRITE/10;
@@ -7645,7 +9550,7 @@ copy_data_between_tables(THD *thd, TABLE *from,TABLE *to,
}
/* Return error if source table isn't empty. */
- if (error_if_not_empty)
+ if (alter_ctx->error_if_not_empty)
{
error= 1;
break;
@@ -7674,44 +9579,67 @@ copy_data_between_tables(THD *thd, TABLE *from,TABLE *to,
to->auto_increment_field_not_null= FALSE;
if (error)
{
- if (!ignore ||
- to->file->is_fatal_error(error, HA_CHECK_DUP))
- {
- if (!to->file->is_fatal_error(error, HA_CHECK_DUP))
- {
- uint key_nr= to->file->get_dup_key(error);
- if ((int) key_nr >= 0)
- {
- const char *err_msg= ER(ER_DUP_ENTRY_WITH_KEY_NAME);
- if (key_nr == 0 &&
- (to->key_info[0].key_part[0].field->flags &
- AUTO_INCREMENT_FLAG))
- err_msg= ER(ER_DUP_ENTRY_AUTOINCREMENT_CASE);
- to->file->print_keydup_error(key_nr, err_msg, MYF(0));
- error= 1;
- break;
- }
- }
-
- to->file->print_error(error,MYF(0));
+ if (to->file->is_fatal_error(error, HA_CHECK_DUP))
+ {
+ /* Not a duplicate key error. */
+ to->file->print_error(error, MYF(0));
error= 1;
break;
}
- to->file->restore_auto_increment(prev_insert_id);
- delete_count++;
+ else
+ {
+ /* Duplicate key error. */
+ if (alter_ctx->fk_error_if_delete_row)
+ {
+ /*
+ We are trying to omit a row from the table which serves as parent
+ in a foreign key. This might have broken referential integrity so
+ emit an error. Note that we can't ignore this error even if we are
+ executing ALTER IGNORE TABLE. IGNORE allows to skip rows, but
+ doesn't allow to break unique or foreign key constraints,
+ */
+ my_error(ER_FK_CANNOT_DELETE_PARENT, MYF(0),
+ alter_ctx->fk_error_id,
+ alter_ctx->fk_error_table);
+ break;
+ }
+
+ if (ignore)
+ {
+ /* This ALTER IGNORE TABLE. Simply skip row and continue. */
+ to->file->restore_auto_increment(prev_insert_id);
+ delete_count++;
+ }
+ else
+ {
+ /* Ordinary ALTER TABLE. Report duplicate key error. */
+ uint key_nr= to->file->get_dup_key(error);
+ if ((int) key_nr >= 0)
+ {
+ const char *err_msg= ER(ER_DUP_ENTRY_WITH_KEY_NAME);
+ if (key_nr == 0 &&
+ (to->key_info[0].key_part[0].field->flags &
+ AUTO_INCREMENT_FLAG))
+ err_msg= ER(ER_DUP_ENTRY_AUTOINCREMENT_CASE);
+ print_keydup_error(to, key_nr == MAX_KEY ? NULL :
+ &to->key_info[key_nr],
+ err_msg, MYF(0));
+ }
+ else
+ to->file->print_error(error, MYF(0));
+ break;
+ }
+ }
}
else
found_count++;
- thd->warning_info->inc_current_row_for_warning();
+ thd->get_stmt_da()->inc_current_row_for_warning();
}
-
-err:
- if (errpos >= 4)
- end_read_record(&info);
+ end_read_record(&info);
free_io_cache(from);
delete [] copy;
- thd_proc_info(thd, "Enabling keys");
+ THD_STAGE_INFO(thd, stage_enabling_keys);
thd_progress_next_stage(thd);
if (error > 0)
@@ -7719,22 +9647,23 @@ err:
/* We are going to drop the temporary table */
to->file->extra(HA_EXTRA_PREPARE_FOR_DROP);
}
- if (errpos >= 3 && to->file->ha_end_bulk_insert() && error <= 0)
+ if (to->file->ha_end_bulk_insert() && error <= 0)
{
to->file->print_error(my_errno,MYF(0));
error= 1;
}
to->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY);
- if (errpos >= 1 && mysql_trans_commit_alter_copy_data(thd))
+ if (mysql_trans_commit_alter_copy_data(thd))
error= 1;
+ err:
thd->variables.sql_mode= save_sql_mode;
thd->abort_on_warning= 0;
*copied= found_count;
*deleted=delete_count;
to->file->ha_release_auto_increment();
- if (errpos >= 2 && to->file->ha_external_lock(thd,F_UNLCK))
+ if (to->file->ha_external_lock(thd,F_UNLCK))
error=1;
if (error < 0 && to->file->extra(HA_EXTRA_PREPARE_FOR_RENAME))
error= 1;
@@ -7750,24 +9679,20 @@ err:
mysql_recreate_table()
thd Thread handler
table_list Table to recreate
+ table_copy Recreate the table by using
+ ALTER TABLE COPY algorithm
RETURN
Like mysql_alter_table().
*/
-bool mysql_recreate_table(THD *thd, TABLE_LIST *table_list)
+
+bool mysql_recreate_table(THD *thd, TABLE_LIST *table_list, bool table_copy)
{
HA_CREATE_INFO create_info;
Alter_info alter_info;
TABLE_LIST *next_table= table_list->next_global;
DBUG_ENTER("mysql_recreate_table");
- /*
- table_list->table has been closed and freed. Do not reference
- uninitialized data. open_tables() could fail.
- */
- table_list->table= NULL;
- /* Same applies to MDL ticket. */
- table_list->mdl_request.ticket= NULL;
/* Set lock type which is appropriate for ALTER TABLE. */
table_list->lock_type= TL_READ_NO_INSERT;
/* Same applies to MDL request. */
@@ -7779,10 +9704,15 @@ bool mysql_recreate_table(THD *thd, TABLE_LIST *table_list)
create_info.row_type=ROW_TYPE_NOT_USED;
create_info.default_table_charset=default_charset_info;
/* Force alter table to recreate table */
- alter_info.flags= (ALTER_CHANGE_COLUMN | ALTER_RECREATE);
+ alter_info.flags= (Alter_info::ALTER_CHANGE_COLUMN |
+ Alter_info::ALTER_RECREATE);
+
+ if (table_copy)
+ alter_info.requested_algorithm= Alter_info::ALTER_TABLE_ALGORITHM_COPY;
+
bool res= mysql_alter_table(thd, NullS, NullS, &create_info,
table_list, &alter_info, 0,
- (ORDER *) 0, 0, 0);
+ (ORDER *) 0, 0);
table_list->next_global= next_table;
DBUG_RETURN(res);
}
@@ -7805,22 +9735,47 @@ bool mysql_checksum_table(THD *thd, TABLE_LIST *tables,
field_list.push_back(item = new Item_empty_string("Table", NAME_LEN*2));
item->maybe_null= 1;
- field_list.push_back(item= new Item_int("Checksum", (longlong) 1,
+ field_list.push_back(item= new Item_int("Checksum",
+ (longlong) 1,
MY_INT64_NUM_DECIMAL_DIGITS));
item->maybe_null= 1;
if (protocol->send_result_set_metadata(&field_list,
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(TRUE);
+ /*
+ Close all temporary tables which were pre-open to simplify
+ privilege checking. Clear all references to closed tables.
+ */
+ close_thread_tables(thd);
+ for (table= tables; table; table= table->next_local)
+ table->table= NULL;
+
/* Open one table after the other to keep lock time as short as possible. */
for (table= tables; table; table= table->next_local)
{
char table_name[SAFE_NAME_LEN*2+2];
TABLE *t;
+ TABLE_LIST *save_next_global;
strxmov(table_name, table->db ,".", table->table_name, NullS);
- t= table->table= open_n_lock_single_table(thd, table, TL_READ, 0);
+ /* Remember old 'next' pointer and break the list. */
+ save_next_global= table->next_global;
+ table->next_global= NULL;
+ table->lock_type= TL_READ;
+ /* Allow to open real tables only. */
+ table->required_type= FRMTYPE_TABLE;
+
+ if (open_temporary_tables(thd, table) ||
+ open_and_lock_tables(thd, table, FALSE, 0))
+ {
+ t= NULL;
+ }
+ else
+ t= table->table;
+
+ table->next_global= save_next_global;
protocol->prepare_for_resend();
protocol->store(table_name, system_charset_info);
@@ -7918,11 +9873,6 @@ bool mysql_checksum_table(THD *thd, TABLE_LIST *tables,
}
trans_rollback_stmt(thd);
close_thread_tables(thd);
- /*
- Don't release metadata locks, this will be done at
- statement end.
- */
- table->table=0; // For query cache
}
if (thd->transaction_rollback_request)
@@ -7972,20 +9922,20 @@ static bool check_engine(THD *thd, const char *db_name,
handlerton **new_engine= &create_info->db_type;
handlerton *req_engine= *new_engine;
bool no_substitution=
- test(thd->variables.sql_mode & MODE_NO_ENGINE_SUBSTITUTION);
+ MY_TEST(thd->variables.sql_mode & MODE_NO_ENGINE_SUBSTITUTION);
if (!(*new_engine= ha_checktype(thd, ha_legacy_type(req_engine),
no_substitution, 1)))
DBUG_RETURN(true);
if (req_engine && req_engine != *new_engine)
{
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
ER_WARN_USING_OTHER_HANDLER,
ER(ER_WARN_USING_OTHER_HANDLER),
ha_resolve_storage_engine_name(*new_engine),
table_name);
}
- if (create_info->options & HA_LEX_CREATE_TMP_TABLE &&
+ if (create_info->tmp_table() &&
ha_check_storage_engine_flag(*new_engine, HTON_TEMPORARY_NOT_SUPPORTED))
{
if (create_info->used_fields & HA_CREATE_USED_ENGINE)
diff --git a/sql/sql_table.h b/sql/sql_table.h
index 838d763cbc8..2b383623873 100644
--- a/sql/sql_table.h
+++ b/sql/sql_table.h
@@ -1,4 +1,5 @@
-/* Copyright (c) 2006, 2013, Oracle and/or its affiliates. All rights reserved.
+/* Copyright (c) 2006, 2014, Oracle and/or its affiliates.
+ Copyright (c) 2011, 2014, Monty Program Ab.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -18,20 +19,22 @@
#include "my_global.h" /* my_bool */
#include "my_sys.h" // pthread_mutex_t
+#include "m_string.h" // LEX_CUSTRING
class Alter_info;
+class Alter_table_ctx;
class Create_field;
struct TABLE_LIST;
class THD;
struct TABLE;
struct handlerton;
+class handler;
typedef struct st_ha_check_opt HA_CHECK_OPT;
-typedef struct st_ha_create_information HA_CREATE_INFO;
+struct HA_CREATE_INFO;
typedef struct st_key KEY;
typedef struct st_key_cache KEY_CACHE;
typedef struct st_lock_param_type ALTER_PARTITION_PARAM_TYPE;
typedef struct st_order ORDER;
-class Alter_table_change_level;
enum ddl_log_entry_code
{
@@ -63,10 +66,19 @@ enum ddl_log_action_code
DDL_LOG_REPLACE_ACTION:
Rename an entity after removing the previous entry with the
new name, that is replace this entry.
+ DDL_LOG_EXCHANGE_ACTION:
+ Exchange two entities by renaming them a -> tmp, b -> a, tmp -> b.
*/
DDL_LOG_DELETE_ACTION = 'd',
DDL_LOG_RENAME_ACTION = 'r',
- DDL_LOG_REPLACE_ACTION = 's'
+ DDL_LOG_REPLACE_ACTION = 's',
+ DDL_LOG_EXCHANGE_ACTION = 'e'
+};
+
+enum enum_ddl_log_exchange_phase {
+ EXCH_PHASE_NAME_TO_TEMP= 0,
+ EXCH_PHASE_FROM_TO_NAME= 1,
+ EXCH_PHASE_TEMP_TO_FROM= 2
};
@@ -75,6 +87,7 @@ typedef struct st_ddl_log_entry
const char *name;
const char *from_name;
const char *handler_name;
+ const char *tmp_name;
uint next_entry;
uint entry_pos;
enum ddl_log_entry_code entry_type;
@@ -116,19 +129,20 @@ enum enum_explain_filename_mode
#define WFRM_KEEP_SHARE 8
/* Flags for conversion functions. */
-#define FN_FROM_IS_TMP (1 << 0)
-#define FN_TO_IS_TMP (1 << 1)
-#define FN_IS_TMP (FN_FROM_IS_TMP | FN_TO_IS_TMP)
-#define NO_FRM_RENAME (1 << 2)
-#define FRM_ONLY (1 << 3)
+static const uint FN_FROM_IS_TMP= 1 << 0;
+static const uint FN_TO_IS_TMP= 1 << 1;
+static const uint FN_IS_TMP= FN_FROM_IS_TMP | FN_TO_IS_TMP;
+static const uint NO_FRM_RENAME= 1 << 2;
+static const uint FRM_ONLY= 1 << 3;
+/** Don't remove table in engine. Remove only .FRM and maybe .PAR files. */
+static const uint NO_HA_TABLE= 1 << 4;
+/** Don't resolve MySQL's fake "foo.sym" symbolic directory names. */
+static const uint SKIP_SYMDIR_ACCESS= 1 << 5;
/** Don't check foreign key constraints while renaming table */
-#define NO_FK_CHECKS (1 << 4)
+static const uint NO_FK_CHECKS= 1 << 6;
-uint filename_to_tablename(const char *from, char *to, uint to_length
-#ifndef DBUG_OFF
- , bool stay_quiet = false
-#endif /* DBUG_OFF */
- );
+uint filename_to_tablename(const char *from, char *to, uint to_length,
+ bool stay_quiet = false);
uint tablename_to_filename(const char *from, char *to, uint to_length);
uint check_n_cut_mysql50_prefix(const char *from, char *to, uint to_length);
bool check_mysql50_prefix(const char *name);
@@ -136,39 +150,82 @@ uint build_table_filename(char *buff, size_t bufflen, const char *db,
const char *table, const char *ext, uint flags);
uint build_table_shadow_filename(char *buff, size_t bufflen,
ALTER_PARTITION_PARAM_TYPE *lpt);
-bool check_table_file_presence(char *old_path, char *path, const char *db,
- const char *table_name, const char *alias,
- bool issue_error);
+uint build_tmptable_filename(THD* thd, char *buff, size_t bufflen);
bool mysql_create_table(THD *thd, TABLE_LIST *create_table,
HA_CREATE_INFO *create_info,
Alter_info *alter_info);
-bool mysql_create_table_no_lock(THD *thd, const char *db,
- const char *table_name,
+
+/*
+ mysql_create_table_no_lock can be called in one of the following
+ mutually exclusive situations:
+
+ - Just a normal ordinary CREATE TABLE statement that explicitly
+ defines the table structure.
+
+ - CREATE TABLE ... SELECT. It is special, because only in this case,
+ the list of fields is allowed to have duplicates, as long as one of the
+ duplicates comes from the select list, and the other doesn't. For
+ example in
+
+ CREATE TABLE t1 (a int(5) NOT NUL) SELECT b+10 as a FROM t2;
+
+ the list in alter_info->create_list will have two fields `a`.
+
+ - ALTER TABLE, that creates a temporary table #sql-xxx, which will be later
+ renamed to replace the original table.
+
+ - ALTER TABLE as above, but which only modifies the frm file, it only
+ creates an frm file for the #sql-xxx, the table in the engine is not
+ created.
+
+ - Assisted discovery, CREATE TABLE statement without the table structure.
+
+ These situations are distinguished by the following "create table mode"
+ values, where a CREATE ... SELECT is denoted by any non-negative number
+ (which should be the number of fields in the SELECT ... part), and other
+ cases use constants as defined below.
+*/
+#define C_CREATE_SELECT(X) ((X) > 0 ? (X) : 0)
+#define C_ORDINARY_CREATE 0
+#define C_ALTER_TABLE -1
+#define C_ALTER_TABLE_FRM_ONLY -2
+#define C_ASSISTED_DISCOVERY -3
+
+int mysql_create_table_no_lock(THD *thd, const char *db,
+ const char *table_name,
+ HA_CREATE_INFO *create_info,
+ Alter_info *alter_info, bool *is_trans,
+ int create_table_mode);
+
+handler *mysql_create_frm_image(THD *thd,
+ const char *db, const char *table_name,
HA_CREATE_INFO *create_info,
Alter_info *alter_info,
- bool tmp_table, uint select_field_count,
- bool *is_trans);
+ int create_table_mode,
+ KEY **key_info,
+ uint *key_count,
+ LEX_CUSTRING *frm);
+
+int mysql_discard_or_import_tablespace(THD *thd,
+ TABLE_LIST *table_list,
+ bool discard);
+
bool mysql_prepare_alter_table(THD *thd, TABLE *table,
HA_CREATE_INFO *create_info,
- Alter_info *alter_info);
+ Alter_info *alter_info,
+ Alter_table_ctx *alter_ctx);
bool mysql_trans_prepare_alter_copy_data(THD *thd);
bool mysql_trans_commit_alter_copy_data(THD *thd);
bool mysql_alter_table(THD *thd, char *new_db, char *new_name,
HA_CREATE_INFO *create_info,
TABLE_LIST *table_list,
Alter_info *alter_info,
- uint order_num, ORDER *order, bool ignore,
- bool require_online);
+ uint order_num, ORDER *order, bool ignore);
bool mysql_compare_tables(TABLE *table,
Alter_info *alter_info,
HA_CREATE_INFO *create_info,
- uint order_num,
- Alter_table_change_level *need_copy_table,
- KEY **key_info_buffer,
- uint **index_drop_buffer, uint *index_drop_count,
- uint **index_add_buffer, uint *index_add_count,
- uint *candidate_key_count);
-bool mysql_recreate_table(THD *thd, TABLE_LIST *table_list);
+ bool *metadata_equal);
+bool mysql_recreate_table(THD *thd, TABLE_LIST *table_list, bool table_copy);
bool mysql_create_like_table(THD *thd, TABLE_LIST *table,
TABLE_LIST *src_table,
HA_CREATE_INFO *create_info);
@@ -185,14 +242,16 @@ bool mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists,
my_bool drop_temporary);
int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists,
bool drop_temporary, bool drop_view,
- bool log_query);
-bool quick_rm_table(handlerton *base,const char *db,
+ bool log_query, bool dont_free_locks);
+bool log_drop_table(THD *thd, const char *db_name, size_t db_name_length,
+ const char *table_name, size_t table_name_length,
+ bool temporary_table);
+bool quick_rm_table(THD *thd, handlerton *base, const char *db,
const char *table_name, uint flags);
void close_cached_table(THD *thd, TABLE *table);
void sp_prepare_create_field(THD *thd, Create_field *sql_field);
int prepare_create_field(Create_field *sql_field,
uint *blob_columns,
- int *timestamps, int *timestamps_with_niladic,
longlong table_flags);
CHARSET_INFO* get_sql_field_charset(Create_field *sql_field,
HA_CREATE_INFO *create_info);
@@ -211,7 +270,9 @@ bool sync_ddl_log();
void release_ddl_log();
void execute_ddl_log_recovery();
bool execute_ddl_log_entry(THD *thd, uint first_entry);
-bool check_duplicate_warning(THD *thd, char *msg, ulong length);
+
+template<typename T> class List;
+void promote_first_timestamp_column(List<Create_field> *column_definitions);
/*
These prototypes where under INNODB_COMPATIBILITY_HOOKS.
diff --git a/sql/sql_tablespace.cc b/sql/sql_tablespace.cc
index 3f6daf7a9ec..2991b16350c 100644
--- a/sql/sql_tablespace.cc
+++ b/sql/sql_tablespace.cc
@@ -15,6 +15,7 @@
/* drop and alter of tablespaces */
+#include <my_global.h>
#include "sql_priv.h"
#include "unireg.h"
#include "sql_tablespace.h"
@@ -35,7 +36,7 @@ int mysql_alter_tablespace(THD *thd, st_alter_tablespace *ts_info)
{
hton= ha_default_handlerton(thd);
if (ts_info->storage_engine != 0)
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_WARN_USING_OTHER_HANDLER,
ER(ER_WARN_USING_OTHER_HANDLER),
hton_name(hton)->str,
@@ -65,7 +66,7 @@ int mysql_alter_tablespace(THD *thd, st_alter_tablespace *ts_info)
}
else
{
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_ILLEGAL_HA_CREATE_OPTION,
ER(ER_ILLEGAL_HA_CREATE_OPTION),
hton_name(hton)->str,
diff --git a/sql/sql_test.cc b/sql/sql_test.cc
index dc6bc8187ff..d73b6db830e 100644
--- a/sql/sql_test.cc
+++ b/sql/sql_test.cc
@@ -16,11 +16,11 @@
/* Write some debug info */
-
+#include <my_global.h>
#include "sql_priv.h"
#include "unireg.h"
#include "sql_test.h"
-#include "sql_base.h" // table_def_cache, table_cache_count, unused_tables
+#include "sql_base.h"
#include "sql_show.h" // calc_sum_of_all_status
#include "sql_select.h"
#include "keycaches.h"
@@ -78,61 +78,35 @@ print_where(COND *cond,const char *info, enum_query_type query_type)
static void print_cached_tables(void)
{
- uint idx,count,unused;
TABLE_SHARE *share;
- TABLE *start_link, *lnk, *entry;
+ TABLE *entry;
+ TDC_iterator tdc_it;
compile_time_assert(TL_WRITE_ONLY+1 == array_elements(lock_descriptions));
/* purecov: begin tested */
- mysql_mutex_lock(&LOCK_open);
puts("DB Table Version Thread Open Lock");
- for (idx=unused=0 ; idx < table_def_cache.records ; idx++)
+ tdc_it.init();
+ while ((share= tdc_it.next()))
{
- share= (TABLE_SHARE*) my_hash_element(&table_def_cache, idx);
-
- I_P_List_iterator<TABLE, TABLE_share> it(share->used_tables);
- while ((entry= it++))
- {
- printf("%-14.14s %-32s%6ld%8ld%6d %s\n",
- entry->s->db.str, entry->s->table_name.str, entry->s->version,
- entry->in_use->thread_id, entry->db_stat ? 1 : 0,
- lock_descriptions[(int)entry->reginfo.lock_type]);
- }
- it.init(share->free_tables);
+ mysql_mutex_lock(&share->tdc.LOCK_table_share);
+ TABLE_SHARE::All_share_tables_list::Iterator it(share->tdc.all_tables);
while ((entry= it++))
{
- unused++;
+ THD *in_use= entry->in_use;
printf("%-14.14s %-32s%6ld%8ld%6d %s\n",
- entry->s->db.str, entry->s->table_name.str, entry->s->version,
- 0L, entry->db_stat ? 1 : 0, "Not in use");
+ entry->s->db.str, entry->s->table_name.str, entry->s->tdc.version,
+ in_use ? in_use->thread_id : 0,
+ entry->db_stat ? 1 : 0,
+ in_use ? lock_descriptions[(int)entry->reginfo.lock_type] :
+ "Not in use");
}
+ mysql_mutex_unlock(&share->tdc.LOCK_table_share);
}
- count=0;
- if ((start_link=lnk=unused_tables))
- {
- do
- {
- if (lnk != lnk->next->prev || lnk != lnk->prev->next)
- {
- printf("unused_links isn't linked properly\n");
- return;
- }
- } while (count++ < cached_open_tables() && (lnk=lnk->next) != start_link);
- if (lnk != start_link)
- {
- printf("Unused_links aren't connected\n");
- }
- }
- if (count != unused)
- printf("Unused_links (%d) doesn't match table_def_cache: %d\n", count,
- unused);
- printf("\nCurrent refresh version: %ld\n",refresh_version);
- if (my_hash_check(&table_def_cache))
- printf("Error: Table definition hash table is corrupted\n");
+ tdc_it.deinit();
+ printf("\nCurrent refresh version: %ld\n", tdc_refresh_version());
fflush(stdout);
- mysql_mutex_unlock(&LOCK_open);
/* purecov: end */
return;
}
@@ -246,7 +220,7 @@ TEST_join(JOIN *join)
}
-#define FT_KEYPART (MAX_REF_PARTS+10)
+#define FT_KEYPART (MAX_FIELDS+10)
static void print_keyuse(KEYUSE *keyuse)
{
@@ -490,7 +464,8 @@ static void display_table_locks(void)
void *saved_base;
DYNAMIC_ARRAY saved_table_locks;
- (void) my_init_dynamic_array(&saved_table_locks,sizeof(TABLE_LOCK_INFO), cached_open_tables() + 20,50);
+ (void) my_init_dynamic_array(&saved_table_locks,sizeof(TABLE_LOCK_INFO),
+ tc_records() + 20, 50, MYF(0));
mysql_mutex_lock(&THR_LOCK_lock);
for (list= thr_lock_thread_list; list; list= list_rest(list))
{
@@ -597,7 +572,6 @@ void mysql_print_status()
/* Print key cache status */
puts("\nKey caches:");
process_key_caches(print_key_cache_status, 0);
- mysql_mutex_lock(&LOCK_status);
printf("\nhandler status:\n\
read_key: %10lu\n\
read_next: %10lu\n\
@@ -613,14 +587,13 @@ update: %10lu\n",
tmp.ha_write_count,
tmp.ha_delete_count,
tmp.ha_update_count);
- mysql_mutex_unlock(&LOCK_status);
printf("\nTable status:\n\
Opened tables: %10lu\n\
Open tables: %10lu\n\
Open files: %10lu\n\
Open streams: %10lu\n",
tmp.opened_tables,
- (ulong) cached_open_tables(),
+ (ulong) tc_records(),
(ulong) my_file_opened,
(ulong) my_stream_opened);
@@ -636,7 +609,6 @@ Next alarm time: %lu\n",
(ulong)alarm_info.next_alarm_time);
#endif
display_table_locks();
- fflush(stdout);
#ifdef HAVE_MALLINFO
struct mallinfo info= mallinfo();
printf("\nMemory status:\n\
@@ -668,4 +640,5 @@ Estimated memory (with thread stack): %ld\n",
Events::dump_internal_status();
#endif
puts("");
+ fflush(stdout);
}
diff --git a/sql/sql_time.cc b/sql/sql_time.cc
index c5c65391758..b55b1d76b99 100644
--- a/sql/sql_time.cc
+++ b/sql/sql_time.cc
@@ -17,11 +17,11 @@
/* Functions to handle date and time */
+#include <my_global.h>
#include "sql_priv.h"
-#include "unireg.h" // REQUIRED by other includes
#include "sql_time.h"
-#include "tztime.h" // struct Time_zone
-#include "sql_class.h" // THD, MODE_INVALID_DATES, MODE_NO_ZERO_DATE
+#include "tztime.h" // struct Time_zone
+#include "sql_class.h" // THD
#include <m_ctype.h>
@@ -106,9 +106,9 @@ uint calc_week(MYSQL_TIME *l_time, uint week_behaviour, uint *year)
uint days;
ulong daynr=calc_daynr(l_time->year,l_time->month,l_time->day);
ulong first_daynr=calc_daynr(l_time->year,1,1);
- bool monday_first= test(week_behaviour & WEEK_MONDAY_FIRST);
- bool week_year= test(week_behaviour & WEEK_YEAR);
- bool first_weekday= test(week_behaviour & WEEK_FIRST_WEEKDAY);
+ bool monday_first= MY_TEST(week_behaviour & WEEK_MONDAY_FIRST);
+ bool week_year= MY_TEST(week_behaviour & WEEK_YEAR);
+ bool first_weekday= MY_TEST(week_behaviour & WEEK_FIRST_WEEKDAY);
uint weekday=calc_weekday(first_daynr, !monday_first);
*year=l_time->year;
@@ -218,11 +218,11 @@ bool
check_date_with_warn(const MYSQL_TIME *ltime, ulonglong fuzzy_date,
timestamp_type ts_type)
{
- int dummy_warnings;
- if (check_date(ltime, fuzzy_date, &dummy_warnings))
+ int unused;
+ if (check_date(ltime, fuzzy_date, &unused))
{
ErrConvTime str(ltime);
- make_truncated_value_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ make_truncated_value_warning(current_thd, Sql_condition::WARN_LEVEL_WARN,
&str, ts_type, 0);
return true;
}
@@ -239,7 +239,7 @@ adjust_time_range_with_warn(MYSQL_TIME *ltime, uint dec)
if (check_time_range(ltime, dec, &warnings))
return true;
if (warnings)
- make_truncated_value_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ make_truncated_value_warning(current_thd, Sql_condition::WARN_LEVEL_WARN,
&str, MYSQL_TIMESTAMP_TIME, NullS);
return false;
}
@@ -279,9 +279,9 @@ to_ascii(CHARSET_INFO *cs,
/* Character set-aware version of str_to_time() */
-timestamp_type
+bool
str_to_time(CHARSET_INFO *cs, const char *str,uint length,
- MYSQL_TIME *l_time, ulonglong fuzzydate, int *warning)
+ MYSQL_TIME *l_time, ulonglong fuzzydate, MYSQL_TIME_STATUS *status)
{
char cnv[32];
if ((cs->state & MY_CS_NONASCII) != 0)
@@ -289,14 +289,14 @@ str_to_time(CHARSET_INFO *cs, const char *str,uint length,
length= to_ascii(cs, str, length, cnv, sizeof(cnv));
str= cnv;
}
- return str_to_time(str, length, l_time, fuzzydate, warning);
+ return str_to_time(str, length, l_time, fuzzydate, status);
}
/* Character set-aware version of str_to_datetime() */
-timestamp_type str_to_datetime(CHARSET_INFO *cs,
- const char *str, uint length,
- MYSQL_TIME *l_time, ulonglong flags, int *was_cut)
+bool str_to_datetime(CHARSET_INFO *cs, const char *str, uint length,
+ MYSQL_TIME *l_time, ulonglong flags,
+ MYSQL_TIME_STATUS *status)
{
char cnv[32];
if ((cs->state & MY_CS_NONASCII) != 0)
@@ -304,7 +304,7 @@ timestamp_type str_to_datetime(CHARSET_INFO *cs,
length= to_ascii(cs, str, length, cnv, sizeof(cnv));
str= cnv;
}
- return str_to_datetime(str, length, l_time, flags, was_cut);
+ return str_to_datetime(str, length, l_time, flags, status);
}
@@ -316,26 +316,24 @@ timestamp_type str_to_datetime(CHARSET_INFO *cs,
See description of str_to_datetime() for more information.
*/
-timestamp_type
+bool
str_to_datetime_with_warn(CHARSET_INFO *cs,
const char *str, uint length, MYSQL_TIME *l_time,
ulonglong flags)
{
- int was_cut;
+ MYSQL_TIME_STATUS status;
THD *thd= current_thd;
- timestamp_type ts_type;
-
- ts_type= str_to_datetime(cs, str, length, l_time,
- (flags | (sql_mode_for_dates(thd))),
- &was_cut);
- if (was_cut || ts_type <= MYSQL_TIMESTAMP_ERROR)
- make_truncated_value_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ bool ret_val= str_to_datetime(cs, str, length, l_time, flags, &status);
+ if (ret_val || status.warnings)
+ make_truncated_value_warning(thd,
+ ret_val ? Sql_condition::WARN_LEVEL_WARN :
+ Sql_condition::time_warn_level(status.warnings),
str, length, flags & TIME_TIME_ONLY ?
- MYSQL_TIMESTAMP_TIME : ts_type, NullS);
+ MYSQL_TIMESTAMP_TIME : l_time->time_type, NullS);
DBUG_EXECUTE_IF("str_to_datetime_warn",
- push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
+ push_warning(thd, Sql_condition::WARN_LEVEL_NOTE,
ER_YES, str););
- return ts_type;
+ return ret_val;
}
@@ -386,7 +384,7 @@ static bool number_to_time_with_warn(bool neg, ulonglong nr, ulong sec_part,
if (res < 0 || have_warnings)
{
make_truncated_value_warning(current_thd,
- MYSQL_ERROR::WARN_LEVEL_WARN, str,
+ Sql_condition::WARN_LEVEL_WARN, str,
res < 0 ? MYSQL_TIMESTAMP_ERROR
: mysql_type_to_time_type(f_type),
field_name);
@@ -838,8 +836,25 @@ const char *get_date_time_format_str(KNOWN_DATE_TIME_FORMAT *format,
}
}
+
+/**
+ Convert TIME/DATE/DATETIME value to String.
+ @param l_time DATE value
+ @param OUT str String to convert to
+ @param dec Number of fractional digits.
+*/
+bool my_TIME_to_str(const MYSQL_TIME *ltime, String *str, uint dec)
+{
+ if (str->alloc(MAX_DATE_STRING_REP_LENGTH))
+ return true;
+ str->set_charset(&my_charset_numeric);
+ str->length(my_TIME_to_str(ltime, const_cast<char*>(str->ptr()), dec));
+ return false;
+}
+
+
void make_truncated_value_warning(THD *thd,
- MYSQL_ERROR::enum_warning_level level,
+ Sql_condition::enum_warning_level level,
const ErrConv *sval,
timestamp_type time_type,
const char *field_name)
@@ -864,7 +879,7 @@ void make_truncated_value_warning(THD *thd,
cs->cset->snprintf(cs, warn_buff, sizeof(warn_buff),
ER(ER_TRUNCATED_WRONG_VALUE_FOR_FIELD),
type_str, sval->ptr(), field_name,
- (ulong) thd->warning_info->current_row_for_warning());
+ (ulong) thd->get_stmt_da()->current_row_for_warning());
else
{
if (time_type > MYSQL_TIMESTAMP_ERROR)
@@ -915,6 +930,9 @@ bool date_add_interval(MYSQL_TIME *ltime, interval_type int_type,
my_bool neg= 0;
enum enum_mysql_timestamp_type time_type= ltime->time_type;
+ if ((ulong) interval.day > MAX_DAY_NUMBER)
+ goto invalid_date;
+
if (time_type != MYSQL_TIMESTAMP_TIME)
ltime->day+= calc_daynr(ltime->year, ltime->month, 1) - 1;
@@ -993,7 +1011,7 @@ bool date_add_interval(MYSQL_TIME *ltime, interval_type int_type,
return 0; // Ok
invalid_date:
- push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
ER_DATETIME_FUNCTION_OVERFLOW,
ER(ER_DATETIME_FUNCTION_OVERFLOW),
ltime->time_type == MYSQL_TIMESTAMP_TIME ?
@@ -1030,8 +1048,8 @@ null_date:
*/
bool
-calc_time_diff(MYSQL_TIME *l_time1, MYSQL_TIME *l_time2, int l_sign, longlong *seconds_out,
- long *microseconds_out)
+calc_time_diff(const MYSQL_TIME *l_time1, const MYSQL_TIME *l_time2,
+ int l_sign, longlong *seconds_out, long *microseconds_out)
{
long days;
bool neg;
@@ -1057,13 +1075,13 @@ calc_time_diff(MYSQL_TIME *l_time1, MYSQL_TIME *l_time2, int l_sign, longlong *s
(uint) l_time2->day);
}
- microseconds= ((longlong)days*LL(86400) +
+ microseconds= ((longlong)days * SECONDS_IN_24H +
(longlong)(l_time1->hour*3600L +
l_time1->minute*60L +
l_time1->second) -
l_sign*(longlong)(l_time2->hour*3600L +
l_time2->minute*60L +
- l_time2->second)) * LL(1000000) +
+ l_time2->second)) * 1000000LL +
(longlong)l_time1->second_part -
l_sign*(longlong)l_time2->second_part;
@@ -1095,7 +1113,7 @@ calc_time_diff(MYSQL_TIME *l_time1, MYSQL_TIME *l_time2, int l_sign, longlong *s
*/
-int my_time_compare(MYSQL_TIME *a, MYSQL_TIME *b)
+int my_time_compare(const MYSQL_TIME *a, const MYSQL_TIME *b)
{
ulonglong a_t= pack_time(a);
ulonglong b_t= pack_time(b);
@@ -1150,7 +1168,7 @@ make_date_with_warn(MYSQL_TIME *ltime, ulonglong fuzzy_date,
{
/* e.g. negative time */
ErrConvTime str(ltime);
- make_truncated_value_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ make_truncated_value_warning(current_thd, Sql_condition::WARN_LEVEL_WARN,
&str, ts_type, 0);
return true;
}
@@ -1178,3 +1196,145 @@ void time_to_daytime_interval(MYSQL_TIME *ltime)
ltime->hour%= 24;
ltime->time_type= MYSQL_TIMESTAMP_NONE;
}
+
+
+/*** Conversion from TIME to DATETIME ***/
+
+/*
+ Simple case: TIME is within normal 24 hours internal.
+ Mix DATE part of ldate and TIME part of ltime together.
+*/
+static void
+mix_date_and_time_simple(MYSQL_TIME *ldate, const MYSQL_TIME *ltime)
+{
+ DBUG_ASSERT(ldate->time_type == MYSQL_TIMESTAMP_DATE ||
+ ldate->time_type == MYSQL_TIMESTAMP_DATETIME);
+ ldate->hour= ltime->hour;
+ ldate->minute= ltime->minute;
+ ldate->second= ltime->second;
+ ldate->second_part= ltime->second_part;
+ ldate->time_type= MYSQL_TIMESTAMP_DATETIME;
+}
+
+
+/*
+ Complex case: TIME is negative or outside of the 24 hour interval.
+*/
+static void
+mix_date_and_time_complex(MYSQL_TIME *ldate, const MYSQL_TIME *ltime)
+{
+ DBUG_ASSERT(ldate->time_type == MYSQL_TIMESTAMP_DATE ||
+ ldate->time_type == MYSQL_TIMESTAMP_DATETIME);
+ longlong seconds;
+ long days, useconds;
+ int sign= ltime->neg ? 1 : -1;
+ ldate->neg= calc_time_diff(ldate, ltime, sign, &seconds, &useconds);
+
+ DBUG_ASSERT(!ldate->neg);
+ DBUG_ASSERT(ldate->year > 0);
+
+ days= (long) (seconds / SECONDS_IN_24H);
+ calc_time_from_sec(ldate, seconds % SECONDS_IN_24H, useconds);
+ get_date_from_daynr(days, &ldate->year, &ldate->month, &ldate->day);
+ ldate->time_type= MYSQL_TIMESTAMP_DATETIME;
+}
+
+
+/**
+ Mix a date value and a time value.
+
+ @param IN/OUT ldate Date value.
+ @param ltime Time value.
+*/
+static void
+mix_date_and_time(MYSQL_TIME *to, const MYSQL_TIME *from)
+{
+ if (!from->neg && from->hour < 24)
+ mix_date_and_time_simple(to, from);
+ else
+ mix_date_and_time_complex(to, from);
+}
+
+
+/**
+ Get current date in DATE format
+*/
+void set_current_date(THD *thd, MYSQL_TIME *to)
+{
+ thd->variables.time_zone->gmt_sec_to_TIME(to, thd->query_start());
+ thd->time_zone_used= 1;
+ datetime_to_date(to);
+}
+
+
+/**
+ 5.5 compatible conversion from TIME to DATETIME
+*/
+static bool
+time_to_datetime_old(THD *thd, const MYSQL_TIME *from, MYSQL_TIME *to)
+{
+ DBUG_ASSERT(from->time_type == MYSQL_TIMESTAMP_TIME);
+
+ if (from->neg)
+ return true;
+
+ /* Set the date part */
+ uint day= from->hour / 24;
+ to->day= day % 31;
+ to->month= day / 31;
+ to->year= 0;
+ /* Set the time part */
+ to->hour= from->hour % 24;
+ to->minute= from->minute;
+ to->second= from->second;
+ to->second_part= from->second_part;
+ /* set sign and type */
+ to->neg= 0;
+ to->time_type= MYSQL_TIMESTAMP_DATETIME;
+ return false;
+}
+
+
+/**
+ Convert time to datetime.
+
+ The time value is added to the current datetime value.
+ @param IN ltime Time value to convert from.
+ @param OUT ltime2 Datetime value to convert to.
+*/
+bool
+time_to_datetime(THD *thd, const MYSQL_TIME *from, MYSQL_TIME *to)
+{
+ if (thd->variables.old_behavior & OLD_MODE_ZERO_DATE_TIME_CAST)
+ return time_to_datetime_old(thd, from, to);
+ set_current_date(thd, to);
+ mix_date_and_time(to, from);
+ return false;
+}
+
+
+bool
+time_to_datetime_with_warn(THD *thd,
+ const MYSQL_TIME *from, MYSQL_TIME *to,
+ ulonglong fuzzydate)
+{
+ int warn= 0;
+ DBUG_ASSERT(from->time_type == MYSQL_TIMESTAMP_TIME);
+ /*
+ After time_to_datetime() we need to do check_date(), as
+ the caller may want TIME_NO_ZERO_DATE or TIME_NO_ZERO_IN_DATE.
+ Note, the SQL standard time->datetime conversion mode always returns
+ a valid date based on CURRENT_DATE. So we need to do check_date()
+ only in the old mode.
+ */
+ if (time_to_datetime(thd, from, to) ||
+ ((thd->variables.old_behavior & OLD_MODE_ZERO_DATE_TIME_CAST) &&
+ check_date(to, fuzzydate, &warn)))
+ {
+ ErrConvTime str(from);
+ make_truncated_value_warning(thd, Sql_condition::WARN_LEVEL_WARN,
+ &str, MYSQL_TIMESTAMP_DATETIME, 0);
+ return true;
+ }
+ return false;
+}
diff --git a/sql/sql_time.h b/sql/sql_time.h
index ad752121044..4eb43cc8b2f 100644
--- a/sql/sql_time.h
+++ b/sql/sql_time.h
@@ -20,7 +20,7 @@
#include "my_global.h" /* ulong */
#include "my_time.h"
#include "mysql_time.h" /* timestamp_type */
-#include "sql_error.h" /* MYSQL_ERROR */
+#include "sql_error.h" /* Sql_condition */
#include "structs.h" /* INTERVAL */
typedef enum enum_mysql_timestamp_type timestamp_type;
@@ -34,15 +34,14 @@ typedef struct st_known_date_time_format KNOWN_DATE_TIME_FORMAT;
ulong convert_period_to_month(ulong period);
ulong convert_month_to_period(ulong month);
+void set_current_date(THD *thd, MYSQL_TIME *to);
bool time_to_datetime(MYSQL_TIME *ltime);
void time_to_daytime_interval(MYSQL_TIME *l_time);
bool get_date_from_daynr(long daynr,uint *year, uint *month, uint *day);
my_time_t TIME_to_timestamp(THD *thd, const MYSQL_TIME *t, uint *error_code);
-bool str_to_time_with_warn(CHARSET_INFO *cs, const char *str, uint length,
- MYSQL_TIME *l_time, ulonglong fuzzydate);
-timestamp_type str_to_datetime_with_warn(CHARSET_INFO *cs, const char *str,
- uint length, MYSQL_TIME *l_time,
- ulonglong flags);
+bool str_to_datetime_with_warn(CHARSET_INFO *cs, const char *str,
+ uint length, MYSQL_TIME *l_time,
+ ulonglong flags);
bool double_to_datetime_with_warn(double value, MYSQL_TIME *ltime,
ulonglong fuzzydate,
const char *name);
@@ -53,13 +52,41 @@ bool int_to_datetime_with_warn(bool neg, ulonglong value, MYSQL_TIME *ltime,
ulonglong fuzzydate,
const char *name);
-void make_truncated_value_warning(THD *thd, MYSQL_ERROR::enum_warning_level level,
+bool time_to_datetime(THD *thd, const MYSQL_TIME *tm, MYSQL_TIME *dt);
+bool time_to_datetime_with_warn(THD *thd,
+ const MYSQL_TIME *tm, MYSQL_TIME *dt,
+ ulonglong fuzzydate);
+inline void datetime_to_time(MYSQL_TIME *ltime)
+{
+ DBUG_ASSERT(ltime->time_type == MYSQL_TIMESTAMP_DATE ||
+ ltime->time_type == MYSQL_TIMESTAMP_DATETIME);
+ DBUG_ASSERT(ltime->neg == 0);
+ ltime->year= ltime->month= ltime->day= 0;
+ ltime->time_type= MYSQL_TIMESTAMP_TIME;
+}
+inline void datetime_to_date(MYSQL_TIME *ltime)
+{
+ DBUG_ASSERT(ltime->time_type == MYSQL_TIMESTAMP_DATE ||
+ ltime->time_type == MYSQL_TIMESTAMP_DATETIME);
+ DBUG_ASSERT(ltime->neg == 0);
+ ltime->hour= ltime->minute= ltime->second= ltime->second_part= 0;
+ ltime->time_type= MYSQL_TIMESTAMP_DATE;
+}
+inline void date_to_datetime(MYSQL_TIME *ltime)
+{
+ DBUG_ASSERT(ltime->time_type == MYSQL_TIMESTAMP_DATE ||
+ ltime->time_type == MYSQL_TIMESTAMP_DATETIME);
+ DBUG_ASSERT(ltime->neg == 0);
+ ltime->time_type= MYSQL_TIMESTAMP_DATETIME;
+}
+void make_truncated_value_warning(THD *thd,
+ Sql_condition::enum_warning_level level,
const ErrConv *str_val,
timestamp_type time_type,
const char *field_name);
static inline void make_truncated_value_warning(THD *thd,
- MYSQL_ERROR::enum_warning_level level, const char *str_val,
+ Sql_condition::enum_warning_level level, const char *str_val,
uint str_length, timestamp_type time_type,
const char *field_name)
{
@@ -74,12 +101,14 @@ extern DATE_TIME_FORMAT *date_time_format_copy(THD *thd,
DATE_TIME_FORMAT *format);
const char *get_date_time_format_str(KNOWN_DATE_TIME_FORMAT *format,
timestamp_type type);
+bool my_TIME_to_str(const MYSQL_TIME *ltime, String *str, uint dec);
+
/* MYSQL_TIME operations */
bool date_add_interval(MYSQL_TIME *ltime, interval_type int_type,
INTERVAL interval);
-bool calc_time_diff(MYSQL_TIME *l_time1, MYSQL_TIME *l_time2, int l_sign,
- longlong *seconds_out, long *microseconds_out);
-int my_time_compare(MYSQL_TIME *a, MYSQL_TIME *b);
+bool calc_time_diff(const MYSQL_TIME *l_time1, const MYSQL_TIME *l_time2,
+ int l_sign, longlong *seconds_out, long *microseconds_out);
+int my_time_compare(const MYSQL_TIME *a, const MYSQL_TIME *b);
void localtime_to_TIME(MYSQL_TIME *to, struct tm *from);
void calc_time_from_sec(MYSQL_TIME *to, long seconds, long microseconds);
uint calc_week(MYSQL_TIME *l_time, uint week_behaviour, uint *year);
@@ -89,12 +118,14 @@ bool parse_date_time_format(timestamp_type format_type,
const char *format, uint format_length,
DATE_TIME_FORMAT *date_time_format);
/* Character set-aware version of str_to_time() */
-timestamp_type str_to_time(CHARSET_INFO *cs, const char *str,uint length,
- MYSQL_TIME *l_time, ulonglong fuzzydate, int *warning);
+bool str_to_time(CHARSET_INFO *cs, const char *str,uint length,
+ MYSQL_TIME *l_time, ulonglong fuzzydate,
+ MYSQL_TIME_STATUS *status);
/* Character set-aware version of str_to_datetime() */
-timestamp_type str_to_datetime(CHARSET_INFO *cs,
- const char *str, uint length,
- MYSQL_TIME *l_time, ulonglong flags, int *was_cut);
+bool str_to_datetime(CHARSET_INFO *cs,
+ const char *str, uint length,
+ MYSQL_TIME *l_time, ulonglong flags,
+ MYSQL_TIME_STATUS *status);
/* convenience wrapper */
inline bool parse_date_time_format(timestamp_type format_type,
diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc
index 2b64239c41b..a2b5f9a68c9 100644
--- a/sql/sql_trigger.cc
+++ b/sql/sql_trigger.cc
@@ -16,7 +16,7 @@
#define MYSQL_LEX 1
-#include "my_global.h" /* NO_EMBEDDED_ACCESS_CHECKS */
+#include <my_global.h> /* NO_EMBEDDED_ACCESS_CHECKS */
#include "sql_priv.h"
#include "unireg.h"
#include "sp_head.h"
@@ -157,7 +157,7 @@ Trigger_creation_ctx::create(THD *thd,
if (invalid_creation_ctx)
{
push_warning_printf(thd,
- MYSQL_ERROR::WARN_LEVEL_WARN,
+ Sql_condition::WARN_LEVEL_WARN,
ER_TRG_INVALID_CREATION_CTX,
ER(ER_TRG_INVALID_CREATION_CTX),
(const char *) db_name,
@@ -329,9 +329,9 @@ public:
virtual bool handle_condition(THD *thd,
uint sql_errno,
const char* sqlstate,
- MYSQL_ERROR::enum_warning_level level,
+ Sql_condition::enum_warning_level level,
const char* message,
- MYSQL_ERROR ** cond_hdl)
+ Sql_condition ** cond_hdl)
{
if (sql_errno != EE_OUTOFMEMORY &&
sql_errno != ER_OUT_OF_RESOURCES)
@@ -434,8 +434,14 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create)
binlogged, so they share the same danger, so trust_function_creators
applies to them too.
*/
+#ifdef WITH_WSREP
+ if (!trust_function_creators &&
+ (WSREP_EMULATE_BINLOG(thd) || mysql_bin_log.is_open()) &&
+ !(thd->security_ctx->master_access & SUPER_ACL))
+#else
if (!trust_function_creators && mysql_bin_log.is_open() &&
!(thd->security_ctx->master_access & SUPER_ACL))
+#endif /* WITH_WSREP */
{
my_error(ER_BINLOG_CREATE_ROUTINE_NEED_SUPER, MYF(0));
DBUG_RETURN(TRUE);
@@ -443,7 +449,7 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create)
if (!create)
{
- bool if_exists= thd->lex->drop_if_exists;
+ bool if_exists= thd->lex->check_exists;
/*
Protect the query table list from the temporary and potentially
@@ -568,7 +574,7 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create)
if (result)
goto end;
- close_all_tables_for_name(thd, table->s, HA_EXTRA_NOT_USED);
+ close_all_tables_for_name(thd, table->s, HA_EXTRA_NOT_USED, NULL);
/*
Reopen the table if we were under LOCK TABLES.
Ignore the return value for now. It's better to
@@ -595,7 +601,7 @@ end:
with the implicit commit.
*/
if (thd->locked_tables_mode && tables && lock_upgrade_done)
- mdl_ticket->downgrade_exclusive_lock(MDL_SHARED_NO_READ_WRITE);
+ mdl_ticket->downgrade_lock(MDL_SHARED_NO_READ_WRITE);
/* Restore the query table list. Used only for drop trigger. */
if (!create)
@@ -670,46 +676,8 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables,
return 1;
}
- if (!lex->definer)
- {
- /*
- DEFINER-clause is missing.
-
- If we are in slave thread, this means that we received CREATE TRIGGER
- from the master, that does not support definer in triggers. So, we
- should mark this trigger as non-SUID. Note that this does not happen
- when we parse triggers' definitions during opening .TRG file.
- LEX::definer is ignored in that case.
-
- Otherwise, we should use CURRENT_USER() as definer.
-
- NOTE: when CREATE TRIGGER statement is allowed to be executed in PS/SP,
- it will be required to create the definer below in persistent MEM_ROOT
- of PS/SP.
- */
-
- if (!thd->slave_thread)
- {
- if (!(lex->definer= create_default_definer(thd)))
- return 1;
- }
- }
-
- /*
- If the specified definer differs from the current user, we should check
- that the current user has SUPER privilege (in order to create trigger
- under another user one must have SUPER privilege).
- */
-
- if (lex->definer &&
- (strcmp(lex->definer->user.str, thd->security_ctx->priv_user) ||
- my_strcasecmp(system_charset_info,
- lex->definer->host.str,
- thd->security_ctx->priv_host)))
- {
- if (check_global_access(thd, SUPER_ACL))
- return TRUE;
- }
+ if (sp_process_definer(thd))
+ return 1;
/*
Let us check if all references to fields in old/new versions of row in
@@ -801,29 +769,14 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables,
*trg_sql_mode= thd->variables.sql_mode;
-#ifndef NO_EMBEDDED_ACCESS_CHECKS
- if (lex->definer && !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 /* NO_EMBEDDED_ACCESS_CHECKS */
-
- if (lex->definer)
+ if (lex->sphead->m_chistics->suid != SP_IS_NOT_SUID)
{
/* SUID trigger. */
definer_user= lex->definer->user;
definer_host= lex->definer->host;
- trg_definer->str= trg_definer_holder;
- trg_definer->length= strxmov(trg_definer->str, definer_user.str, "@",
- definer_host.str, NullS) - trg_definer->str;
+ lex->definer->set_lex_string(trg_definer, trg_definer_holder);
}
else
{
@@ -861,7 +814,7 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables,
stmt_query->append(STRING_WITH_LEN("CREATE "));
- if (trg_definer)
+ if (lex->sphead->m_chistics->suid != SP_IS_NOT_SUID)
{
/*
Append definer-clause if the trigger is SUID (a usual trigger in
@@ -1281,7 +1234,7 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db,
DBUG_RETURN(1); // EOM
}
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_TRG_NO_CREATION_CTX,
ER(ER_TRG_NO_CREATION_CTX),
(const char*) db,
@@ -1465,7 +1418,7 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db,
warning here.
*/
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_TRG_NO_DEFINER, ER(ER_TRG_NO_DEFINER),
(const char*) db,
(const char*) sp->m_name.str);
@@ -1738,7 +1691,7 @@ bool add_table_for_trigger(THD *thd,
if (if_exists)
{
push_warning_printf(thd,
- MYSQL_ERROR::WARN_LEVEL_NOTE,
+ Sql_condition::WARN_LEVEL_NOTE,
ER_TRG_DOES_NOT_EXIST,
ER(ER_TRG_DOES_NOT_EXIST));
@@ -1783,7 +1736,7 @@ bool Table_triggers_list::drop_all_triggers(THD *thd, char *db, char *name)
DBUG_ENTER("drop_all_triggers");
bzero(&table, sizeof(table));
- init_sql_alloc(&table.mem_root, 8192, 0);
+ init_sql_alloc(&table.mem_root, 8192, 0, MYF(0));
if (Table_triggers_list::check_n_load(thd, db, name, &table, 1))
{
@@ -2003,7 +1956,7 @@ bool Table_triggers_list::change_table_name(THD *thd, const char *db,
DBUG_ENTER("change_table_name");
bzero(&table, sizeof(table));
- init_sql_alloc(&table.mem_root, 8192, 0);
+ init_sql_alloc(&table.mem_root, 8192, 0, MYF(0));
/*
This method interfaces the mysql server code protected by
@@ -2218,6 +2171,37 @@ add_tables_and_routines_for_triggers(THD *thd,
/**
+ Check if any of the marked fields are used in the trigger.
+
+ @param used_fields Bitmap over fields to check
+ @param event_type Type of event triggers for which we are going to inspect
+ @param action_time Type of trigger action time we are going to inspect
+*/
+
+bool Table_triggers_list::is_fields_updated_in_trigger(MY_BITMAP *used_fields,
+ trg_event_type event_type,
+ trg_action_time_type action_time)
+{
+ Item_trigger_field *trg_field;
+ sp_head *sp= bodies[event_type][action_time];
+ DBUG_ASSERT(used_fields->n_bits == trigger_table->s->fields);
+
+ for (trg_field= sp->m_trg_table_fields.first; trg_field;
+ trg_field= trg_field->next_trg_field)
+ {
+ /* We cannot check fields which does not present in table. */
+ if (trg_field->field_idx != (uint)-1)
+ {
+ if (bitmap_is_set(used_fields, trg_field->field_idx) &&
+ trg_field->get_settable_routine_parameter())
+ return true;
+ }
+ }
+ return false;
+}
+
+
+/**
Mark fields of subject table which we read/set in its triggers
as such.
@@ -2309,7 +2293,7 @@ Handle_old_incorrect_sql_modes_hook::process_unknown_string(char *&unknown_key,
DBUG_PRINT("info", ("sql_modes affected by BUG#14090 detected"));
push_warning_printf(current_thd,
- MYSQL_ERROR::WARN_LEVEL_NOTE,
+ Sql_condition::WARN_LEVEL_NOTE,
ER_OLD_FILE_FORMAT,
ER(ER_OLD_FILE_FORMAT),
(char *)path, "TRIGGER");
@@ -2350,7 +2334,7 @@ process_unknown_string(char *&unknown_key, uchar* base, MEM_ROOT *mem_root,
DBUG_PRINT("info", ("trigger_table affected by BUG#15921 detected"));
push_warning_printf(current_thd,
- MYSQL_ERROR::WARN_LEVEL_NOTE,
+ Sql_condition::WARN_LEVEL_NOTE,
ER_OLD_FILE_FORMAT,
ER(ER_OLD_FILE_FORMAT),
(char *)path, "TRIGGER");
@@ -2472,7 +2456,7 @@ int wsrep_create_trigger_query(THD *thd, uchar** buf, size_t* buf_len)
{
if (!thd->slave_thread)
{
- if (!(lex->definer= create_default_definer(thd)))
+ if (!(lex->definer= create_default_definer(thd, false)))
return 1;
}
}
@@ -2480,9 +2464,13 @@ int wsrep_create_trigger_query(THD *thd, uchar** buf, size_t* buf_len)
if (lex->definer)
{
/* SUID trigger. */
+ LEX_USER *d= get_current_user(thd, lex->definer);
- definer_user= lex->definer->user;
- definer_host= lex->definer->host;
+ if (!d)
+ return 1;
+
+ definer_user= d->user;
+ definer_host= d->host;
}
else
{
diff --git a/sql/sql_trigger.h b/sql/sql_trigger.h
index 47b1d19ae54..7dfe8939945 100644
--- a/sql/sql_trigger.h
+++ b/sql/sql_trigger.h
@@ -17,6 +17,8 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+#include <mysqld_error.h>
+
/* Forward declarations */
class Item_trigger_field;
@@ -207,6 +209,10 @@ public:
Query_tables_list *prelocking_ctx,
TABLE_LIST *table_list);
+ bool is_fields_updated_in_trigger(MY_BITMAP *used_fields,
+ trg_event_type event_type,
+ trg_action_time_type action_time);
+
private:
bool prepare_record1_accessors(TABLE *table);
LEX_STRING* change_table_name_in_trignames(const char *old_db_name,
diff --git a/sql/sql_truncate.cc b/sql/sql_truncate.cc
index 388232b2a21..16c2a5027e3 100644
--- a/sql/sql_truncate.cc
+++ b/sql/sql_truncate.cc
@@ -1,4 +1,5 @@
-/* Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved.
+/* Copyright (c) 2010, 2015, Oracle and/or its affiliates.
+ Copyright (c) 2013, 2015, MariaDB
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
@@ -18,9 +19,8 @@
#include "sql_class.h" // THD
#include "sql_base.h" // open_and_lock_tables
#include "sql_table.h" // write_bin_log
-#include "sql_handler.h" // mysql_ha_rm_tables
#include "datadict.h" // dd_recreate_table()
-#include "lock.h" // MYSQL_OPEN_TEMPORARY_ONLY
+#include "lock.h" // MYSQL_OPEN_* flags
#include "sql_acl.h" // DROP_ACL
#include "sql_parse.h" // check_one_table_access()
#include "sql_truncate.h"
@@ -195,13 +195,13 @@ fk_truncate_illegal_if_parent(THD *thd, TABLE *table)
binlong the statement.
*/
-enum Truncate_statement::truncate_result
-Truncate_statement::handler_truncate(THD *thd, TABLE_LIST *table_ref,
- bool is_tmp_table)
+enum Sql_cmd_truncate_table::truncate_result
+Sql_cmd_truncate_table::handler_truncate(THD *thd, TABLE_LIST *table_ref,
+ bool is_tmp_table)
{
int error= 0;
- uint flags;
- DBUG_ENTER("Truncate_statement::handler_truncate");
+ uint flags= 0;
+ DBUG_ENTER("Sql_cmd_truncate_table::handler_truncate");
/*
Can't recreate, the engine must mechanically delete all rows
@@ -209,9 +209,7 @@ Truncate_statement::handler_truncate(THD *thd, TABLE_LIST *table_ref,
*/
/* If it is a temporary table, no need to take locks. */
- if (is_tmp_table)
- flags= MYSQL_OPEN_TEMPORARY_ONLY;
- else
+ if (!is_tmp_table)
{
/* We don't need to load triggers. */
DBUG_ASSERT(table_ref->trg_event_map == 0);
@@ -226,7 +224,7 @@ Truncate_statement::handler_truncate(THD *thd, TABLE_LIST *table_ref,
the MDL lock taken above and otherwise there is no way to
wait for FLUSH TABLES in deadlock-free fashion.
*/
- flags= MYSQL_OPEN_IGNORE_FLUSH | MYSQL_OPEN_SKIP_TEMPORARY;
+ flags= MYSQL_OPEN_IGNORE_FLUSH;
/*
Even though we have an MDL lock on the table here, we don't
pass MYSQL_OPEN_HAS_MDL_LOCK to open_and_lock_tables
@@ -282,13 +280,10 @@ static bool recreate_temporary_table(THD *thd, TABLE *table)
{
bool error= TRUE;
TABLE_SHARE *share= table->s;
- HA_CREATE_INFO create_info;
handlerton *table_type= table->s->db_type();
+ TABLE *new_table;
DBUG_ENTER("recreate_temporary_table");
- memset(&create_info, 0, sizeof(create_info));
- create_info.options|= HA_LEX_CREATE_TMP_TABLE;
-
table->file->info(HA_STATUS_AUTO | HA_STATUS_NO_LOCK);
/*
@@ -300,19 +295,16 @@ static bool recreate_temporary_table(THD *thd, TABLE *table)
/* Don't free share. */
close_temporary_table(thd, table, FALSE, FALSE);
- /*
- We must use share->normalized_path.str since for temporary tables it
- differs from what dd_recreate_table() would generate based
- on table and schema names.
- */
- ha_create_table(thd, share->normalized_path.str, share->db.str,
- share->table_name.str, &create_info, 1);
+ dd_recreate_table(thd, share->db.str, share->table_name.str,
+ share->normalized_path.str);
- if (open_table_uncached(thd, share->path.str, share->db.str,
- share->table_name.str, TRUE))
+ if ((new_table= open_table_uncached(thd, table_type, share->path.str,
+ share->db.str,
+ share->table_name.str, true, true)))
{
error= FALSE;
thd->thread_specific_used= TRUE;
+ new_table->s->table_creation_was_logged= share->table_creation_was_logged;
}
else
rm_temporary_table(table_type, share->path.str);
@@ -337,11 +329,11 @@ static bool recreate_temporary_table(THD *thd, TABLE *table)
@retval TRUE Error.
*/
-bool Truncate_statement::lock_table(THD *thd, TABLE_LIST *table_ref,
- bool *hton_can_recreate)
+bool Sql_cmd_truncate_table::lock_table(THD *thd, TABLE_LIST *table_ref,
+ bool *hton_can_recreate)
{
TABLE *table= NULL;
- DBUG_ENTER("Truncate_statement::lock_table");
+ DBUG_ENTER("Sql_cmd_truncate_table::lock_table");
/* Lock types are set in the parser. */
DBUG_ASSERT(table_ref->lock_type == TL_WRITE);
@@ -376,13 +368,30 @@ bool Truncate_statement::lock_table(THD *thd, TABLE_LIST *table_ref,
/* Acquire an exclusive lock. */
DBUG_ASSERT(table_ref->next_global == NULL);
if (lock_table_names(thd, table_ref, NULL,
- thd->variables.lock_wait_timeout,
- MYSQL_OPEN_SKIP_TEMPORARY))
+ thd->variables.lock_wait_timeout, 0))
DBUG_RETURN(TRUE);
- if (dd_check_storage_engine_flag(thd, table_ref->db, table_ref->table_name,
- HTON_CAN_RECREATE, hton_can_recreate))
+ handlerton *hton;
+ if (!ha_table_exists(thd, table_ref->db, table_ref->table_name, &hton) ||
+ hton == view_pseudo_hton)
+ {
+ my_error(ER_NO_SUCH_TABLE, MYF(0), table_ref->db, table_ref->table_name);
DBUG_RETURN(TRUE);
+ }
+
+ if (!hton)
+ {
+ /*
+ The table exists, but its storage engine is unknown, perhaps not
+ loaded at the moment. We need to open and parse the frm to know the
+ storage engine in question, so let's proceed with the truncation and
+ try to open the table. This will produce the correct error message
+ about unknown engine.
+ */
+ *hton_can_recreate= false;
+ }
+ else
+ *hton_can_recreate= hton->flags & HTON_CAN_RECREATE;
}
/*
@@ -394,13 +403,12 @@ bool Truncate_statement::lock_table(THD *thd, TABLE_LIST *table_ref,
{
DEBUG_SYNC(thd, "upgrade_lock_for_truncate");
/* To remove the table from the cache we need an exclusive lock. */
- if (wait_while_table_is_used(thd, table, HA_EXTRA_PREPARE_FOR_DROP,
- TDC_RT_REMOVE_NOT_OWN_AND_MARK_NOT_USABLE))
+ if (wait_while_table_is_used(thd, table, HA_EXTRA_PREPARE_FOR_DROP))
DBUG_RETURN(TRUE);
m_ticket_downgrade= table->mdl_ticket;
/* Close if table is going to be recreated. */
if (*hton_can_recreate)
- close_all_tables_for_name(thd, table->s, HA_EXTRA_NOT_USED);
+ close_all_tables_for_name(thd, table->s, HA_EXTRA_NOT_USED, NULL);
}
else
{
@@ -427,29 +435,31 @@ bool Truncate_statement::lock_table(THD *thd, TABLE_LIST *table_ref,
@retval TRUE Error.
*/
-bool Truncate_statement::truncate_table(THD *thd, TABLE_LIST *table_ref)
+bool Sql_cmd_truncate_table::truncate_table(THD *thd, TABLE_LIST *table_ref)
{
int error;
- TABLE *table;
bool binlog_stmt;
- DBUG_ENTER("Truncate_statement::truncate_table");
+ DBUG_ENTER("Sql_cmd_truncate_table::truncate_table");
+
+ DBUG_ASSERT((!table_ref->table) ||
+ (table_ref->table && table_ref->table->s));
/* Initialize, or reinitialize in case of reexecution (SP). */
m_ticket_downgrade= NULL;
- /* Remove table from the HANDLER's hash. */
- mysql_ha_rm_tables(thd, table_ref);
-
/* If it is a temporary table, no need to take locks. */
- if ((table= find_temporary_table(thd, table_ref)))
+ if (is_temporary_table(table_ref))
{
+ TABLE *tmp_table= table_ref->table;
+
/* In RBR, the statement is not binlogged if the table is temporary. */
binlog_stmt= !thd->is_current_stmt_binlog_format_row();
/* Note that a temporary table cannot be partitioned. */
- if (ha_check_storage_engine_flag(table->s->db_type(), HTON_CAN_RECREATE))
+ if (ha_check_storage_engine_flag(tmp_table->s->db_type(),
+ HTON_CAN_RECREATE))
{
- if ((error= recreate_temporary_table(thd, table)))
+ if ((error= recreate_temporary_table(thd, tmp_table)))
binlog_stmt= FALSE; /* No need to binlog failed truncate-by-recreate. */
DBUG_ASSERT(! thd->transaction.stmt.modified_non_trans_table);
@@ -539,7 +549,7 @@ bool Truncate_statement::truncate_table(THD *thd, TABLE_LIST *table_ref)
to a shared one.
*/
if (m_ticket_downgrade)
- m_ticket_downgrade->downgrade_exclusive_lock(MDL_SHARED_NO_READ_WRITE);
+ m_ticket_downgrade->downgrade_lock(MDL_SHARED_NO_READ_WRITE);
DBUG_RETURN(error);
}
@@ -553,11 +563,11 @@ bool Truncate_statement::truncate_table(THD *thd, TABLE_LIST *table_ref)
@return FALSE on success.
*/
-bool Truncate_statement::execute(THD *thd)
+bool Sql_cmd_truncate_table::execute(THD *thd)
{
bool res= TRUE;
TABLE_LIST *first_table= thd->lex->select_lex.table_list.first;
- DBUG_ENTER("Truncate_statement::execute");
+ DBUG_ENTER("Sql_cmd_truncate_table::execute");
if (check_one_table_access(thd, DROP_ACL, first_table))
DBUG_RETURN(res);
diff --git a/sql/sql_truncate.h b/sql/sql_truncate.h
index 0280ecc4932..b8525fd6abb 100644
--- a/sql/sql_truncate.h
+++ b/sql/sql_truncate.h
@@ -19,9 +19,9 @@ class THD;
struct TABLE_LIST;
/**
- Truncate_statement represents the TRUNCATE statement.
+ Sql_cmd_truncate_table represents the TRUNCATE statement.
*/
-class Truncate_statement : public Sql_statement
+class Sql_cmd_truncate_table : public Sql_cmd
{
private:
/* Set if a lock must be downgraded after truncate is done. */
@@ -29,14 +29,12 @@ private:
public:
/**
- Constructor, used to represent a ALTER TABLE statement.
- @param lex the LEX structure for this statement.
+ Constructor, used to represent a TRUNCATE statement.
*/
- Truncate_statement(LEX *lex)
- : Sql_statement(lex)
+ Sql_cmd_truncate_table()
{}
- virtual ~Truncate_statement()
+ virtual ~Sql_cmd_truncate_table()
{}
/**
@@ -46,6 +44,11 @@ public:
*/
bool execute(THD *thd);
+ virtual enum_sql_command sql_command_code() const
+ {
+ return SQLCOM_TRUNCATE;
+ }
+
protected:
enum truncate_result{
TRUNCATE_OK=0,
diff --git a/sql/sql_udf.cc b/sql/sql_udf.cc
index 626e5569ccc..74d2f6bc252 100644
--- a/sql/sql_udf.cc
+++ b/sql/sql_udf.cc
@@ -31,6 +31,7 @@
#pragma implementation // gcc: Class implementation
#endif
+#include <my_global.h>
#include "sql_priv.h"
#include "unireg.h"
#include "sql_base.h" // close_mysql_tables
@@ -151,7 +152,7 @@ void udf_init()
mysql_rwlock_init(key_rwlock_THR_LOCK_udf, &THR_LOCK_udf);
- init_sql_alloc(&mem, UDF_ALLOC_BLOCK_SIZE, 0);
+ init_sql_alloc(&mem, UDF_ALLOC_BLOCK_SIZE, 0, MYF(0));
THD *new_thd = new THD;
if (!new_thd ||
my_hash_init(&udf_hash,system_charset_info,32,0,0,get_hash_key, NULL, 0))
@@ -257,8 +258,6 @@ void udf_init()
end:
close_mysql_tables(new_thd);
delete new_thd;
- /* Remember that we don't have a THD */
- my_pthread_setspecific_ptr(THR_THD, 0);
DBUG_VOID_RETURN;
}
@@ -431,7 +430,6 @@ int mysql_create_function(THD *thd,udf_func *udf)
TABLE *table;
TABLE_LIST tables;
udf_func *u_d;
- bool save_binlog_row_based;
DBUG_ENTER("mysql_create_function");
if (!initialized)
@@ -462,13 +460,6 @@ int mysql_create_function(THD *thd,udf_func *udf)
DBUG_RETURN(1);
}
- /*
- Turn off row binlogging of this statement and use statement-based
- so that all supporting tables are updated for CREATE FUNCTION command.
- */
- if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
- thd->clear_current_stmt_binlog_format_row();
-
tables.init_one_table(STRING_WITH_LEN("mysql"), STRING_WITH_LEN("func"),
"func", TL_WRITE);
table= open_ltable(thd, &tables, TL_WRITE, MYSQL_LOCK_IGNORE_TIMEOUT);
@@ -540,27 +531,14 @@ int mysql_create_function(THD *thd,udf_func *udf)
/* Binlog the create function. */
if (write_bin_log(thd, TRUE, thd->query(), thd->query_length()))
- {
- /* Restore the state of binlog format */
- DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
- if (save_binlog_row_based)
- thd->set_current_stmt_binlog_format_row();
DBUG_RETURN(1);
- }
- /* Restore the state of binlog format */
- DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
- if (save_binlog_row_based)
- thd->set_current_stmt_binlog_format_row();
+
DBUG_RETURN(0);
err:
if (new_dl)
dlclose(dl);
mysql_rwlock_unlock(&THR_LOCK_udf);
- /* Restore the state of binlog format */
- DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
- if (save_binlog_row_based)
- thd->set_current_stmt_binlog_format_row();
DBUG_RETURN(1);
}
@@ -572,7 +550,6 @@ int mysql_drop_function(THD *thd,const LEX_STRING *udf_name)
udf_func *udf;
char *exact_name_str;
uint exact_name_len;
- bool save_binlog_row_based;
DBUG_ENTER("mysql_drop_function");
if (!initialized)
@@ -584,13 +561,6 @@ int mysql_drop_function(THD *thd,const LEX_STRING *udf_name)
DBUG_RETURN(1);
}
- /*
- Turn off row binlogging of this statement and use statement-based
- so that all supporting tables are updated for DROP FUNCTION command.
- */
- if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
- thd->clear_current_stmt_binlog_format_row();
-
tables.init_one_table(STRING_WITH_LEN("mysql"), STRING_WITH_LEN("func"),
"func", TL_WRITE);
table= open_ltable(thd, &tables, TL_WRITE, MYSQL_LOCK_IGNORE_TIMEOUT);
@@ -633,24 +603,12 @@ int mysql_drop_function(THD *thd,const LEX_STRING *udf_name)
while binlogging, to avoid binlog inconsistency.
*/
if (write_bin_log(thd, TRUE, thd->query(), thd->query_length()))
- {
- /* Restore the state of binlog format */
- DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
- if (save_binlog_row_based)
- thd->set_current_stmt_binlog_format_row();
DBUG_RETURN(1);
- }
- /* Restore the state of binlog format */
- DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
- if (save_binlog_row_based)
- thd->set_current_stmt_binlog_format_row();
+
DBUG_RETURN(0);
+
err:
mysql_rwlock_unlock(&THR_LOCK_udf);
- /* Restore the state of binlog format */
- DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
- if (save_binlog_row_based)
- thd->set_current_stmt_binlog_format_row();
DBUG_RETURN(1);
}
diff --git a/sql/sql_udf.h b/sql/sql_udf.h
index 68c01964687..f10cbc3bbc2 100644
--- a/sql/sql_udf.h
+++ b/sql/sql_udf.h
@@ -103,14 +103,14 @@ class udf_handler :public Sql_alloc
if (get_arguments())
{
*null_value=1;
- return LL(0);
+ return 0;
}
Udf_func_longlong func= (Udf_func_longlong) u_d->func;
longlong tmp=func(&initid, &f_args, &is_null, &error);
if (is_null || error)
{
*null_value=1;
- return LL(0);
+ return 0;
}
*null_value=0;
return tmp;
diff --git a/sql/sql_union.cc b/sql/sql_union.cc
index 87d3e86b2c7..71875433c39 100644
--- a/sql/sql_union.cc
+++ b/sql/sql_union.cc
@@ -20,7 +20,7 @@
UNION's were introduced by Monty and Sinisa <sinisa@mysql.com>
*/
-
+#include <my_global.h>
#include "sql_priv.h"
#include "unireg.h"
#include "sql_union.h"
@@ -64,7 +64,7 @@ int select_union::send_data(List<Item> &values)
return 0;
if (table->no_rows_with_nulls)
table->null_catch_flags= CHECK_ROW_FOR_NULLS_TO_REJECT;
- fill_record(thd, table->field, values, TRUE, FALSE);
+ fill_record(thd, table, table->field, values, TRUE, FALSE);
if (thd->is_error())
return 1;
if (table->no_rows_with_nulls)
@@ -244,7 +244,7 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
bool is_union_select;
DBUG_ENTER("st_select_lex_unit::prepare");
- describe= test(additional_options & SELECT_DESCRIBE);
+ describe= MY_TEST(additional_options & SELECT_DESCRIBE);
/*
result object should be reassigned even if preparing already done for
@@ -286,8 +286,6 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
{
if (!(tmp_result= union_result= new select_union))
goto err;
- if (describe)
- tmp_result= sel_result;
}
else
tmp_result= sel_result;
@@ -451,7 +449,7 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
if (global_parameters->ftfunc_list->elements)
create_options= create_options | TMP_TABLE_FORCE_MYISAM;
- if (union_result->create_result_table(thd, &types, test(union_distinct),
+ if (union_result->create_result_table(thd, &types, MY_TEST(union_distinct),
create_options, "", FALSE, TRUE))
goto err;
if (fake_select_lex && !fake_select_lex->first_cond_optimization)
@@ -633,6 +631,7 @@ bool st_select_lex_unit::exec()
ha_rows examined_rows= 0;
bool first_execution= !executed;
DBUG_ENTER("st_select_lex_unit::exec");
+ bool was_executed= executed;
if (executed && !uncacheable && !describe)
DBUG_RETURN(FALSE);
@@ -640,7 +639,14 @@ bool st_select_lex_unit::exec()
if (!(uncacheable & ~UNCACHEABLE_EXPLAIN) && item)
item->make_const();
- if ((saved_error= optimize()))
+ saved_error= optimize();
+
+ create_explain_query_if_not_exists(thd->lex, thd->mem_root);
+
+ if (!saved_error && !was_executed)
+ save_union_explain(thd->lex->explain);
+
+ if (saved_error)
DBUG_RETURN(saved_error);
if (uncacheable || !item || !item->assigned() || describe)
@@ -693,13 +699,8 @@ bool st_select_lex_unit::exec()
0);
if (!saved_error)
{
- /*
- Save the current examined row count locally and clear the global
- counter, so that we can accumulate the number of evaluated rows for
- the current query block.
- */
- examined_rows+= thd->examined_row_count;
- thd->examined_row_count= 0;
+ examined_rows+= thd->get_examined_row_count();
+ thd->set_examined_row_count(0);
if (union_result->flush())
{
thd->lex->current_select= lex_select_save;
@@ -737,7 +738,7 @@ bool st_select_lex_unit::exec()
Stop execution of the remaining queries in the UNIONS, and produce
the current result.
*/
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_QUERY_EXCEEDED_ROWS_EXAMINED_LIMIT,
ER(ER_QUERY_EXCEEDED_ROWS_EXAMINED_LIMIT),
thd->accessed_rows_and_keys,
@@ -748,6 +749,8 @@ bool st_select_lex_unit::exec()
}
}
+ DBUG_EXECUTE_IF("show_explain_probe_union_read",
+ dbug_serve_apcs(thd, 1););
/* Send result to 'result' */
saved_error= TRUE;
{
@@ -796,6 +799,9 @@ bool st_select_lex_unit::exec()
*/
if (!fake_select_lex->ref_pointer_array)
fake_select_lex->n_child_sum_items+= global_parameters->n_sum_items;
+
+ if (!was_executed)
+ save_union_explain_part2(thd->lex->explain);
saved_error= mysql_select(thd, &fake_select_lex->ref_pointer_array,
&result_table_list,
@@ -841,7 +847,7 @@ bool st_select_lex_unit::exec()
if (!saved_error)
{
thd->limit_found_rows = (ulonglong)table->file->stats.records + add_rows;
- thd->examined_row_count+= examined_rows;
+ thd->inc_examined_row_count(examined_rows);
}
/*
Mark for slow query log if any of the union parts didn't use
@@ -984,7 +990,7 @@ bool st_select_lex_unit::change_result(select_result_interceptor *new_result,
List<Item> *st_select_lex_unit::get_unit_column_types()
{
SELECT_LEX *sl= first_select();
- bool is_procedure= test(sl->join->procedure);
+ bool is_procedure= MY_TEST(sl->join->procedure);
if (is_procedure)
{
diff --git a/sql/sql_update.cc b/sql/sql_update.cc
index 9f80b063901..c07f492a485 100644
--- a/sql/sql_update.cc
+++ b/sql/sql_update.cc
@@ -1,5 +1,5 @@
/* Copyright (c) 2000, 2013, Oracle and/or its affiliates.
- Copyright (c) 2011, 2013, Monty Program Ab.
+ Copyright (c) 2011, 2015, MariaDB
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
@@ -20,9 +20,8 @@
Multi-table updates were introduced by Sinisa & Monty
*/
-#include "my_global.h" /* NO_EMBEDDED_ACCESS_CHECKS */
+#include <my_global.h> /* NO_EMBEDDED_ACCESS_CHECKS */
#include "sql_priv.h"
-#include "unireg.h" // REQUIRED: for other includes
#include "sql_update.h"
#include "sql_cache.h" // query_cache_*
#include "sql_base.h" // close_tables_for_reopen
@@ -32,6 +31,7 @@
#include "sql_view.h" // check_key_in_view
#include "sp_head.h"
#include "sql_trigger.h"
+#include "sql_statistics.h"
#include "probes_mysql.h"
#include "debug_sync.h"
#include "key.h" // is_key_used
@@ -137,7 +137,7 @@ static bool check_fields(THD *thd, List<Item> &items)
while ((item= it++))
{
- if (!(field= item->filed_for_view_update()))
+ if (!(field= item->field_for_view_update()))
{
/* item has name, because it comes from VIEW SELECT list */
my_error(ER_NONUPDATEABLE_COLUMN, MYF(0), item->name);
@@ -190,7 +190,7 @@ static void prepare_record_for_error_message(int error, TABLE *table)
DBUG_VOID_RETURN;
/* Create unique_map with all fields used by that index. */
- bitmap_init(&unique_map, unique_map_buf, table->s->fields, FALSE);
+ my_bitmap_init(&unique_map, unique_map_buf, table->s->fields, FALSE);
table->mark_columns_used_by_index_no_reset(keynr, &unique_map);
/* Subtract read_set and write_set. */
@@ -254,12 +254,12 @@ int mysql_update(THD *thd,
ha_rows *found_return, ha_rows *updated_return)
{
bool using_limit= limit != HA_POS_ERROR;
- bool safe_update= test(thd->variables.option_bits & OPTION_SAFE_UPDATES);
+ bool safe_update= MY_TEST(thd->variables.option_bits & OPTION_SAFE_UPDATES);
bool used_key_is_modified= FALSE, transactional_table, will_batch;
bool can_compare_record;
int res;
int error, loc_error;
- uint used_index, dup_key_found;
+ uint dup_key_found;
bool need_sort= TRUE;
bool reverse= FALSE;
#ifndef NO_EMBEDDED_ACCESS_CHECKS
@@ -269,14 +269,18 @@ int mysql_update(THD *thd,
ha_rows updated, found;
key_map old_covering_keys;
TABLE *table;
- SQL_SELECT *select;
+ SQL_SELECT *select= NULL;
READ_RECORD info;
SELECT_LEX *select_lex= &thd->lex->select_lex;
ulonglong id;
List<Item> all_fields;
killed_state killed_status= NOT_KILLED;
+ Update_plan query_plan(thd->mem_root);
+ query_plan.index= MAX_KEY;
+ query_plan.using_filesort= FALSE;
DBUG_ENTER("mysql_update");
+ create_explain_query(thd->lex, thd->mem_root);
if (open_tables(thd, &table_list, &table_count, 0))
DBUG_RETURN(1);
@@ -301,7 +305,7 @@ int mysql_update(THD *thd,
if (table_list->handle_derived(thd->lex, DT_PREPARE))
DBUG_RETURN(1);
- thd_proc_info(thd, "init");
+ THD_STAGE_INFO(thd, stage_init);
table= table_list->table;
if (!table_list->single_table_updatable())
@@ -309,10 +313,14 @@ int mysql_update(THD *thd,
my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias, "UPDATE");
DBUG_RETURN(1);
}
+ query_plan.updating_a_view= MY_TEST(table_list->view);
+
/* Calculate "table->covering_keys" based on the WHERE */
table->covering_keys= table->s->keys_in_use;
table->quick_keys.clear_all();
+ query_plan.select_lex= &thd->lex->select_lex;
+ query_plan.table= table;
#ifndef NO_EMBEDDED_ACCESS_CHECKS
/* Force privilege re-checking for views after they have been opened. */
want_privilege= (table_list->view ? UPDATE_ACL :
@@ -342,19 +350,8 @@ int mysql_update(THD *thd,
my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias, "UPDATE");
DBUG_RETURN(1);
}
- if (table->timestamp_field)
- {
- // Don't set timestamp column if this is modified
- if (bitmap_is_set(table->write_set,
- table->timestamp_field->field_index))
- table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
- else
- {
- if (((uint) table->timestamp_field_type) & TIMESTAMP_AUTO_SET_ON_UPDATE)
- bitmap_set_bit(table->write_set,
- table->timestamp_field->field_index);
- }
- }
+ if (table->default_field)
+ table->mark_default_fields_for_write();
#ifndef NO_EMBEDDED_ACCESS_CHECKS
/* Check values */
@@ -383,7 +380,12 @@ int mysql_update(THD *thd,
Item::cond_result cond_value;
conds= remove_eq_conds(thd, conds, &cond_value);
if (cond_value == Item::COND_FALSE)
+ {
limit= 0; // Impossible WHERE
+ query_plan.set_impossible_where();
+ if (thd->lex->describe)
+ goto exit_without_my_ok;
+ }
}
/*
@@ -392,7 +394,7 @@ int mysql_update(THD *thd,
to compare records and detect data change.
*/
if ((table->file->ha_table_flags() & HA_PARTIAL_COLUMN_READ) &&
- (((uint) table->timestamp_field_type) & TIMESTAMP_AUTO_SET_ON_UPDATE))
+ table->default_field && table->has_default_function(true))
bitmap_union(table->read_set, table->write_set);
// Don't count on usage of 'only index' when calculating which key to use
table->covering_keys.clear_all();
@@ -401,17 +403,27 @@ int mysql_update(THD *thd,
if (prune_partitions(thd, table, conds))
{
free_underlaid_joins(thd, select_lex);
+
+ query_plan.set_no_partitions();
+ if (thd->lex->describe)
+ goto exit_without_my_ok;
+
my_ok(thd); // No matching records
DBUG_RETURN(0);
}
#endif
/* Update the table->file->stats.records number */
table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK);
+ set_statistics_for_table(thd, table);
select= make_select(table, 0, 0, conds, 0, &error);
if (error || !limit || thd->is_error() ||
(select && select->check_quick(thd, safe_update, limit)))
{
+ query_plan.set_impossible_where();
+ if (thd->lex->describe)
+ goto exit_without_my_ok;
+
delete select;
free_underlaid_joins(thd, select_lex);
/*
@@ -432,7 +444,7 @@ int mysql_update(THD *thd,
/* If running in safe sql mode, don't allow updates without keys */
if (table->quick_keys.is_clear_all())
{
- thd->server_status|=SERVER_QUERY_NO_INDEX_USED;
+ thd->set_status_no_index_used();
if (safe_update && !using_limit)
{
my_message(ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE,
@@ -446,55 +458,92 @@ int mysql_update(THD *thd,
table->update_const_key_parts(conds);
order= simple_remove_const(order, conds);
+ query_plan.scanned_rows= select? select->records: table->file->stats.records;
if (select && select->quick && select->quick->unique_key_range())
- { // Single row select (always "ordered"): Ok to use with key field UPDATE
+ {
+ /* Single row select (always "ordered"): Ok to use with key field UPDATE */
need_sort= FALSE;
- used_index= MAX_KEY;
+ query_plan.index= MAX_KEY;
used_key_is_modified= FALSE;
}
else
{
- used_index= get_index_for_order(order, table, select, limit,
- &need_sort, &reverse);
+ ha_rows scanned_limit= query_plan.scanned_rows;
+ query_plan.index= get_index_for_order(order, table, select, limit,
+ &scanned_limit, &need_sort,
+ &reverse);
+ if (!need_sort)
+ query_plan.scanned_rows= scanned_limit;
+
if (select && select->quick)
{
- DBUG_ASSERT(need_sort || used_index == select->quick->index);
+ DBUG_ASSERT(need_sort || query_plan.index == select->quick->index);
used_key_is_modified= (!select->quick->unique_key_range() &&
select->quick->is_keys_used(table->write_set));
}
else
{
if (need_sort)
- { // Assign table scan index to check below for modified key fields:
- used_index= table->file->key_used_on_scan;
+ {
+ /* Assign table scan index to check below for modified key fields: */
+ query_plan.index= table->file->key_used_on_scan;
}
- if (used_index != MAX_KEY)
- { // Check if we are modifying a key that we are used to search with:
- used_key_is_modified= is_key_used(table, used_index, table->write_set);
+ if (query_plan.index != MAX_KEY)
+ {
+ /* Check if we are modifying a key that we are used to search with: */
+ used_key_is_modified= is_key_used(table, query_plan.index,
+ table->write_set);
}
}
}
-
+
+ /*
+ Query optimization is finished at this point.
+ - Save the decisions in the query plan
+ - if we're running EXPLAIN UPDATE, get out
+ */
+ query_plan.select= select;
+ query_plan.possible_keys= select? select->possible_keys: key_map(0);
+
if (used_key_is_modified || order ||
partition_key_modified(table, table->write_set))
{
+ if (order && need_sort)
+ query_plan.using_filesort= true;
+ else
+ query_plan.using_io_buffer= true;
+ }
+
+
+ /*
+ Ok, we have generated a query plan for the UPDATE.
+ - if we're running EXPLAIN UPDATE, goto produce explain output
+ - otherwise, execute the query plan
+ */
+ if (thd->lex->describe)
+ goto exit_without_my_ok;
+ query_plan.save_explain_data(thd->lex->explain);
+
+ DBUG_EXECUTE_IF("show_explain_probe_update_exec_start",
+ dbug_serve_apcs(thd, 1););
+
+ if (query_plan.using_filesort || query_plan.using_io_buffer)
+ {
/*
We can't update table directly; We must first search after all
matching rows before updating the table!
*/
+ MY_BITMAP *save_read_set= table->read_set;
+ MY_BITMAP *save_write_set= table->write_set;
- // Verify that table->restore_column_maps_after_mark_index() will work
- DBUG_ASSERT(table->read_set == &table->def_read_set);
- DBUG_ASSERT(table->write_set == &table->def_write_set);
-
- if (used_index < MAX_KEY && old_covering_keys.is_set(used_index))
- table->add_read_columns_used_by_index(used_index);
+ if (query_plan.index < MAX_KEY && old_covering_keys.is_set(query_plan.index))
+ table->add_read_columns_used_by_index(query_plan.index);
else
table->use_all_columns();
/* note: We avoid sorting if we sort on the used index */
- if (order && (need_sort || used_key_is_modified))
+ if (query_plan.using_filesort)
{
/*
Doing an ORDER BY; Let filesort find and sort the rows we are going
@@ -504,18 +553,21 @@ int mysql_update(THD *thd,
uint length= 0;
SORT_FIELD *sortorder;
ha_rows examined_rows;
+ ha_rows found_rows;
table->sort.io_cache = (IO_CACHE *) my_malloc(sizeof(IO_CACHE),
- MYF(MY_FAE | MY_ZEROFILL));
+ MYF(MY_FAE | MY_ZEROFILL |
+ MY_THREAD_SPECIFIC));
if (!(sortorder=make_unireg_sortorder(order, &length, NULL)) ||
(table->sort.found_records= filesort(thd, table, sortorder, length,
- select, limit, 1,
- &examined_rows))
+ select, limit,
+ true,
+ &examined_rows, &found_rows))
== HA_POS_ERROR)
{
goto err;
}
- thd->examined_row_count+= examined_rows;
+ thd->inc_examined_row_count(examined_rows);
/*
Filesort has already found and selected the rows we want to update,
so we don't need the where clause
@@ -546,24 +598,28 @@ int mysql_update(THD *thd,
/*
When we get here, we have one of the following options:
- A. used_index == MAX_KEY
+ A. query_plan.index == MAX_KEY
This means we should use full table scan, and start it with
init_read_record call
- B. used_index != MAX_KEY
+ B. query_plan.index != MAX_KEY
B.1 quick select is used, start the scan with init_read_record
B.2 quick select is not used, this is full index scan (with LIMIT)
- Full index scan must be started with init_read_record_idx
+ Full index scan must be started with init_read_record_idx
*/
- if (used_index == MAX_KEY || (select && select->quick))
+ if (query_plan.index == MAX_KEY || (select && select->quick))
+ error= init_read_record(&info, thd, table, select, 0, 1, FALSE);
+ else
+ error= init_read_record_idx(&info, thd, table, 1, query_plan.index,
+ reverse);
+
+ if (error)
{
- if (init_read_record(&info, thd, table, select, 0, 1, FALSE))
- goto err;
+ close_cached_file(&tempfile);
+ goto err;
}
- else
- init_read_record_idx(&info, thd, table, 1, used_index, reverse);
- thd_proc_info(thd, "Searching rows for update");
+ THD_STAGE_INFO(thd, stage_searching_rows_for_update);
ha_rows tmp_limit= limit;
while (!(error=info.read_record(&info)) && !thd->killed)
@@ -572,7 +628,7 @@ int mysql_update(THD *thd,
update_virtual_fields(thd, table,
table->triggers ? VCOL_UPDATE_ALL :
VCOL_UPDATE_FOR_READ);
- thd->examined_row_count++;
+ thd->inc_examined_row_count(1);
if (!select || (error= select->skip_record(thd)) > 0)
{
if (table->file->was_semi_consistent_read())
@@ -627,17 +683,15 @@ int mysql_update(THD *thd,
select= new SQL_SELECT;
select->head=table;
}
+ //psergey-todo: disable SHOW EXPLAIN because the plan was deleted?
if (reinit_io_cache(&tempfile,READ_CACHE,0L,0,0))
error=1; /* purecov: inspected */
select->file=tempfile; // Read row ptrs from this file
if (error >= 0)
goto err;
}
- /*
- This restore bitmaps, works for add_read_columns_used_by_index() and
- use_all_columns():
- */
- table->restore_column_maps_after_mark_index();
+ table->disable_keyread();
+ table->column_bitmaps_set(save_read_set, save_write_set);
}
if (ignore)
@@ -656,23 +710,12 @@ int mysql_update(THD *thd,
*/
thd->count_cuted_fields= CHECK_FIELD_WARN;
thd->cuted_fields=0L;
- thd_proc_info(thd, "Updating");
+ THD_STAGE_INFO(thd, stage_updating);
transactional_table= table->file->has_transactions();
- thd->abort_on_warning= test(!ignore &&
- (thd->variables.sql_mode &
- (MODE_STRICT_TRANS_TABLES |
- MODE_STRICT_ALL_TABLES)));
- if (table->triggers &&
- table->triggers->has_triggers(TRG_EVENT_UPDATE,
- TRG_ACTION_AFTER))
+ thd->abort_on_warning= !ignore && thd->is_strict_mode();
+ if (table->prepare_triggers_for_update_stmt_or_event())
{
- /*
- The table has AFTER UPDATE triggers that might access to subject
- table and therefore might need update to be done immediately.
- So we turn-off the batching.
- */
- (void) table->file->extra(HA_EXTRA_UPDATE_CANNOT_BATCH);
will_batch= FALSE;
}
else
@@ -685,6 +728,8 @@ int mysql_update(THD *thd,
if (table->file->ha_table_flags() & HA_PARTIAL_COLUMN_READ)
table->prepare_for_position();
+ table->reset_default_fields();
+
/*
We can use compare_record() to optimize away updates if
the table handler is returning all columns OR if
@@ -698,15 +743,14 @@ int mysql_update(THD *thd,
update_virtual_fields(thd, table,
table->triggers ? VCOL_UPDATE_ALL :
VCOL_UPDATE_FOR_READ);
- thd->examined_row_count++;
+ thd->inc_examined_row_count(1);
if (!select || select->skip_record(thd) > 0)
{
if (table->file->was_semi_consistent_read())
continue; /* repeat the read of the same row if it still exists */
store_record(table,record[1]);
- if (fill_record_n_invoke_before_triggers(thd, fields, values, 0,
- table->triggers,
+ if (fill_record_n_invoke_before_triggers(thd, table, fields, values, 0,
TRG_EVENT_UPDATE))
break; /* purecov: inspected */
@@ -714,6 +758,11 @@ int mysql_update(THD *thd,
if (!can_compare_record || compare_record(table))
{
+ if (table->default_field && table->update_default_fields())
+ {
+ error= 1;
+ break;
+ }
if ((res= table_list->view_check_option(thd, ignore)) !=
VIEW_CHECK_OK)
{
@@ -854,7 +903,7 @@ int mysql_update(THD *thd,
error= 1;
break;
}
- thd->warning_info->inc_current_row_for_warning();
+ thd->get_stmt_da()->inc_current_row_for_warning();
if (thd->is_error())
{
error= 1;
@@ -908,7 +957,7 @@ int mysql_update(THD *thd,
end_read_record(&info);
delete select;
- thd_proc_info(thd, "end");
+ THD_STAGE_INFO(thd, stage_end);
(void) table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY);
/*
@@ -966,7 +1015,7 @@ int mysql_update(THD *thd,
char buff[MYSQL_ERRMSG_SIZE];
my_snprintf(buff, sizeof(buff), ER(ER_UPDATE_INFO), (ulong) found,
(ulong) updated,
- (ulong) thd->warning_info->statement_warn_count());
+ (ulong) thd->get_stmt_da()->current_statement_warn_count());
my_ok(thd, (thd->client_capabilities & CLIENT_FOUND_ROWS) ? found : updated,
id, buff);
DBUG_PRINT("info",("%ld records updated", (long) updated));
@@ -983,11 +1032,21 @@ int mysql_update(THD *thd,
DBUG_RETURN((error >= 0 || thd->is_error()) ? 1 : 0);
err:
+
delete select;
free_underlaid_joins(thd, select_lex);
table->disable_keyread();
thd->abort_on_warning= 0;
DBUG_RETURN(1);
+
+exit_without_my_ok:
+ query_plan.save_explain_data(thd->lex->explain);
+
+ int err2= thd->lex->explain->send_explain(thd);
+
+ delete select;
+ free_underlaid_joins(thd, select_lex);
+ DBUG_RETURN((err2 || thd->is_error()) ? 1 : 0);
}
/*
@@ -1125,7 +1184,7 @@ bool unsafe_key_update(List<TABLE_LIST> leaves, table_map tables_for_update)
while ((tl= it++))
{
- if (tl->table->map & tables_for_update)
+ if (!tl->is_jtbm() && (tl->table->map & tables_for_update))
{
TABLE *table1= tl->table;
bool primkey_clustered= (table1->file->primary_key_is_clustered() &&
@@ -1142,6 +1201,8 @@ bool unsafe_key_update(List<TABLE_LIST> leaves, table_map tables_for_update)
it2.rewind();
while ((tl2= it2++))
{
+ if (tl2->is_jtbm())
+ continue;
/*
Look at "next" tables only since all previous tables have
already been checked
@@ -1169,7 +1230,7 @@ bool unsafe_key_update(List<TABLE_LIST> leaves, table_map tables_for_update)
// The primary key can cover multiple columns
KEY key_info= table1->key_info[table1->s->primary_key];
KEY_PART_INFO *key_part= key_info.key_part;
- KEY_PART_INFO *key_part_end= key_part + key_info.key_parts;
+ KEY_PART_INFO *key_part_end= key_part + key_info.user_defined_key_parts;
for (;key_part != key_part_end; ++key_part)
{
@@ -1193,6 +1254,87 @@ bool unsafe_key_update(List<TABLE_LIST> leaves, table_map tables_for_update)
return false;
}
+/**
+ Check if there is enough privilege on specific table used by the
+ main select list of multi-update directly or indirectly (through
+ a view).
+
+ @param[in] thd Thread context.
+ @param[in] table Table list element for the table.
+ @param[in] tables_for_update Bitmap with tables being updated.
+ @param[in/out] updated_arg Set to true if table in question is
+ updated, also set to true if it is
+ a view and one of its underlying
+ tables is updated. Should be
+ initialized to false by the caller
+ before a sequence of calls to this
+ function.
+
+ @note To determine which tables/views are updated we have to go from
+ leaves to root since tables_for_update contains map of leaf
+ tables being updated and doesn't include non-leaf tables
+ (fields are already resolved to leaf tables).
+
+ @retval false - Success, all necessary privileges on all tables are
+ present or might be present on column-level.
+ @retval true - Failure, some necessary privilege on some table is
+ missing.
+*/
+
+static bool multi_update_check_table_access(THD *thd, TABLE_LIST *table,
+ table_map tables_for_update,
+ bool *updated_arg)
+{
+ if (table->view)
+ {
+ bool updated= false;
+ /*
+ If it is a mergeable view then we need to check privileges on its
+ underlying tables being merged (including views). We also need to
+ check if any of them is updated in order to find if this view is
+ updated.
+ If it is a non-mergeable view then it can't be updated.
+ */
+ DBUG_ASSERT(table->merge_underlying_list ||
+ (!table->updatable &&
+ !(table->table->map & tables_for_update)));
+
+ for (TABLE_LIST *tbl= table->merge_underlying_list; tbl;
+ tbl= tbl->next_local)
+ {
+ if (multi_update_check_table_access(thd, tbl, tables_for_update,
+ &updated))
+ {
+ tbl->hide_view_error(thd);
+ return true;
+ }
+ }
+ if (check_table_access(thd, updated ? UPDATE_ACL: SELECT_ACL, table,
+ FALSE, 1, FALSE))
+ return true;
+ *updated_arg|= updated;
+ /* We only need SELECT privilege for columns in the values list. */
+ table->grant.want_privilege= SELECT_ACL & ~table->grant.privilege;
+ }
+ else
+ {
+ /* Must be a base or derived table. */
+ const bool updated= table->table->map & tables_for_update;
+ if (check_table_access(thd, updated ? UPDATE_ACL : SELECT_ACL, table,
+ FALSE, 1, FALSE))
+ return true;
+ *updated_arg|= updated;
+ /* We only need SELECT privilege for columns in the values list. */
+ if (!table->derived)
+ {
+ table->grant.want_privilege= SELECT_ACL & ~table->grant.privilege;
+ table->table->grant.want_privilege= (SELECT_ACL &
+ ~table->table->grant.privilege);
+ }
+ }
+ return false;
+}
+
/*
make update specific preparation and checks after opening tables
@@ -1291,11 +1433,9 @@ int mysql_multi_update_prepare(THD *thd)
while ((tl= ti++))
{
TABLE *table= tl->table;
- /* Only set timestamp column if this is not modified */
- if (table->timestamp_field &&
- bitmap_is_set(table->write_set,
- table->timestamp_field->field_index))
- table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
+
+ if (tl->is_jtbm())
+ continue;
/* if table will be updated then check that it is unique */
if (table->map & tables_for_update)
@@ -1324,32 +1464,38 @@ int mysql_multi_update_prepare(THD *thd)
another table instance used by this statement which is going to
be write-locked (for example, trigger to be invoked might try
to update this table).
+ Last argument routine_modifies_data for read_lock_type_for_table()
+ is ignored, as prelocking placeholder will never be set here.
*/
+ DBUG_ASSERT(tl->prelocking_placeholder == false);
+ thr_lock_type lock_type= read_lock_type_for_table(thd, lex, tl, true);
if (using_lock_tables)
- tl->lock_type= read_lock_type_for_table(thd, lex, tl);
+ tl->lock_type= lock_type;
else
- tl->set_lock_type(thd, read_lock_type_for_table(thd, lex, tl));
+ tl->set_lock_type(thd, lock_type);
tl->updating= 0;
}
}
+
+ /*
+ Check access privileges for tables being updated or read.
+ Note that unlike in the above loop we need to iterate here not only
+ through all leaf tables but also through all view hierarchy.
+ */
for (tl= table_list; tl; tl= tl->next_local)
{
- /* Check access privileges for table */
- if (!tl->is_derived())
- {
- uint want_privilege= tl->updating ? UPDATE_ACL : SELECT_ACL;
- if (check_access(thd, want_privilege, tl->db,
- &tl->grant.privilege,
- &tl->grant.m_internal,
- 0, 0) ||
- check_grant(thd, want_privilege, tl, FALSE, 1, FALSE))
- DBUG_RETURN(TRUE);
- }
+ bool not_used= false;
+ if (tl->is_jtbm())
+ continue;
+ if (multi_update_check_table_access(thd, tl, tables_for_update, &not_used))
+ DBUG_RETURN(TRUE);
}
/* check single table update for view compound from several tables */
for (tl= table_list; tl; tl= tl->next_local)
{
+ if (tl->is_jtbm())
+ continue;
if (tl->is_merged_derived())
{
TABLE_LIST *for_update= 0;
@@ -1379,6 +1525,8 @@ int mysql_multi_update_prepare(THD *thd)
ti.rewind();
while ((tl= ti++))
{
+ if (tl->is_jtbm())
+ continue;
TABLE *table= tl->table;
TABLE_LIST *tlist;
if (!(tlist= tl->top_table())->derived)
@@ -1431,19 +1579,16 @@ bool mysql_multi_update(THD *thd,
{
bool res;
DBUG_ENTER("mysql_multi_update");
-
+
if (!(*result= new multi_update(table_list,
- &thd->lex->select_lex.leaf_tables,
- fields, values,
- handle_duplicates, ignore)))
+ &thd->lex->select_lex.leaf_tables,
+ fields, values,
+ handle_duplicates, ignore)))
{
DBUG_RETURN(TRUE);
}
- thd->abort_on_warning= test(thd->variables.sql_mode &
- (MODE_STRICT_TRANS_TABLES |
- MODE_STRICT_ALL_TABLES));
-
+ thd->abort_on_warning= thd->is_strict_mode();
List<Item> total_list;
res= mysql_select(thd, &select_lex->ref_pointer_array,
@@ -1459,6 +1604,11 @@ bool mysql_multi_update(THD *thd,
res|= thd->is_error();
if (unlikely(res))
(*result)->abort_result_set();
+ else
+ {
+ if (thd->lex->describe)
+ res= thd->lex->explain->send_explain(thd);
+ }
thd->abort_on_warning= 0;
DBUG_RETURN(res);
}
@@ -1473,7 +1623,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(0), ignore(ignore_arg), error_handled(0)
+ transactional_tables(0), ignore(ignore_arg), error_handled(0), prepared(0)
{}
@@ -1496,9 +1646,13 @@ int multi_update::prepare(List<Item> &not_used_values,
List_iterator<TABLE_LIST> ti(*leaves);
DBUG_ENTER("multi_update::prepare");
+ if (prepared)
+ DBUG_RETURN(0);
+ prepared= true;
+
thd->count_cuted_fields= CHECK_FIELD_WARN;
thd->cuted_fields=0L;
- thd_proc_info(thd, "updating main table");
+ THD_STAGE_INFO(thd, stage_updating_main_table);
tables_to_update= get_table_map(fields);
@@ -1515,6 +1669,9 @@ int multi_update::prepare(List<Item> &not_used_values,
*/
while ((table_ref= ti++))
{
+ if (table_ref->is_jtbm())
+ continue;
+
TABLE *table= table_ref->table;
if (tables_to_update & table->map)
{
@@ -1534,6 +1691,9 @@ int multi_update::prepare(List<Item> &not_used_values,
ti.rewind();
while ((table_ref= ti++))
{
+ if (table_ref->is_jtbm())
+ continue;
+
TABLE *table= table_ref->table;
if (tables_to_update & table->map)
{
@@ -1545,8 +1705,7 @@ int multi_update::prepare(List<Item> &not_used_values,
to compare records and detect data change.
*/
if ((table->file->ha_table_flags() & HA_PARTIAL_COLUMN_READ) &&
- (((uint) table->timestamp_field_type) &
- TIMESTAMP_AUTO_SET_ON_UPDATE))
+ table->default_field && table->has_default_function(true))
bitmap_union(table->read_set, table->write_set);
}
}
@@ -1565,6 +1724,8 @@ int multi_update::prepare(List<Item> &not_used_values,
while ((table_ref= ti++))
{
/* TODO: add support of view of join support */
+ if (table_ref->is_jtbm())
+ continue;
TABLE *table=table_ref->table;
leaf_table_count++;
if (tables_to_update & table->map)
@@ -1578,17 +1739,8 @@ int multi_update::prepare(List<Item> &not_used_values,
table->no_keyread=1;
table->covering_keys.clear_all();
table->pos_in_table_list= tl;
- if (table->triggers &&
- table->triggers->has_triggers(TRG_EVENT_UPDATE,
- TRG_ACTION_AFTER))
- {
- /*
- The table has AFTER UPDATE triggers that might access to subject
- table and therefore might need update to be done immediately.
- So we turn-off the batching.
- */
- (void) table->file->extra(HA_EXTRA_UPDATE_CANNOT_BATCH);
- }
+ table->prepare_triggers_for_update_stmt_or_event();
+ table->reset_default_fields();
}
}
@@ -1824,6 +1976,13 @@ loop_end:
TABLE *tbl= table;
do
{
+ /*
+ Signal each table (including tables referenced by WITH CHECK OPTION
+ clause) for which we will store row position in the temporary table
+ that we need a position to be read first.
+ */
+ tbl->prepare_for_position();
+
Field_string *field= new Field_string(tbl->file->ref_length, 0,
tbl->alias.c_ptr(),
&my_charset_bin);
@@ -1934,9 +2093,8 @@ int multi_update::send_data(List<Item> &not_used_values)
table->status|= STATUS_UPDATED;
store_record(table,record[1]);
- if (fill_record_n_invoke_before_triggers(thd, *fields_for_table[offset],
+ if (fill_record_n_invoke_before_triggers(thd, table, *fields_for_table[offset],
*values_for_table[offset], 0,
- table->triggers,
TRG_EVENT_UPDATE))
DBUG_RETURN(1);
@@ -1949,6 +2107,10 @@ int multi_update::send_data(List<Item> &not_used_values)
if (!can_compare_record || compare_record(table))
{
int error;
+
+ if (table->default_field && table->update_default_fields())
+ DBUG_RETURN(1);
+
if ((error= cur_table->view_check_option(thd, ignore)) !=
VIEW_CHECK_OK)
{
@@ -2038,7 +2200,7 @@ int multi_update::send_data(List<Item> &not_used_values)
} while ((tbl= tbl_it++));
/* Store regular updated fields in the row. */
- fill_record(thd,
+ fill_record(thd, tmp_table,
tmp_table->field + 1 + unupdated_check_opt_tables.elements,
*values_for_table[offset], TRUE, FALSE);
@@ -2063,13 +2225,6 @@ int multi_update::send_data(List<Item> &not_used_values)
}
-void multi_update::send_error(uint errcode,const char *err)
-{
- /* First send error what ever it is ... */
- my_error(errcode, MYF(0), err);
-}
-
-
void multi_update::abort_result_set()
{
/* the error was handled or nothing deleted and no side effects return */
@@ -2232,7 +2387,10 @@ int multi_update::do_updates()
for (copy_field_ptr=copy_field;
copy_field_ptr != copy_field_end;
copy_field_ptr++)
+ {
(*copy_field_ptr->do_copy)(copy_field_ptr);
+ copy_field_ptr->to_field->set_has_explicit_value();
+ }
if (table->triggers &&
table->triggers->process_triggers(thd, TRG_EVENT_UPDATE,
@@ -2242,11 +2400,11 @@ int multi_update::do_updates()
if (!can_compare_record || compare_record(table))
{
int error;
+ if (table->default_field && (error= table->update_default_fields()))
+ goto err2;
if (table->vfield &&
update_virtual_fields(thd, table,
- (table->triggers ?
- VCOL_UPDATE_ALL :
- VCOL_UPDATE_FOR_WRITE)))
+ (table->triggers ? VCOL_UPDATE_ALL : VCOL_UPDATE_FOR_WRITE)))
goto err2;
if ((error= cur_table->view_check_option(thd, ignore)) !=
VIEW_CHECK_OK)
@@ -2341,7 +2499,7 @@ bool multi_update::send_eof()
ulonglong id;
killed_state killed_status= NOT_KILLED;
DBUG_ENTER("multi_update::send_eof");
- thd_proc_info(thd, "updating reference tables");
+ THD_STAGE_INFO(thd, stage_updating_reference_tables);
/*
Does updates for the last n - 1 tables, returns 0 if ok;
@@ -2355,7 +2513,7 @@ bool multi_update::send_eof()
later carried out killing should not affect binlogging.
*/
killed_status= (local_error == 0) ? NOT_KILLED : thd->killed;
- thd_proc_info(thd, "end");
+ THD_STAGE_INFO(thd, stage_end);
/* We must invalidate the query cache before binlog writing and
ha_autocommit_... */
@@ -2401,7 +2559,7 @@ bool multi_update::send_eof()
thd->transaction.stmt.modified_non_trans_table);
if (local_error != 0)
- error_handled= TRUE; // to force early leave from ::send_error()
+ error_handled= TRUE; // to force early leave from ::abort_result_set()
if (local_error > 0) // if the above log write did not fail ...
{
diff --git a/sql/sql_view.cc b/sql/sql_view.cc
index 5bd82fdd842..0d88b3a1eda 100644
--- a/sql/sql_view.cc
+++ b/sql/sql_view.cc
@@ -1,5 +1,5 @@
/* Copyright (c) 2004, 2013, Oracle and/or its affiliates.
- Copyright (c) 2011, 2014, SkySQL Ab.
+ Copyright (c) 2011, 2015, MariaDB
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
@@ -16,7 +16,7 @@
*/
#define MYSQL_LEX 1
-#include "my_global.h" /* NO_EMBEDDED_ACCESS_CHECKS */
+#include <my_global.h> /* NO_EMBEDDED_ACCESS_CHECKS */
#include "sql_priv.h"
#include "unireg.h"
#include "sql_view.h"
@@ -33,7 +33,8 @@
#include "sp_head.h"
#include "sp.h"
#include "sp_cache.h"
-#include "datadict.h" // dd_frm_type()
+#include "datadict.h" // dd_frm_is_view()
+#include "sql_derived.h"
#define MD5_BUFF_LENGTH 33
@@ -210,16 +211,12 @@ static void make_valid_column_names(List<Item> &item_list)
static bool
fill_defined_view_parts (THD *thd, TABLE_LIST *view)
{
- char key[MAX_DBKEY_LENGTH];
- uint key_length;
LEX *lex= thd->lex;
TABLE_LIST decoy;
memcpy (&decoy, view, sizeof (TABLE_LIST));
- key_length= create_table_def_key(thd, key, view, 0);
-
- if (tdc_open_view(thd, &decoy, decoy.alias, key, key_length,
- thd->mem_root, OPEN_VIEW_NO_PARSE))
+ if (tdc_open_view(thd, &decoy, decoy.alias, thd->mem_root,
+ OPEN_VIEW_NO_PARSE))
return TRUE;
if (!lex->definer)
@@ -353,7 +350,7 @@ bool create_view_precheck(THD *thd, TABLE_LIST *tables, TABLE_LIST *view,
while ((item= it++))
{
Item_field *field;
- if ((field= item->filed_for_view_update()))
+ if ((field= item->field_for_view_update()))
{
/*
any_privileges may be reset later by the Item_field::set_field
@@ -433,7 +430,8 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views,
lex->link_first_table_back(view, link_to_local);
view->open_type= OT_BASE_ONLY;
- if (open_and_lock_tables(thd, lex->query_tables, TRUE, 0))
+ if (open_temporary_tables(thd, lex->query_tables) ||
+ open_and_lock_tables(thd, lex->query_tables, TRUE, 0))
{
view= lex->unlink_first_table(&link_to_local);
res= TRUE;
@@ -467,60 +465,9 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views,
}
sp_cache_invalidate();
+ if (sp_process_definer(thd))
+ goto err;
- 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
/*
check that tables are not temporary and this VIEW do not used in query
(it is possible with ALTERing VIEW).
@@ -638,7 +585,7 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views,
Item *item;
while ((item= it++))
{
- Item_field *fld= item->filed_for_view_update();
+ Item_field *fld= item->field_for_view_update();
uint priv= (get_column_grant(thd, &view->grant, view->db,
view->table_name, item->name) &
VIEW_ANY_ACL);
@@ -668,6 +615,15 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views,
res= mysql_register_view(thd, view, mode);
+ /*
+ View TABLE_SHARE must be removed from the table definition cache in order to
+ make ALTER VIEW work properly. Otherwise, we would not be able to detect
+ meta-data changes after ALTER VIEW.
+ */
+
+ if (!res)
+ tdc_remove_table(thd, TDC_RT_REMOVE_ALL, view->db, view->table_name, false);
+
if (!res && mysql_bin_log.is_open())
{
String buff;
@@ -722,7 +678,7 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views,
DBUG_RETURN(0);
err:
- thd_proc_info(thd, "end");
+ THD_STAGE_INFO(thd, stage_end);
lex->link_first_table_back(view, link_to_local);
unit->cleanup();
DBUG_RETURN(res || thd->is_error());
@@ -953,14 +909,18 @@ static int mysql_register_view(THD *thd, TABLE_LIST *view,
view->source= thd->lex->create_view_select;
if (!thd->make_lex_string(&view->select_stmt, view_query.ptr(),
- view_query.length(), false))
+ view_query.length()))
{
my_error(ER_OUT_OF_RESOURCES, MYF(0));
error= -1;
goto err;
}
- view->file_version= 1;
+ /*
+ version 1 - before 10.0.5
+ version 2 - empty definer_host means a role
+ */
+ view->file_version= 2;
view->mariadb_version= MYSQL_VERSION_ID;
view->calc_md5(md5);
if (!(view->md5.str= (char*) thd->memdup(md5, 32)))
@@ -974,7 +934,7 @@ static int mysql_register_view(THD *thd, TABLE_LIST *view,
if (lex->create_view_algorithm == VIEW_ALGORITHM_MERGE &&
!lex->can_be_merged())
{
- push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_VIEW_MERGE,
+ push_warning(thd, Sql_condition::WARN_LEVEL_WARN, ER_WARN_VIEW_MERGE,
ER(ER_WARN_VIEW_MERGE));
lex->create_view_algorithm= DTYPE_ALGORITHM_UNDEFINED;
}
@@ -1025,7 +985,7 @@ loop_out:
fn_format(path_buff, file.str, dir.str, "", MY_UNPACK_FILENAME);
path.length= strlen(path_buff);
- if (!access(path.str, F_OK))
+ if (ha_table_exists(thd, view->db, view->table_name, NULL))
{
if (mode == VIEW_CREATE_NEW)
{
@@ -1079,7 +1039,7 @@ loop_out:
view->view_creation_ctx->get_connection_cl()->name);
if (!thd->make_lex_string(&view->view_body_utf8, is_query.ptr(),
- is_query.length(), false))
+ is_query.length()))
{
my_error(ER_OUT_OF_RESOURCES, MYF(0));
error= -1;
@@ -1148,19 +1108,16 @@ err:
bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table,
uint flags)
{
- SELECT_LEX *end, *view_select;
+ SELECT_LEX *end, *UNINIT_VAR(view_select);
LEX *old_lex, *lex;
Query_arena *arena, backup;
TABLE_LIST *top_view= table->top_table();
- bool parse_status;
+ bool UNINIT_VAR(parse_status);
bool result, view_is_mergeable;
TABLE_LIST *UNINIT_VAR(view_main_select_tables);
DBUG_ENTER("mysql_make_view");
DBUG_PRINT("info", ("table: 0x%lx (%s)", (ulong) table, table->table_name));
- LINT_INIT(parse_status);
- LINT_INIT(view_select);
-
if (table->view)
{
/*
@@ -1180,6 +1137,15 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table,
DBUG_PRINT("info",
("VIEW %s.%s is already processed on previous PS/SP execution",
table->view_db.str, table->view_name.str));
+
+ /*
+ Clear old variables in the TABLE_LIST that could be left from an old view
+ This is only needed if there was an error at last usage of view,
+ in which case the reinit call wasn't done.
+ See MDEV-6668 for details.
+ */
+ mysql_derived_reinit(thd, NULL, table);
+
DBUG_RETURN(0);
}
@@ -1240,11 +1206,19 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table,
DBUG_ASSERT(!table->definer.host.str &&
!table->definer.user.length &&
!table->definer.host.length);
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_VIEW_FRM_NO_USER, ER(ER_VIEW_FRM_NO_USER),
table->db, table->table_name);
- get_default_definer(thd, &table->definer);
+ get_default_definer(thd, &table->definer, false);
}
+
+ /*
+ since 10.0.5 definer.host can never be "" for a User, but it's
+ always "" for a Role. Before 10.0.5 it could be "" for a User,
+ but roles didn't exist. file_version helps.
+ */
+ if (!table->definer.host.str[0] && table->file_version < 2)
+ table->definer.host= host_not_specified; // User, not Role
/*
Initialize view definition context by character set names loaded from
@@ -1368,15 +1342,14 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table,
TABLE_LIST *view_tables= lex->query_tables;
TABLE_LIST *view_tables_tail= 0;
TABLE_LIST *tbl;
- Security_context *security_ctx;
+ Security_context *security_ctx= 0;
/*
Check rights to run commands (EXPLAIN SELECT & SHOW CREATE) which show
underlying tables.
Skip this step if we are opening view for prelocking only.
*/
- if (!table->prelocking_placeholder &&
- (old_lex->sql_command == SQLCOM_SELECT && old_lex->describe))
+ if (!table->prelocking_placeholder && (old_lex->describe))
{
/*
The user we run EXPLAIN as (either the connected user who issued
@@ -1548,6 +1521,7 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table,
if (view_select->options & OPTION_TO_QUERY_CACHE)
old_lex->select_lex.options|= OPTION_TO_QUERY_CACHE;
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
if (table->view_suid)
{
/*
@@ -1568,6 +1542,7 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table,
*/
security_ctx= table->security_ctx;
}
+#endif
/* Assign the context to the tables referenced in the view */
if (view_tables)
@@ -1645,7 +1620,7 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table,
lex->select_lex.order_list.elements &&
!table->select_lex->master_unit()->is_union())
{
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
ER_VIEW_ORDERBY_IGNORED,
ER(ER_VIEW_ORDERBY_IGNORED),
table->db, table->table_name);
@@ -1727,7 +1702,6 @@ bool mysql_drop_view(THD *thd, TABLE_LIST *views, enum_drop_mode drop_mode)
String non_existant_views;
char *wrong_object_db= NULL, *wrong_object_name= NULL;
bool error= FALSE;
- enum legacy_db_type not_used;
bool some_views_deleted= FALSE;
bool something_wrong= FALSE;
DBUG_ENTER("mysql_drop_view");
@@ -1744,29 +1718,33 @@ bool mysql_drop_view(THD *thd, TABLE_LIST *views, enum_drop_mode drop_mode)
DBUG_RETURN(TRUE);
}
- if (lock_table_names(thd, views, 0, thd->variables.lock_wait_timeout,
- MYSQL_OPEN_SKIP_TEMPORARY))
+ if (lock_table_names(thd, views, 0, thd->variables.lock_wait_timeout, 0))
DBUG_RETURN(TRUE);
for (view= views; view; view= view->next_local)
{
- frm_type_enum type= FRMTYPE_ERROR;
+ bool not_exist;
build_table_filename(path, sizeof(path) - 1,
view->db, view->table_name, reg_ext, 0);
- if (access(path, F_OK) ||
- FRMTYPE_VIEW != (type= dd_frm_type(thd, path, &not_used)))
+ if ((not_exist= my_access(path, F_OK)) || !dd_frm_is_view(thd, path))
{
char name[FN_REFLEN];
my_snprintf(name, sizeof(name), "%s.%s", view->db, view->table_name);
- if (thd->lex->drop_if_exists)
+ if (thd->lex->check_exists)
{
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
ER_BAD_TABLE_ERROR, ER(ER_BAD_TABLE_ERROR),
name);
continue;
}
- if (type == FRMTYPE_TABLE)
+ if (not_exist)
+ {
+ if (non_existant_views.length())
+ non_existant_views.append(',');
+ non_existant_views.append(name);
+ }
+ else
{
if (!wrong_object_name)
{
@@ -1774,12 +1752,6 @@ bool mysql_drop_view(THD *thd, TABLE_LIST *views, enum_drop_mode drop_mode)
wrong_object_name= view->table_name;
}
}
- else
- {
- if (non_existant_views.length())
- non_existant_views.append(',');
- non_existant_views.append(String(view->table_name,system_charset_info));
- }
continue;
}
if (mysql_file_delete(key_file_frm, path, MYF(MY_WME)))
@@ -1788,9 +1760,8 @@ bool mysql_drop_view(THD *thd, TABLE_LIST *views, enum_drop_mode drop_mode)
some_views_deleted= TRUE;
/*
- For a view, there is a TABLE_SHARE object, but its
- ref_count never goes above 1. Remove it from the table
- definition cache, in case the view was cached.
+ For a view, there is a TABLE_SHARE object.
+ Remove it from the table definition cache, in case the view was cached.
*/
tdc_remove_table(thd, TDC_RT_REMOVE_ALL, view->db, view->table_name,
FALSE);
@@ -1897,7 +1868,7 @@ bool check_key_in_view(THD *thd, TABLE_LIST *view)
if ((key_info->flags & (HA_NOSAME | HA_NULL_PART_KEY)) == HA_NOSAME)
{
KEY_PART_INFO *key_part= key_info->key_part;
- KEY_PART_INFO *key_part_end= key_part + key_info->key_parts;
+ KEY_PART_INFO *key_part_end= key_part + key_info->user_defined_key_parts;
/* check that all key parts are used */
for (;;)
@@ -1906,7 +1877,7 @@ bool check_key_in_view(THD *thd, TABLE_LIST *view)
for (k= trans; k < end_of_trans; k++)
{
Item_field *field;
- if ((field= k->item->filed_for_view_update()) &&
+ if ((field= k->item->field_for_view_update()) &&
field->field == key_part->field)
break;
}
@@ -1928,7 +1899,7 @@ bool check_key_in_view(THD *thd, TABLE_LIST *view)
for (fld= trans; fld < end_of_trans; fld++)
{
Item_field *field;
- if ((field= fld->item->filed_for_view_update()) &&
+ if ((field= fld->item->field_for_view_update()) &&
field->field == *field_ptr)
break;
}
@@ -1942,7 +1913,7 @@ bool check_key_in_view(THD *thd, TABLE_LIST *view)
if (thd->variables.updatable_views_with_limit)
{
/* update allowed, but issue warning */
- push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
+ push_warning(thd, Sql_condition::WARN_LEVEL_NOTE,
ER_WARN_VIEW_WITHOUT_KEY, ER(ER_WARN_VIEW_WITHOUT_KEY));
DBUG_RETURN(FALSE);
}
@@ -1982,7 +1953,7 @@ bool insert_view_fields(THD *thd, List<Item> *list, TABLE_LIST *view)
for (Field_translator *entry= trans; entry < trans_end; entry++)
{
Item_field *fld;
- if ((fld= entry->item->filed_for_view_update()))
+ if ((fld= entry->item->field_for_view_update()))
list->push_back(fld);
else
{
diff --git a/sql/sql_view.h b/sql/sql_view.h
index 1026c57aaaf..8d733a1867c 100644
--- a/sql/sql_view.h
+++ b/sql/sql_view.h
@@ -2,7 +2,8 @@
#define SQL_VIEW_INCLUDED
/* -*- C++ -*- */
-/* Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
+/* Copyright (c) 2004, 2010, Oracle and/or its affiliates.
+ Copyright (c) 2015, MariaDB
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
@@ -37,6 +38,7 @@ bool mysql_create_view(THD *thd, TABLE_LIST *view,
bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table,
uint flags);
+
bool mysql_drop_view(THD *thd, TABLE_LIST *view, enum_drop_mode drop_mode);
bool check_key_in_view(THD *thd, TABLE_LIST * view);
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index 3b42fe1e74a..16916fb919d 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -23,8 +23,9 @@
*/
%{
-#define YYLIP (& thd->m_parser_state->m_lip)
-#define YYPS (& thd->m_parser_state->m_yacc)
+#define YYLIP (& thd->m_parser_state->m_lip)
+#define YYPS (& thd->m_parser_state->m_yacc)
+#define YYCSCL (thd->variables.character_set_client)
#define MYSQL_YACC
#define YYINITDEPTH 100
@@ -32,8 +33,8 @@
#define Lex (thd->lex)
#define Select Lex->current_select
+#include <my_global.h>
#include "sql_priv.h"
-#include "unireg.h" // REQUIRED: for other includes
#include "sql_parse.h" /* comp_*_creator */
#include "sql_table.h" /* primary_key_name */
#include "sql_partition.h" /* mem_alloc_error, partition_info, HASH_PARTITION */
@@ -47,17 +48,21 @@
#include "sp_pcontext.h"
#include "sp_rcontext.h"
#include "sp.h"
-#include "sql_alter.h" // Alter_table*_statement
-#include "sql_truncate.h" // Truncate_statement
-#include "sql_admin.h" // Analyze/Check..._table_stmt
-#include "sql_partition_admin.h" // Alter_table_*_partition_stmt
+#include "sql_alter.h" // Sql_cmd_alter_table*
+#include "sql_truncate.h" // Sql_cmd_truncate_table
+#include "sql_admin.h" // Sql_cmd_analyze/Check..._table
+#include "sql_partition_admin.h" // Sql_cmd_alter_table_*_part.
+#include "sql_handler.h" // Sql_cmd_handler_*
#include "sql_signal.h"
+#include "sql_get_diagnostics.h" // Sql_cmd_get_diagnostics
#include "event_parse_data.h"
#include "create_options.h"
#include <myisam.h>
#include <myisammrg.h>
#include "keycaches.h"
#include "set_var.h"
+#include "rpl_mi.h"
+#include "lex_token.h"
/* this is to get the bison compilation windows warnings out */
#ifdef _MSC_VER
@@ -272,7 +277,7 @@ void case_stmt_action_case(LEX *lex)
(Instruction 12 in the example)
*/
- lex->spcont->push_label((char *)"", lex->sphead->instructions());
+ lex->spcont->push_label(current_thd, empty_lex_str, lex->sphead->instructions());
}
/**
@@ -340,8 +345,8 @@ int case_stmt_action_when(LEX *lex, Item *when, bool simple)
(jump_if_not from instruction 2 to 5, 5 to 8 ... in the example)
*/
- return !test(i) ||
- sp->push_backpatch(i, ctx->push_label((char *)"", 0)) ||
+ return !MY_TEST(i) ||
+ sp->push_backpatch(i, ctx->push_label(current_thd, empty_lex_str, 0)) ||
sp->add_cont_backpatch(i) ||
sp->add_instr(i);
}
@@ -358,7 +363,7 @@ int case_stmt_action_then(LEX *lex)
sp_pcontext *ctx= lex->spcont;
uint ip= sp->instructions();
sp_instr_jump *i = new sp_instr_jump(ip, ctx);
- if (!test(i) || sp->add_instr(i))
+ if (!MY_TEST(i) || sp->add_instr(i))
return 1;
/*
@@ -464,7 +469,7 @@ set_system_variable(THD *thd, struct sys_var_with_base *tmp,
*/
static bool
-set_local_variable(THD *thd, sp_variable_t *spv, Item *val)
+set_local_variable(THD *thd, sp_variable *spv, Item *val)
{
Item *it;
LEX *lex= thd->lex;
@@ -472,8 +477,8 @@ set_local_variable(THD *thd, sp_variable_t *spv, Item *val)
if (val)
it= val;
- else if (spv->dflt)
- it= spv->dflt;
+ else if (spv->default_value)
+ it= spv->default_value;
else
{
it= new (thd->mem_root) Item_null();
@@ -539,6 +544,57 @@ set_trigger_new_row(THD *thd, LEX_STRING *name, Item *val)
/**
+ Create an object to represent a SP variable in the Item-hierarchy.
+
+ @param thd The current thread.
+ @param name The SP variable name.
+ @param spvar The SP variable (optional).
+ @param start_in_q Start position of the SP variable name in the query.
+ @param end_in_q End position of the SP variable name in the query.
+
+ @remark If spvar is not specified, the name is used to search for the
+ variable in the parse-time context. If the variable does not
+ exist, a error is set and NULL is returned to the caller.
+
+ @return An Item_splocal object representing the SP variable, or NULL on error.
+*/
+static Item_splocal*
+create_item_for_sp_var(THD *thd, LEX_STRING name, sp_variable *spvar,
+ const char *start_in_q, const char *end_in_q)
+{
+ Item_splocal *item;
+ LEX *lex= thd->lex;
+ uint pos_in_q, len_in_q;
+ sp_pcontext *spc = lex->spcont;
+
+ /* If necessary, look for the variable. */
+ if (spc && !spvar)
+ spvar= spc->find_variable(name, false);
+
+ if (!spvar)
+ {
+ my_error(ER_SP_UNDECLARED_VAR, MYF(0), name.str);
+ return NULL;
+ }
+
+ DBUG_ASSERT(spc && spvar);
+
+ /* Position and length of the SP variable name in the query. */
+ pos_in_q= start_in_q - lex->sphead->m_tmp_query;
+ len_in_q= end_in_q - start_in_q;
+
+ item= new (thd->mem_root)
+ Item_splocal(name, spvar->offset, spvar->type, pos_in_q, len_in_q);
+
+#ifndef DBUG_OFF
+ if (item)
+ item->m_sp= lex->sphead;
+#endif
+
+ return item;
+}
+
+/**
Helper to resolve the SQL:2003 Syntax exception 1) in <in predicate>.
See SQL:2003, Part 2, section 8.4 <in predicate>, Note 184, page 383.
This function returns the proper item for the SQL expression
@@ -703,10 +759,10 @@ static bool add_create_index_prepare (LEX *lex, Table_ident *table)
if (!lex->current_select->add_table_to_list(lex->thd, table, NULL,
TL_OPTION_UPDATING,
TL_READ_NO_INSERT,
- MDL_SHARED_NO_WRITE))
+ MDL_SHARED_UPGRADABLE))
return TRUE;
lex->alter_info.reset();
- lex->alter_info.flags= ALTER_ADD_INDEX;
+ lex->alter_info.flags= Alter_info::ALTER_ADD_INDEX;
lex->col_list.empty();
lex->change= NullS;
lex->option_list= NULL;
@@ -719,7 +775,7 @@ static bool add_create_index (LEX *lex, Key::Keytype type,
{
Key *key;
key= new Key(type, name, info ? info : &lex->key_create_info, generated,
- lex->col_list, lex->option_list);
+ lex->col_list, lex->option_list, lex->check_exists);
if (key == NULL)
return TRUE;
@@ -728,6 +784,115 @@ static bool add_create_index (LEX *lex, Key::Keytype type,
return FALSE;
}
+
+/**
+ Create a separate LEX for each assignment if in SP.
+
+ If we are in SP we want have own LEX for each assignment.
+ This is mostly because it is hard for several sp_instr_set
+ and sp_instr_set_trigger instructions share one LEX.
+ (Well, it is theoretically possible but adds some extra
+ overhead on preparation for execution stage and IMO less
+ robust).
+
+ QQ: May be we should simply prohibit group assignments in SP?
+
+ @see sp_create_assignment_instr
+
+ @param thd Thread context
+ @param no_lookahead True if the parser has no lookahead
+*/
+
+static void sp_create_assignment_lex(THD *thd, bool no_lookahead)
+{
+ LEX *lex= thd->lex;
+
+ if (lex->sphead)
+ {
+ Lex_input_stream *lip= &thd->m_parser_state->m_lip;
+ LEX *old_lex= lex;
+ lex->sphead->reset_lex(thd);
+ lex= thd->lex;
+
+ /* Set new LEX as if we at start of set rule. */
+ lex->sql_command= SQLCOM_SET_OPTION;
+ mysql_init_select(lex);
+ lex->var_list.empty();
+ lex->autocommit= 0;
+ /* get_ptr() is only correct with no lookahead. */
+ DBUG_ASSERT(no_lookahead);
+ lex->sphead->m_tmp_query= lip->get_ptr();
+ /* Inherit from outer lex. */
+ lex->option_type= old_lex->option_type;
+ }
+}
+
+
+/**
+ Create a SP instruction for a SET assignment.
+
+ @see sp_create_assignment_lex
+
+ @param thd Thread context
+ @param no_lookahead True if the parser has no lookahead
+
+ @return false if success, true otherwise.
+*/
+
+static bool sp_create_assignment_instr(THD *thd, bool no_lookahead)
+{
+ LEX *lex= thd->lex;
+
+ if (lex->sphead)
+ {
+ sp_head *sp= lex->sphead;
+
+ if (!lex->var_list.is_empty())
+ {
+ /*
+ We have assignment to user or system variable or
+ option setting, so we should construct sp_instr_stmt
+ for it.
+ */
+ LEX_STRING qbuff;
+ sp_instr_stmt *i;
+ Lex_input_stream *lip= &thd->m_parser_state->m_lip;
+
+ if (!(i= new sp_instr_stmt(sp->instructions(), lex->spcont,
+ lex)))
+ return true;
+
+ /*
+ Extract the query statement from the tokenizer. The
+ end is either lip->ptr, if there was no lookahead,
+ lip->tok_end otherwise.
+ */
+ if (no_lookahead)
+ qbuff.length= lip->get_ptr() - sp->m_tmp_query;
+ else
+ qbuff.length= lip->get_tok_end() - sp->m_tmp_query;
+
+ if (!(qbuff.str= (char*) alloc_root(thd->mem_root,
+ qbuff.length + 5)))
+ return true;
+
+ strmake(strmake(qbuff.str, "SET ", 4), sp->m_tmp_query,
+ qbuff.length);
+ qbuff.length+= 4;
+ i->m_query= qbuff;
+ if (sp->add_instr(i))
+ return true;
+ }
+ enum_var_type inner_option_type= lex->option_type;
+ if (lex->sphead->restore_lex(thd))
+ return true;
+ /* Copy option_type to outer lex in case it has changed. */
+ thd->lex->option_type= inner_option_type;
+ }
+ return false;
+}
+
+
%}
%union {
int num;
@@ -766,7 +931,7 @@ static bool add_create_index (LEX *lex, Key::Keytype type,
timestamp_type date_time_type;
st_select_lex *select_lex;
chooser_compare_func_creator boolfunc2creator;
- struct sp_cond_type *spcondtype;
+ class sp_condition_value *spcondvalue;
struct { int vars, conds, hndlrs, curs; } spblock;
sp_name *spname;
LEX *lex;
@@ -777,6 +942,14 @@ static bool add_create_index (LEX *lex, Key::Keytype type,
enum Foreign_key::fk_option m_fk_option;
enum enum_yes_no_unknown m_yes_no_unk;
Diag_condition_item_name diag_condition_item_name;
+ Diagnostics_information::Which_area diag_area;
+ Diagnostics_information *diag_info;
+ Statement_information_item *stmt_info_item;
+ Statement_information_item::Name stmt_info_item_name;
+ List<Statement_information_item> *stmt_info_list;
+ Condition_information_item *cond_info_item;
+ Condition_information_item::Name cond_info_item_name;
+ List<Condition_information_item> *cond_info_list;
DYNCALL_CREATE_DEF *dyncol_def;
List<DYNCALL_CREATE_DEF> *dyncol_def_list;
bool is_not_empty;
@@ -790,10 +963,10 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%parse-param { THD *thd }
%lex-param { THD *thd }
/*
- Currently there are 175 shift/reduce conflicts.
+ Currently there are 163 shift/reduce conflicts.
We should not introduce new conflicts any more.
*/
-%expect 175
+%expect 163
/*
Comments for TOKENS.
@@ -807,7 +980,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
MYSQL-FUNC : MySQL extention, function
INTERNAL : Not a real token, lex optimization
OPERATOR : SQL operator
- FUTURE-USE : Reserved for futur use
+ FUTURE-USE : Reserved for future use
This makes the code grep-able, and helps maintenance.
*/
@@ -816,6 +989,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token ACCESSIBLE_SYM
%token ACTION /* SQL-2003-N */
%token ADD /* SQL-2003-R */
+%token ADMIN_SYM /* SQL-2003-N */
%token ADDDATE_SYM /* MYSQL-FUNC */
%token AFTER_SYM /* SQL-2003-N */
%token AGAINST
@@ -836,6 +1010,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token AUTHORS_SYM
%token AUTOEXTEND_SIZE_SYM
%token AUTO_INC
+%token AUTO_SYM
%token AVG_ROW_LENGTH
%token AVG_SYM /* SQL-2003-N */
%token BACKUP_SYM
@@ -884,11 +1059,10 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token COLLATION_SYM /* SQL-2003-N */
%token COLUMNS
%token COLUMN_ADD_SYM
+%token COLUMN_CHECK_SYM
%token COLUMN_CREATE_SYM
%token COLUMN_DELETE_SYM
-%token COLUMN_EXISTS_SYM
%token COLUMN_GET_SYM
-%token COLUMN_LIST_SYM
%token COLUMN_SYM /* SQL-2003-R */
%token COLUMN_NAME_SYM /* SQL-2003-N */
%token COMMENT_SYM
@@ -916,7 +1090,10 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token CROSS /* SQL-2003-R */
%token CUBE_SYM /* SQL-2003-R */
%token CURDATE /* MYSQL-FUNC */
+%token CURRENT_SYM /* SQL-2003-R */
%token CURRENT_USER /* SQL-2003-R */
+%token CURRENT_ROLE /* SQL-2003-R */
+%token CURRENT_POS_SYM
%token CURSOR_SYM /* SQL-2003-R */
%token CURSOR_NAME_SYM /* SQL-2003-N */
%token CURTIME /* MYSQL-FUNC */
@@ -946,6 +1123,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token DESCRIBE /* SQL-2003-R */
%token DES_KEY_FILE
%token DETERMINISTIC_SYM /* SQL-2003-R */
+%token DIAGNOSTICS_SYM /* SQL-2003-N */
%token DIRECTORY_SYM
%token DISABLE_SYM
%token DISCARD
@@ -979,11 +1157,13 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token EVENTS_SYM
%token EVENT_SYM
%token EVERY_SYM /* SQL-2003-N */
+%token EXCHANGE_SYM
%token EXAMINED_SYM
%token EXECUTE_SYM /* SQL-2003-R */
%token EXISTS /* SQL-2003-R */
%token EXIT_SYM
%token EXPANSION_SYM
+%token EXPORT_SYM
%token EXTENDED_SYM
%token EXTENT_SIZE_SYM
%token EXTRACT_SYM /* SQL-2003-N */
@@ -1011,6 +1191,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token GEOMETRYCOLLECTION
%token GEOMETRY_SYM
%token GET_FORMAT /* MYSQL-FUNC */
+%token GET_SYM /* SQL-2003-R */
%token GLOBAL_SYM /* SQL-2003-R */
%token GRANT /* SQL-2003-R */
%token GRANTS
@@ -1031,6 +1212,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token HOUR_MINUTE_SYM
%token HOUR_SECOND_SYM
%token HOUR_SYM /* SQL-2003-R */
+%token ID_SYM /* MYSQL */
%token IDENT
%token IDENTIFIED_SYM
%token IDENT_QUOTED
@@ -1097,6 +1279,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token LOW_PRIORITY
%token LT /* OPERATOR */
%token MASTER_CONNECT_RETRY_SYM
+%token MASTER_GTID_POS_SYM
%token MASTER_HOST_SYM
%token MASTER_LOG_FILE_SYM
%token MASTER_LOG_POS_SYM
@@ -1107,11 +1290,14 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token MASTER_SSL_CA_SYM
%token MASTER_SSL_CERT_SYM
%token MASTER_SSL_CIPHER_SYM
+%token MASTER_SSL_CRL_SYM
+%token MASTER_SSL_CRLPATH_SYM
%token MASTER_SSL_KEY_SYM
%token MASTER_SSL_SYM
%token MASTER_SSL_VERIFY_SERVER_CERT_SYM
%token MASTER_SYM
%token MASTER_USER_SYM
+%token MASTER_USE_GTID_SYM
%token MASTER_HEARTBEAT_PERIOD_SYM
%token MATCH /* SQL-2003-R */
%token MAX_CONNECTIONS_PER_HOUR
@@ -1168,13 +1354,14 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token NO_WRITE_TO_BINLOG
%token NULL_SYM /* SQL-2003-R */
%token NUM
+%token NUMBER_SYM /* SQL-2003-N */
%token NUMERIC_SYM /* SQL-2003-R */
%token NVARCHAR_SYM
%token OFFSET_SYM
%token OLD_PASSWORD
%token ON /* SQL-2003-R */
-%token ONE_SHOT_SYM
%token ONE_SYM
+%token ONLY_SYM /* SQL-2003-R */
%token ONLINE_SYM
%token OPEN_SYM /* SQL-2003-R */
%token OPTIMIZE
@@ -1196,9 +1383,9 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token PARSER_SYM
%token PARSE_VCOL_EXPR_SYM
%token PARTIAL /* SQL-2003-N */
-%token PARTITIONING_SYM
-%token PARTITIONS_SYM
%token PARTITION_SYM /* SQL-2003-R */
+%token PARTITIONS_SYM
+%token PARTITIONING_SYM
%token PASSWORD
%token PERSISTENT_SYM
%token PHASE_SYM
@@ -1259,16 +1446,21 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token RESTORE_SYM
%token RESTRICT
%token RESUME_SYM
+%token RETURNED_SQLSTATE_SYM /* SQL-2003-N */
+%token RETURNING_SYM
%token RETURNS_SYM /* SQL-2003-R */
%token RETURN_SYM /* SQL-2003-R */
+%token REVERSE_SYM
%token REVOKE /* SQL-2003-R */
%token RIGHT /* SQL-2003-R */
+%token ROLE_SYM
%token ROLLBACK_SYM /* SQL-2003-R */
%token ROLLUP_SYM /* SQL-2003-R */
%token ROUTINE_SYM /* SQL-2003-N */
%token ROWS_SYM /* SQL-2003-R */
%token ROW_FORMAT_SYM
%token ROW_SYM /* SQL-2003-R */
+%token ROW_COUNT_SYM /* SQL-2003-N */
%token RTREE_SYM
%token SAVEPOINT_SYM /* SQL-2003-R */
%token SCHEDULE_SYM
@@ -1295,6 +1487,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token SIGNED_SYM
%token SIMPLE_SYM /* SQL-2003-N */
%token SLAVE
+%token SLAVES
+%token SLAVE_POS_SYM
%token SLOW
%token SMALLINT /* SQL-2003-R */
%token SNAPSHOT_SYM
@@ -1320,6 +1514,9 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token STARTING
%token STARTS_SYM
%token START_SYM /* SQL-2003-R */
+%token STATS_AUTO_RECALC_SYM
+%token STATS_PERSISTENT_SYM
+%token STATS_SAMPLE_PAGES_SYM
%token STATUS_SYM
%token STDDEV_SAMP_SYM /* SQL-2003-N */
%token STD_SYM
@@ -1412,6 +1609,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token WAIT_SYM
%token WARNINGS
%token WEEK_SYM
+%token WEIGHT_STRING_SYM
%token WHEN_SYM /* SQL-2003-R */
%token WHERE /* SQL-2003-R */
%token WHILE_SYM
@@ -1453,41 +1651,48 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%type <lex_str>
IDENT IDENT_QUOTED TEXT_STRING DECIMAL_NUM FLOAT_NUM NUM LONG_NUM
- HEX_NUM HEX_STRING hex_num_or_string
+ HEX_NUM HEX_STRING
LEX_HOSTNAME ULONGLONG_NUM field_ident select_alias ident ident_or_text
IDENT_sys TEXT_STRING_sys TEXT_STRING_literal
NCHAR_STRING opt_component key_cache_name
sp_opt_label BIN_NUM label_ident TEXT_STRING_filesystem ident_or_empty
- opt_constraint constraint opt_ident
+ opt_constraint constraint opt_ident opt_if_not_exists_ident
%type <lex_str_ptr>
opt_table_alias
%type <table>
table_ident table_ident_nodb references xid
- table_ident_opt_wild
+ table_ident_opt_wild create_like
%type <simple_string>
remember_name remember_end opt_db text_or_password
%type <string>
- text_string opt_gconcat_separator
+ text_string hex_or_bin_String opt_gconcat_separator
%type <lex_type> field_def
%type <num>
type type_with_opt_collate int_type real_type order_dir lock_option
- udf_type if_exists opt_local opt_table_options table_options
- table_option opt_if_not_exists opt_no_write_to_binlog
+ udf_type opt_if_exists opt_local opt_table_options table_options
+ table_option opt_if_not_exists create_or_replace opt_no_write_to_binlog
opt_temporary all_or_any opt_distinct
opt_ignore_leaves fulltext_options spatial_type union_option
- start_transaction_opts
- union_opt select_derived_init option_type2
+ union_opt select_derived_init transaction_access_mode_types
opt_natural_language_mode opt_query_expansion
opt_ev_status opt_ev_on_completion ev_on_completion opt_ev_comment
ev_alter_on_schedule_completion opt_ev_rename_to opt_ev_sql_stmt
optional_flush_tables_arguments opt_dyncol_type dyncol_type
opt_time_precision kill_type kill_option int_num
+ opt_default_time_precision
+
+/*
+ Bit field of MYSQL_START_TRANS_OPT_* flags.
+*/
+%type <num> opt_start_transaction_option_list
+%type <num> start_transaction_option_list
+%type <num> start_transaction_option
%type <m_yes_no_unk>
opt_chain opt_release
@@ -1497,6 +1702,10 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%type <ulong_num>
ulong_num real_ulong_num merge_insert_types
+ ws_nweights
+ ws_level_flag_desc ws_level_flag_reverse ws_level_flags
+ opt_ws_levels ws_level_list ws_level_list_item ws_level_number
+ ws_level_range ws_level_list_or_range
%type <ulonglong_number>
ulonglong_num real_ulonglong_num size_number
@@ -1507,7 +1716,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
replace_lock_option opt_low_priority insert_lock_option load_data_lock
%type <item>
- literal text_literal insert_ident order_ident
+ literal text_literal insert_ident order_ident temporal_literal
simple_ident expr opt_expr opt_else sum_expr in_sum_expr
variable variable_aux bool_pri
predicate bit_expr
@@ -1524,6 +1733,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
function_call_generic
function_call_conflict kill_expr
signal_allowed_expr
+ simple_target_specification
+ condition_number
%type <item_num>
NUM_literal
@@ -1542,7 +1753,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
btree_or_rtree
%type <string_list>
- using_list
+ using_list opt_use_partition use_partition
%type <key_part>
key_part
@@ -1570,7 +1781,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%type <symbol> keyword keyword_sp
-%type <lex_user> user grant_user
+%type <lex_user> user grant_user grant_role user_or_role current_role
+ admin_option_for_role user_maybe_role
%type <charset>
opt_collate
@@ -1601,7 +1813,12 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
show describe load alter optimize keycache preload flush
reset purge begin commit rollback savepoint release
slave master_def master_defs master_file_def slave_until_opts
- repair analyze check start checksum
+ repair analyze opt_with_admin opt_with_admin_option
+ analyze_table_list analyze_table_elem_spec
+ opt_persistent_stat_clause persistent_stat_spec
+ persistent_column_stat_spec persistent_index_stat_spec
+ table_column_list table_index_list table_index_name
+ check start checksum
field_list field_list_item field_spec kill column_def key_def
keycache_list keycache_list_or_parts assign_to_keycache
assign_to_keycache_parts
@@ -1616,14 +1833,15 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
ref_list opt_match_clause opt_on_update_delete use
opt_delete_options opt_delete_option varchar nchar nvarchar
opt_outer table_list table_name table_alias_ref_list table_alias_ref
- opt_option opt_place
+ opt_place
opt_attribute opt_attribute_list attribute column_list column_list_id
opt_column_list grant_privileges grant_ident grant_list grant_option
- object_privilege object_privilege_list user_list rename_list
- clear_privileges flush_options flush_option
- opt_with_read_lock flush_options_list
+ object_privilege object_privilege_list user_list user_and_role_list
+ rename_list
+ clear_privileges flush_options flush_option table_or_tables
+ opt_flush_lock flush_lock flush_options_list
equal optional_braces
- opt_mi_check_type opt_to mi_check_types normal_join
+ opt_mi_check_type opt_to mi_check_types
table_to_table_list table_to_table opt_table_list opt_as
handler_rkey_function handler_read_or_scan
single_multi table_wild_list table_wild_one opt_wild
@@ -1631,12 +1849,11 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
precision subselect_start opt_and charset
subselect_end select_var_list select_var_list_init help
field_length opt_field_length
- opt_extended_describe
+ opt_extended_describe shutdown
prepare prepare_src execute deallocate
statement sp_suid
sp_c_chistics sp_a_chistics sp_chistic sp_c_chistic xa
opt_field_or_var_spec fields_or_vars opt_load_data_set_spec
- view_replace_or_algorithm view_replace
view_algorithm view_or_trigger_or_sp_or_event
definer_tail no_definer_tail
view_suid view_tail view_list_opt view_list view_select
@@ -1649,9 +1866,10 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
key_using_alg
part_column_list
server_def server_options_list server_option
- definer_opt no_definer definer
+ definer_opt no_definer definer get_diagnostics
parse_vcol_expr vcol_opt_specifier vcol_opt_attribute
vcol_opt_attribute_list vcol_attribute
+ explainable_command
END_OF_INPUT
%type <NONE> call sp_proc_stmts sp_proc_stmts1 sp_proc_stmt
@@ -1665,17 +1883,26 @@ END_OF_INPUT
%type <NONE> case_stmt_specification simple_case_stmt searched_case_stmt
%type <num> sp_decl_idents sp_opt_inout sp_handler_type sp_hcond_list
-%type <spcondtype> sp_cond sp_hcond sqlstate signal_value opt_signal_value
+%type <spcondvalue> sp_cond sp_hcond sqlstate signal_value opt_signal_value
%type <spblock> sp_decls sp_decl
%type <lex> sp_cursor_stmt
%type <spname> sp_name
%type <index_hint> index_hint_type
-%type <num> index_hint_clause
+%type <num> index_hint_clause normal_join inner_join
%type <filetype> data_or_xml
%type <NONE> signal_stmt resignal_stmt
%type <diag_condition_item_name> signal_condition_information_item_name
+%type <diag_area> which_area;
+%type <diag_info> diagnostics_information;
+%type <stmt_info_item> statement_information_item;
+%type <stmt_info_item_name> statement_information_item_name;
+%type <stmt_info_list> statement_information;
+%type <cond_info_item> condition_information_item;
+%type <cond_info_item_name> condition_information_item_name;
+%type <cond_info_list> condition_information;
+
%type <NONE>
'-' '+' '*' '/' '%' '(' ')'
',' '!' '{' '}' '&' '|' AND_SYM OR_SYM OR_OR_SYM BETWEEN_SYM CASE_SYM
@@ -1683,8 +1910,11 @@ END_OF_INPUT
%type <is_not_empty> opt_union_order_or_limit
+%type <NONE> ROLE_SYM
+
%%
+
/*
Indentation of grammar rules:
@@ -1778,6 +2008,7 @@ statement:
| drop
| execute
| flush
+ | get_diagnostics
| grant
| handler
| help
@@ -1807,6 +2038,7 @@ statement:
| set
| signal_stmt
| show
+ | shutdown
| slave
| start
| truncate
@@ -1908,7 +2140,7 @@ help:
/* change master */
change:
- CHANGE MASTER_SYM TO_SYM
+ CHANGE MASTER_SYM optional_connection_name TO_SYM
{
Lex->sql_command = SQLCOM_CHANGE_MASTER;
}
@@ -1972,6 +2204,14 @@ master_def:
Lex->mi.ssl_verify_server_cert= $3 ?
LEX_MASTER_INFO::LEX_MI_ENABLE : LEX_MASTER_INFO::LEX_MI_DISABLE;
}
+ | MASTER_SSL_CRL_SYM EQ TEXT_STRING_sys
+ {
+ Lex->mi.ssl_crl= $3.str;
+ }
+ | MASTER_SSL_CRLPATH_SYM EQ TEXT_STRING_sys
+ {
+ Lex->mi.ssl_crlpath= $3.str;
+ }
| MASTER_HEARTBEAT_PERIOD_SYM EQ NUM_literal
{
@@ -1987,7 +2227,7 @@ master_def:
}
if (Lex->mi.heartbeat_period > slave_net_timeout)
{
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_SLAVE_HEARTBEAT_VALUE_OUT_OF_RANGE_MAX,
ER(ER_SLAVE_HEARTBEAT_VALUE_OUT_OF_RANGE_MAX));
}
@@ -1995,7 +2235,7 @@ master_def:
{
if (Lex->mi.heartbeat_period != 0.0)
{
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_SLAVE_HEARTBEAT_VALUE_OUT_OF_RANGE_MIN,
ER(ER_SLAVE_HEARTBEAT_VALUE_OUT_OF_RANGE_MIN));
Lex->mi.heartbeat_period= 0.0;
@@ -2043,7 +2283,7 @@ master_file_def:
from 0" (4 in fact), unspecified means "don't change the position
(keep the preceding value)").
*/
- Lex->mi.pos = max(BIN_LOG_HEADER_SIZE, Lex->mi.pos);
+ Lex->mi.pos= MY_MAX(BIN_LOG_HEADER_SIZE, Lex->mi.pos);
}
| RELAY_LOG_FILE_SYM EQ TEXT_STRING_sys
{
@@ -2053,38 +2293,93 @@ master_file_def:
{
Lex->mi.relay_log_pos = $3;
/* Adjust if < BIN_LOG_HEADER_SIZE (same comment as Lex->mi.pos) */
- Lex->mi.relay_log_pos = max(BIN_LOG_HEADER_SIZE, Lex->mi.relay_log_pos);
+ Lex->mi.relay_log_pos= MY_MAX(BIN_LOG_HEADER_SIZE, Lex->mi.relay_log_pos);
+ }
+ | MASTER_USE_GTID_SYM EQ CURRENT_POS_SYM
+ {
+ if (Lex->mi.use_gtid_opt != LEX_MASTER_INFO::LEX_GTID_UNCHANGED)
+ {
+ my_error(ER_DUP_ARGUMENT, MYF(0), "MASTER_use_gtid");
+ MYSQL_YYABORT;
+ }
+ Lex->mi.use_gtid_opt= LEX_MASTER_INFO::LEX_GTID_CURRENT_POS;
}
;
+ | MASTER_USE_GTID_SYM EQ SLAVE_POS_SYM
+ {
+ if (Lex->mi.use_gtid_opt != LEX_MASTER_INFO::LEX_GTID_UNCHANGED)
+ {
+ my_error(ER_DUP_ARGUMENT, MYF(0), "MASTER_use_gtid");
+ MYSQL_YYABORT;
+ }
+ Lex->mi.use_gtid_opt= LEX_MASTER_INFO::LEX_GTID_SLAVE_POS;
+ }
+ ;
+ | MASTER_USE_GTID_SYM EQ NO_SYM
+ {
+ if (Lex->mi.use_gtid_opt != LEX_MASTER_INFO::LEX_GTID_UNCHANGED)
+ {
+ my_error(ER_DUP_ARGUMENT, MYF(0), "MASTER_use_gtid");
+ MYSQL_YYABORT;
+ }
+ Lex->mi.use_gtid_opt= LEX_MASTER_INFO::LEX_GTID_NO;
+ }
+ ;
+
+optional_connection_name:
+ /* empty */
+ {
+ LEX *lex= thd->lex;
+ lex->mi.connection_name= thd->variables.default_master_connection;
+ }
+ | connection_name;
+ ;
+
+connection_name:
+ TEXT_STRING_sys
+ {
+ Lex->mi.connection_name= $1;
+#ifdef HAVE_REPLICATION
+ if (check_master_connection_name(&$1))
+ {
+ my_error(ER_WRONG_ARGUMENTS, MYF(0), "MASTER_CONNECTION_NAME");
+ MYSQL_YYABORT;
+ }
+#endif
+ }
/* create a table */
create:
- CREATE opt_table_options TABLE_SYM opt_if_not_exists table_ident
+ create_or_replace opt_table_options TABLE_SYM opt_if_not_exists table_ident
{
LEX *lex= thd->lex;
lex->sql_command= SQLCOM_CREATE_TABLE;
+ if ($1 && $4)
+ {
+ my_error(ER_WRONG_USAGE, MYF(0), "OR REPLACE", "IF NOT EXISTS");
+ MYSQL_YYABORT;
+ }
if (!lex->select_lex.add_table_to_list(thd, $5, NULL,
TL_OPTION_UPDATING,
TL_WRITE, MDL_EXCLUSIVE))
MYSQL_YYABORT;
- /*
- For CREATE TABLE, an non-existing table is not an error.
- Instruct open_tables() to just take an MDL lock if the
- table does not exist.
- */
- lex->query_tables->open_strategy= TABLE_LIST::OPEN_IF_EXISTS;
lex->alter_info.reset();
lex->col_list.empty();
lex->change=NullS;
bzero((char*) &lex->create_info,sizeof(lex->create_info));
- lex->create_info.options=$2 | $4;
+ /*
+ For CREATE TABLE we should not open the table even if it exists.
+ If the table exists, we should either not create it or replace it
+ */
+ lex->query_tables->open_strategy= TABLE_LIST::OPEN_STUB;
+ lex->create_info.options= ($1 | $2 | $4);
lex->create_info.default_table_charset= NULL;
lex->name.str= 0;
lex->name.length= 0;
lex->create_last_non_select_table= lex->last_table();
}
- create2
+ create_body
{
LEX *lex= thd->lex;
lex->current_select= &lex->select_lex;
@@ -2092,7 +2387,7 @@ create:
!lex->create_info.db_type)
{
lex->create_info.db_type= ha_default_handlerton(thd);
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_WARN_USING_OTHER_HANDLER,
ER(ER_WARN_USING_OTHER_HANDLER),
hton_name(lex->create_info.db_type)->str,
@@ -2100,38 +2395,41 @@ create:
}
create_table_set_open_action_and_adjust_tables(lex);
}
- | CREATE opt_unique INDEX_SYM ident key_alg ON table_ident
+ | CREATE opt_unique INDEX_SYM opt_if_not_exists ident key_alg ON table_ident
{
- if (add_create_index_prepare(Lex, $7))
+ if (add_create_index_prepare(Lex, $8))
MYSQL_YYABORT;
}
'(' key_list ')' normal_key_options
{
- if (add_create_index(Lex, $2, $4))
+ if (add_create_index(Lex, $2, $5))
MYSQL_YYABORT;
}
- | CREATE fulltext INDEX_SYM ident init_key_options ON
+ opt_index_lock_algorithm { }
+ | CREATE fulltext INDEX_SYM opt_if_not_exists ident init_key_options ON
table_ident
{
- if (add_create_index_prepare(Lex, $7))
+ if (add_create_index_prepare(Lex, $8))
MYSQL_YYABORT;
}
'(' key_list ')' fulltext_key_options
{
- if (add_create_index(Lex, $2, $4))
+ if (add_create_index(Lex, $2, $5))
MYSQL_YYABORT;
}
- | CREATE spatial INDEX_SYM ident init_key_options ON
+ opt_index_lock_algorithm { }
+ | CREATE spatial INDEX_SYM opt_if_not_exists ident init_key_options ON
table_ident
{
- if (add_create_index_prepare(Lex, $7))
+ if (add_create_index_prepare(Lex, $8))
MYSQL_YYABORT;
}
'(' key_list ')' spatial_key_options
{
- if (add_create_index(Lex, $2, $4))
+ if (add_create_index(Lex, $2, $5))
MYSQL_YYABORT;
}
+ opt_index_lock_algorithm { }
| CREATE DATABASE opt_if_not_exists ident
{
Lex->create_info.default_table_charset= NULL;
@@ -2144,18 +2442,30 @@ create:
lex->name= $4;
lex->create_info.options=$3;
}
- | CREATE
+ | create_or_replace
{
- Lex->create_view_mode= VIEW_CREATE_NEW;
+ Lex->create_view_mode= ($1 == 0 ? VIEW_CREATE_NEW :
+ VIEW_CREATE_OR_REPLACE);
Lex->create_view_algorithm= DTYPE_ALGORITHM_UNDEFINED;
Lex->create_view_suid= TRUE;
}
view_or_trigger_or_sp_or_event
- {}
+ {
+ if ($1 && Lex->sql_command != SQLCOM_CREATE_VIEW)
+ {
+ my_error(ER_WRONG_USAGE, MYF(0), "OR REPLACE",
+ "TRIGGERS / SP / EVENT");
+ MYSQL_YYABORT;
+ }
+ }
| CREATE USER clear_privileges grant_list
{
Lex->sql_command = SQLCOM_CREATE_USER;
}
+ | CREATE ROLE_SYM clear_privileges role_list opt_with_admin
+ {
+ Lex->sql_command = SQLCOM_CREATE_ROLE;
+ }
| CREATE LOGFILE_SYM GROUP_SYM logfile_group_info
{
Lex->alter_tablespace_info->ts_cmd_type= CREATE_LOGFILE_GROUP;
@@ -2576,14 +2886,16 @@ sp_fdparam:
LEX *lex= Lex;
sp_pcontext *spc= lex->spcont;
- if (spc->find_variable(&$1, TRUE))
+ if (spc->find_variable($1, TRUE))
{
my_error(ER_SP_DUP_PARAM, MYF(0), $1.str);
MYSQL_YYABORT;
}
- sp_variable_t *spvar= spc->push_variable(&$1,
- (enum enum_field_types)$3,
- sp_param_in);
+
+ sp_variable *spvar= spc->add_variable(thd,
+ $1,
+ (enum enum_field_types) $3,
+ sp_variable::MODE_IN);
if (lex->sphead->fill_field_definition(thd, lex,
(enum enum_field_types) $3,
@@ -2613,14 +2925,15 @@ sp_pdparam:
LEX *lex= Lex;
sp_pcontext *spc= lex->spcont;
- if (spc->find_variable(&$3, TRUE))
+ if (spc->find_variable($3, TRUE))
{
my_error(ER_SP_DUP_PARAM, MYF(0), $3.str);
MYSQL_YYABORT;
}
- sp_variable_t *spvar= spc->push_variable(&$3,
- (enum enum_field_types)$4,
- (sp_param_mode_t)$1);
+ sp_variable *spvar= spc->add_variable(thd,
+ $3,
+ (enum enum_field_types) $4,
+ (sp_variable::enum_mode) $1);
if (lex->sphead->fill_field_definition(thd, lex,
(enum enum_field_types) $4,
@@ -2634,10 +2947,10 @@ sp_pdparam:
;
sp_opt_inout:
- /* Empty */ { $$= sp_param_in; }
- | IN_SYM { $$= sp_param_in; }
- | OUT_SYM { $$= sp_param_out; }
- | INOUT_SYM { $$= sp_param_inout; }
+ /* Empty */ { $$= sp_variable::MODE_IN; }
+ | IN_SYM { $$= sp_variable::MODE_IN; }
+ | OUT_SYM { $$= sp_variable::MODE_OUT; }
+ | INOUT_SYM { $$= sp_variable::MODE_INOUT; }
;
sp_proc_stmts:
@@ -2708,13 +3021,13 @@ sp_decl:
for (uint i = num_vars-$2 ; i < num_vars ; i++)
{
uint var_idx= pctx->var_context2runtime(i);
- sp_variable_t *spvar= pctx->find_variable(var_idx);
+ sp_variable *spvar= pctx->find_variable(var_idx);
if (!spvar)
MYSQL_YYABORT;
spvar->type= var_type;
- spvar->dflt= dflt_value_item;
+ spvar->default_value= dflt_value_item;
if (lex->sphead->fill_field_definition(thd, lex, var_type,
&spvar->field_def))
@@ -2750,12 +3063,12 @@ sp_decl:
LEX *lex= Lex;
sp_pcontext *spc= lex->spcont;
- if (spc->find_cond(&$2, TRUE))
+ if (spc->find_condition($2, TRUE))
{
my_error(ER_SP_DUP_COND, MYF(0), $2.str);
MYSQL_YYABORT;
}
- if(thd->lex->spcont->push_cond(&$2, $5))
+ if(spc->add_condition(thd, $2, $5))
MYSQL_YYABORT;
$$.vars= $$.hndlrs= $$.curs= 0;
$$.conds= 1;
@@ -2765,21 +3078,25 @@ sp_decl:
LEX *lex= Lex;
sp_head *sp= lex->sphead;
- lex->spcont= lex->spcont->push_context(LABEL_HANDLER_SCOPE);
+ sp_handler *h= lex->spcont->add_handler(thd,
+ (sp_handler::enum_type) $2);
+
+ lex->spcont= lex->spcont->push_context(thd,
+ sp_pcontext::HANDLER_SCOPE);
sp_pcontext *ctx= lex->spcont;
sp_instr_hpush_jump *i=
- new sp_instr_hpush_jump(sp->instructions(), ctx, $2,
- ctx->current_var_count());
+ new sp_instr_hpush_jump(sp->instructions(), ctx, h);
+
if (i == NULL || sp->add_instr(i))
MYSQL_YYABORT;
/* For continue handlers, mark end of handler scope. */
- if ($2 == SP_HANDLER_CONTINUE &&
+ if ($2 == sp_handler::CONTINUE &&
sp->push_backpatch(i, ctx->last_label()))
MYSQL_YYABORT;
- if (sp->push_backpatch(i, ctx->push_label(empty_c_string, 0)))
+ if (sp->push_backpatch(i, ctx->push_label(thd, empty_lex_str, 0)))
MYSQL_YYABORT;
}
sp_hcond_list sp_proc_stmt
@@ -2787,20 +3104,19 @@ sp_decl:
LEX *lex= Lex;
sp_head *sp= lex->sphead;
sp_pcontext *ctx= lex->spcont;
- sp_label_t *hlab= lex->spcont->pop_label(); /* After this hdlr */
+ sp_label *hlab= lex->spcont->pop_label(); /* After this hdlr */
sp_instr_hreturn *i;
- if ($2 == SP_HANDLER_CONTINUE)
+ if ($2 == sp_handler::CONTINUE)
{
- i= new sp_instr_hreturn(sp->instructions(), ctx,
- ctx->current_var_count());
+ i= new sp_instr_hreturn(sp->instructions(), ctx);
if (i == NULL ||
sp->add_instr(i))
MYSQL_YYABORT;
}
else
{ /* EXIT or UNDO handler, just jump to the end of the block */
- i= new sp_instr_hreturn(sp->instructions(), ctx, 0);
+ i= new sp_instr_hreturn(sp->instructions(), ctx);
if (i == NULL ||
sp->add_instr(i) ||
sp->push_backpatch(i, lex->spcont->last_label())) /* Block end */
@@ -2811,8 +3127,7 @@ sp_decl:
lex->spcont= ctx->pop_context();
$$.vars= $$.conds= $$.curs= 0;
- $$.hndlrs= $6;
- lex->spcont->add_handlers($6);
+ $$.hndlrs= 1;
}
| DECLARE_SYM ident CURSOR_SYM FOR_SYM sp_cursor_stmt
{
@@ -2822,7 +3137,7 @@ sp_decl:
uint offp;
sp_instr_cpush *i;
- if (ctx->find_cursor(&$2, &offp, TRUE))
+ if (ctx->find_cursor($2, &offp, TRUE))
{
my_error(ER_SP_DUP_CURS, MYF(0), $2.str);
delete $5;
@@ -2832,7 +3147,7 @@ sp_decl:
ctx->current_cursor_count());
if (i == NULL ||
sp->add_instr(i) ||
- ctx->push_cursor(&$2))
+ ctx->add_cursor($2))
MYSQL_YYABORT;
$$.vars= $$.conds= $$.hndlrs= 0;
$$.curs= 1;
@@ -2863,9 +3178,9 @@ sp_cursor_stmt:
;
sp_handler_type:
- EXIT_SYM { $$= SP_HANDLER_EXIT; }
- | CONTINUE_SYM { $$= SP_HANDLER_CONTINUE; }
- /*| UNDO_SYM { QQ No yet } */
+ EXIT_SYM { $$= sp_handler::EXIT; }
+ | CONTINUE_SYM { $$= sp_handler::CONTINUE; }
+ /*| UNDO_SYM { QQ No yet } */
;
sp_hcond_list:
@@ -2882,7 +3197,7 @@ sp_hcond_element:
sp_head *sp= lex->sphead;
sp_pcontext *ctx= lex->spcont->parent_context();
- if (ctx->find_handler($1))
+ if (ctx->check_duplicate_handler($1))
{
my_message(ER_SP_DUP_HANDLER, ER(ER_SP_DUP_HANDLER), MYF(0));
MYSQL_YYABORT;
@@ -2893,7 +3208,6 @@ sp_hcond_element:
(sp_instr_hpush_jump *)sp->last_instruction();
i->add_condition($1);
- ctx->push_handler($1);
}
}
;
@@ -2906,11 +3220,9 @@ sp_cond:
my_error(ER_WRONG_VALUE, MYF(0), "CONDITION", "0");
MYSQL_YYABORT;
}
- $$= (sp_cond_type_t *) thd->alloc(sizeof(sp_cond_type_t));
+ $$= new (thd->mem_root) sp_condition_value($1);
if ($$ == NULL)
MYSQL_YYABORT;
- $$->type= sp_cond_type_t::number;
- $$->mysqlerr= $1;
}
| sqlstate
;
@@ -2918,17 +3230,22 @@ sp_cond:
sqlstate:
SQLSTATE_SYM opt_value TEXT_STRING_literal
{ /* SQLSTATE */
- if (!sp_cond_check(&$3))
+
+ /*
+ An error is triggered:
+ - if the specified string is not a valid SQLSTATE,
+ - or if it represents the completion condition -- it is not
+ allowed to SIGNAL, or declare a handler for the completion
+ condition.
+ */
+ if (!is_sqlstate_valid(&$3) || is_sqlstate_completion($3.str))
{
my_error(ER_SP_BAD_SQLSTATE, MYF(0), $3.str);
MYSQL_YYABORT;
}
- $$= (sp_cond_type_t *) thd->alloc(sizeof(sp_cond_type_t));
+ $$= new (thd->mem_root) sp_condition_value($3.str);
if ($$ == NULL)
MYSQL_YYABORT;
- $$->type= sp_cond_type_t::state;
- memcpy($$->sqlstate, $3.str, SQLSTATE_LENGTH);
- $$->sqlstate[SQLSTATE_LENGTH]= '\0';
}
;
@@ -2944,7 +3261,7 @@ sp_hcond:
}
| ident /* CONDITION name */
{
- $$= Lex->spcont->find_cond(&$1);
+ $$= Lex->spcont->find_condition($1, false);
if ($$ == NULL)
{
my_error(ER_SP_COND_MISMATCH, MYF(0), $1.str);
@@ -2953,24 +3270,21 @@ sp_hcond:
}
| SQLWARNING_SYM /* SQLSTATEs 01??? */
{
- $$= (sp_cond_type_t *) thd->alloc(sizeof(sp_cond_type_t));
+ $$= new (thd->mem_root) sp_condition_value(sp_condition_value::WARNING);
if ($$ == NULL)
MYSQL_YYABORT;
- $$->type= sp_cond_type_t::warning;
}
| not FOUND_SYM /* SQLSTATEs 02??? */
{
- $$= (sp_cond_type_t *) thd->alloc(sizeof(sp_cond_type_t));
+ $$= new (thd->mem_root) sp_condition_value(sp_condition_value::NOT_FOUND);
if ($$ == NULL)
MYSQL_YYABORT;
- $$->type= sp_cond_type_t::notfound;
}
| SQLEXCEPTION_SYM /* All other SQLSTATEs */
{
- $$= (sp_cond_type_t *) thd->alloc(sizeof(sp_cond_type_t));
+ $$= new (thd->mem_root) sp_condition_value(sp_condition_value::EXCEPTION);
if ($$ == NULL)
MYSQL_YYABORT;
- $$->type= sp_cond_type_t::exception;
}
;
@@ -2981,9 +3295,9 @@ signal_stmt:
Yacc_state *state= & thd->m_parser_state->m_yacc;
lex->sql_command= SQLCOM_SIGNAL;
- lex->m_stmt= new (thd->mem_root) Signal_statement(lex, $2,
- state->m_set_signal_info);
- if (lex->m_stmt == NULL)
+ lex->m_sql_cmd=
+ new (thd->mem_root) Sql_cmd_signal($2, state->m_set_signal_info);
+ if (lex->m_sql_cmd == NULL)
MYSQL_YYABORT;
}
;
@@ -2992,20 +3306,20 @@ signal_value:
ident
{
LEX *lex= Lex;
- sp_cond_type_t *cond;
+ sp_condition_value *cond;
if (lex->spcont == NULL)
{
/* SIGNAL foo cannot be used outside of stored programs */
my_error(ER_SP_COND_MISMATCH, MYF(0), $1.str);
MYSQL_YYABORT;
}
- cond= lex->spcont->find_cond(&$1);
+ cond= lex->spcont->find_condition($1, false);
if (cond == NULL)
{
my_error(ER_SP_COND_MISMATCH, MYF(0), $1.str);
MYSQL_YYABORT;
}
- if (cond->type != sp_cond_type_t::state)
+ if (cond->type != sp_condition_value::SQLSTATE)
{
my_error(ER_SIGNAL_BAD_CONDITION_TYPE, MYF(0));
MYSQL_YYABORT;
@@ -3119,13 +3433,160 @@ resignal_stmt:
Yacc_state *state= & thd->m_parser_state->m_yacc;
lex->sql_command= SQLCOM_RESIGNAL;
- lex->m_stmt= new (thd->mem_root) Resignal_statement(lex, $2,
- state->m_set_signal_info);
- if (lex->m_stmt == NULL)
+ lex->m_sql_cmd=
+ new (thd->mem_root) Sql_cmd_resignal($2,
+ state->m_set_signal_info);
+ if (lex->m_sql_cmd == NULL)
MYSQL_YYABORT;
}
;
+get_diagnostics:
+ GET_SYM which_area DIAGNOSTICS_SYM diagnostics_information
+ {
+ Diagnostics_information *info= $4;
+
+ info->set_which_da($2);
+
+ Lex->sql_command= SQLCOM_GET_DIAGNOSTICS;
+ Lex->m_sql_cmd= new (thd->mem_root) Sql_cmd_get_diagnostics(info);
+
+ if (Lex->m_sql_cmd == NULL)
+ MYSQL_YYABORT;
+ }
+ ;
+
+which_area:
+ /* If <which area> is not specified, then CURRENT is implicit. */
+ { $$= Diagnostics_information::CURRENT_AREA; }
+ | CURRENT_SYM
+ { $$= Diagnostics_information::CURRENT_AREA; }
+ ;
+
+diagnostics_information:
+ statement_information
+ {
+ $$= new (thd->mem_root) Statement_information($1);
+ if ($$ == NULL)
+ MYSQL_YYABORT;
+ }
+ | CONDITION_SYM condition_number condition_information
+ {
+ $$= new (thd->mem_root) Condition_information($2, $3);
+ if ($$ == NULL)
+ MYSQL_YYABORT;
+ }
+ ;
+
+statement_information:
+ statement_information_item
+ {
+ $$= new (thd->mem_root) List<Statement_information_item>;
+ if ($$ == NULL || $$->push_back($1))
+ MYSQL_YYABORT;
+ }
+ | statement_information ',' statement_information_item
+ {
+ if ($1->push_back($3))
+ MYSQL_YYABORT;
+ $$= $1;
+ }
+ ;
+
+statement_information_item:
+ simple_target_specification EQ statement_information_item_name
+ {
+ $$= new (thd->mem_root) Statement_information_item($3, $1);
+ if ($$ == NULL)
+ MYSQL_YYABORT;
+ }
+
+simple_target_specification:
+ ident
+ {
+ Lex_input_stream *lip= &thd->m_parser_state->m_lip;
+ $$= create_item_for_sp_var(thd, $1, NULL,
+ lip->get_tok_start(), lip->get_ptr());
+
+ if ($$ == NULL)
+ MYSQL_YYABORT;
+ }
+ | '@' ident_or_text
+ {
+ $$= new (thd->mem_root) Item_func_get_user_var($2);
+ if ($$ == NULL)
+ MYSQL_YYABORT;
+ }
+ ;
+
+statement_information_item_name:
+ NUMBER_SYM
+ { $$= Statement_information_item::NUMBER; }
+ | ROW_COUNT_SYM
+ { $$= Statement_information_item::ROW_COUNT; }
+ ;
+
+/*
+ Only a limited subset of <expr> are allowed in GET DIAGNOSTICS
+ <condition number>, same subset as for SIGNAL/RESIGNAL.
+*/
+condition_number:
+ signal_allowed_expr
+ { $$= $1; }
+ ;
+
+condition_information:
+ condition_information_item
+ {
+ $$= new (thd->mem_root) List<Condition_information_item>;
+ if ($$ == NULL || $$->push_back($1))
+ MYSQL_YYABORT;
+ }
+ | condition_information ',' condition_information_item
+ {
+ if ($1->push_back($3))
+ MYSQL_YYABORT;
+ $$= $1;
+ }
+ ;
+
+condition_information_item:
+ simple_target_specification EQ condition_information_item_name
+ {
+ $$= new (thd->mem_root) Condition_information_item($3, $1);
+ if ($$ == NULL)
+ MYSQL_YYABORT;
+ }
+
+condition_information_item_name:
+ CLASS_ORIGIN_SYM
+ { $$= Condition_information_item::CLASS_ORIGIN; }
+ | SUBCLASS_ORIGIN_SYM
+ { $$= Condition_information_item::SUBCLASS_ORIGIN; }
+ | CONSTRAINT_CATALOG_SYM
+ { $$= Condition_information_item::CONSTRAINT_CATALOG; }
+ | CONSTRAINT_SCHEMA_SYM
+ { $$= Condition_information_item::CONSTRAINT_SCHEMA; }
+ | CONSTRAINT_NAME_SYM
+ { $$= Condition_information_item::CONSTRAINT_NAME; }
+ | CATALOG_NAME_SYM
+ { $$= Condition_information_item::CATALOG_NAME; }
+ | SCHEMA_NAME_SYM
+ { $$= Condition_information_item::SCHEMA_NAME; }
+ | TABLE_NAME_SYM
+ { $$= Condition_information_item::TABLE_NAME; }
+ | COLUMN_NAME_SYM
+ { $$= Condition_information_item::COLUMN_NAME; }
+ | CURSOR_NAME_SYM
+ { $$= Condition_information_item::CURSOR_NAME; }
+ | MESSAGE_TEXT_SYM
+ { $$= Condition_information_item::MESSAGE_TEXT; }
+ | MYSQL_ERRNO_SYM
+ { $$= Condition_information_item::MYSQL_ERRNO; }
+ | RETURNED_SQLSTATE_SYM
+ { $$= Condition_information_item::RETURNED_SQLSTATE; }
+ ;
+
sp_decl_idents:
ident
{
@@ -3134,12 +3595,15 @@ sp_decl_idents:
LEX *lex= Lex;
sp_pcontext *spc= lex->spcont;
- if (spc->find_variable(&$1, TRUE))
+ if (spc->find_variable($1, TRUE))
{
my_error(ER_SP_DUP_VAR, MYF(0), $1.str);
MYSQL_YYABORT;
}
- spc->push_variable(&$1, (enum_field_types)0, sp_param_in);
+ spc->add_variable(thd,
+ $1,
+ MYSQL_TYPE_DECIMAL,
+ sp_variable::MODE_IN);
$$= 1;
}
| sp_decl_idents ',' ident
@@ -3149,12 +3613,15 @@ sp_decl_idents:
LEX *lex= Lex;
sp_pcontext *spc= lex->spcont;
- if (spc->find_variable(&$3, TRUE))
+ if (spc->find_variable($3, TRUE))
{
my_error(ER_SP_DUP_VAR, MYF(0), $3.str);
MYSQL_YYABORT;
}
- spc->push_variable(&$3, (enum_field_types)0, sp_param_in);
+ spc->add_variable(thd,
+ $3,
+ MYSQL_TYPE_DECIMAL,
+ sp_variable::MODE_IN);
$$= $1 + 1;
}
;
@@ -3229,7 +3696,7 @@ sp_proc_stmt_statement:
if (yychar == YYEMPTY)
i->m_query.length= lip->get_ptr() - sp->m_tmp_query;
else
- i->m_query.length= lip->get_tok_end() - sp->m_tmp_query;
+ i->m_query.length= lip->get_tok_start() - sp->m_tmp_query;;
if (!(i->m_query.str= strmake_root(thd->mem_root,
sp->m_tmp_query,
i->m_query.length)) ||
@@ -3274,7 +3741,8 @@ sp_proc_stmt_unlabeled:
{ /* Unlabeled controls get a secret label. */
LEX *lex= Lex;
- lex->spcont->push_label((char *)"", lex->sphead->instructions());
+ lex->spcont->push_label(thd, empty_lex_str,
+ lex->sphead->instructions());
}
sp_unlabeled_control
{
@@ -3290,7 +3758,7 @@ sp_proc_stmt_leave:
LEX *lex= Lex;
sp_head *sp = lex->sphead;
sp_pcontext *ctx= lex->spcont;
- sp_label_t *lab= ctx->find_label($2.str);
+ sp_label *lab= ctx->find_label($2);
if (! lab)
{
@@ -3310,7 +3778,7 @@ sp_proc_stmt_leave:
there are no hpop/cpop at the jump destination,
so we should include the block context here for cleanup.
*/
- bool exclusive= (lab->type == SP_LAB_BEGIN);
+ bool exclusive= (lab->type == sp_label::BEGIN);
n= ctx->diff_handlers(lab->ctx, exclusive);
if (n)
@@ -3343,9 +3811,9 @@ sp_proc_stmt_iterate:
LEX *lex= Lex;
sp_head *sp= lex->sphead;
sp_pcontext *ctx= lex->spcont;
- sp_label_t *lab= ctx->find_label($2.str);
+ sp_label *lab= ctx->find_label($2);
- if (! lab || lab->type != SP_LAB_ITER)
+ if (! lab || lab->type != sp_label::ITERATION)
{
my_error(ER_SP_LILABEL_MISMATCH, MYF(0), "ITERATE", $2.str);
MYSQL_YYABORT;
@@ -3388,7 +3856,7 @@ sp_proc_stmt_open:
uint offset;
sp_instr_copen *i;
- if (! lex->spcont->find_cursor(&$2, &offset))
+ if (! lex->spcont->find_cursor($2, &offset, false))
{
my_error(ER_SP_CURSOR_MISMATCH, MYF(0), $2.str);
MYSQL_YYABORT;
@@ -3408,7 +3876,7 @@ sp_proc_stmt_fetch:
uint offset;
sp_instr_cfetch *i;
- if (! lex->spcont->find_cursor(&$3, &offset))
+ if (! lex->spcont->find_cursor($3, &offset, false))
{
my_error(ER_SP_CURSOR_MISMATCH, MYF(0), $3.str);
MYSQL_YYABORT;
@@ -3430,7 +3898,7 @@ sp_proc_stmt_close:
uint offset;
sp_instr_cclose *i;
- if (! lex->spcont->find_cursor(&$2, &offset))
+ if (! lex->spcont->find_cursor($2, &offset, false))
{
my_error(ER_SP_CURSOR_MISMATCH, MYF(0), $2.str);
MYSQL_YYABORT;
@@ -3454,9 +3922,9 @@ sp_fetch_list:
LEX *lex= Lex;
sp_head *sp= lex->sphead;
sp_pcontext *spc= lex->spcont;
- sp_variable_t *spv;
+ sp_variable *spv;
- if (!spc || !(spv = spc->find_variable(&$1)))
+ if (!spc || !(spv = spc->find_variable($1, false)))
{
my_error(ER_SP_UNDECLARED_VAR, MYF(0), $1.str);
MYSQL_YYABORT;
@@ -3474,9 +3942,9 @@ sp_fetch_list:
LEX *lex= Lex;
sp_head *sp= lex->sphead;
sp_pcontext *spc= lex->spcont;
- sp_variable_t *spv;
+ sp_variable *spv;
- if (!spc || !(spv = spc->find_variable(&$3)))
+ if (!spc || !(spv = spc->find_variable($3, false)))
{
my_error(ER_SP_UNDECLARED_VAR, MYF(0), $3.str);
MYSQL_YYABORT;
@@ -3502,7 +3970,7 @@ sp_if:
sp_instr_jump_if_not *i = new sp_instr_jump_if_not(ip, ctx,
$2, lex);
if (i == NULL ||
- sp->push_backpatch(i, ctx->push_label((char *)"", 0)) ||
+ sp->push_backpatch(i, ctx->push_label(thd, empty_lex_str, 0)) ||
sp->add_cont_backpatch(i) ||
sp->add_instr(i))
MYSQL_YYABORT;
@@ -3519,7 +3987,7 @@ sp_if:
sp->add_instr(i))
MYSQL_YYABORT;
sp->backpatch(ctx->pop_label());
- sp->push_backpatch(i, ctx->push_label((char *)"", 0));
+ sp->push_backpatch(i, ctx->push_label(thd, empty_lex_str, 0));
}
sp_elseifs
{
@@ -3661,7 +4129,7 @@ sp_labeled_control:
{
LEX *lex= Lex;
sp_pcontext *ctx= lex->spcont;
- sp_label_t *lab= ctx->find_label($1.str);
+ sp_label *lab= ctx->find_label($1);
if (lab)
{
@@ -3670,19 +4138,18 @@ sp_labeled_control:
}
else
{
- lab= lex->spcont->push_label($1.str,
- lex->sphead->instructions());
- lab->type= SP_LAB_ITER;
+ lab= lex->spcont->push_label(thd, $1, lex->sphead->instructions());
+ lab->type= sp_label::ITERATION;
}
}
sp_unlabeled_control sp_opt_label
{
LEX *lex= Lex;
- sp_label_t *lab= lex->spcont->pop_label();
+ sp_label *lab= lex->spcont->pop_label();
if ($5.str)
{
- if (my_strcasecmp(system_charset_info, $5.str, lab->name) != 0)
+ if (my_strcasecmp(system_charset_info, $5.str, lab->name.str) != 0)
{
my_error(ER_SP_LABEL_MISMATCH, MYF(0), $5.str);
MYSQL_YYABORT;
@@ -3702,7 +4169,7 @@ sp_labeled_block:
{
LEX *lex= Lex;
sp_pcontext *ctx= lex->spcont;
- sp_label_t *lab= ctx->find_label($1.str);
+ sp_label *lab= ctx->find_label($1);
if (lab)
{
@@ -3710,18 +4177,17 @@ sp_labeled_block:
MYSQL_YYABORT;
}
- lab= lex->spcont->push_label($1.str,
- lex->sphead->instructions());
- lab->type= SP_LAB_BEGIN;
+ lab= lex->spcont->push_label(thd, $1, lex->sphead->instructions());
+ lab->type= sp_label::BEGIN;
}
sp_block_content sp_opt_label
{
LEX *lex= Lex;
- sp_label_t *lab= lex->spcont->pop_label();
+ sp_label *lab= lex->spcont->pop_label();
if ($5.str)
{
- if (my_strcasecmp(system_charset_info, $5.str, lab->name) != 0)
+ if (my_strcasecmp(system_charset_info, $5.str, lab->name.str) != 0)
{
my_error(ER_SP_LABEL_MISMATCH, MYF(0), $5.str);
MYSQL_YYABORT;
@@ -3734,8 +4200,8 @@ sp_unlabeled_block:
{ /* Unlabeled blocks get a secret label. */
LEX *lex= Lex;
uint ip= lex->sphead->instructions();
- sp_label_t *lab= lex->spcont->push_label((char *)"", ip);
- lab->type= SP_LAB_BEGIN;
+ sp_label *lab= lex->spcont->push_label(thd, empty_lex_str, ip);
+ lab->type= sp_label::BEGIN;
}
sp_block_content
{
@@ -3750,7 +4216,8 @@ sp_block_content:
together. No [[NOT] ATOMIC] yet, and we need to figure out how
make it coexist with the existing BEGIN COMMIT/ROLLBACK. */
LEX *lex= Lex;
- lex->spcont= lex->spcont->push_context(LABEL_DEFAULT_SCOPE);
+ lex->spcont= lex->spcont->push_context(thd,
+ sp_pcontext::REGULAR_SCOPE);
}
sp_decls
sp_proc_stmts
@@ -3786,7 +4253,7 @@ sp_unlabeled_control:
{
LEX *lex= Lex;
uint ip= lex->sphead->instructions();
- sp_label_t *lab= lex->spcont->last_label(); /* Jumping back */
+ sp_label *lab= lex->spcont->last_label(); /* Jumping back */
sp_instr_jump *i = new sp_instr_jump(ip, lex->spcont, lab->ip);
if (i == NULL ||
lex->sphead->add_instr(i))
@@ -3814,7 +4281,7 @@ sp_unlabeled_control:
{
LEX *lex= Lex;
uint ip= lex->sphead->instructions();
- sp_label_t *lab= lex->spcont->last_label(); /* Jumping back */
+ sp_label *lab= lex->spcont->last_label(); /* Jumping back */
sp_instr_jump *i = new sp_instr_jump(ip, lex->spcont, lab->ip);
if (i == NULL ||
lex->sphead->add_instr(i))
@@ -3827,7 +4294,7 @@ sp_unlabeled_control:
{
LEX *lex= Lex;
uint ip= lex->sphead->instructions();
- sp_label_t *lab= lex->spcont->last_label(); /* Jumping back */
+ sp_label *lab= lex->spcont->last_label(); /* Jumping back */
sp_instr_jump_if_not *i = new sp_instr_jump_if_not(ip, lex->spcont,
$5, lab->ip,
lex);
@@ -4250,34 +4717,23 @@ size_number:
End tablespace part
*/
-create2:
- '(' create2a {}
- | opt_create_table_options
- opt_create_partitioning
- create3 {}
- | LIKE table_ident
- {
- TABLE_LIST *src_table;
- LEX *lex= thd->lex;
-
- lex->create_info.options|= HA_LEX_CREATE_TABLE_LIKE;
- src_table= lex->select_lex.add_table_to_list(thd, $2, NULL, 0,
- TL_READ,
- MDL_SHARED_READ);
- if (! src_table)
- MYSQL_YYABORT;
- /* CREATE TABLE ... LIKE is not allowed for views. */
- src_table->required_type= FRMTYPE_TABLE;
- }
- | '(' LIKE table_ident ')'
+create_body:
+ '(' create_field_list ')'
+ { Lex->create_info.option_list= NULL; }
+ opt_create_table_options opt_create_partitioning opt_create_select {}
+ | opt_create_table_options opt_create_partitioning opt_create_select {}
+ /*
+ the following rule is redundant, but there's a shift/reduce
+ conflict that prevents the rule above from parsing a syntax like
+ CREATE TABLE t1 (SELECT 1);
+ */
+ | '(' create_select ')' { Select->set_braces(1);} union_opt {}
+ | create_like
{
- TABLE_LIST *src_table;
- LEX *lex= thd->lex;
- lex->create_info.options|= HA_LEX_CREATE_TABLE_LIKE;
- src_table= lex->select_lex.add_table_to_list(thd, $3, NULL, 0,
- TL_READ,
- MDL_SHARED_READ);
+ Lex->create_info.options|= HA_LEX_CREATE_TABLE_LIKE;
+ TABLE_LIST *src_table= Lex->select_lex.add_table_to_list(thd,
+ $1, NULL, 0, TL_READ, MDL_SHARED_READ);
if (! src_table)
MYSQL_YYABORT;
/* CREATE TABLE ... LIKE is not allowed for views. */
@@ -4285,21 +4741,12 @@ create2:
}
;
-create2a:
- create_field_list ')'
- {
- Lex->create_info.option_list= NULL;
- }
- opt_create_table_options
- opt_create_partitioning
- create3 {}
- | opt_create_partitioning
- create_select ')'
- { Select->set_braces(1);}
- union_opt {}
+create_like:
+ LIKE table_ident { $$= $2; }
+ | '(' LIKE table_ident ')' { $$= $3; }
;
-create3:
+opt_create_select:
/* empty */ {}
| opt_duplicate opt_as create_select
{ Select->set_braces(0);}
@@ -4363,7 +4810,7 @@ partitioning:
}
if (lex->sql_command == SQLCOM_ALTER_TABLE)
{
- lex->alter_info.flags|= ALTER_PARTITION;
+ lex->alter_info.flags|= Alter_info::ALTER_PARTITION;
}
}
partition
@@ -5078,9 +5525,28 @@ table_option:
;
opt_if_not_exists:
- /* empty */ { $$= 0; }
- | IF not EXISTS { $$=HA_LEX_CREATE_IF_NOT_EXISTS; }
- ;
+ /* empty */
+ {
+ Lex->check_exists= FALSE;
+ $$= 0;
+ }
+ | IF not EXISTS
+ {
+ Lex->check_exists= TRUE;
+ $$=HA_LEX_CREATE_IF_NOT_EXISTS;
+ }
+ ;
+
+create_or_replace:
+ CREATE /* empty */
+ {
+ $$= 0;
+ }
+ | CREATE OR_SYM REPLACE
+ {
+ $$= HA_LEX_CREATE_REPLACE;
+ }
+ ;
opt_create_table_options:
/* empty */
@@ -5155,6 +5621,70 @@ create_table_option:
~(HA_OPTION_PACK_KEYS | HA_OPTION_NO_PACK_KEYS);
Lex->create_info.used_fields|= HA_CREATE_USED_PACK_KEYS;
}
+ | STATS_AUTO_RECALC_SYM opt_equal ulong_num
+ {
+ switch($3) {
+ case 0:
+ Lex->create_info.stats_auto_recalc= HA_STATS_AUTO_RECALC_OFF;
+ break;
+ case 1:
+ Lex->create_info.stats_auto_recalc= HA_STATS_AUTO_RECALC_ON;
+ break;
+ default:
+ my_parse_error(ER(ER_SYNTAX_ERROR));
+ MYSQL_YYABORT;
+ }
+ Lex->create_info.used_fields|= HA_CREATE_USED_STATS_AUTO_RECALC;
+ }
+ | STATS_AUTO_RECALC_SYM opt_equal DEFAULT
+ {
+ Lex->create_info.stats_auto_recalc= HA_STATS_AUTO_RECALC_DEFAULT;
+ Lex->create_info.used_fields|= HA_CREATE_USED_STATS_AUTO_RECALC;
+ }
+ | STATS_PERSISTENT_SYM opt_equal ulong_num
+ {
+ switch($3) {
+ case 0:
+ Lex->create_info.table_options|= HA_OPTION_NO_STATS_PERSISTENT;
+ break;
+ case 1:
+ Lex->create_info.table_options|= HA_OPTION_STATS_PERSISTENT;
+ break;
+ default:
+ my_parse_error(ER(ER_SYNTAX_ERROR));
+ MYSQL_YYABORT;
+ }
+ Lex->create_info.used_fields|= HA_CREATE_USED_STATS_PERSISTENT;
+ }
+ | STATS_PERSISTENT_SYM opt_equal DEFAULT
+ {
+ Lex->create_info.table_options&=
+ ~(HA_OPTION_STATS_PERSISTENT | HA_OPTION_NO_STATS_PERSISTENT);
+ Lex->create_info.used_fields|= HA_CREATE_USED_STATS_PERSISTENT;
+ }
+ | STATS_SAMPLE_PAGES_SYM opt_equal ulong_num
+ {
+ /* From user point of view STATS_SAMPLE_PAGES can be specified as
+ STATS_SAMPLE_PAGES=N (where 0<N<=65535, it does not make sense to
+ scan 0 pages) or STATS_SAMPLE_PAGES=default. Internally we record
+ =default as 0. See create_frm() in sql/table.cc, we use only two
+ bytes for stats_sample_pages and this is why we do not allow
+ larger values. 65535 pages, 16kb each means to sample 1GB, which
+ is impractical. If at some point this needs to be extended, then
+ we can store the higher bits from stats_sample_pages in .frm too. */
+ if ($3 == 0 || $3 > 0xffff)
+ {
+ my_parse_error(ER(ER_SYNTAX_ERROR));
+ MYSQL_YYABORT;
+ }
+ Lex->create_info.stats_sample_pages=$3;
+ Lex->create_info.used_fields|= HA_CREATE_USED_STATS_SAMPLE_PAGES;
+ }
+ | STATS_SAMPLE_PAGES_SYM opt_equal DEFAULT
+ {
+ Lex->create_info.stats_sample_pages=0;
+ Lex->create_info.used_fields|= HA_CREATE_USED_STATS_SAMPLE_PAGES;
+ }
| CHECKSUM_SYM opt_equal ulong_num
{
Lex->create_info.table_options|= $3 ? HA_OPTION_CHECKSUM : HA_OPTION_NO_CHECKSUM;
@@ -5274,18 +5804,8 @@ create_table_option:
default_charset:
opt_default charset opt_equal charset_name_or_default
{
- HA_CREATE_INFO *cinfo= &Lex->create_info;
- if ((cinfo->used_fields & HA_CREATE_USED_DEFAULT_CHARSET) &&
- cinfo->default_table_charset && $4 &&
- !my_charset_same(cinfo->default_table_charset,$4))
- {
- my_error(ER_CONFLICTING_DECLARATIONS, MYF(0),
- "CHARACTER SET ", cinfo->default_table_charset->csname,
- "CHARACTER SET ", $4->csname);
+ if (Lex->create_info.add_table_option_default_charset($4))
MYSQL_YYABORT;
- }
- Lex->create_info.default_table_charset= $4;
- Lex->create_info.used_fields|= HA_CREATE_USED_DEFAULT_CHARSET;
}
;
@@ -5312,7 +5832,7 @@ storage_engines:
plugin_ref plugin= ha_resolve_by_name(thd, &$1);
if (plugin)
- $$= plugin_data(plugin, handlerton*);
+ $$= plugin_hton(plugin);
else
{
if (thd->variables.sql_mode & MODE_NO_ENGINE_SUBSTITUTION)
@@ -5321,7 +5841,7 @@ storage_engines:
MYSQL_YYABORT;
}
$$= 0;
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_UNKNOWN_STORAGE_ENGINE,
ER(ER_UNKNOWN_STORAGE_ENGINE),
$1.str);
@@ -5334,7 +5854,7 @@ known_storage_engines:
{
plugin_ref plugin;
if ((plugin= ha_resolve_by_name(thd, &$1)))
- $$= plugin_data(plugin, handlerton*);
+ $$= plugin_hton(plugin);
else
{
my_error(ER_UNKNOWN_STORAGE_ENGINE, MYF(0), $1.str);
@@ -5398,14 +5918,14 @@ column_def:
;
key_def:
- normal_key_type opt_ident key_alg '(' key_list ')'
+ normal_key_type opt_if_not_exists_ident key_alg '(' key_list ')'
{ Lex->option_list= NULL; }
normal_key_options
{
if (add_create_index (Lex, $1, $2))
MYSQL_YYABORT;
}
- | fulltext opt_key_or_index opt_ident init_key_options
+ | fulltext opt_key_or_index opt_if_not_exists_ident init_key_options
'(' key_list ')'
{ Lex->option_list= NULL; }
fulltext_key_options
@@ -5413,7 +5933,7 @@ key_def:
if (add_create_index (Lex, $1, $3))
MYSQL_YYABORT;
}
- | spatial opt_key_or_index opt_ident init_key_options
+ | spatial opt_key_or_index opt_if_not_exists_ident init_key_options
'(' key_list ')'
{ Lex->option_list= NULL; }
spatial_key_options
@@ -5421,7 +5941,7 @@ key_def:
if (add_create_index (Lex, $1, $3))
MYSQL_YYABORT;
}
- | opt_constraint constraint_key_type opt_ident key_alg
+ | opt_constraint constraint_key_type opt_if_not_exists_ident key_alg
'(' key_list ')'
{ Lex->option_list= NULL; }
normal_key_options
@@ -5429,15 +5949,17 @@ key_def:
if (add_create_index (Lex, $2, $3.str ? $3 : $1))
MYSQL_YYABORT;
}
- | opt_constraint FOREIGN KEY_SYM opt_ident '(' key_list ')' references
+ | opt_constraint FOREIGN KEY_SYM opt_if_not_exists_ident '(' key_list ')' references
{
LEX *lex=Lex;
Key *key= new Foreign_key($4.str ? $4 : $1, lex->col_list,
- $8,
+ $8->db,
+ $8->table,
lex->ref_list,
lex->fk_delete_opt,
lex->fk_update_opt,
- lex->fk_match_option);
+ lex->fk_match_option,
+ lex->check_exists);
if (key == NULL)
MYSQL_YYABORT;
lex->alter_info.key_list.push_back(key);
@@ -5446,7 +5968,7 @@ key_def:
&default_key_create_info, 1))
MYSQL_YYABORT;
/* Only used for ALTER TABLE. Ignored otherwise. */
- lex->alter_info.flags|= ALTER_FOREIGN_KEY;
+ lex->alter_info.flags|= Alter_info::ADD_FOREIGN_KEY;
}
| opt_constraint check_constraint
{
@@ -5546,13 +6068,13 @@ vcol_attribute:
{
LEX *lex=Lex;
lex->type|= UNIQUE_FLAG;
- lex->alter_info.flags|= ALTER_ADD_INDEX;
+ lex->alter_info.flags|= Alter_info::ALTER_ADD_INDEX;
}
| UNIQUE_SYM KEY_SYM
{
LEX *lex=Lex;
lex->type|= UNIQUE_KEY_FLAG;
- lex->alter_info.flags|= ALTER_ADD_INDEX;
+ lex->alter_info.flags|= Alter_info::ALTER_ADD_INDEX;
}
| COMMENT_SYM TEXT_STRING_sys { Lex->comment= $2; }
;
@@ -5667,7 +6189,7 @@ type:
{
char buff[sizeof("YEAR()") + MY_INT64_NUM_DECIMAL_DIGITS + 1];
my_snprintf(buff, sizeof(buff), "YEAR(%lu)", length);
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
ER_WARN_DEPRECATED_SYNTAX,
ER(ER_WARN_DEPRECATED_SYNTAX),
buff, "YEAR(4)");
@@ -5887,9 +6409,9 @@ attribute:
NULL_SYM { Lex->type&= ~ NOT_NULL_FLAG; }
| not NULL_SYM { Lex->type|= NOT_NULL_FLAG; }
| DEFAULT now_or_signed_literal { Lex->default_value=$2; }
- | ON UPDATE_SYM NOW_SYM optional_braces
+ | ON UPDATE_SYM NOW_SYM opt_default_time_precision
{
- Item *item= new (thd->mem_root) Item_func_now_local(6);
+ Item *item= new (thd->mem_root) Item_func_now_local($4);
if (item == NULL)
MYSQL_YYABORT;
Lex->on_update_value= item;
@@ -5899,25 +6421,25 @@ attribute:
{
LEX *lex=Lex;
lex->type|= AUTO_INCREMENT_FLAG | NOT_NULL_FLAG | UNIQUE_FLAG;
- lex->alter_info.flags|= ALTER_ADD_INDEX;
+ lex->alter_info.flags|= Alter_info::ALTER_ADD_INDEX;
}
| opt_primary KEY_SYM
{
LEX *lex=Lex;
lex->type|= PRI_KEY_FLAG | NOT_NULL_FLAG;
- lex->alter_info.flags|= ALTER_ADD_INDEX;
+ lex->alter_info.flags|= Alter_info::ALTER_ADD_INDEX;
}
| UNIQUE_SYM
{
LEX *lex=Lex;
lex->type|= UNIQUE_FLAG;
- lex->alter_info.flags|= ALTER_ADD_INDEX;
+ lex->alter_info.flags|= Alter_info::ALTER_ADD_INDEX;
}
| UNIQUE_SYM KEY_SYM
{
LEX *lex=Lex;
lex->type|= UNIQUE_KEY_FLAG;
- lex->alter_info.flags|= ALTER_ADD_INDEX;
+ lex->alter_info.flags|= Alter_info::ALTER_ADD_INDEX;
}
| COMMENT_SYM TEXT_STRING_sys { Lex->comment= $2; }
| COLLATE_SYM collation_name
@@ -5981,9 +6503,9 @@ type_with_opt_collate:
now_or_signed_literal:
- NOW_SYM optional_braces
+ NOW_SYM opt_default_time_precision
{
- $$= new (thd->mem_root) Item_func_now_local(6);
+ $$= new (thd->mem_root) Item_func_now_local($2);
if ($$ == NULL)
MYSQL_YYABORT;
}
@@ -5991,11 +6513,6 @@ now_or_signed_literal:
{ $$=$1; }
;
-hex_num_or_string:
- HEX_NUM {}
- | HEX_STRING {}
- ;
-
charset:
CHAR_SYM SET {}
| CHARSET {}
@@ -6044,11 +6561,8 @@ old_or_new_charset_name_or_default:
collation_name:
ident_or_text
{
- if (!($$=get_charset_by_name($1.str,MYF(0))))
- {
- my_error(ER_UNKNOWN_COLLATION, MYF(0), $1.str);
+ if (!($$= mysqld_collation_get_by_name($1.str)))
MYSQL_YYABORT;
- }
}
;
@@ -6092,19 +6606,13 @@ unicode:
}
| UNICODE_SYM BINARY
{
- if (!(Lex->charset=get_charset_by_name("ucs2_bin", MYF(0))))
- {
- my_error(ER_UNKNOWN_COLLATION, MYF(0), "ucs2_bin");
+ if (!(Lex->charset= mysqld_collation_get_by_name("ucs2_bin")))
MYSQL_YYABORT;
- }
}
| BINARY UNICODE_SYM
{
- if (!(Lex->charset=get_charset_by_name("ucs2_bin", MYF(0))))
- {
- my_error(ER_UNKNOWN_COLLATION, MYF(0), "ucs2_bin");
+ if (!(Lex->charset= mysqld_collation_get_by_name("ucs2_bin")))
MYSQL_YYABORT;
- }
}
;
@@ -6131,6 +6639,73 @@ opt_bin_mod:
| BINARY { Lex->type|= BINCMP_FLAG; }
;
+ws_nweights:
+ '(' real_ulong_num
+ {
+ if ($2 == 0)
+ {
+ my_parse_error(ER(ER_SYNTAX_ERROR));
+ MYSQL_YYABORT;
+ }
+ }
+ ')'
+ { $$= $2; }
+ ;
+
+ws_level_flag_desc:
+ ASC { $$= 0; }
+ | DESC { $$= 1 << MY_STRXFRM_DESC_SHIFT; }
+ ;
+
+ws_level_flag_reverse:
+ REVERSE_SYM { $$= 1 << MY_STRXFRM_REVERSE_SHIFT; } ;
+
+ws_level_flags:
+ /* empty */ { $$= 0; }
+ | ws_level_flag_desc { $$= $1; }
+ | ws_level_flag_desc ws_level_flag_reverse { $$= $1 | $2; }
+ | ws_level_flag_reverse { $$= $1 ; }
+ ;
+
+ws_level_number:
+ real_ulong_num
+ {
+ $$= $1 < 1 ? 1 : ($1 > MY_STRXFRM_NLEVELS ? MY_STRXFRM_NLEVELS : $1);
+ $$--;
+ }
+ ;
+
+ws_level_list_item:
+ ws_level_number ws_level_flags
+ {
+ $$= (1 | $2) << $1;
+ }
+ ;
+
+ws_level_list:
+ ws_level_list_item { $$= $1; }
+ | ws_level_list ',' ws_level_list_item { $$|= $3; }
+ ;
+
+ws_level_range:
+ ws_level_number '-' ws_level_number
+ {
+ uint start= $1;
+ uint end= $3;
+ for ($$= 0; start <= end; start++)
+ $$|= (1 << start);
+ }
+ ;
+
+ws_level_list_or_range:
+ ws_level_list { $$= $1; }
+ | ws_level_range { $$= $1; }
+ ;
+
+opt_ws_levels:
+ /* empty*/ { $$= 0; }
+ | LEVEL_SYM ws_level_list_or_range { $$= $2; }
+ ;
opt_primary:
/* empty */
@@ -6414,6 +6989,18 @@ opt_ident:
| field_ident { $$= $1; }
;
+opt_if_not_exists_ident:
+ opt_if_not_exists opt_ident
+ {
+ LEX *lex= Lex;
+ if (lex->check_exists && lex->sql_command != SQLCOM_ALTER_TABLE)
+ {
+ my_parse_error(ER(ER_SYNTAX_ERROR));
+ MYSQL_YYABORT;
+ }
+ $$= $2;
+ };
+
opt_component:
/* empty */ { $$= null_lex_str; }
| '.' ident { $$= $2; }
@@ -6428,39 +7015,41 @@ string_list:
*/
alter:
- ALTER alter_options TABLE_SYM table_ident
+ ALTER
+ {
+ Lex->name.str= 0;
+ Lex->name.length= 0;
+ Lex->only_view= FALSE;
+ Lex->sql_command= SQLCOM_ALTER_TABLE;
+ Lex->duplicates= DUP_ERROR;
+ Lex->col_list.empty();
+ Lex->select_lex.init_order();
+ bzero(&Lex->create_info, sizeof(Lex->create_info));
+ Lex->create_info.db_type= 0;
+ Lex->create_info.default_table_charset= NULL;
+ Lex->create_info.row_type= ROW_TYPE_NOT_USED;
+ Lex->alter_info.reset();
+ Lex->no_write_to_binlog= 0;
+ Lex->create_info.storage_media= HA_SM_DEFAULT;
+ DBUG_ASSERT(!Lex->m_sql_cmd);
+ }
+ alter_options TABLE_SYM table_ident
{
- LEX *lex= thd->lex;
- lex->name.str= 0;
- lex->name.length= 0;
- lex->sql_command= SQLCOM_ALTER_TABLE;
- lex->duplicates= DUP_ERROR;
- if (!lex->select_lex.add_table_to_list(thd, $4, NULL,
+ if (!Lex->select_lex.add_table_to_list(thd, $5, NULL,
TL_OPTION_UPDATING,
TL_READ_NO_INSERT,
- MDL_SHARED_NO_WRITE))
+ MDL_SHARED_UPGRADABLE))
MYSQL_YYABORT;
- lex->col_list.empty();
- lex->select_lex.init_order();
- lex->select_lex.db= (lex->select_lex.table_list.first)->db;
- bzero((char*) &lex->create_info,sizeof(lex->create_info));
- lex->create_info.db_type= 0;
- lex->create_info.default_table_charset= NULL;
- lex->create_info.row_type= ROW_TYPE_NOT_USED;
- lex->alter_info.reset();
- lex->no_write_to_binlog= 0;
- lex->create_info.storage_media= HA_SM_DEFAULT;
- lex->create_last_non_select_table= lex->last_table();
- DBUG_ASSERT(!lex->m_stmt);
+ Lex->select_lex.db= (Lex->select_lex.table_list.first)->db;
+ Lex->create_last_non_select_table= Lex->last_table();
}
alter_commands
{
- LEX *lex= thd->lex;
- if (!lex->m_stmt)
+ if (!Lex->m_sql_cmd)
{
/* Create a generic ALTER TABLE statment. */
- lex->m_stmt= new (thd->mem_root) Alter_table_statement(lex);
- if (lex->m_stmt == NULL)
+ Lex->m_sql_cmd= new (thd->mem_root) Sql_cmd_alter_table();
+ if (Lex->m_sql_cmd == NULL)
MYSQL_YYABORT;
}
}
@@ -6657,8 +7246,22 @@ ident_or_empty:
alter_commands:
/* empty */
- | DISCARD TABLESPACE { Lex->alter_info.tablespace_op= DISCARD_TABLESPACE; }
- | IMPORT TABLESPACE { Lex->alter_info.tablespace_op= IMPORT_TABLESPACE; }
+ | DISCARD TABLESPACE
+ {
+ Lex->m_sql_cmd= new (thd->mem_root)
+ Sql_cmd_discard_import_tablespace(
+ Sql_cmd_discard_import_tablespace::DISCARD_TABLESPACE);
+ if (Lex->m_sql_cmd == NULL)
+ MYSQL_YYABORT;
+ }
+ | IMPORT TABLESPACE
+ {
+ Lex->m_sql_cmd= new (thd->mem_root)
+ Sql_cmd_discard_import_tablespace(
+ Sql_cmd_discard_import_tablespace::IMPORT_TABLESPACE);
+ if (Lex->m_sql_cmd == NULL)
+ MYSQL_YYABORT;
+ }
| alter_list
opt_partitioning
| alter_list
@@ -6670,19 +7273,18 @@ alter_commands:
From here we insert a number of commands to manage the partitions of a
partitioned table such as adding partitions, dropping partitions,
reorganising partitions in various manners. In future releases the list
- will be longer and also include moving partitions to a
- new table and so forth.
+ will be longer.
*/
| add_partition_rule
- | DROP PARTITION_SYM alt_part_name_list
+ | DROP PARTITION_SYM opt_if_exists alt_part_name_list
{
- Lex->alter_info.flags|= ALTER_DROP_PARTITION;
+ Lex->alter_info.flags|= Alter_info::ALTER_DROP_PARTITION;
}
| REBUILD_SYM PARTITION_SYM opt_no_write_to_binlog
all_or_alt_part_name_list
{
LEX *lex= Lex;
- lex->alter_info.flags|= ALTER_REBUILD_PARTITION;
+ lex->alter_info.flags|= Alter_info::ALTER_REBUILD_PARTITION;
lex->no_write_to_binlog= $3;
}
| OPTIMIZE PARTITION_SYM opt_no_write_to_binlog
@@ -6691,10 +7293,10 @@ alter_commands:
LEX *lex= thd->lex;
lex->no_write_to_binlog= $3;
lex->check_opt.init();
- DBUG_ASSERT(!lex->m_stmt);
- lex->m_stmt= new (thd->mem_root)
- Alter_table_optimize_partition_statement(lex);
- if (lex->m_stmt == NULL)
+ DBUG_ASSERT(!lex->m_sql_cmd);
+ lex->m_sql_cmd= new (thd->mem_root)
+ Sql_cmd_alter_table_optimize_partition();
+ if (lex->m_sql_cmd == NULL)
MYSQL_YYABORT;
}
opt_no_write_to_binlog
@@ -6704,20 +7306,20 @@ alter_commands:
LEX *lex= thd->lex;
lex->no_write_to_binlog= $3;
lex->check_opt.init();
- DBUG_ASSERT(!lex->m_stmt);
- lex->m_stmt= new (thd->mem_root)
- Alter_table_analyze_partition_statement(lex);
- if (lex->m_stmt == NULL)
- MYSQL_YYABORT;
+ DBUG_ASSERT(!lex->m_sql_cmd);
+ lex->m_sql_cmd= new (thd->mem_root)
+ Sql_cmd_alter_table_analyze_partition();
+ if (lex->m_sql_cmd == NULL)
+ MYSQL_YYABORT;
}
| CHECK_SYM PARTITION_SYM all_or_alt_part_name_list
{
LEX *lex= thd->lex;
lex->check_opt.init();
- DBUG_ASSERT(!lex->m_stmt);
- lex->m_stmt= new (thd->mem_root)
- Alter_table_check_partition_statement(lex);
- if (lex->m_stmt == NULL)
+ DBUG_ASSERT(!lex->m_sql_cmd);
+ lex->m_sql_cmd= new (thd->mem_root)
+ Sql_cmd_alter_table_check_partition();
+ if (lex->m_sql_cmd == NULL)
MYSQL_YYABORT;
}
opt_mi_check_type
@@ -6727,17 +7329,17 @@ alter_commands:
LEX *lex= thd->lex;
lex->no_write_to_binlog= $3;
lex->check_opt.init();
- DBUG_ASSERT(!lex->m_stmt);
- lex->m_stmt= new (thd->mem_root)
- Alter_table_repair_partition_statement(lex);
- if (lex->m_stmt == NULL)
+ DBUG_ASSERT(!lex->m_sql_cmd);
+ lex->m_sql_cmd= new (thd->mem_root)
+ Sql_cmd_alter_table_repair_partition();
+ if (lex->m_sql_cmd == NULL)
MYSQL_YYABORT;
}
opt_mi_repair_type
| COALESCE PARTITION_SYM opt_no_write_to_binlog real_ulong_num
{
LEX *lex= Lex;
- lex->alter_info.flags|= ALTER_COALESCE_PARTITION;
+ lex->alter_info.flags|= Alter_info::ALTER_COALESCE_PARTITION;
lex->no_write_to_binlog= $3;
lex->alter_info.num_parts= $4;
}
@@ -6745,32 +7347,56 @@ alter_commands:
{
LEX *lex= thd->lex;
lex->check_opt.init();
- DBUG_ASSERT(!lex->m_stmt);
- lex->m_stmt= new (thd->mem_root)
- Alter_table_truncate_partition_statement(lex);
- if (lex->m_stmt == NULL)
+ DBUG_ASSERT(!lex->m_sql_cmd);
+ lex->m_sql_cmd= new (thd->mem_root)
+ Sql_cmd_alter_table_truncate_partition();
+ if (lex->m_sql_cmd == NULL)
MYSQL_YYABORT;
}
| reorg_partition_rule
+ | EXCHANGE_SYM PARTITION_SYM alt_part_name_item
+ WITH TABLE_SYM table_ident have_partitioning
+ {
+ LEX *lex= thd->lex;
+ size_t dummy;
+ lex->select_lex.db=$6->db.str;
+ if (lex->select_lex.db == NULL &&
+ lex->copy_db_to(&lex->select_lex.db, &dummy))
+ {
+ MYSQL_YYABORT;
+ }
+ lex->name= $6->table;
+ lex->alter_info.flags|= Alter_info::ALTER_EXCHANGE_PARTITION;
+ if (!lex->select_lex.add_table_to_list(thd, $6, NULL,
+ TL_OPTION_UPDATING,
+ TL_READ_NO_INSERT,
+ MDL_SHARED_NO_WRITE))
+ MYSQL_YYABORT;
+ DBUG_ASSERT(!lex->m_sql_cmd);
+ lex->m_sql_cmd= new (thd->mem_root)
+ Sql_cmd_alter_table_exchange_partition();
+ if (lex->m_sql_cmd == NULL)
+ MYSQL_YYABORT;
+ }
;
remove_partitioning:
REMOVE_SYM PARTITIONING_SYM
{
- Lex->alter_info.flags|= ALTER_REMOVE_PARTITIONING;
+ Lex->alter_info.flags|= Alter_info::ALTER_REMOVE_PARTITIONING;
}
;
all_or_alt_part_name_list:
ALL
{
- Lex->alter_info.flags|= ALTER_ALL_PARTITION;
+ Lex->alter_info.flags|= Alter_info::ALTER_ALL_PARTITION;
}
| alt_part_name_list
;
add_partition_rule:
- ADD PARTITION_SYM opt_no_write_to_binlog
+ ADD PARTITION_SYM opt_if_not_exists opt_no_write_to_binlog
{
LEX *lex= Lex;
lex->part_info= new partition_info();
@@ -6779,8 +7405,8 @@ add_partition_rule:
mem_alloc_error(sizeof(partition_info));
MYSQL_YYABORT;
}
- lex->alter_info.flags|= ALTER_ADD_PARTITION;
- lex->no_write_to_binlog= $3;
+ lex->alter_info.flags|= Alter_info::ALTER_ADD_PARTITION;
+ lex->no_write_to_binlog= $4;
}
add_part_extra
{}
@@ -6817,11 +7443,11 @@ reorg_partition_rule:
reorg_parts_rule:
/* empty */
{
- Lex->alter_info.flags|= ALTER_TABLE_REORG;
+ Lex->alter_info.flags|= Alter_info::ALTER_TABLE_REORG;
}
| alt_part_name_list
{
- Lex->alter_info.flags|= ALTER_REORGANIZE_PARTITION;
+ Lex->alter_info.flags|= Alter_info::ALTER_REORGANIZE_PARTITION;
}
INTO '(' part_def_list ')'
{
@@ -6856,11 +7482,11 @@ alter_list:
;
add_column:
- ADD opt_column
+ ADD opt_column opt_if_not_exists
{
LEX *lex=Lex;
lex->change=0;
- lex->alter_info.flags|= ALTER_ADD_COLUMN;
+ lex->alter_info.flags|= Alter_info::ALTER_ADD_COLUMN;
}
;
@@ -6872,43 +7498,44 @@ alter_list_item:
| ADD key_def
{
Lex->create_last_non_select_table= Lex->last_table();
- Lex->alter_info.flags|= ALTER_ADD_INDEX;
+ Lex->alter_info.flags|= Alter_info::ALTER_ADD_INDEX;
}
| add_column '(' create_field_list ')'
{
- Lex->alter_info.flags|= ALTER_ADD_COLUMN | ALTER_ADD_INDEX;
+ Lex->alter_info.flags|= Alter_info::ALTER_ADD_COLUMN |
+ Alter_info::ALTER_ADD_INDEX;
}
- | CHANGE opt_column field_ident
+ | CHANGE opt_column opt_if_exists field_ident
{
LEX *lex=Lex;
- lex->change= $3.str;
- lex->alter_info.flags|= ALTER_CHANGE_COLUMN;
+ lex->change= $4.str;
+ lex->alter_info.flags|= Alter_info::ALTER_CHANGE_COLUMN;
lex->option_list= NULL;
}
field_spec opt_place
{
Lex->create_last_non_select_table= Lex->last_table();
}
- | MODIFY_SYM opt_column field_ident
+ | MODIFY_SYM opt_column opt_if_exists field_ident
{
LEX *lex=Lex;
lex->length=lex->dec=0; lex->type=0;
lex->default_value= lex->on_update_value= 0;
lex->comment=null_lex_str;
lex->charset= NULL;
- lex->alter_info.flags|= ALTER_CHANGE_COLUMN;
+ lex->alter_info.flags|= Alter_info::ALTER_CHANGE_COLUMN;
lex->vcol_info= 0;
lex->option_list= NULL;
}
field_def
{
LEX *lex=Lex;
- if (add_field_to_list(lex->thd,&$3,
- $5.type,
- $5.length, $5.dec, lex->type,
+ if (add_field_to_list(lex->thd,&$4,
+ $6.type,
+ $6.length, $6.dec, lex->type,
lex->default_value, lex->on_update_value,
&lex->comment,
- $3.str, &lex->interval_list, $5.charset,
+ $4.str, &lex->interval_list, $6.charset,
lex->uint_geom_type,
lex->vcol_info, lex->option_list))
MYSQL_YYABORT;
@@ -6917,48 +7544,54 @@ alter_list_item:
{
Lex->create_last_non_select_table= Lex->last_table();
}
- | DROP opt_column field_ident opt_restrict
+ | DROP opt_column opt_if_exists field_ident opt_restrict
{
LEX *lex=Lex;
- Alter_drop *ad= new Alter_drop(Alter_drop::COLUMN, $3.str);
+ Alter_drop *ad= new Alter_drop(Alter_drop::COLUMN, $4.str, $3);
if (ad == NULL)
MYSQL_YYABORT;
lex->alter_info.drop_list.push_back(ad);
- lex->alter_info.flags|= ALTER_DROP_COLUMN;
+ lex->alter_info.flags|= Alter_info::ALTER_DROP_COLUMN;
}
- | DROP FOREIGN KEY_SYM opt_ident
+ | DROP FOREIGN KEY_SYM opt_if_exists field_ident
{
- Lex->alter_info.flags|= ALTER_DROP_INDEX | ALTER_FOREIGN_KEY;
+ LEX *lex=Lex;
+ Alter_drop *ad= new Alter_drop(Alter_drop::FOREIGN_KEY, $5.str, $4);
+ if (ad == NULL)
+ MYSQL_YYABORT;
+ lex->alter_info.drop_list.push_back(ad);
+ lex->alter_info.flags|= Alter_info::DROP_FOREIGN_KEY;
}
| DROP PRIMARY_SYM KEY_SYM
{
LEX *lex=Lex;
- Alter_drop *ad= new Alter_drop(Alter_drop::KEY, primary_key_name);
+ Alter_drop *ad= new Alter_drop(Alter_drop::KEY, primary_key_name,
+ FALSE);
if (ad == NULL)
MYSQL_YYABORT;
lex->alter_info.drop_list.push_back(ad);
- lex->alter_info.flags|= ALTER_DROP_INDEX;
+ lex->alter_info.flags|= Alter_info::ALTER_DROP_INDEX;
}
- | DROP key_or_index field_ident
+ | DROP key_or_index opt_if_exists field_ident
{
LEX *lex=Lex;
- Alter_drop *ad= new Alter_drop(Alter_drop::KEY, $3.str);
+ Alter_drop *ad= new Alter_drop(Alter_drop::KEY, $4.str, $3);
if (ad == NULL)
MYSQL_YYABORT;
lex->alter_info.drop_list.push_back(ad);
- lex->alter_info.flags|= ALTER_DROP_INDEX;
+ lex->alter_info.flags|= Alter_info::ALTER_DROP_INDEX;
}
| DISABLE_SYM KEYS
{
LEX *lex=Lex;
- lex->alter_info.keys_onoff= DISABLE;
- lex->alter_info.flags|= ALTER_KEYS_ONOFF;
+ lex->alter_info.keys_onoff= Alter_info::DISABLE;
+ lex->alter_info.flags|= Alter_info::ALTER_KEYS_ONOFF;
}
| ENABLE_SYM KEYS
{
LEX *lex=Lex;
- lex->alter_info.keys_onoff= ENABLE;
- lex->alter_info.flags|= ALTER_KEYS_ONOFF;
+ lex->alter_info.keys_onoff= Alter_info::ENABLE;
+ lex->alter_info.flags|= Alter_info::ALTER_KEYS_ONOFF;
}
| ALTER opt_column field_ident SET DEFAULT signed_literal
{
@@ -6967,7 +7600,7 @@ alter_list_item:
if (ac == NULL)
MYSQL_YYABORT;
lex->alter_info.alter_list.push_back(ac);
- lex->alter_info.flags|= ALTER_CHANGE_COLUMN_DEFAULT;
+ lex->alter_info.flags|= Alter_info::ALTER_CHANGE_COLUMN_DEFAULT;
}
| ALTER opt_column field_ident DROP DEFAULT
{
@@ -6976,7 +7609,7 @@ alter_list_item:
if (ac == NULL)
MYSQL_YYABORT;
lex->alter_info.alter_list.push_back(ac);
- lex->alter_info.flags|= ALTER_CHANGE_COLUMN_DEFAULT;
+ lex->alter_info.flags|= Alter_info::ALTER_CHANGE_COLUMN_DEFAULT;
}
| RENAME opt_to table_ident
{
@@ -6995,7 +7628,7 @@ alter_list_item:
MYSQL_YYABORT;
}
lex->name= $3->table;
- lex->alter_info.flags|= ALTER_RENAME;
+ lex->alter_info.flags|= Alter_info::ALTER_RENAME;
}
| CONVERT_SYM TO_SYM charset charset_name_or_default opt_collate
{
@@ -7011,16 +7644,14 @@ alter_list_item:
MYSQL_YYABORT;
}
LEX *lex= Lex;
- lex->create_info.table_charset=
- lex->create_info.default_table_charset= $5;
- lex->create_info.used_fields|= (HA_CREATE_USED_CHARSET |
- HA_CREATE_USED_DEFAULT_CHARSET);
- lex->alter_info.flags|= ALTER_CONVERT;
+ if (lex->create_info.add_alter_list_item_convert_to_charset($5))
+ MYSQL_YYABORT;
+ lex->alter_info.flags|= Alter_info::ALTER_CONVERT;
}
| create_table_options_space_separated
{
LEX *lex=Lex;
- lex->alter_info.flags|= ALTER_OPTIONS;
+ lex->alter_info.flags|= Alter_info::ALTER_OPTIONS;
if ((lex->create_info.used_fields & HA_CREATE_USED_ENGINE) &&
!lex->create_info.db_type)
{
@@ -7029,12 +7660,53 @@ alter_list_item:
}
| FORCE_SYM
{
- Lex->alter_info.flags|= ALTER_RECREATE;
+ Lex->alter_info.flags|= Alter_info::ALTER_RECREATE;
}
| alter_order_clause
{
LEX *lex=Lex;
- lex->alter_info.flags|= ALTER_ORDER;
+ lex->alter_info.flags|= Alter_info::ALTER_ORDER;
+ }
+ | alter_algorithm_option
+ | alter_lock_option
+ ;
+
+opt_index_lock_algorithm:
+ /* empty */
+ | alter_lock_option
+ | alter_algorithm_option
+ | alter_lock_option alter_algorithm_option
+ | alter_algorithm_option alter_lock_option
+
+alter_algorithm_option:
+ ALGORITHM_SYM opt_equal DEFAULT
+ {
+ Lex->alter_info.requested_algorithm=
+ Alter_info::ALTER_TABLE_ALGORITHM_DEFAULT;
+ }
+ | ALGORITHM_SYM opt_equal ident
+ {
+ if (Lex->alter_info.set_requested_algorithm(&$3))
+ {
+ my_error(ER_UNKNOWN_ALTER_ALGORITHM, MYF(0), $3.str);
+ MYSQL_YYABORT;
+ }
+ }
+ ;
+
+alter_lock_option:
+ LOCK_SYM opt_equal DEFAULT
+ {
+ Lex->alter_info.requested_lock=
+ Alter_info::ALTER_TABLE_LOCK_DEFAULT;
+ }
+ | LOCK_SYM opt_equal ident
+ {
+ if (Lex->alter_info.set_requested_lock(&$3))
+ {
+ my_error(ER_UNKNOWN_ALTER_LOCK, MYF(0), $3.str);
+ MYSQL_YYABORT;
+ }
}
;
@@ -7049,7 +7721,7 @@ opt_ignore:
;
alter_options:
- { Lex->ignore= Lex->online= 0;} alter_options_part2
+ { Lex->ignore= 0;} alter_options_part2
;
alter_options_part2:
@@ -7064,7 +7736,11 @@ alter_option_list:
alter_option:
IGNORE_SYM { Lex->ignore= 1;}
- | ONLINE_SYM { Lex->online= 1;}
+ | ONLINE_SYM
+ {
+ Lex->alter_info.requested_lock=
+ Alter_info::ALTER_TABLE_LOCK_NONE;
+ }
opt_restrict:
@@ -7075,8 +7751,16 @@ opt_restrict:
opt_place:
/* empty */ {}
- | AFTER_SYM ident { store_position_for_column($2.str); }
- | FIRST_SYM { store_position_for_column(first_keyword); }
+ | AFTER_SYM ident
+ {
+ store_position_for_column($2.str);
+ Lex->alter_info.flags |= Alter_info::ALTER_COLUMN_ORDER;
+ }
+ | FIRST_SYM
+ {
+ store_position_for_column(first_keyword);
+ Lex->alter_info.flags |= Alter_info::ALTER_COLUMN_ORDER;
+ }
;
opt_to:
@@ -7086,12 +7770,8 @@ opt_to:
| AS {}
;
-/*
- SLAVE START and SLAVE STOP are deprecated. We keep them for compatibility.
-*/
-
slave:
- START_SYM SLAVE slave_thread_opts
+ START_SYM SLAVE optional_connection_name slave_thread_opts
{
LEX *lex=Lex;
lex->sql_command = SQLCOM_SLAVE_START;
@@ -7100,44 +7780,80 @@ slave:
}
slave_until
{}
- | STOP_SYM SLAVE slave_thread_opts
+ | START_SYM ALL SLAVES slave_thread_opts
{
LEX *lex=Lex;
- lex->sql_command = SQLCOM_SLAVE_STOP;
+ lex->sql_command = SQLCOM_SLAVE_ALL_START;
lex->type = 0;
- /* If you change this code don't forget to update SLAVE STOP too */
}
- | SLAVE START_SYM slave_thread_opts
+ {}
+ | STOP_SYM SLAVE optional_connection_name slave_thread_opts
{
LEX *lex=Lex;
- lex->sql_command = SQLCOM_SLAVE_START;
+ lex->sql_command = SQLCOM_SLAVE_STOP;
lex->type = 0;
+ /* If you change this code don't forget to update SLAVE STOP too */
}
- slave_until
- {}
- | SLAVE STOP_SYM slave_thread_opts
+ | STOP_SYM ALL SLAVES slave_thread_opts
{
LEX *lex=Lex;
- lex->sql_command = SQLCOM_SLAVE_STOP;
+ lex->sql_command = SQLCOM_SLAVE_ALL_STOP;
lex->type = 0;
+ /* If you change this code don't forget to update SLAVE STOP too */
}
;
start:
- START_SYM TRANSACTION_SYM start_transaction_opts
+ START_SYM TRANSACTION_SYM opt_start_transaction_option_list
{
LEX *lex= Lex;
lex->sql_command= SQLCOM_BEGIN;
+ /* READ ONLY and READ WRITE are mutually exclusive. */
+ if (($3 & MYSQL_START_TRANS_OPT_READ_WRITE) &&
+ ($3 & MYSQL_START_TRANS_OPT_READ_ONLY))
+ {
+ my_parse_error(ER(ER_SYNTAX_ERROR));
+ MYSQL_YYABORT;
+ }
lex->start_transaction_opt= $3;
}
;
-start_transaction_opts:
- /*empty*/ { $$ = 0; }
- | WITH CONSISTENT_SYM SNAPSHOT_SYM
+opt_start_transaction_option_list:
+ /* empty */
+ {
+ $$= 0;
+ }
+ | start_transaction_option_list
+ {
+ $$= $1;
+ }
+ ;
+
+start_transaction_option_list:
+ start_transaction_option
+ {
+ $$= $1;
+ }
+ | start_transaction_option_list ',' start_transaction_option
+ {
+ $$= $1 | $3;
+ }
+ ;
+
+start_transaction_option:
+ WITH CONSISTENT_SYM SNAPSHOT_SYM
{
$$= MYSQL_START_TRANS_OPT_WITH_CONS_SNAPSHOT;
}
+ | READ_SYM ONLY_SYM
+ {
+ $$= MYSQL_START_TRANS_OPT_READ_ONLY;
+ }
+ | READ_SYM WRITE_SYM
+ {
+ $$= MYSQL_START_TRANS_OPT_READ_WRITE;
+ }
;
slave_thread_opts:
@@ -7172,6 +7888,10 @@ slave_until:
MYSQL_YYABORT;
}
}
+ | UNTIL_SYM MASTER_GTID_POS_SYM EQ TEXT_STRING_sys
+ {
+ Lex->mi.gtid_pos_str = $4;
+ }
;
slave_until_opts:
@@ -7216,9 +7936,9 @@ repair:
repair_table_or_view
{
LEX* lex= thd->lex;
- DBUG_ASSERT(!lex->m_stmt);
- lex->m_stmt= new (thd->mem_root) Repair_table_statement(lex);
- if (lex->m_stmt == NULL)
+ DBUG_ASSERT(!lex->m_sql_cmd);
+ lex->m_sql_cmd= new (thd->mem_root) Sql_cmd_repair_table();
+ if (lex->m_sql_cmd == NULL)
MYSQL_YYABORT;
}
;
@@ -7255,16 +7975,103 @@ analyze:
/* Will be overriden during execution. */
YYPS->m_lock_type= TL_UNLOCK;
}
- table_list
+ analyze_table_list
{
LEX* lex= thd->lex;
- DBUG_ASSERT(!lex->m_stmt);
- lex->m_stmt= new (thd->mem_root) Analyze_table_statement(lex);
- if (lex->m_stmt == NULL)
+ DBUG_ASSERT(!lex->m_sql_cmd);
+ lex->m_sql_cmd= new (thd->mem_root) Sql_cmd_analyze_table();
+ if (lex->m_sql_cmd == NULL)
+ MYSQL_YYABORT;
+ }
+ ;
+
+analyze_table_list:
+ analyze_table_elem_spec
+ | analyze_table_list ',' analyze_table_elem_spec
+ ;
+
+analyze_table_elem_spec:
+ table_name opt_persistent_stat_clause
+ ;
+
+opt_persistent_stat_clause:
+ /* empty */
+ {}
+ | PERSISTENT_SYM FOR_SYM persistent_stat_spec
+ {
+ thd->lex->with_persistent_for_clause= TRUE;
+ }
+ ;
+
+persistent_stat_spec:
+ ALL
+ {}
+ | COLUMNS persistent_column_stat_spec INDEXES persistent_index_stat_spec
+ {}
+
+persistent_column_stat_spec:
+ ALL {}
+ | '('
+ {
+ LEX* lex= thd->lex;
+ lex->column_list= new List<LEX_STRING>;
+ if (lex->column_list == NULL)
+ MYSQL_YYABORT;
+ }
+ table_column_list
+ ')'
+ ;
+
+persistent_index_stat_spec:
+ ALL {}
+ | '('
+ {
+ LEX* lex= thd->lex;
+ lex->index_list= new List<LEX_STRING>;
+ if (lex->index_list == NULL)
MYSQL_YYABORT;
}
+ table_index_list
+ ')'
+ ;
+
+table_column_list:
+ /* empty */
+ {}
+ | ident
+ {
+ Lex->column_list->push_back((LEX_STRING*)
+ sql_memdup(&$1, sizeof(LEX_STRING)));
+ }
+ | table_column_list ',' ident
+ {
+ Lex->column_list->push_back((LEX_STRING*)
+ sql_memdup(&$3, sizeof(LEX_STRING)));
+ }
+ ;
+
+table_index_list:
+ /* empty */
+ {}
+ | table_index_name
+ | table_index_list ',' table_index_name
;
+table_index_name:
+ ident
+ {
+ Lex->index_list->push_back(
+ (LEX_STRING*) sql_memdup(&$1, sizeof(LEX_STRING)));
+ }
+ |
+ PRIMARY_SYM
+ {
+ LEX_STRING str= {(char*) "PRIMARY", 7};
+ Lex->index_list->push_back(
+ (LEX_STRING*) sql_memdup(&str, sizeof(LEX_STRING)));
+ }
+ ;
+
binlog_base64_event:
BINLOG_SYM TEXT_STRING_sys
{
@@ -7296,9 +8103,9 @@ check: CHECK_SYM
my_error(ER_SP_BADSTATEMENT, MYF(0), "CHECK");
MYSQL_YYABORT;
}
- DBUG_ASSERT(!lex->m_stmt);
- lex->m_stmt= new (thd->mem_root) Check_table_statement(lex);
- if (lex->m_stmt == NULL)
+ DBUG_ASSERT(!lex->m_sql_cmd);
+ lex->m_sql_cmd= new (thd->mem_root) Sql_cmd_check_table();
+ if (lex->m_sql_cmd == NULL)
MYSQL_YYABORT;
}
;
@@ -7341,9 +8148,9 @@ optimize:
table_list
{
LEX* lex= thd->lex;
- DBUG_ASSERT(!lex->m_stmt);
- lex->m_stmt= new (thd->mem_root) Optimize_table_statement(lex);
- if (lex->m_stmt == NULL)
+ DBUG_ASSERT(!lex->m_sql_cmd);
+ lex->m_sql_cmd= new (thd->mem_root) Sql_cmd_optimize_table();
+ if (lex->m_sql_cmd == NULL)
MYSQL_YYABORT;
}
;
@@ -7490,7 +8297,7 @@ preload_keys_parts:
adm_partition:
PARTITION_SYM have_partitioning
{
- Lex->alter_info.flags|= ALTER_ADMIN_PARTITION;
+ Lex->alter_info.flags|= Alter_info::ALTER_ADMIN_PARTITION;
}
'(' all_or_alt_part_name_list ')'
;
@@ -7771,6 +8578,12 @@ select_alias:
| TEXT_STRING_sys { $$=$1; }
;
+opt_default_time_precision:
+ /* empty */ { $$= NOT_FIXED_DEC; }
+ | '(' ')' { $$= NOT_FIXED_DEC; }
+ | '(' real_ulong_num ')' { $$= $2; };
+ ;
+
opt_time_precision:
/* empty */ { $$= 0; }
| '(' ')' { $$= 0; }
@@ -8289,7 +9102,7 @@ dyncall_create_element:
alloc_root(thd->mem_root, sizeof(DYNCALL_CREATE_DEF));
if ($$ == NULL)
MYSQL_YYABORT;
- $$->num= $1;
+ $$->key= $1;
$$->value= $3;
$$->type= (DYNAMIC_COLUMN_TYPE)$4;
$$->cs= lex->charset;
@@ -8396,7 +9209,29 @@ simple_expr:
MYSQL_YYABORT;
}
| '{' ident expr '}'
- { $$= $3; }
+ {
+ $$= NULL;
+ /*
+ If "expr" is reasonably short pure ASCII string literal,
+ try to parse known ODBC style date, time or timestamp literals,
+ e.g:
+ SELECT {d'2001-01-01'};
+ SELECT {t'10:20:30'};
+ SELECT {ts'2001-01-01 10:20:30'};
+ */
+ if ($3->type() == Item::STRING_ITEM)
+ {
+ Item_string *item= (Item_string *) $3;
+ enum_field_types type= item->odbc_temporal_literal_type(&$2);
+ if (type != MYSQL_TYPE_STRING)
+ {
+ $$= create_temporal_literal(thd, item->val_str(NULL),
+ type, false);
+ }
+ }
+ if ($$ == NULL)
+ $$= $3;
+ }
| MATCH ident_list_arg AGAINST '(' bit_expr fulltext_options ')'
{
$2->push_front($5);
@@ -8497,6 +9332,14 @@ function_call_keyword:
Lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_SYSTEM_FUNCTION);
Lex->safe_to_cache_query= 0;
}
+ | CURRENT_ROLE optional_braces
+ {
+ $$= new (thd->mem_root) Item_func_current_role(Lex->current_context());
+ if ($$ == NULL)
+ MYSQL_YYABORT;
+ Lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_SYSTEM_FUNCTION);
+ Lex->safe_to_cache_query= 0;
+ }
| DATE_SYM '(' expr ')'
{
$$= new (thd->mem_root) Item_date_typecast($3);
@@ -8840,16 +9683,9 @@ function_call_nonkeyword:
MYSQL_YYABORT;
}
|
- COLUMN_EXISTS_SYM '(' expr ',' expr ')'
- {
- $$= new (thd->mem_root) Item_func_dyncol_exists($3, $5);
- if ($$ == NULL)
- MYSQL_YYABORT;
- }
- |
- COLUMN_LIST_SYM '(' expr ')'
+ COLUMN_CHECK_SYM '(' expr ')'
{
- $$= new (thd->mem_root) Item_func_dyncol_list($3);
+ $$= new (thd->mem_root) Item_func_dyncol_check($3);
if ($$ == NULL)
MYSQL_YYABORT;
}
@@ -8968,6 +9804,20 @@ function_call_conflict:
if ($$ == NULL)
MYSQL_YYABORT;
}
+ | REVERSE_SYM '(' expr ')'
+ {
+ $$= new (thd->mem_root) Item_func_reverse($3);
+ if ($$ == NULL)
+ MYSQL_YYABORT;
+ }
+ | ROW_COUNT_SYM '(' ')'
+ {
+ $$= new (thd->mem_root) Item_func_row_count();
+ if ($$ == NULL)
+ MYSQL_YYABORT;
+ Lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_SYSTEM_FUNCTION);
+ Lex->safe_to_cache_query= 0;
+ }
| TRUNCATE_SYM '(' expr ',' expr ')'
{
$$= new (thd->mem_root) Item_func_round($3,$5,1);
@@ -8991,6 +9841,36 @@ function_call_conflict:
if ($$ == NULL)
MYSQL_YYABORT;
}
+ | WEIGHT_STRING_SYM '(' expr opt_ws_levels ')'
+ {
+ $$= new (thd->mem_root) Item_func_weight_string($3, 0, 0, $4);
+ if ($$ == NULL)
+ MYSQL_YYABORT;
+ }
+ | WEIGHT_STRING_SYM '(' expr AS CHAR_SYM ws_nweights opt_ws_levels ')'
+ {
+ $$= new (thd->mem_root)
+ Item_func_weight_string($3, 0, $6,
+ $7 | MY_STRXFRM_PAD_WITH_SPACE);
+ if ($$ == NULL)
+ MYSQL_YYABORT;
+ }
+ | WEIGHT_STRING_SYM '(' expr AS BINARY ws_nweights ')'
+ {
+ Item *item= new (thd->mem_root) Item_char_typecast($3, $6, &my_charset_bin);
+ if (item == NULL)
+ MYSQL_YYABORT;
+ $$= new (thd->mem_root)
+ Item_func_weight_string(item, 0, $6, MY_STRXFRM_PAD_WITH_SPACE);
+ if ($$ == NULL)
+ MYSQL_YYABORT;
+ }
+ | WEIGHT_STRING_SYM '(' expr ',' ulong_num ',' ulong_num ',' ulong_num ')'
+ {
+ $$= new (thd->mem_root) Item_func_weight_string($3, $5, $7, $9);
+ if ($$ == NULL)
+ MYSQL_YYABORT;
+ }
| geometry_function
{
#ifdef HAVE_SPATIAL
@@ -9653,9 +10533,7 @@ join_table:
left-associative joins.
*/
table_ref normal_join table_ref %prec TABLE_REF_PRIORITY
- { MYSQL_YYABORT_UNLESS($1 && ($$=$3)); }
- | table_ref STRAIGHT_JOIN table_factor
- { MYSQL_YYABORT_UNLESS($1 && ($$=$3)); $3->straight=1; }
+ { MYSQL_YYABORT_UNLESS($1 && ($$=$3)); $3->straight=$2; }
| table_ref normal_join table_ref
ON
{
@@ -9667,22 +10545,7 @@ join_table:
}
expr
{
- add_join_on($3,$6);
- Lex->pop_context();
- Select->parsing_place= NO_MATTER;
- }
- | table_ref STRAIGHT_JOIN table_factor
- ON
- {
- MYSQL_YYABORT_UNLESS($1 && $3);
- /* Change the current name resolution context to a local context. */
- if (push_new_name_resolution_context(thd, $1, $3))
- MYSQL_YYABORT;
- Select->parsing_place= IN_ON;
- }
- expr
- {
- $3->straight=1;
+ $3->straight=$2;
add_join_on($3,$6);
Lex->pop_context();
Select->parsing_place= NO_MATTER;
@@ -9693,10 +10556,15 @@ join_table:
MYSQL_YYABORT_UNLESS($1 && $3);
}
'(' using_list ')'
- { add_join_natural($1,$3,$7,Select); $$=$3; }
- | table_ref NATURAL JOIN_SYM table_factor
+ {
+ $3->straight=$2;
+ add_join_natural($1,$3,$7,Select);
+ $$=$3;
+ }
+ | table_ref NATURAL inner_join table_factor
{
MYSQL_YYABORT_UNLESS($1 && ($$=$4));
+ $4->straight=$3;
add_join_natural($1,$4,NULL,Select);
}
@@ -9776,12 +10644,34 @@ join_table:
}
;
+
+inner_join: /* $$ set if using STRAIGHT_JOIN, false otherwise */
+ JOIN_SYM { $$ = 0; }
+ | INNER_SYM JOIN_SYM { $$ = 0; }
+ | STRAIGHT_JOIN { $$ = 1; }
+ ;
+
normal_join:
- JOIN_SYM {}
- | INNER_SYM JOIN_SYM {}
- | CROSS JOIN_SYM {}
+ inner_join { $$ = $1; }
+ | CROSS JOIN_SYM { $$ = 0; }
;
+/*
+ table PARTITION (list of partitions), reusing using_list instead of creating
+ a new rule for partition_list.
+*/
+opt_use_partition:
+ /* empty */ { $$= 0;}
+ | use_partition
+ ;
+
+use_partition:
+ PARTITION_SYM '(' using_list ')' have_partitioning
+ {
+ $$= $3;
+ }
+ ;
+
/*
This is a flattening of the rules <table factor> and <table primary>
in the SQL:2003 standard, since we don't have <sample clause>
@@ -9795,13 +10685,14 @@ table_factor:
SELECT_LEX *sel= Select;
sel->table_join_options= 0;
}
- table_ident opt_table_alias opt_key_definition
+ table_ident opt_use_partition opt_table_alias opt_key_definition
{
- if (!($$= Select->add_table_to_list(thd, $2, $3,
+ if (!($$= Select->add_table_to_list(thd, $2, $4,
Select->get_table_join_options(),
YYPS->m_lock_type,
YYPS->m_mdl_type,
- Select->pop_index_hints())))
+ Select->pop_index_hints(),
+ $3)))
MYSQL_YYABORT;
Select->add_joined_table($$);
}
@@ -9871,7 +10762,7 @@ table_factor:
if (ti == NULL)
MYSQL_YYABORT;
if (!($$= sel->add_table_to_list(lex->thd,
- new Table_ident(unit), $5, 0,
+ ti, $5, 0,
TL_READ, MDL_SHARED_READ)))
MYSQL_YYABORT;
@@ -10263,8 +11154,8 @@ opt_escape:
{
Lex->escape_used= FALSE;
$$= ((thd->variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES) ?
- new (thd->mem_root) Item_string("", 0, &my_charset_latin1) :
- new (thd->mem_root) Item_string("\\", 1, &my_charset_latin1));
+ new (thd->mem_root) Item_string_ascii("", 0) :
+ new (thd->mem_root) Item_string_ascii("\\", 1));
if ($$ == NULL)
MYSQL_YYABORT;
}
@@ -10427,7 +11318,10 @@ opt_limit_clause:
limit_clause:
LIMIT limit_options
{
- Lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_LIMIT);
+ SELECT_LEX *sel= Select;
+ if (!sel->select_limit->basic_const_item() ||
+ sel->select_limit->val_int() > 0)
+ Lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_LIMIT);
}
| LIMIT limit_options ROWS_SYM EXAMINED_SYM limit_rows_option
{
@@ -10469,9 +11363,9 @@ limit_option:
Item_splocal *splocal;
LEX *lex= thd->lex;
Lex_input_stream *lip= & thd->m_parser_state->m_lip;
- sp_variable_t *spv;
+ sp_variable *spv;
sp_pcontext *spc = lex->spcont;
- if (spc && (spv = spc->find_variable(&$1)))
+ if (spc && (spv = spc->find_variable($1, false)))
{
splocal= new (thd->mem_root)
Item_splocal($1, spv->offset, spv->type,
@@ -10701,9 +11595,9 @@ select_var_ident:
| ident_or_text
{
LEX *lex=Lex;
- sp_variable_t *t;
+ sp_variable *t;
- if (!lex->spcont || !(t=lex->spcont->find_variable(&$1)))
+ if (!lex->spcont || !(t=lex->spcont->find_variable($1, false)))
{
my_error(ER_SP_UNDECLARED_VAR, MYF(0), $1.str);
MYSQL_YYABORT;
@@ -10793,41 +11687,41 @@ do:
*/
drop:
- DROP opt_temporary table_or_tables if_exists
+ DROP opt_temporary table_or_tables opt_if_exists
{
LEX *lex=Lex;
lex->sql_command = SQLCOM_DROP_TABLE;
lex->drop_temporary= $2;
- lex->drop_if_exists= $4;
+ lex->check_exists= $4;
YYPS->m_lock_type= TL_UNLOCK;
YYPS->m_mdl_type= MDL_EXCLUSIVE;
}
table_list opt_restrict
{}
- | DROP INDEX_SYM ident ON table_ident {}
+ | DROP INDEX_SYM opt_if_exists ident ON table_ident {}
{
LEX *lex=Lex;
- Alter_drop *ad= new Alter_drop(Alter_drop::KEY, $3.str);
+ Alter_drop *ad= new Alter_drop(Alter_drop::KEY, $4.str, $3);
if (ad == NULL)
MYSQL_YYABORT;
lex->sql_command= SQLCOM_DROP_INDEX;
lex->alter_info.reset();
- lex->alter_info.flags= ALTER_DROP_INDEX;
+ lex->alter_info.flags= Alter_info::ALTER_DROP_INDEX;
lex->alter_info.drop_list.push_back(ad);
- if (!lex->current_select->add_table_to_list(lex->thd, $5, NULL,
+ if (!lex->current_select->add_table_to_list(lex->thd, $6, NULL,
TL_OPTION_UPDATING,
TL_READ_NO_INSERT,
- MDL_SHARED_NO_WRITE))
+ MDL_SHARED_UPGRADABLE))
MYSQL_YYABORT;
}
- | DROP DATABASE if_exists ident
+ | DROP DATABASE opt_if_exists ident
{
LEX *lex=Lex;
lex->sql_command= SQLCOM_DROP_DB;
- lex->drop_if_exists=$3;
+ lex->check_exists=$3;
lex->name= $4;
}
- | DROP FUNCTION_SYM if_exists ident '.' ident
+ | DROP FUNCTION_SYM opt_if_exists ident '.' ident
{
LEX *lex= thd->lex;
sp_name *spname;
@@ -10842,14 +11736,14 @@ drop:
MYSQL_YYABORT;
}
lex->sql_command = SQLCOM_DROP_FUNCTION;
- lex->drop_if_exists= $3;
+ lex->check_exists= $3;
spname= new sp_name($4, $6, true);
if (spname == NULL)
MYSQL_YYABORT;
spname->init_qname(thd);
lex->spname= spname;
}
- | DROP FUNCTION_SYM if_exists ident
+ | DROP FUNCTION_SYM opt_if_exists ident
{
LEX *lex= thd->lex;
LEX_STRING db= {0, 0};
@@ -10862,14 +11756,14 @@ drop:
if (thd->db && lex->copy_db_to(&db.str, &db.length))
MYSQL_YYABORT;
lex->sql_command = SQLCOM_DROP_FUNCTION;
- lex->drop_if_exists= $3;
+ lex->check_exists= $3;
spname= new sp_name(db, $4, false);
if (spname == NULL)
MYSQL_YYABORT;
spname->init_qname(thd);
lex->spname= spname;
}
- | DROP PROCEDURE_SYM if_exists sp_name
+ | DROP PROCEDURE_SYM opt_if_exists sp_name
{
LEX *lex=Lex;
if (lex->sphead)
@@ -10878,34 +11772,38 @@ drop:
MYSQL_YYABORT;
}
lex->sql_command = SQLCOM_DROP_PROCEDURE;
- lex->drop_if_exists= $3;
+ lex->check_exists= $3;
lex->spname= $4;
}
| DROP USER clear_privileges user_list
{
Lex->sql_command = SQLCOM_DROP_USER;
}
- | DROP VIEW_SYM if_exists
+ | DROP ROLE_SYM clear_privileges role_list
+ {
+ Lex->sql_command = SQLCOM_DROP_ROLE;
+ }
+ | DROP VIEW_SYM opt_if_exists
{
LEX *lex= Lex;
lex->sql_command= SQLCOM_DROP_VIEW;
- lex->drop_if_exists= $3;
+ lex->check_exists= $3;
YYPS->m_lock_type= TL_UNLOCK;
YYPS->m_mdl_type= MDL_EXCLUSIVE;
}
table_list opt_restrict
{}
- | DROP EVENT_SYM if_exists sp_name
+ | DROP EVENT_SYM opt_if_exists sp_name
{
- Lex->drop_if_exists= $3;
+ Lex->check_exists= $3;
Lex->spname= $4;
Lex->sql_command = SQLCOM_DROP_EVENT;
}
- | DROP TRIGGER_SYM if_exists sp_name
+ | DROP TRIGGER_SYM opt_if_exists sp_name
{
LEX *lex= Lex;
lex->sql_command= SQLCOM_DROP_TRIGGER;
- lex->drop_if_exists= $3;
+ lex->check_exists= $3;
lex->spname= $4;
}
| DROP TABLESPACE tablespace_name opt_ts_engine opt_ts_wait
@@ -10918,10 +11816,10 @@ drop:
LEX *lex= Lex;
lex->alter_tablespace_info->ts_cmd_type= DROP_LOGFILE_GROUP;
}
- | DROP SERVER_SYM if_exists ident_or_text
+ | DROP SERVER_SYM opt_if_exists ident_or_text
{
Lex->sql_command = SQLCOM_DROP_SERVER;
- Lex->drop_if_exists= $3;
+ Lex->check_exists= $3;
Lex->server_options.server_name= $4.str;
Lex->server_options.server_name_length= $4.length;
}
@@ -10943,6 +11841,19 @@ table_name:
}
;
+table_name_with_opt_use_partition:
+ table_ident opt_use_partition
+ {
+ if (!Select->add_table_to_list(thd, $1, NULL,
+ TL_OPTION_UPDATING,
+ YYPS->m_lock_type,
+ YYPS->m_mdl_type,
+ NULL,
+ $2))
+ MYSQL_YYABORT;
+ }
+ ;
+
table_alias_ref_list:
table_alias_ref
| table_alias_ref_list ',' table_alias_ref
@@ -10959,9 +11870,17 @@ table_alias_ref:
}
;
-if_exists:
- /* empty */ { $$= 0; }
- | IF EXISTS { $$= 1; }
+opt_if_exists:
+ /* empty */
+ {
+ Lex->check_exists= FALSE;
+ $$= 0;
+ }
+ | IF EXISTS
+ {
+ Lex->check_exists= TRUE;
+ $$= 1;
+ }
;
opt_temporary:
@@ -11047,7 +11966,7 @@ insert2:
;
insert_table:
- table_name
+ table_name_with_opt_use_partition
{
LEX *lex=Lex;
lex->field_list.empty();
@@ -11247,17 +12166,20 @@ delete:
;
single_multi:
- FROM table_ident
+ FROM table_ident opt_use_partition
{
if (!Select->add_table_to_list(thd, $2, NULL, TL_OPTION_UPDATING,
YYPS->m_lock_type,
- YYPS->m_mdl_type))
+ YYPS->m_mdl_type,
+ NULL,
+ $3))
MYSQL_YYABORT;
YYPS->m_lock_type= TL_READ_DEFAULT;
YYPS->m_mdl_type= MDL_SHARED_READ;
}
where_clause opt_order_clause
delete_limit_clause {}
+ opt_select_expressions {}
| table_wild_list
{
mysql_init_multi_delete(Lex);
@@ -11282,6 +12204,11 @@ single_multi:
}
;
+opt_select_expressions:
+ /* empty */
+ | RETURNING_SYM select_item_list
+ ;
+
table_wild_list:
table_wild_one
| table_wild_list ',' table_wild_one
@@ -11347,9 +12274,9 @@ truncate:
table_name
{
LEX* lex= thd->lex;
- DBUG_ASSERT(!lex->m_stmt);
- lex->m_stmt= new (thd->mem_root) Truncate_statement(lex);
- if (lex->m_stmt == NULL)
+ DBUG_ASSERT(!lex->m_sql_cmd);
+ lex->m_sql_cmd= new (thd->mem_root) Sql_cmd_truncate_table();
+ if (lex->m_sql_cmd == NULL)
MYSQL_YYABORT;
}
;
@@ -11424,6 +12351,7 @@ show:
{
LEX *lex=Lex;
lex->wild=0;
+ lex->ident=null_lex_str;
mysql_init_select(lex);
lex->current_select->parsing_place= SELECT_LIST;
bzero((char*) &lex->create_info,sizeof(lex->create_info));
@@ -11489,6 +12417,19 @@ show_param:
if (prepare_schema_table(thd, lex, 0, SCH_PLUGINS))
MYSQL_YYABORT;
}
+ | PLUGINS_SYM SONAME_SYM TEXT_STRING_sys
+ {
+ Lex->ident= $3;
+ Lex->sql_command= SQLCOM_SHOW_PLUGINS;
+ if (prepare_schema_table(thd, Lex, 0, SCH_ALL_PLUGINS))
+ MYSQL_YYABORT;
+ }
+ | PLUGINS_SYM SONAME_SYM wild_and_where
+ {
+ Lex->sql_command= SQLCOM_SHOW_PLUGINS;
+ if (prepare_schema_table(thd, Lex, 0, SCH_ALL_PLUGINS))
+ MYSQL_YYABORT;
+ }
| ENGINE_SYM known_storage_engines show_engine_param
{ Lex->create_info.db_type= $2; }
| ENGINE_SYM ALL show_engine_param
@@ -11515,7 +12456,7 @@ show_param:
LEX *lex= Lex;
lex->sql_command= SQLCOM_SHOW_BINLOG_EVENTS;
} opt_limit_clause_init
- | RELAYLOG_SYM EVENTS_SYM binlog_in binlog_from
+ | RELAYLOG_SYM optional_connection_name EVENTS_SYM binlog_in binlog_from
{
LEX *lex= Lex;
lex->sql_command= SQLCOM_SHOW_RELAYLOG_EVENTS;
@@ -11540,19 +12481,11 @@ show_param:
{
LEX *lex=Lex;
lex->sql_command= SQLCOM_SHOW_AUTHORS;
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
- ER_WARN_DEPRECATED_SYNTAX_NO_REPLACEMENT,
- ER(ER_WARN_DEPRECATED_SYNTAX_NO_REPLACEMENT),
- "SHOW AUTHORS");
}
| CONTRIBUTORS_SYM
{
LEX *lex=Lex;
lex->sql_command= SQLCOM_SHOW_CONTRIBUTORS;
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
- ER_WARN_DEPRECATED_SYNTAX_NO_REPLACEMENT,
- ER(ER_WARN_DEPRECATED_SYNTAX_NO_REPLACEMENT),
- "SHOW CONTRIBUTORS");
}
| PRIVILEGES
{
@@ -11610,20 +12543,16 @@ show_param:
}
| GRANTS
{
- LEX *lex=Lex;
- lex->sql_command= SQLCOM_SHOW_GRANTS;
- LEX_USER *curr_user;
- if (!(curr_user= (LEX_USER*) lex->thd->alloc(sizeof(st_lex_user))))
+ Lex->sql_command= SQLCOM_SHOW_GRANTS;
+ if (!(Lex->grant_user= (LEX_USER*)thd->alloc(sizeof(LEX_USER))))
MYSQL_YYABORT;
- bzero(curr_user, sizeof(st_lex_user));
- lex->grant_user= curr_user;
+ Lex->grant_user->user= current_user_and_current_role;
}
- | GRANTS FOR_SYM user
+ | GRANTS FOR_SYM user_or_role
{
LEX *lex=Lex;
lex->sql_command= SQLCOM_SHOW_GRANTS;
lex->grant_user=$3;
- lex->grant_user->password=null_lex_str;
}
| CREATE DATABASE opt_if_not_exists ident
{
@@ -11651,9 +12580,22 @@ show_param:
{
Lex->sql_command = SQLCOM_SHOW_MASTER_STAT;
}
+ | ALL SLAVES STATUS_SYM
+ {
+ Lex->sql_command = SQLCOM_SHOW_SLAVE_STAT;
+ Lex->verbose= 1;
+ }
| SLAVE STATUS_SYM
{
+ LEX *lex= thd->lex;
+ lex->mi.connection_name= thd->variables.default_master_connection;
+ lex->sql_command = SQLCOM_SHOW_SLAVE_STAT;
+ lex->verbose= 0;
+ }
+ | SLAVE connection_name STATUS_SYM
+ {
Lex->sql_command = SQLCOM_SHOW_SLAVE_STAT;
+ Lex->verbose= 0;
}
| CLIENT_STATS_SYM
{
@@ -11732,6 +12674,13 @@ show_param:
Lex->spname= $3;
Lex->sql_command = SQLCOM_SHOW_CREATE_EVENT;
}
+ | describe_command FOR_SYM expr
+ {
+ Lex->sql_command= SQLCOM_SHOW_EXPLAIN;
+ if (prepare_schema_table(thd, Lex, 0, SCH_EXPLAIN))
+ MYSQL_YYABORT;
+ add_value_to_list(thd, $3);
+ }
;
show_engine_param:
@@ -11814,13 +12763,21 @@ describe:
}
| describe_command opt_extended_describe
{ Lex->describe|= DESCRIBE_NORMAL; }
- select
+ explainable_command
{
LEX *lex=Lex;
lex->select_lex.options|= SELECT_DESCRIBE;
}
;
+explainable_command:
+ select
+ | insert
+ | replace
+ | update
+ | delete
+ ;
+
describe_command:
DESC
| DESCRIBE
@@ -11871,24 +12828,36 @@ flush_options:
YYPS->m_lock_type= TL_READ_NO_INSERT;
YYPS->m_mdl_type= MDL_SHARED_HIGH_PRIO;
}
- opt_table_list {}
- opt_with_read_lock {}
+ opt_table_list opt_flush_lock
| flush_options_list
;
-opt_with_read_lock:
+opt_flush_lock:
/* empty */ {}
- | WITH READ_SYM LOCK_SYM optional_flush_tables_arguments
+ | flush_lock
+ {
+ TABLE_LIST *tables= Lex->query_tables;
+ for (; tables; tables= tables->next_global)
{
- TABLE_LIST *tables= Lex->query_tables;
- Lex->type|= REFRESH_READ_LOCK | $4;
- for (; tables; tables= tables->next_global)
- {
- tables->mdl_request.set_type(MDL_SHARED_NO_WRITE);
- tables->required_type= FRMTYPE_TABLE; /* Don't try to flush views. */
- tables->open_type= OT_BASE_ONLY; /* Ignore temporary tables. */
- }
+ tables->mdl_request.set_type(MDL_SHARED_NO_WRITE);
+ tables->required_type= FRMTYPE_TABLE; /* Don't try to flush views. */
+ tables->open_type= OT_BASE_ONLY; /* Ignore temporary tables. */
}
+ }
+ ;
+
+flush_lock:
+ WITH READ_SYM LOCK_SYM optional_flush_tables_arguments
+ { Lex->type|= REFRESH_READ_LOCK | $4; }
+ | FOR_SYM
+ {
+ if (Lex->query_tables == NULL) // Table list can't be empty
+ {
+ my_parse_error(ER(ER_NO_TABLES_USED));
+ MYSQL_YYABORT;
+ }
+ Lex->type|= REFRESH_FOR_EXPORT;
+ } EXPORT_SYM {}
;
flush_options_list:
@@ -11908,8 +12877,17 @@ flush_option:
{ Lex->type|= REFRESH_SLOW_LOG; }
| BINARY LOGS_SYM
{ Lex->type|= REFRESH_BINARY_LOG; }
- | RELAY LOGS_SYM
- { Lex->type|= REFRESH_RELAY_LOG; }
+ | RELAY LOGS_SYM optional_connection_name
+ {
+ LEX *lex= Lex;
+ if (lex->type & REFRESH_RELAY_LOG)
+ {
+ my_error(ER_WRONG_USAGE, MYF(0), "FLUSH", "RELAY LOGS");
+ MYSQL_YYABORT;
+ }
+ lex->type|= REFRESH_RELAY_LOG;
+ lex->relay_log_connection_name= lex->mi.connection_name;
+ }
| QUERY_SYM CACHE_SYM
{ Lex->type|= REFRESH_QUERY_CACHE_FREE; }
| HOSTS_SYM
@@ -11917,13 +12895,23 @@ flush_option:
| PRIVILEGES
{ Lex->type|= REFRESH_GRANT; }
| LOGS_SYM
- { Lex->type|= REFRESH_LOG; }
+ {
+ Lex->type|= REFRESH_LOG;
+ Lex->relay_log_connection_name.str= (char*) "";
+ Lex->relay_log_connection_name.length= 0;
+ }
| STATUS_SYM
{ Lex->type|= REFRESH_STATUS; }
- | SLAVE
+ | SLAVE optional_connection_name
{
- Lex->type|= REFRESH_SLAVE;
- Lex->reset_slave_info.all= false;
+ LEX *lex= Lex;
+ if (lex->type & REFRESH_SLAVE)
+ {
+ my_error(ER_WRONG_USAGE, MYF(0), "FLUSH","SLAVE");
+ MYSQL_YYABORT;
+ }
+ lex->type|= REFRESH_SLAVE;
+ lex->reset_slave_info.all= false;
}
| CLIENT_STATS_SYM
{ Lex->type|= REFRESH_CLIENT_STATS; }
@@ -11967,6 +12955,7 @@ reset_options:
reset_option:
SLAVE { Lex->type|= REFRESH_SLAVE; }
+ optional_connection_name
slave_reset_options { }
| MASTER_SYM { Lex->type|= REFRESH_MASTER; }
| QUERY_SYM CACHE_SYM { Lex->type|= REFRESH_QUERY_CACHE;}
@@ -12015,6 +13004,7 @@ kill:
lex->value_list.empty();
lex->users_list.empty();
lex->sql_command= SQLCOM_KILL;
+ lex->kill_type= KILL_TYPE_ID;
}
kill_type kill_option kill_expr
{
@@ -12031,13 +13021,17 @@ kill_option:
/* empty */ { $$= (int) KILL_CONNECTION; }
| CONNECTION_SYM { $$= (int) KILL_CONNECTION; }
| QUERY_SYM { $$= (int) KILL_QUERY; }
+ | QUERY_SYM ID_SYM
+ {
+ $$= (int) KILL_QUERY;
+ Lex->kill_type= KILL_TYPE_QUERY;
+ }
;
kill_expr:
expr
{
Lex->value_list.push_front($$);
- Lex->kill_type= KILL_TYPE_ID;
}
| USER user
{
@@ -12046,6 +13040,11 @@ kill_expr:
}
;
+
+shutdown:
+ SHUTDOWN { Lex->sql_command= SQLCOM_SHUTDOWN; }
+ ;
+
/* change database */
use:
@@ -12081,18 +13080,18 @@ load:
if (!(lex->exchange= new sql_exchange($7.str, 0, $2)))
MYSQL_YYABORT;
}
- opt_duplicate INTO TABLE_SYM table_ident
+ opt_duplicate INTO TABLE_SYM table_ident opt_use_partition
{
LEX *lex=Lex;
if (!Select->add_table_to_list(thd, $12, NULL, TL_OPTION_UPDATING,
- $4, MDL_SHARED_WRITE))
+ $4, MDL_SHARED_WRITE, NULL, $13))
MYSQL_YYABORT;
lex->field_list.empty();
lex->update_list.empty();
lex->value_list.empty();
}
opt_load_data_charset
- { Lex->exchange->cs= $14; }
+ { Lex->exchange->cs= $15; }
opt_xml_rows_identified_by
opt_field_term opt_line_term opt_ignore_lines opt_field_or_var_spec
opt_load_data_set_spec
@@ -12290,14 +13289,10 @@ text_literal:
}
| UNDERSCORE_CHARSET TEXT_STRING
{
- Item_string *str= new (thd->mem_root) Item_string($2.str,
+ $$= new (thd->mem_root) Item_string_with_introducer($2.str,
$2.length, $1);
- if (str == NULL)
+ if ($$ == NULL)
MYSQL_YYABORT;
- str->set_repertoire_from_value();
- str->set_cs_specified(TRUE);
-
- $$= str;
}
| text_literal TEXT_STRING_literal
{
@@ -12326,7 +13321,12 @@ text_string:
if ($$ == NULL)
MYSQL_YYABORT;
}
- | HEX_NUM
+ | hex_or_bin_String { $$= $1; }
+ ;
+
+
+hex_or_bin_String:
+ HEX_NUM
{
Item *tmp= new (thd->mem_root) Item_hex_hybrid($1.str, $1.length);
if (tmp == NULL)
@@ -12393,8 +13393,16 @@ signed_literal:
literal:
text_literal { $$ = $1; }
| NUM_literal { $$ = $1; }
+ | temporal_literal { $$= $1; }
| NULL_SYM
{
+ /*
+ For the digest computation, in this context only,
+ NULL is considered a literal, hence reduced to '?'
+ REDUCE:
+ TOK_GENERIC_VALUE := NULL_SYM
+ */
+ YYLIP->reduce_digest_token(TOK_GENERIC_VALUE, NULL_SYM);
$$ = new (thd->mem_root) Item_null();
if ($$ == NULL)
MYSQL_YYABORT;
@@ -12430,66 +13438,20 @@ literal:
if ($$ == NULL)
MYSQL_YYABORT;
}
- | UNDERSCORE_CHARSET hex_num_or_string
- {
- Item *tmp= new (thd->mem_root) Item_hex_string($2.str, $2.length);
- if (tmp == NULL)
- MYSQL_YYABORT;
- /*
- it is OK only emulate fix_fieds, because we need only
- value of constant
- */
- tmp->quick_fix_field();
- String *str= tmp->val_str((String*) 0);
-
- Item_string *item_str;
- item_str= new (thd->mem_root)
- 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_repertoire_from_value();
- item_str->set_cs_specified(TRUE);
-
- $$= item_str;
- }
- | UNDERSCORE_CHARSET BIN_NUM
+ | UNDERSCORE_CHARSET hex_or_bin_String
{
- Item *tmp= new (thd->mem_root) Item_bin_string($2.str, $2.length);
- if (tmp == NULL)
- MYSQL_YYABORT;
+ Item_string_with_introducer *item_str;
/*
- it is OK only emulate fix_fieds, because we need only
- value of constant
+ Pass NULL as name. Name will be set in the "select_item" rule and
+ will include the introducer and the original hex/bin notation.
*/
- tmp->quick_fix_field();
- String *str= tmp->val_str((String*) 0);
-
- Item_string *item_str;
item_str= new (thd->mem_root)
- 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))
- {
+ Item_string_with_introducer(NULL, $2->ptr(), $2->length(), $1);
+ if (!item_str || !item_str->check_well_formed_result(true))
MYSQL_YYABORT;
- }
-
- item_str->set_cs_specified(TRUE);
$$= item_str;
}
- | DATE_SYM text_literal { $$ = $2; }
- | TIME_SYM text_literal { $$ = $2; }
- | TIMESTAMP text_literal { $$ = $2; }
;
NUM_literal:
@@ -12538,6 +13500,31 @@ NUM_literal:
}
;
+
+temporal_literal:
+ DATE_SYM TEXT_STRING
+ {
+ if (!($$= create_temporal_literal(thd, $2.str, $2.length, YYCSCL,
+ MYSQL_TYPE_DATE, true)))
+ MYSQL_YYABORT;
+ }
+ | TIME_SYM TEXT_STRING
+ {
+ if (!($$= create_temporal_literal(thd, $2.str, $2.length, YYCSCL,
+ MYSQL_TYPE_TIME, true)))
+ MYSQL_YYABORT;
+ }
+ | TIMESTAMP TEXT_STRING
+ {
+ if (!($$= create_temporal_literal(thd, $2.str, $2.length, YYCSCL,
+ MYSQL_TYPE_DATETIME, true)))
+ MYSQL_YYABORT;
+ }
+ ;
+
+
+
+
/**********************************************************************
** Creating different items.
**********************************************************************/
@@ -12580,9 +13567,9 @@ simple_ident:
{
LEX *lex= thd->lex;
Lex_input_stream *lip= YYLIP;
- sp_variable_t *spv;
+ sp_variable *spv;
sp_pcontext *spc = lex->spcont;
- if (spc && (spv = spc->find_variable(&$1)))
+ if (spc && (spv = spc->find_variable($1, false)))
{
/* We're compiling a stored procedure and found a variable */
if (! lex->parsing_options.allows_variable)
@@ -12956,14 +13943,13 @@ ident_or_text:
| LEX_HOSTNAME { $$=$1;}
;
-user:
+user_maybe_role:
ident_or_text
{
if (!($$=(LEX_USER*) thd->alloc(sizeof(st_lex_user))))
MYSQL_YYABORT;
$$->user = $1;
- $$->host.str= (char *) "%";
- $$->host.length= 1;
+ $$->host= null_lex_str; // User or Role, see get_current_user()
$$->password= null_lex_str;
$$->plugin= empty_lex_str;
$$->auth= empty_lex_str;
@@ -12987,26 +13973,44 @@ user:
system_charset_info, 0) ||
check_host_name(&$$->host))
MYSQL_YYABORT;
- /*
- Convert hostname part of username to lowercase.
- It's OK to use in-place lowercase as long as
- the character set is utf8.
- */
- my_casedn_str(system_charset_info, $$->host.str);
+ if ($$->host.str[0])
+ {
+ /*
+ Convert hostname part of username to lowercase.
+ It's OK to use in-place lowercase as long as
+ the character set is utf8.
+ */
+ my_casedn_str(system_charset_info, $$->host.str);
+ }
+ else
+ {
+ /*
+ fix historical undocumented convention that empty host is the
+ same as '%'
+ */
+ $$->host= host_not_specified;
+ }
}
| CURRENT_USER optional_braces
{
- if (!($$=(LEX_USER*) thd->alloc(sizeof(st_lex_user))))
+ if (!($$=(LEX_USER*)thd->calloc(sizeof(LEX_USER))))
MYSQL_YYABORT;
- /*
- empty LEX_USER means current_user and
- will be handled in the get_current_user() function
- later
- */
- bzero($$, sizeof(LEX_USER));
+ $$->user= current_user;
+ $$->plugin= empty_lex_str;
+ $$->auth= empty_lex_str;
}
;
+user_or_role: user_maybe_role | current_role;
+
+user: user_maybe_role
+ {
+ if ($1->user.str != current_user.str && $1->host.str == 0)
+ $1->host= host_not_specified;
+ $$= $1;
+ }
+ ;
+
/* Keyword that we allow for identifiers (except SP labels) */
keyword:
keyword_sp {}
@@ -13020,11 +14024,10 @@ keyword:
| CHECKPOINT_SYM {}
| CLOSE_SYM {}
| COLUMN_ADD_SYM {}
+ | COLUMN_CHECK_SYM {}
| COLUMN_CREATE_SYM {}
| COLUMN_DELETE_SYM {}
- | COLUMN_EXISTS_SYM {}
| COLUMN_GET_SYM {}
- | COLUMN_LIST_SYM {}
| COMMENT_SYM {}
| COMMIT_SYM {}
| CONTAINS_SYM {}
@@ -13034,6 +14037,7 @@ keyword:
| EXAMINED_SYM {}
| EXECUTE_SYM {}
| FLUSH_SYM {}
+ | GET_SYM {}
| HANDLER_SYM {}
| HELP_SYM {}
| HOST_SYM {}
@@ -13045,7 +14049,6 @@ keyword:
| OPTIONS_SYM {}
| OWNER_SYM {}
| PARSER_SYM {}
- | PARTITION_SYM {}
| PORT_SYM {}
| PREPARE_SYM {}
| REMOVE_SYM {}
@@ -13056,9 +14059,11 @@ keyword:
| SAVEPOINT_SYM {}
| SECURITY_SYM {}
| SERVER_SYM {}
+ | SHUTDOWN {}
| SIGNED_SYM {}
| SOCKET_SYM {}
| SLAVE {}
+ | SLAVES {}
| SONAME_SYM {}
| START_SYM {}
| STOP_SYM {}
@@ -13079,6 +14084,7 @@ keyword:
keyword_sp:
ACTION {}
| ADDDATE_SYM {}
+ | ADMIN_SYM {}
| AFTER_SYM {}
| AGAINST {}
| AGGREGATE_SYM {}
@@ -13089,6 +14095,7 @@ keyword_sp:
| AUTHORS_SYM {}
| AUTO_INC {}
| AUTOEXTEND_SIZE_SYM {}
+ | AUTO_SYM {}
| AVG_ROW_LENGTH {}
| AVG_SYM {}
| BINLOG_SYM {}
@@ -13122,8 +14129,14 @@ keyword_sp:
| CONSTRAINT_NAME_SYM {}
| CONTEXT_SYM {}
| CONTRIBUTORS_SYM {}
+ | CURRENT_POS_SYM {}
| CPU_SYM {}
| CUBE_SYM {}
+ /*
+ Although a reserved keyword in SQL:2003 (and :2008),
+ not reserved in MySQL per WL#2111 specification.
+ */
+ | CURRENT_SYM {}
| CURSOR_NAME_SYM {}
| DATA_SYM {}
| DATAFILE_SYM {}
@@ -13133,6 +14146,7 @@ keyword_sp:
| DEFINER_SYM {}
| DELAY_KEY_WRITE_SYM {}
| DES_KEY_FILE {}
+ | DIAGNOSTICS_SYM {}
| DIRECTORY_SYM {}
| DISABLE_SYM {}
| DISCARD {}
@@ -13150,7 +14164,9 @@ keyword_sp:
| EVENT_SYM {}
| EVENTS_SYM {}
| EVERY_SYM {}
+ | EXCHANGE_SYM {}
| EXPANSION_SYM {}
+ | EXPORT_SYM {}
| EXTENDED_SYM {}
| EXTENT_SIZE_SYM {}
| FAULTS_SYM {}
@@ -13172,6 +14188,7 @@ keyword_sp:
| HARD_SYM {}
| HOSTS_SYM {}
| HOUR_SYM {}
+ | ID_SYM {}
| IDENTIFIED_SYM {}
| IGNORE_SERVER_IDS_SYM {}
| INDEX_STATS_SYM {}
@@ -13199,11 +14216,13 @@ keyword_sp:
| MAX_ROWS {}
| MASTER_SYM {}
| MASTER_HEARTBEAT_PERIOD_SYM {}
+ | MASTER_GTID_POS_SYM {}
| MASTER_HOST_SYM {}
| MASTER_PORT_SYM {}
| MASTER_LOG_FILE_SYM {}
| MASTER_LOG_POS_SYM {}
| MASTER_USER_SYM {}
+ | MASTER_USE_GTID_SYM {}
| MASTER_PASSWORD_SYM {}
| MASTER_SERVER_ID_SYM {}
| MASTER_CONNECT_RETRY_SYM {}
@@ -13212,6 +14231,8 @@ keyword_sp:
| MASTER_SSL_CAPATH_SYM {}
| MASTER_SSL_CERT_SYM {}
| MASTER_SSL_CIPHER_SYM {}
+ | MASTER_SSL_CRL_SYM {}
+ | MASTER_SSL_CRLPATH_SYM {}
| MASTER_SSL_KEY_SYM {}
| MAX_CONNECTIONS_PER_HOUR {}
| MAX_QUERIES_PER_HOUR {}
@@ -13245,12 +14266,13 @@ keyword_sp:
| NO_WAIT_SYM {}
| NODEGROUP_SYM {}
| NONE_SYM {}
+ | NUMBER_SYM {}
| NVARCHAR_SYM {}
| OFFSET_SYM {}
| OLD_PASSWORD {}
- | ONE_SHOT_SYM {}
| ONE_SYM {}
| ONLINE_SYM {}
+ | ONLY_SYM {}
| PACK_KEYS_SYM {}
| PAGE_SYM {}
| PARTIAL {}
@@ -13291,10 +14313,14 @@ keyword_sp:
| REPLICATION {}
| RESOURCES {}
| RESUME_SYM {}
+ | RETURNED_SQLSTATE_SYM {}
| RETURNS_SYM {}
+ | REVERSE_SYM {}
+ | ROLE_SYM {}
| ROLLUP_SYM {}
| ROUTINE_SYM {}
| ROWS_SYM {}
+ | ROW_COUNT_SYM {}
| ROW_FORMAT_SYM {}
| ROW_SYM {}
| RTREE_SYM {}
@@ -13306,7 +14332,7 @@ keyword_sp:
| SESSION_SYM {}
| SIMPLE_SYM {}
| SHARE_SYM {}
- | SHUTDOWN {}
+ | SLAVE_POS_SYM {}
| SLOW {}
| SNAPSHOT_SYM {}
| SOFT_SYM {}
@@ -13365,6 +14391,7 @@ keyword_sp:
| WARNINGS {}
| WAIT_SYM {}
| WEEK_SYM {}
+ | WEIGHT_STRING_SYM {}
| WORK_SYM {}
| X509_SYM {}
| XML_SYM {}
@@ -13372,125 +14399,112 @@ keyword_sp:
| VIA_SYM {}
;
-/* Option functions */
+/*
+ SQLCOM_SET_OPTION statement.
+
+ Note that to avoid shift/reduce conflicts, we have separate rules for the
+ first option listed in the statement.
+*/
set:
- SET opt_option
+ SET
{
LEX *lex=Lex;
lex->sql_command= SQLCOM_SET_OPTION;
mysql_init_select(lex);
lex->option_type=OPT_SESSION;
lex->var_list.empty();
- lex->one_shot_set= 0;
lex->autocommit= 0;
+ sp_create_assignment_lex(thd, yychar == YYEMPTY);
}
- option_value_list
+ start_option_value_list
{}
;
-opt_option:
- /* empty */ {}
- | OPTION {}
- ;
-option_value_list:
- option_type_value
- | option_value_list ',' option_type_value
- ;
-
-option_type_value:
+// Start of option value list
+start_option_value_list:
+ option_value_no_option_type
{
- LEX *lex= thd->lex;
- Lex_input_stream *lip= YYLIP;
-
- if (lex->sphead)
- {
- /*
- If we are in SP we want have own LEX for each assignment.
- This is mostly because it is hard for several sp_instr_set
- and sp_instr_set_trigger instructions share one LEX.
- (Well, it is theoretically possible but adds some extra
- overhead on preparation for execution stage and IMO less
- robust).
-
- QQ: May be we should simply prohibit group assignments in SP?
- */
- lex->sphead->reset_lex(thd);
- lex= thd->lex;
-
- /* Set new LEX as if we at start of set rule. */
- lex->sql_command= SQLCOM_SET_OPTION;
- mysql_init_select(lex);
- lex->option_type=OPT_SESSION;
- lex->var_list.empty();
- lex->one_shot_set= 0;
- lex->autocommit= 0;
- lex->sphead->m_tmp_query= lip->get_tok_start();
- }
+ if (sp_create_assignment_instr(thd, yychar == YYEMPTY))
+ MYSQL_YYABORT;
}
- ext_option_value
+ option_value_list_continued
+ | TRANSACTION_SYM
{
- LEX *lex= thd->lex;
- Lex_input_stream *lip= YYLIP;
-
- if (lex->sphead)
- {
- sp_head *sp= lex->sphead;
+ Lex->option_type= OPT_DEFAULT;
+ }
+ transaction_characteristics
+ {
+ if (sp_create_assignment_instr(thd, yychar == YYEMPTY))
+ MYSQL_YYABORT;
+ }
+ | option_type
+ {
+ Lex->option_type= $1;
+ }
+ start_option_value_list_following_option_type
+ ;
- if (!lex->var_list.is_empty())
- {
- /*
- We have assignment to user or system variable or
- option setting, so we should construct sp_instr_stmt
- for it.
- */
- LEX_STRING qbuff;
- sp_instr_stmt *i;
- if (!(i= new sp_instr_stmt(sp->instructions(), lex->spcont,
- lex)))
- MYSQL_YYABORT;
+// Start of option value list, option_type was given
+start_option_value_list_following_option_type:
+ option_value_following_option_type
+ {
+ if (sp_create_assignment_instr(thd, yychar == YYEMPTY))
+ MYSQL_YYABORT;
+ }
+ option_value_list_continued
+ | TRANSACTION_SYM transaction_characteristics
+ {
+ if (sp_create_assignment_instr(thd, yychar == YYEMPTY))
+ MYSQL_YYABORT;
+ }
+ ;
- /*
- Extract the query statement from the tokenizer. The
- end is either lip->ptr, if there was no lookahead,
- lip->tok_end otherwise.
- */
- if (yychar == YYEMPTY)
- qbuff.length= lip->get_ptr() - sp->m_tmp_query;
- else
- qbuff.length= lip->get_tok_end() - sp->m_tmp_query;
+// Remainder of the option value list after first option value.
+option_value_list_continued:
+ /* empty */
+ | ',' option_value_list
+ ;
- if (!(qbuff.str= (char*) alloc_root(thd->mem_root,
- qbuff.length + 5)))
- MYSQL_YYABORT;
+// Repeating list of option values after first option value.
+option_value_list:
+ {
+ sp_create_assignment_lex(thd, yychar == YYEMPTY);
+ }
+ option_value
+ {
+ if (sp_create_assignment_instr(thd, yychar == YYEMPTY))
+ MYSQL_YYABORT;
+ }
+ | option_value_list ','
+ {
+ sp_create_assignment_lex(thd, yychar == YYEMPTY);
+ }
+ option_value
+ {
+ if (sp_create_assignment_instr(thd, yychar == YYEMPTY))
+ MYSQL_YYABORT;
+ }
+ ;
- strmake(strmake(qbuff.str, "SET ", 4), sp->m_tmp_query,
- qbuff.length);
- qbuff.length+= 4;
- i->m_query= qbuff;
- if (sp->add_instr(i))
- MYSQL_YYABORT;
- }
- if (lex->sphead->restore_lex(thd))
- MYSQL_YYABORT;
- }
+// Wrapper around option values following the first option value in the stmt.
+option_value:
+ option_type
+ {
+ Lex->option_type= $1;
}
+ option_value_following_option_type
+ | option_value_no_option_type
;
option_type:
- option_type2 {}
- | GLOBAL_SYM { $$=OPT_GLOBAL; }
+ GLOBAL_SYM { $$=OPT_GLOBAL; }
| LOCAL_SYM { $$=OPT_SESSION; }
| SESSION_SYM { $$=OPT_SESSION; }
;
-option_type2:
- /* empty */ { $$= OPT_DEFAULT; }
- | ONE_SHOT_SYM { Lex->one_shot_set= 1; $$= OPT_SESSION; }
- ;
-
opt_var_type:
/* empty */ { $$=OPT_SESSION; }
| GLOBAL_SYM { $$=OPT_GLOBAL; }
@@ -13505,72 +14519,59 @@ opt_var_ident_type:
| SESSION_SYM '.' { $$=OPT_SESSION; }
;
-ext_option_value:
- sys_option_value
- | option_type2 option_value
+// Option values with preceding option_type.
+option_value_following_option_type:
+ internal_variable_name equal set_expr_or_default
+ {
+ LEX *lex= Lex;
+
+ if ($1.var && $1.var != trg_new_row_fake_var)
+ {
+ /* It is a system variable. */
+ if (set_system_variable(thd, &$1, lex->option_type, $3))
+ MYSQL_YYABORT;
+ }
+ else
+ {
+ /*
+ Not in trigger assigning value to new row,
+ and option_type preceding local variable is illegal.
+ */
+ my_parse_error(ER(ER_SYNTAX_ERROR));
+ MYSQL_YYABORT;
+ }
+ }
;
-sys_option_value:
- option_type internal_variable_name equal set_expr_or_default
+// Option values without preceding option_type.
+option_value_no_option_type:
+ internal_variable_name equal set_expr_or_default
{
LEX *lex= Lex;
- LEX_STRING *name= &$2.base_name;
- if ($2.var == trg_new_row_fake_var)
+ if ($1.var == trg_new_row_fake_var)
{
/* We are in trigger and assigning value to field of new row */
- if ($1)
- {
- my_parse_error(ER(ER_SYNTAX_ERROR));
- MYSQL_YYABORT;
- }
- if (set_trigger_new_row(thd, name, $4))
+ if (set_trigger_new_row(thd, &$1.base_name, $3))
MYSQL_YYABORT;
}
- else if ($2.var)
+ else if ($1.var)
{
- if ($1)
- lex->option_type= $1;
-
/* It is a system variable. */
- if (set_system_variable(thd, &$2, lex->option_type, $4))
+ if (set_system_variable(thd, &$1, lex->option_type, $3))
MYSQL_YYABORT;
}
else
{
sp_pcontext *spc= lex->spcont;
- sp_variable_t *spv= spc->find_variable(name);
-
- if ($1)
- {
- my_parse_error(ER(ER_SYNTAX_ERROR));
- MYSQL_YYABORT;
- }
+ sp_variable *spv= spc->find_variable($1.base_name, false);
/* It is a local variable. */
- if (set_local_variable(thd, spv, $4))
+ if (set_local_variable(thd, spv, $3))
MYSQL_YYABORT;
}
}
- | option_type TRANSACTION_SYM ISOLATION LEVEL_SYM isolation_types
- {
- LEX *lex=Lex;
- lex->option_type= $1;
- Item *item= new (thd->mem_root) Item_int((int32) $5);
- if (item == NULL)
- MYSQL_YYABORT;
- set_var *var= new set_var(lex->option_type,
- find_sys_var(thd, "tx_isolation"),
- &null_lex_str,
- item);
- if (var == NULL)
- MYSQL_YYABORT;
- lex->var_list.push_back(var);
- }
- ;
-
-option_value:
- '@' ident_or_text equal expr
+ | '@' ident_or_text equal expr
{
Item_func_set_user_var *item;
item= new (thd->mem_root) Item_func_set_user_var($2, $4);
@@ -13614,7 +14615,7 @@ option_value:
names.str= (char *)"names";
names.length= 5;
- if (spc && spc->find_variable(&names))
+ if (spc && spc->find_variable(names, false))
my_error(ER_SP_BAD_VAR_SHADOW, MYF(0), names.str);
else
my_parse_error(ER(ER_SYNTAX_ERROR));
@@ -13640,6 +14641,12 @@ option_value:
MYSQL_YYABORT;
lex->var_list.push_back(var);
}
+ | ROLE_SYM ident_or_text
+ {
+ LEX *lex = Lex;
+ set_var_role *var= new set_var_role($2);
+ lex->var_list.push_back(var);
+ }
| PASSWORD equal text_or_password
{
LEX *lex= thd->lex;
@@ -13649,15 +14656,14 @@ option_value:
pw.str= (char *)"password";
pw.length= 8;
- if (spc && spc->find_variable(&pw))
+ if (spc && spc->find_variable(pw, false))
{
my_error(ER_SP_BAD_VAR_SHADOW, MYF(0), pw.str);
MYSQL_YYABORT;
}
- if (!(user=(LEX_USER*) thd->alloc(sizeof(LEX_USER))))
+ if (!(user=(LEX_USER*) thd->calloc(sizeof(LEX_USER))))
MYSQL_YYABORT;
- user->host=null_lex_str;
- user->user.str=thd->security_ctx->user;
+ user->user= current_user;
set_var_password *var= new set_var_password(user, $3);
if (var == NULL)
MYSQL_YYABORT;
@@ -13682,10 +14688,10 @@ internal_variable_name:
ident
{
sp_pcontext *spc= thd->lex->spcont;
- sp_variable_t *spv;
+ sp_variable *spv;
/* Best effort lookup for system variable. */
- if (!spc || !(spv = spc->find_variable(&$1)))
+ if (!spc || !(spv = spc->find_variable($1, false)))
{
struct sys_var_with_base tmp= {NULL, $1};
@@ -13761,6 +14767,52 @@ internal_variable_name:
}
;
+transaction_characteristics:
+ transaction_access_mode
+ | isolation_level
+ | transaction_access_mode ',' isolation_level
+ | isolation_level ',' transaction_access_mode
+ ;
+
+transaction_access_mode:
+ transaction_access_mode_types
+ {
+ LEX *lex=Lex;
+ Item *item= new (thd->mem_root) Item_int((int32) $1);
+ if (item == NULL)
+ MYSQL_YYABORT;
+ set_var *var= new set_var(lex->option_type,
+ find_sys_var(thd, "tx_read_only"),
+ &null_lex_str,
+ item);
+ if (var == NULL)
+ MYSQL_YYABORT;
+ lex->var_list.push_back(var);
+ }
+ ;
+
+isolation_level:
+ ISOLATION LEVEL_SYM isolation_types
+ {
+ LEX *lex=Lex;
+ Item *item= new (thd->mem_root) Item_int((int32) $3);
+ if (item == NULL)
+ MYSQL_YYABORT;
+ set_var *var= new set_var(lex->option_type,
+ find_sys_var(thd, "tx_isolation"),
+ &null_lex_str,
+ item);
+ if (var == NULL)
+ MYSQL_YYABORT;
+ lex->var_list.push_back(var);
+ }
+ ;
+
+transaction_access_mode_types:
+ READ_SYM ONLY_SYM { $$= true; }
+ | READ_SYM WRITE_SYM { $$= false; }
+ ;
+
isolation_types:
READ_SYM UNCOMMITTED_SYM { $$= ISO_READ_UNCOMMITTED; }
| READ_SYM COMMITTED_SYM { $$= ISO_READ_COMMITTED; }
@@ -13781,33 +14833,32 @@ text_or_password:
}
| OLD_PASSWORD '(' TEXT_STRING ')'
{
- $$= $3.length ? Item_func_old_password::alloc(thd, $3.str,
- $3.length) :
+ $$= $3.length ? Item_func_old_password::
+ alloc(thd, $3.str, $3.length) :
$3.str;
if ($$ == NULL)
MYSQL_YYABORT;
}
;
-
set_expr_or_default:
expr { $$=$1; }
| DEFAULT { $$=0; }
| ON
{
- $$=new (thd->mem_root) Item_string("ON", 2, system_charset_info);
+ $$=new (thd->mem_root) Item_string_sys("ON", 2);
if ($$ == NULL)
MYSQL_YYABORT;
}
| ALL
{
- $$=new (thd->mem_root) Item_string("ALL", 3, system_charset_info);
+ $$=new (thd->mem_root) Item_string_sys("ALL", 3);
if ($$ == NULL)
MYSQL_YYABORT;
}
| BINARY
{
- $$=new (thd->mem_root) Item_string("binary", 6, system_charset_info);
+ $$=new (thd->mem_root) Item_string_sys("binary", 6);
if ($$ == NULL)
MYSQL_YYABORT;
}
@@ -13989,13 +15040,13 @@ revoke:
;
revoke_command:
- grant_privileges ON opt_table grant_ident FROM user_list
+ grant_privileges ON opt_table grant_ident FROM user_and_role_list
{
LEX *lex= Lex;
lex->sql_command= SQLCOM_REVOKE;
lex->type= 0;
}
- | grant_privileges ON FUNCTION_SYM grant_ident FROM user_list
+ | grant_privileges ON FUNCTION_SYM grant_ident FROM user_and_role_list
{
LEX *lex= Lex;
if (lex->columns.elements)
@@ -14006,7 +15057,7 @@ revoke_command:
lex->sql_command= SQLCOM_REVOKE;
lex->type= TYPE_ENUM_FUNCTION;
}
- | grant_privileges ON PROCEDURE_SYM grant_ident FROM user_list
+ | grant_privileges ON PROCEDURE_SYM grant_ident FROM user_and_role_list
{
LEX *lex= Lex;
if (lex->columns.elements)
@@ -14017,7 +15068,7 @@ revoke_command:
lex->sql_command= SQLCOM_REVOKE;
lex->type= TYPE_ENUM_PROCEDURE;
}
- | ALL opt_privileges ',' GRANT OPTION FROM user_list
+ | ALL opt_privileges ',' GRANT OPTION FROM user_and_role_list
{
Lex->sql_command = SQLCOM_REVOKE_ALL;
}
@@ -14027,9 +15078,22 @@ revoke_command:
lex->users_list.push_front ($3);
lex->sql_command= SQLCOM_REVOKE;
lex->type= TYPE_ENUM_PROXY;
- }
+ }
+ | admin_option_for_role FROM user_and_role_list
+ {
+ Lex->sql_command= SQLCOM_REVOKE_ROLE;
+ if (Lex->users_list.push_front($1))
+ MYSQL_YYABORT;
+ }
;
+admin_option_for_role:
+ ADMIN_SYM OPTION FOR_SYM grant_role
+ { Lex->with_admin_option= true; $$= $4; }
+ | grant_role
+ { Lex->with_admin_option= false; $$= $1; }
+ ;
+
grant:
GRANT clear_privileges grant_command
{}
@@ -14073,7 +15137,74 @@ grant_command:
lex->users_list.push_front ($3);
lex->sql_command= SQLCOM_GRANT;
lex->type= TYPE_ENUM_PROXY;
- }
+ }
+ | grant_role TO_SYM grant_list opt_with_admin_option
+ {
+ LEX *lex= Lex;
+ lex->sql_command= SQLCOM_GRANT_ROLE;
+ /* The first role is the one that is granted */
+ if (Lex->users_list.push_front($1))
+ MYSQL_YYABORT;
+ }
+
+ ;
+
+opt_with_admin:
+ /* nothing */ { Lex->definer = 0; }
+ | WITH ADMIN_SYM user_or_role { Lex->definer = $3; }
+
+opt_with_admin_option:
+ /* nothing */ { Lex->with_admin_option= false; }
+ | WITH ADMIN_SYM OPTION { Lex->with_admin_option= true; }
+
+role_list:
+ grant_role
+ {
+ if (Lex->users_list.push_back($1))
+ MYSQL_YYABORT;
+ }
+ | role_list ',' grant_role
+ {
+ if (Lex->users_list.push_back($3))
+ MYSQL_YYABORT;
+ }
+ ;
+
+current_role:
+ CURRENT_ROLE optional_braces
+ {
+ if (!($$=(LEX_USER*) thd->calloc(sizeof(LEX_USER))))
+ MYSQL_YYABORT;
+ $$->user= current_role;
+ $$->plugin= empty_lex_str;
+ $$->auth= empty_lex_str;
+ }
+ ;
+
+grant_role:
+ ident_or_text
+ {
+ CHARSET_INFO *cs= system_charset_info;
+ /* trim end spaces (as they'll be lost in mysql.user anyway) */
+ $1.length= cs->cset->lengthsp(cs, $1.str, $1.length);
+ if ($1.length == 0)
+ {
+ my_error(ER_INVALID_ROLE, MYF(0), "");
+ MYSQL_YYABORT;
+ }
+ if (!($$=(LEX_USER*) thd->alloc(sizeof(st_lex_user))))
+ MYSQL_YYABORT;
+ $$->user = $1;
+ $$->host= empty_lex_str;
+ $$->password= null_lex_str;
+ $$->plugin= empty_lex_str;
+ $$->auth= empty_lex_str;
+
+ if (check_string_char_length(&$$->user, ER(ER_USERNAME),
+ username_char_length, cs, 0))
+ MYSQL_YYABORT;
+ }
+ | current_role
;
opt_table:
@@ -14263,6 +15394,19 @@ grant_list:
}
;
+user_and_role_list:
+ user_or_role
+ {
+ if (Lex->users_list.push_back($1))
+ MYSQL_YYABORT;
+ }
+ | user_and_role_list ',' user_or_role
+ {
+ if (Lex->users_list.push_back($3))
+ MYSQL_YYABORT;
+ }
+ ;
+
via_or_with: VIA_SYM | WITH ;
using_or_as: USING | AS ;
@@ -14270,9 +15414,11 @@ grant_user:
user IDENTIFIED_SYM BY TEXT_STRING
{
$$=$1; $1->password=$4;
+ if (Lex->sql_command == SQLCOM_REVOKE)
+ MYSQL_YYABORT;
if ($4.length)
{
- if (thd->variables.old_passwords)
+ if (thd->variables.old_passwords == 1)
{
char *buff=
(char *) thd->alloc(SCRAMBLED_PASSWORD_CHAR_LENGTH_323+1);
@@ -14311,7 +15457,7 @@ grant_user:
$1->plugin= $4;
$1->auth= $6;
}
- | user
+ | user_or_role
{ $$= $1; $1->password= null_lex_str; }
;
@@ -14703,7 +15849,7 @@ view_or_trigger_or_sp_or_event:
{}
| no_definer no_definer_tail
{}
- | view_replace_or_algorithm definer_opt view_tail
+ | view_algorithm definer_opt view_tail
{}
;
@@ -14750,9 +15896,9 @@ no_definer:
;
definer:
- DEFINER_SYM EQ user
+ DEFINER_SYM EQ user_or_role
{
- thd->lex->definer= get_current_user(thd, $3);
+ thd->lex->definer= $3;
}
;
@@ -14762,20 +15908,6 @@ definer:
**************************************************************************/
-view_replace_or_algorithm:
- view_replace
- {}
- | view_replace view_algorithm
- {}
- | view_algorithm
- {}
- ;
-
-view_replace:
- OR_SYM REPLACE
- { Lex->create_view_mode= VIEW_CREATE_OR_REPLACE; }
- ;
-
view_algorithm:
ALGORITHM_SYM EQ UNDEFINED_SYM
{ Lex->create_view_algorithm= DTYPE_ALGORITHM_UNDEFINED; }
@@ -15115,7 +16247,7 @@ sf_tail:
If a collision exists, it should not be silenced but fixed.
*/
push_warning_printf(thd,
- MYSQL_ERROR::WARN_LEVEL_NOTE,
+ Sql_condition::WARN_LEVEL_NOTE,
ER_NATIVE_FCT_NAME_COLLISION,
ER(ER_NATIVE_FCT_NAME_COLLISION),
sp->m_name.str);
diff --git a/sql/strfunc.cc b/sql/strfunc.cc
index 06bd92e0bc7..b8100e05ce5 100644
--- a/sql/strfunc.cc
+++ b/sql/strfunc.cc
@@ -15,6 +15,7 @@
/* Some useful string utility functions used by the MySQL server */
+#include <my_global.h>
#include "sql_priv.h"
#include "unireg.h"
#include "strfunc.h"
@@ -264,27 +265,22 @@ uint check_word(TYPELIB *lib, const char *val, const char *end,
*/
-uint strconvert(CHARSET_INFO *from_cs, const char *from,
+uint strconvert(CHARSET_INFO *from_cs, const char *from, uint from_length,
CHARSET_INFO *to_cs, char *to, uint to_length, uint *errors)
{
int cnvres;
my_wc_t wc;
char *to_start= to;
uchar *to_end= (uchar*) to + to_length - 1;
+ const uchar *from_end= (const uchar*) from + from_length;
my_charset_conv_mb_wc mb_wc= from_cs->cset->mb_wc;
my_charset_conv_wc_mb wc_mb= to_cs->cset->wc_mb;
uint error_count= 0;
while (1)
{
- /*
- Using 'from + 10' is safe:
- - it is enough to scan a single character in any character set.
- - if remaining string is shorter than 10, then mb_wc will return
- with error because of unexpected '\0' character.
- */
if ((cnvres= (*mb_wc)(from_cs, &wc,
- (uchar*) from, (uchar*) from + 10)) > 0)
+ (uchar*) from, from_end)) > 0)
{
if (!wc)
break;
diff --git a/sql/strfunc.h b/sql/strfunc.h
index 57c5427fcd0..7b031710c76 100644
--- a/sql/strfunc.h
+++ b/sql/strfunc.h
@@ -43,7 +43,7 @@ char *set_to_string(THD *thd, LEX_STRING *result, ulonglong set,
/*
These functions were protected by INNODB_COMPATIBILITY_HOOKS
*/
-uint strconvert(CHARSET_INFO *from_cs, const char *from,
+uint strconvert(CHARSET_INFO *from_cs, const char *from, uint from_length,
CHARSET_INFO *to_cs, char *to, uint to_length, uint *errors);
#endif /* STRFUNC_INCLUDED */
diff --git a/sql/structs.h b/sql/structs.h
index 9840cec6a35..ee61b8d3b3a 100644
--- a/sql/structs.h
+++ b/sql/structs.h
@@ -29,6 +29,7 @@
struct TABLE;
class Field;
+class Index_statistics;
class THD;
@@ -89,14 +90,19 @@ struct ha_index_option_struct;
typedef struct st_key {
uint key_length; /* Tot length of key */
ulong flags; /* dupp key and pack flags */
- uint key_parts; /* How many key_parts */
- uint usable_key_parts; /* Should normally be = key_parts */
+ uint user_defined_key_parts; /* How many key_parts */
+ uint usable_key_parts; /* Should normally be = user_defined_key_parts */
uint ext_key_parts; /* Number of key parts in extended key */
ulong ext_key_flags; /* Flags for extended key */
key_part_map ext_key_part_map; /* Bitmap of pk key parts in extension */
uint block_size;
uint name_length;
enum ha_key_alg algorithm;
+ /*
+ The flag is on if statistical data for the index prefixes
+ has to be taken from the system statistical tables.
+ */
+ bool is_statistics_from_stat_tables;
/*
Note that parser is used when the table is opened for use, and
parser_name is used when the table is being created.
@@ -116,6 +122,18 @@ typedef struct st_key {
For temporary heap tables this member is NULL.
*/
ulong *rec_per_key;
+
+ /*
+ This structure is used for statistical data on the index
+ that has been read from the statistical table index_stat
+ */
+ Index_statistics *read_stats;
+ /*
+ This structure is used for statistical data on the index that
+ is collected by the function collect_statistics_for_table
+ */
+ Index_statistics *collected_stats;
+
union {
int bdb_return_if_eq;
} handler;
@@ -124,6 +142,9 @@ typedef struct st_key {
/** reference to the list of options or NULL */
engine_option_value *option_list;
ha_index_option_struct *option_struct; /* structure with parsed options */
+
+ double actual_rec_per_key(uint i);
+
} KEY;
@@ -170,6 +191,14 @@ typedef int *(*update_var)(THD *, struct st_mysql_show_var *);
typedef struct st_lex_user {
LEX_STRING user, host, password, plugin, auth;
+ bool is_role() { return user.str[0] && !host.str[0]; }
+ void set_lex_string(LEX_STRING *l, char *buf)
+ {
+ if (is_role())
+ *l= user;
+ else
+ l->length= strxmov(l->str= buf, user.str, "@", host.str, NullS) - buf;
+ }
} LEX_USER;
/*
@@ -236,10 +265,10 @@ typedef struct user_conn {
typedef struct st_user_stats
{
- char user[max(USERNAME_LENGTH, LIST_PROCESS_HOST_LEN) + 1];
+ char user[MY_MAX(USERNAME_LENGTH, LIST_PROCESS_HOST_LEN) + 1];
// Account name the user is mapped to when this is a user from mapped_user.
// Otherwise, the same value as user.
- char priv_user[max(USERNAME_LENGTH, LIST_PROCESS_HOST_LEN) + 1];
+ char priv_user[MY_MAX(USERNAME_LENGTH, LIST_PROCESS_HOST_LEN) + 1];
uint user_name_length;
uint total_connections;
uint concurrent_connections;
diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc
index a8ddcc2203b..c49a5de75e3 100644
--- a/sql/sys_vars.cc
+++ b/sql/sys_vars.cc
@@ -14,7 +14,10 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
-/*
+/**
+ @file
+ Definitions of all server's session or global variables.
+
How to add new variables:
1. copy one of the existing variables, and edit the declaration.
@@ -28,7 +31,7 @@
(for example in storage/myisam/ha_myisam.cc) !
*/
-#include "my_global.h" /* NO_EMBEDDED_ACCESS_CHECKS */
+#include "sql_plugin.h" // Includes my_global.h
#include "sql_priv.h"
#include "sql_class.h" // set_var.h: THD
#include "sys_vars.h"
@@ -45,6 +48,7 @@
// mysql_user_table_is_in_short_password_format
#include "derror.h" // read_texts
#include "sql_base.h" // close_cached_tables
+#include "hostname.h" // host_cache_size
#include <myisam.h>
#include "log_slow.h"
#include "debug_sync.h" // DEBUG_SYNC
@@ -55,6 +59,9 @@
#include "../storage/perfschema/pfs_server.h"
#endif /* WITH_PERFSCHEMA_STORAGE_ENGINE */
#include "threadpool.h"
+#include "sql_repl.h"
+#include "opt_range.h"
+#include "rpl_parallel.h"
/*
The rule for this file: everything should be 'static'. When a sys_var
@@ -72,20 +79,22 @@ static Sys_var_mybool Sys_pfs_enabled(
PARSED_EARLY READ_ONLY GLOBAL_VAR(pfs_param.m_enabled),
CMD_LINE(OPT_ARG), DEFAULT(FALSE));
-static Sys_var_ulong Sys_pfs_events_waits_history_long_size(
+static Sys_var_long Sys_pfs_events_waits_history_long_size(
"performance_schema_events_waits_history_long_size",
- "Number of rows in EVENTS_WAITS_HISTORY_LONG.",
+ "Number of rows in EVENTS_WAITS_HISTORY_LONG."
+ " Use 0 to disable, -1 for automated sizing.",
PARSED_EARLY READ_ONLY
GLOBAL_VAR(pfs_param.m_events_waits_history_long_sizing),
- CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, 1024*1024),
- DEFAULT(PFS_WAITS_HISTORY_LONG_SIZE), BLOCK_SIZE(1));
+ CMD_LINE(REQUIRED_ARG), VALID_RANGE(-1, 1024*1024),
+ DEFAULT(-1), BLOCK_SIZE(1));
-static Sys_var_ulong Sys_pfs_events_waits_history_size(
+static Sys_var_long Sys_pfs_events_waits_history_size(
"performance_schema_events_waits_history_size",
- "Number of rows per thread in EVENTS_WAITS_HISTORY.",
+ "Number of rows per thread in EVENTS_WAITS_HISTORY."
+ " Use 0 to disable, -1 for automated sizing.",
PARSED_EARLY READ_ONLY GLOBAL_VAR(pfs_param.m_events_waits_history_sizing),
- CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, 1024),
- DEFAULT(PFS_WAITS_HISTORY_SIZE), BLOCK_SIZE(1));
+ CMD_LINE(REQUIRED_ARG), VALID_RANGE(-1, 1024),
+ DEFAULT(-1), BLOCK_SIZE(1));
static Sys_var_ulong Sys_pfs_max_cond_classes(
"performance_schema_max_cond_classes",
@@ -94,12 +103,13 @@ static Sys_var_ulong Sys_pfs_max_cond_classes(
CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, 256),
DEFAULT(PFS_MAX_COND_CLASS), BLOCK_SIZE(1));
-static Sys_var_ulong Sys_pfs_max_cond_instances(
+static Sys_var_long Sys_pfs_max_cond_instances(
"performance_schema_max_cond_instances",
- "Maximum number of instrumented condition objects.",
+ "Maximum number of instrumented condition objects."
+ " Use 0 to disable, -1 for automated sizing.",
PARSED_EARLY READ_ONLY GLOBAL_VAR(pfs_param.m_cond_sizing),
- CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, 1024*1024),
- DEFAULT(PFS_MAX_COND), BLOCK_SIZE(1));
+ CMD_LINE(REQUIRED_ARG), VALID_RANGE(-1, 1024*1024),
+ DEFAULT(-1), BLOCK_SIZE(1));
static Sys_var_ulong Sys_pfs_max_file_classes(
"performance_schema_max_file_classes",
@@ -115,12 +125,30 @@ static Sys_var_ulong Sys_pfs_max_file_handles(
CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, 1024*1024),
DEFAULT(PFS_MAX_FILE_HANDLE), BLOCK_SIZE(1));
-static Sys_var_ulong Sys_pfs_max_file_instances(
+static Sys_var_long Sys_pfs_max_file_instances(
"performance_schema_max_file_instances",
- "Maximum number of instrumented files.",
+ "Maximum number of instrumented files."
+ " Use 0 to disable, -1 for automated sizing.",
PARSED_EARLY READ_ONLY GLOBAL_VAR(pfs_param.m_file_sizing),
- CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, 1024*1024),
- DEFAULT(PFS_MAX_FILE), BLOCK_SIZE(1));
+ CMD_LINE(REQUIRED_ARG), VALID_RANGE(-1, 1024*1024),
+ DEFAULT(-1), BLOCK_SIZE(1));
+
+static Sys_var_long Sys_pfs_max_sockets(
+ "performance_schema_max_socket_instances",
+ "Maximum number of opened instrumented sockets."
+ " Use 0 to disable, -1 for automated sizing.",
+ PARSED_EARLY READ_ONLY GLOBAL_VAR(pfs_param.m_socket_sizing),
+ CMD_LINE(REQUIRED_ARG), VALID_RANGE(-1, 1024*1024),
+ DEFAULT(-1),
+ BLOCK_SIZE(1));
+
+static Sys_var_ulong Sys_pfs_max_socket_classes(
+ "performance_schema_max_socket_classes",
+ "Maximum number of socket instruments.",
+ PARSED_EARLY READ_ONLY GLOBAL_VAR(pfs_param.m_socket_class_sizing),
+ CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, 256),
+ DEFAULT(PFS_MAX_SOCKET_CLASS),
+ BLOCK_SIZE(1));
static Sys_var_ulong Sys_pfs_max_mutex_classes(
"performance_schema_max_mutex_classes",
@@ -129,12 +157,13 @@ static Sys_var_ulong Sys_pfs_max_mutex_classes(
CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, 256),
DEFAULT(PFS_MAX_MUTEX_CLASS), BLOCK_SIZE(1));
-static Sys_var_ulong Sys_pfs_max_mutex_instances(
+static Sys_var_long Sys_pfs_max_mutex_instances(
"performance_schema_max_mutex_instances",
- "Maximum number of instrumented MUTEX objects.",
+ "Maximum number of instrumented MUTEX objects."
+ " Use 0 to disable, -1 for automated sizing.",
PARSED_EARLY READ_ONLY GLOBAL_VAR(pfs_param.m_mutex_sizing),
- CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, 100*1024*1024),
- DEFAULT(PFS_MAX_MUTEX), BLOCK_SIZE(1));
+ CMD_LINE(REQUIRED_ARG), VALID_RANGE(-1, 100*1024*1024),
+ DEFAULT(-1), BLOCK_SIZE(1));
static Sys_var_ulong Sys_pfs_max_rwlock_classes(
"performance_schema_max_rwlock_classes",
@@ -143,26 +172,29 @@ static Sys_var_ulong Sys_pfs_max_rwlock_classes(
CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, 256),
DEFAULT(PFS_MAX_RWLOCK_CLASS), BLOCK_SIZE(1));
-static Sys_var_ulong Sys_pfs_max_rwlock_instances(
+static Sys_var_long Sys_pfs_max_rwlock_instances(
"performance_schema_max_rwlock_instances",
- "Maximum number of instrumented RWLOCK objects.",
+ "Maximum number of instrumented RWLOCK objects."
+ " Use 0 to disable, -1 for automated sizing.",
PARSED_EARLY READ_ONLY GLOBAL_VAR(pfs_param.m_rwlock_sizing),
- CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, 100*1024*1024),
- DEFAULT(PFS_MAX_RWLOCK), BLOCK_SIZE(1));
+ CMD_LINE(REQUIRED_ARG), VALID_RANGE(-1, 100*1024*1024),
+ DEFAULT(-1), BLOCK_SIZE(1));
-static Sys_var_ulong Sys_pfs_max_table_handles(
+static Sys_var_long Sys_pfs_max_table_handles(
"performance_schema_max_table_handles",
- "Maximum number of opened instrumented tables.",
+ "Maximum number of opened instrumented tables."
+ " Use 0 to disable, -1 for automated sizing.",
PARSED_EARLY READ_ONLY GLOBAL_VAR(pfs_param.m_table_sizing),
- CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, 1024*1024),
- DEFAULT(PFS_MAX_TABLE), BLOCK_SIZE(1));
+ CMD_LINE(REQUIRED_ARG), VALID_RANGE(-1, 1024*1024),
+ DEFAULT(-1), BLOCK_SIZE(1));
-static Sys_var_ulong Sys_pfs_max_table_instances(
+static Sys_var_long Sys_pfs_max_table_instances(
"performance_schema_max_table_instances",
- "Maximum number of instrumented tables.",
+ "Maximum number of instrumented tables."
+ " Use 0 to disable, -1 for automated sizing.",
PARSED_EARLY READ_ONLY GLOBAL_VAR(pfs_param.m_table_share_sizing),
- CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, 1024*1024),
- DEFAULT(PFS_MAX_TABLE_SHARE), BLOCK_SIZE(1));
+ CMD_LINE(REQUIRED_ARG), VALID_RANGE(-1, 1024*1024),
+ DEFAULT(-1), BLOCK_SIZE(1));
static Sys_var_ulong Sys_pfs_max_thread_classes(
"performance_schema_max_thread_classes",
@@ -171,12 +203,145 @@ static Sys_var_ulong Sys_pfs_max_thread_classes(
CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, 256),
DEFAULT(PFS_MAX_THREAD_CLASS), BLOCK_SIZE(1));
-static Sys_var_ulong Sys_pfs_max_thread_instances(
+static Sys_var_long Sys_pfs_max_thread_instances(
"performance_schema_max_thread_instances",
- "Maximum number of instrumented threads.",
+ "Maximum number of instrumented threads."
+ " Use 0 to disable, -1 for automated sizing.",
PARSED_EARLY READ_ONLY GLOBAL_VAR(pfs_param.m_thread_sizing),
+ CMD_LINE(REQUIRED_ARG), VALID_RANGE(-1, 1024*1024),
+ DEFAULT(-1), BLOCK_SIZE(1));
+
+static Sys_var_ulong Sys_pfs_setup_actors_size(
+ "performance_schema_setup_actors_size",
+ "Maximum number of rows in SETUP_ACTORS.",
+ PARSED_EARLY READ_ONLY GLOBAL_VAR(pfs_param.m_setup_actor_sizing),
+ CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, 1024),
+ DEFAULT(PFS_MAX_SETUP_ACTOR),
+ BLOCK_SIZE(1));
+
+static Sys_var_ulong Sys_pfs_setup_objects_size(
+ "performance_schema_setup_objects_size",
+ "Maximum number of rows in SETUP_OBJECTS.",
+ PARSED_EARLY READ_ONLY GLOBAL_VAR(pfs_param.m_setup_object_sizing),
CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, 1024*1024),
- DEFAULT(PFS_MAX_THREAD), BLOCK_SIZE(1));
+ DEFAULT(PFS_MAX_SETUP_OBJECT),
+ BLOCK_SIZE(1));
+
+static Sys_var_long Sys_pfs_accounts_size(
+ "performance_schema_accounts_size",
+ "Maximum number of instrumented user@host accounts."
+ " Use 0 to disable, -1 for automated sizing.",
+ PARSED_EARLY READ_ONLY GLOBAL_VAR(pfs_param.m_account_sizing),
+ CMD_LINE(REQUIRED_ARG), VALID_RANGE(-1, 1024*1024),
+ DEFAULT(-1),
+ BLOCK_SIZE(1));
+
+static Sys_var_long Sys_pfs_hosts_size(
+ "performance_schema_hosts_size",
+ "Maximum number of instrumented hosts."
+ " Use 0 to disable, -1 for automated sizing.",
+ PARSED_EARLY READ_ONLY GLOBAL_VAR(pfs_param.m_host_sizing),
+ CMD_LINE(REQUIRED_ARG), VALID_RANGE(-1, 1024*1024),
+ DEFAULT(-1),
+ BLOCK_SIZE(1));
+
+static Sys_var_long Sys_pfs_users_size(
+ "performance_schema_users_size",
+ "Maximum number of instrumented users."
+ " Use 0 to disable, -1 for automated sizing.",
+ PARSED_EARLY READ_ONLY GLOBAL_VAR(pfs_param.m_user_sizing),
+ CMD_LINE(REQUIRED_ARG), VALID_RANGE(-1, 1024*1024),
+ DEFAULT(-1),
+ BLOCK_SIZE(1));
+
+static Sys_var_ulong Sys_pfs_max_stage_classes(
+ "performance_schema_max_stage_classes",
+ "Maximum number of stage instruments.",
+ PARSED_EARLY READ_ONLY GLOBAL_VAR(pfs_param.m_stage_class_sizing),
+ CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, 256),
+ DEFAULT(PFS_MAX_STAGE_CLASS),
+ BLOCK_SIZE(1));
+
+static Sys_var_long Sys_pfs_events_stages_history_long_size(
+ "performance_schema_events_stages_history_long_size",
+ "Number of rows in EVENTS_STAGES_HISTORY_LONG."
+ " Use 0 to disable, -1 for automated sizing.",
+ PARSED_EARLY READ_ONLY GLOBAL_VAR(pfs_param.m_events_stages_history_long_sizing),
+ CMD_LINE(REQUIRED_ARG), VALID_RANGE(-1, 1024*1024),
+ DEFAULT(-1),
+ BLOCK_SIZE(1));
+
+static Sys_var_long Sys_pfs_events_stages_history_size(
+ "performance_schema_events_stages_history_size",
+ "Number of rows per thread in EVENTS_STAGES_HISTORY."
+ " Use 0 to disable, -1 for automated sizing.",
+ PARSED_EARLY READ_ONLY GLOBAL_VAR(pfs_param.m_events_stages_history_sizing),
+ CMD_LINE(REQUIRED_ARG), VALID_RANGE(-1, 1024),
+ DEFAULT(-1),
+ BLOCK_SIZE(1));
+
+/**
+ Variable performance_schema_max_statement_classes.
+ The default number of statement classes is the sum of:
+ - COM_END for all regular "statement/com/...",
+ - 1 for "statement/com/new_packet", for unknown enum_server_command
+ - 1 for "statement/com/Error", for invalid enum_server_command
+ - SQLCOM_END for all regular "statement/sql/...",
+ - 1 for "statement/sql/error", for invalid enum_sql_command
+ - 1 for "statement/rpl/relay_log", for replicated statements.
+*/
+static Sys_var_ulong Sys_pfs_max_statement_classes(
+ "performance_schema_max_statement_classes",
+ "Maximum number of statement instruments.",
+ PARSED_EARLY READ_ONLY GLOBAL_VAR(pfs_param.m_statement_class_sizing),
+ CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, 256),
+ DEFAULT((ulong) SQLCOM_END + (ulong) COM_END + 4),
+ BLOCK_SIZE(1));
+
+static Sys_var_long Sys_pfs_events_statements_history_long_size(
+ "performance_schema_events_statements_history_long_size",
+ "Number of rows in EVENTS_STATEMENTS_HISTORY_LONG."
+ " Use 0 to disable, -1 for automated sizing.",
+ PARSED_EARLY READ_ONLY GLOBAL_VAR(pfs_param.m_events_statements_history_long_sizing),
+ CMD_LINE(REQUIRED_ARG), VALID_RANGE(-1, 1024*1024),
+ DEFAULT(-1),
+ BLOCK_SIZE(1));
+
+static Sys_var_long Sys_pfs_events_statements_history_size(
+ "performance_schema_events_statements_history_size",
+ "Number of rows per thread in EVENTS_STATEMENTS_HISTORY."
+ " Use 0 to disable, -1 for automated sizing.",
+ PARSED_EARLY READ_ONLY GLOBAL_VAR(pfs_param.m_events_statements_history_sizing),
+ CMD_LINE(REQUIRED_ARG), VALID_RANGE(-1, 1024),
+ DEFAULT(-1),
+ BLOCK_SIZE(1));
+
+static Sys_var_long Sys_pfs_digest_size(
+ "performance_schema_digests_size",
+ "Size of the statement digest."
+ " Use 0 to disable, -1 for automated sizing.",
+ PARSED_EARLY READ_ONLY GLOBAL_VAR(pfs_param.m_digest_sizing),
+ CMD_LINE(REQUIRED_ARG), VALID_RANGE(-1, 200),
+ DEFAULT(-1),
+ BLOCK_SIZE(1));
+
+static Sys_var_long Sys_pfs_max_digest_length(
+ "performance_schema_max_digest_length",
+ "Maximum length considered for digest text, when stored in performance_schema tables.",
+ PARSED_EARLY READ_ONLY GLOBAL_VAR(pfs_param.m_max_digest_length),
+ CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, 1024 * 1024),
+ DEFAULT(1024),
+ BLOCK_SIZE(1));
+
+static Sys_var_long Sys_pfs_connect_attrs_size(
+ "performance_schema_session_connect_attrs_size",
+ "Size of session attribute string buffer per thread."
+ " Use 0 to disable, -1 for automated sizing.",
+ PARSED_EARLY READ_ONLY
+ GLOBAL_VAR(pfs_param.m_session_connect_attrs_sizing),
+ CMD_LINE(REQUIRED_ARG), VALID_RANGE(-1, 1024 * 1024),
+ DEFAULT(-1),
+ BLOCK_SIZE(1));
#endif /* WITH_PERFSCHEMA_STORAGE_ENGINE */
@@ -208,7 +373,7 @@ static Sys_var_ulong Sys_back_log(
"MySQL can have. This comes into play when the main MySQL thread "
"gets very many connection requests in a very short time",
READ_ONLY GLOBAL_VAR(back_log), CMD_LINE(REQUIRED_ARG),
- VALID_RANGE(1, 65535), DEFAULT(50), BLOCK_SIZE(1));
+ VALID_RANGE(1, 65535), DEFAULT(150), BLOCK_SIZE(1));
static Sys_var_charptr Sys_basedir(
"basedir", "Path to installation directory. All paths are "
@@ -288,7 +453,7 @@ static bool binlog_format_check(sys_var *self, THD *thd, set_var *var)
var->save_result.ulonglong_value != BINLOG_FORMAT_ROW)
{
// Push a warning to the error log.
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_UNKNOWN_ERROR,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_UNKNOWN_ERROR,
"MariaDB Galera does not support binlog format: %s",
binlog_format_names[var->save_result.ulonglong_value]);
@@ -406,16 +571,19 @@ static bool check_charset(sys_var *self, THD *thd, set_var *var)
if (var->value->result_type() == STRING_RESULT)
{
String str(buff, sizeof(buff), system_charset_info), *res;
- if (!(res=var->value->val_str(&str)))
+ if (!(res= var->value->val_str(&str)))
var->save_result.ptr= NULL;
- else if (!(var->save_result.ptr= get_charset_by_csname(res->c_ptr(),
- MY_CS_PRIMARY,
- MYF(0))) &&
- !(var->save_result.ptr=get_old_charset_by_name(res->c_ptr())))
+ else
{
- ErrConvString err(res);
- my_error(ER_UNKNOWN_CHARACTER_SET, MYF(0), err.ptr());
- return true;
+ ErrConvString err(res); /* Get utf8 '\0' terminated string */
+ if (!(var->save_result.ptr= get_charset_by_csname(err.ptr(),
+ MY_CS_PRIMARY,
+ MYF(0))) &&
+ !(var->save_result.ptr= get_old_charset_by_name(err.ptr())))
+ {
+ my_error(ER_UNKNOWN_CHARACTER_SET, MYF(0), err.ptr());
+ return true;
+ }
}
}
else // INT_RESULT
@@ -455,7 +623,7 @@ static bool check_charset_db(sys_var *self, THD *thd, set_var *var)
}
static Sys_var_struct Sys_character_set_database(
"character_set_database",
- " The character set used by the default database",
+ "The character set used by the default database",
SESSION_VAR(collation_database), NO_CMD_LINE,
offsetof(CHARSET_INFO, csname), DEFAULT(&default_charset_info),
NO_MUTEX_GUARD, IN_BINLOG, ON_CHECK(check_charset_db));
@@ -526,11 +694,14 @@ static bool check_collation_not_null(sys_var *self, THD *thd, set_var *var)
String str(buff, sizeof(buff), system_charset_info), *res;
if (!(res= var->value->val_str(&str)))
var->save_result.ptr= NULL;
- else if (!(var->save_result.ptr= get_charset_by_name(res->c_ptr(), MYF(0))))
+ else
{
- ErrConvString err(res);
- my_error(ER_UNKNOWN_COLLATION, MYF(0), err.ptr());
- return true;
+ ErrConvString err(res); /* Get utf8 '\0'-terminated string */
+ if (!(var->save_result.ptr= get_charset_by_name(err.ptr(), MYF(0))))
+ {
+ my_error(ER_UNKNOWN_COLLATION, MYF(0), err.ptr());
+ return true;
+ }
}
}
else // INT_RESULT
@@ -665,30 +836,26 @@ static Sys_var_ulong Sys_delayed_queue_size(
VALID_RANGE(1, UINT_MAX), DEFAULT(DELAYED_QUEUE_SIZE), BLOCK_SIZE(1));
#ifdef HAVE_EVENT_SCHEDULER
-static const char *event_scheduler_names[]= { "OFF", "ON", "DISABLED", NullS };
+static const char *event_scheduler_names[]= { "OFF", "ON", "DISABLED",
+ "ORIGINAL", NullS };
static bool event_scheduler_check(sys_var *self, THD *thd, set_var *var)
{
- /* DISABLED is only accepted on the command line */
- if (var->save_result.ulonglong_value == Events::EVENTS_DISABLED)
- return true;
- /*
- If the scheduler was disabled because there are no/bad
- system tables, produce a more meaningful error message
- than ER_OPTION_PREVENTS_STATEMENT
- */
- if (Events::check_if_system_tables_error())
- return true;
if (Events::opt_event_scheduler == Events::EVENTS_DISABLED)
{
my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0),
"--event-scheduler=DISABLED or --skip-grant-tables");
return true;
}
+ /* DISABLED is only accepted on the command line */
+ if (var->save_result.ulonglong_value == Events::EVENTS_DISABLED)
+ return true;
return false;
}
+
static bool event_scheduler_update(sys_var *self, THD *thd, enum_var_type type)
{
int err_no= 0;
+ bool ret;
uint opt_event_scheduler_value= Events::opt_event_scheduler;
mysql_mutex_unlock(&LOCK_global_system_variables);
/*
@@ -707,9 +874,25 @@ static bool event_scheduler_update(sys_var *self, THD *thd, enum_var_type type)
rare and it's difficult to avoid it without opening up possibilities
for deadlocks. See bug#51160.
*/
- bool ret= opt_event_scheduler_value == Events::EVENTS_ON
- ? Events::start(&err_no)
- : Events::stop();
+
+ /* EVENTS_ORIGINAL means we should revert back to the startup state */
+ if (opt_event_scheduler_value == Events::EVENTS_ORIGINAL)
+ {
+ opt_event_scheduler_value= Events::opt_event_scheduler=
+ Events::startup_state;
+ }
+
+ /*
+ If the scheduler was not properly inited (because of wrong system tables),
+ try to init it again. This is needed for mysql_upgrade to work properly if
+ the event tables where upgraded.
+ */
+ if (!Events::inited && (Events::init(thd, 0) || !Events::inited))
+ ret= 1;
+ else
+ ret= opt_event_scheduler_value == Events::EVENTS_ON ?
+ Events::start(&err_no) :
+ Events::stop();
mysql_mutex_lock(&LOCK_global_system_variables);
if (ret)
{
@@ -822,6 +1005,30 @@ static Sys_var_lexstring Sys_init_connect(
DEFAULT(""), &PLock_sys_init_connect, NOT_IN_BINLOG,
ON_CHECK(check_init_string));
+#ifdef HAVE_REPLICATION
+static bool check_master_connection(sys_var *self, THD *thd, set_var *var)
+{
+ LEX_STRING tmp;
+ tmp.str= var->save_result.string_value.str;
+ tmp.length= var->save_result.string_value.length;
+ if (!tmp.str || check_master_connection_name(&tmp))
+ {
+ my_error(ER_WRONG_ARGUMENTS, MYF(ME_JUST_WARNING),
+ var->var->name.str);
+ return true;
+ }
+ return false;
+}
+
+static Sys_var_session_lexstring Sys_default_master_connection(
+ "default_master_connection",
+ "Master connection to use for all slave variables and slave commands",
+ SESSION_ONLY(default_master_connection),
+ NO_CMD_LINE, IN_SYSTEM_CHARSET,
+ DEFAULT(""), MAX_CONNECTION_NAME, ON_CHECK(check_master_connection),
+ ON_UPDATE(0));
+#endif
+
static Sys_var_charptr Sys_init_file(
"init_file", "Read SQL commands from this file at startup",
READ_ONLY GLOBAL_VAR(opt_init_file),
@@ -894,10 +1101,21 @@ static Sys_var_keycache Sys_key_cache_age_threshold(
BLOCK_SIZE(100), NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0),
ON_UPDATE(change_keycache_param));
+static Sys_var_keycache Sys_key_cache_file_hash_size(
+ "key_cache_file_hash_size",
+ "Number of hash buckets for open and changed files. If you have a lot of MyISAM "
+ "files open you should increase this for faster flush of changes. A good "
+ "value is probably 1/10 of number of possible open MyISAM files.",
+ KEYCACHE_VAR(changed_blocks_hash_size),
+ CMD_LINE(REQUIRED_ARG, OPT_KEY_CACHE_CHANGED_BLOCKS_HASH_SIZE),
+ VALID_RANGE(128, 16384), DEFAULT(512),
+ BLOCK_SIZE(1), NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0),
+ ON_UPDATE(resize_keycache));
+
static Sys_var_mybool Sys_large_files_support(
"large_files_support",
"Whether mysqld was compiled with options for large file support",
- READ_ONLY GLOBAL_VAR(opt_large_files),
+ READ_ONLY SHOW_VALUE_IN_HELP GLOBAL_VAR(opt_large_files),
NO_CMD_LINE, DEFAULT(sizeof(my_off_t) > 4));
static Sys_var_uint Sys_large_page_size(
@@ -1015,20 +1233,12 @@ static Sys_var_mybool Sys_low_priority_updates(
DEFAULT(FALSE), NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0),
ON_UPDATE(fix_low_prio_updates));
-#ifndef TO_BE_DELETED /* Alias for the low_priority_updates */
-static Sys_var_mybool Sys_sql_low_priority_updates(
- "sql_low_priority_updates",
- "INSERT/DELETE/UPDATE has lower priority than selects",
- SESSION_VAR(low_priority_updates), NO_CMD_LINE,
- DEFAULT(FALSE), NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0),
- ON_UPDATE(fix_low_prio_updates));
-#endif
-
static Sys_var_mybool Sys_lower_case_file_system(
"lower_case_file_system",
"Case sensitivity of file names on the file system where the "
"data directory is located",
- READ_ONLY GLOBAL_VAR(lower_case_file_system), NO_CMD_LINE,
+ READ_ONLY SHOW_VALUE_IN_HELP GLOBAL_VAR(lower_case_file_system),
+ NO_CMD_LINE,
DEFAULT(FALSE));
static Sys_var_uint Sys_lower_case_table_names(
@@ -1064,7 +1274,7 @@ static bool check_max_allowed_packet(sys_var *self, THD *thd, set_var *var)
val= var->save_result.ulonglong_value;
if (val < (longlong) global_system_variables.net_buffer_length)
{
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
WARN_OPTION_BELOW_LIMIT, ER(WARN_OPTION_BELOW_LIMIT),
"max_allowed_packet", "net_buffer_length");
}
@@ -1107,16 +1317,12 @@ static Sys_var_ulonglong Sys_max_binlog_stmt_cache_size(
static bool fix_max_binlog_size(sys_var *self, THD *thd, enum_var_type type)
{
mysql_bin_log.set_max_size(max_binlog_size);
-#ifdef HAVE_REPLICATION
- if (!max_relay_log_size)
- active_mi->rli.relay_log.set_max_size(max_binlog_size);
-#endif
return false;
}
static Sys_var_ulong Sys_max_binlog_size(
"max_binlog_size",
"Binary log will be rotated automatically when the size exceeds this "
- "value. Will also apply to relay logs if max_relay_log_size is 0",
+ "value.",
GLOBAL_VAR(max_binlog_size), CMD_LINE(REQUIRED_ARG),
VALID_RANGE(IO_SIZE, 1024*1024L*1024L), DEFAULT(1024*1024L*1024L),
BLOCK_SIZE(IO_SIZE), NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0),
@@ -1135,8 +1341,9 @@ static bool fix_max_connections(sys_var *self, THD *thd, enum_var_type type)
// children, to avoid "too many connections" error in a common setup
static Sys_var_ulong Sys_max_connections(
"max_connections", "The number of simultaneous clients allowed",
- GLOBAL_VAR(max_connections), CMD_LINE(REQUIRED_ARG),
- VALID_RANGE(1, 100000), DEFAULT(151), BLOCK_SIZE(1), NO_MUTEX_GUARD,
+ PARSED_EARLY GLOBAL_VAR(max_connections), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(1, 100000),
+ DEFAULT(MAX_CONNECTIONS_DEFAULT), BLOCK_SIZE(1), NO_MUTEX_GUARD,
NOT_IN_BINLOG, ON_CHECK(0), ON_UPDATE(fix_max_connections));
static Sys_var_ulong Sys_max_connect_errors(
@@ -1147,6 +1354,12 @@ static Sys_var_ulong Sys_max_connect_errors(
VALID_RANGE(1, UINT_MAX), DEFAULT(MAX_CONNECT_ERRORS),
BLOCK_SIZE(1));
+static Sys_var_long Sys_max_digest_length(
+ "max_digest_length", "Maximum length considered for digest text.",
+ READ_ONLY GLOBAL_VAR(max_digest_length),
+ CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(0, 1024 * 1024), DEFAULT(1024), BLOCK_SIZE(1));
+
static bool check_max_delayed_threads(sys_var *self, THD *thd, set_var *var)
{
return var->type != OPT_GLOBAL &&
@@ -1193,6 +1406,12 @@ static Sys_var_ulong Sys_metadata_locks_cache_size(
VALID_RANGE(1, 1024*1024), DEFAULT(MDL_LOCKS_CACHE_SIZE_DEFAULT),
BLOCK_SIZE(1));
+static Sys_var_ulong Sys_metadata_locks_hash_instances(
+ "metadata_locks_hash_instances", "Number of metadata locks hash instances",
+ READ_ONLY GLOBAL_VAR(mdl_locks_hash_partitions), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(1, 1024), DEFAULT(MDL_LOCKS_HASH_PARTITIONS_DEFAULT),
+ BLOCK_SIZE(1));
+
/*
"pseudo_thread_id" variable used in the test suite to detect 32/64bit
systems. If you change it to something else then ulong then fix the tests
@@ -1206,6 +1425,523 @@ static Sys_var_ulong Sys_pseudo_thread_id(
BLOCK_SIZE(1), NO_MUTEX_GUARD, IN_BINLOG,
ON_CHECK(check_has_super));
+static bool
+check_gtid_domain_id(sys_var *self, THD *thd, set_var *var)
+{
+ if (check_has_super(self, thd, var))
+ return true;
+ if (var->type != OPT_GLOBAL &&
+ error_if_in_trans_or_substatement(thd,
+ ER_STORED_FUNCTION_PREVENTS_SWITCH_GTID_DOMAIN_ID_SEQ_NO,
+ ER_INSIDE_TRANSACTION_PREVENTS_SWITCH_GTID_DOMAIN_ID_SEQ_NO))
+ return true;
+
+ return false;
+}
+
+
+static Sys_var_uint Sys_gtid_domain_id(
+ "gtid_domain_id",
+ "Used with global transaction ID to identify logically independent "
+ "replication streams. When events can propagate through multiple "
+ "parallel paths (for example multiple masters), each independent "
+ "source server must use a distinct domain_id. For simple tree-shaped "
+ "replication topologies, it can be left at its default, 0.",
+ SESSION_VAR(gtid_domain_id),
+ CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, UINT_MAX32), DEFAULT(0),
+ BLOCK_SIZE(1), NO_MUTEX_GUARD, NOT_IN_BINLOG,
+ ON_CHECK(check_gtid_domain_id));
+
+
+static bool check_gtid_seq_no(sys_var *self, THD *thd, set_var *var)
+{
+ uint32 domain_id, server_id;
+ uint64 seq_no;
+
+ if (check_has_super(self, thd, var))
+ return true;
+ if (error_if_in_trans_or_substatement(thd,
+ ER_STORED_FUNCTION_PREVENTS_SWITCH_GTID_DOMAIN_ID_SEQ_NO,
+ ER_INSIDE_TRANSACTION_PREVENTS_SWITCH_GTID_DOMAIN_ID_SEQ_NO))
+ return true;
+
+ domain_id= thd->variables.gtid_domain_id;
+ server_id= thd->variables.server_id;
+ seq_no= (uint64)var->value->val_uint();
+ DBUG_EXECUTE_IF("ignore_set_gtid_seq_no_check", return 0;);
+ if (opt_gtid_strict_mode && opt_bin_log &&
+ mysql_bin_log.check_strict_gtid_sequence(domain_id, server_id, seq_no))
+ return true;
+
+ return false;
+}
+
+
+static Sys_var_ulonglong Sys_gtid_seq_no(
+ "gtid_seq_no",
+ "Internal server usage, for replication with global transaction id. "
+ "When set, next event group logged to the binary log will use this "
+ "sequence number, not generate a new one, thus allowing to preserve "
+ "master's GTID in slave's binlog.",
+ SESSION_ONLY(gtid_seq_no),
+ NO_CMD_LINE, VALID_RANGE(0, ULONGLONG_MAX), DEFAULT(0),
+ BLOCK_SIZE(1), NO_MUTEX_GUARD, NOT_IN_BINLOG,
+ ON_CHECK(check_gtid_seq_no));
+
+
+#ifdef HAVE_REPLICATION
+static unsigned char opt_gtid_binlog_pos_dummy;
+static Sys_var_gtid_binlog_pos Sys_gtid_binlog_pos(
+ "gtid_binlog_pos", "Last GTID logged to the binary log, per replication"
+ "domain",
+ READ_ONLY GLOBAL_VAR(opt_gtid_binlog_pos_dummy), NO_CMD_LINE);
+
+
+uchar *
+Sys_var_gtid_binlog_pos::global_value_ptr(THD *thd, LEX_STRING *base)
+{
+ char buf[128];
+ String str(buf, sizeof(buf), system_charset_info);
+ char *p;
+
+ str.length(0);
+ if ((opt_bin_log && mysql_bin_log.append_state_pos(&str)) ||
+ !(p= thd->strmake(str.ptr(), str.length())))
+ {
+ my_error(ER_OUT_OF_RESOURCES, MYF(0));
+ return NULL;
+ }
+
+ return (uchar *)p;
+}
+
+
+static unsigned char opt_gtid_current_pos_dummy;
+static Sys_var_gtid_current_pos Sys_gtid_current_pos(
+ "gtid_current_pos", "Current GTID position of the server. Per "
+ "replication domain, this is either the last GTID replicated by a "
+ "slave thread, or the GTID logged to the binary log, whichever is "
+ "most recent.",
+ READ_ONLY GLOBAL_VAR(opt_gtid_current_pos_dummy), NO_CMD_LINE);
+
+
+uchar *
+Sys_var_gtid_current_pos::global_value_ptr(THD *thd, LEX_STRING *base)
+{
+ String str;
+ char *p;
+
+ str.length(0);
+ if (rpl_append_gtid_state(&str, true) ||
+ !(p= thd->strmake(str.ptr(), str.length())))
+ {
+ my_error(ER_OUT_OF_RESOURCES, MYF(0));
+ return NULL;
+ }
+
+ return (uchar *)p;
+}
+
+
+bool
+Sys_var_gtid_slave_pos::do_check(THD *thd, set_var *var)
+{
+ String str, *res;
+ bool running;
+
+ DBUG_ASSERT(var->type == OPT_GLOBAL);
+
+ if (rpl_load_gtid_slave_state(thd))
+ {
+ my_error(ER_CANNOT_LOAD_SLAVE_GTID_STATE, MYF(0), "mysql",
+ rpl_gtid_slave_state_table_name.str);
+ return true;
+ }
+
+ mysql_mutex_lock(&LOCK_active_mi);
+ running= master_info_index->give_error_if_slave_running();
+ mysql_mutex_unlock(&LOCK_active_mi);
+ if (running)
+ return true;
+ if (!(res= var->value->val_str(&str)))
+ return true;
+ if (thd->in_active_multi_stmt_transaction())
+ {
+ my_error(ER_CANT_DO_THIS_DURING_AN_TRANSACTION, MYF(0));
+ return true;
+ }
+ if (rpl_gtid_pos_check(thd, &((*res)[0]), res->length()))
+ return true;
+
+ if (!(var->save_result.string_value.str=
+ thd->strmake(res->ptr(), res->length())))
+ {
+ my_error(ER_OUT_OF_RESOURCES, MYF(0));
+ return true;
+ }
+ var->save_result.string_value.length= res->length();
+ return false;
+}
+
+
+bool
+Sys_var_gtid_slave_pos::global_update(THD *thd, set_var *var)
+{
+ bool err;
+
+ DBUG_ASSERT(var->type == OPT_GLOBAL);
+
+ if (!var->value)
+ {
+ my_error(ER_NO_DEFAULT, MYF(0), var->var->name.str);
+ return true;
+ }
+
+ mysql_mutex_unlock(&LOCK_global_system_variables);
+ mysql_mutex_lock(&LOCK_active_mi);
+ if (master_info_index->give_error_if_slave_running())
+ err= true;
+ else
+ err= rpl_gtid_pos_update(thd, var->save_result.string_value.str,
+ var->save_result.string_value.length);
+ mysql_mutex_unlock(&LOCK_active_mi);
+ mysql_mutex_lock(&LOCK_global_system_variables);
+ return err;
+}
+
+
+uchar *
+Sys_var_gtid_slave_pos::global_value_ptr(THD *thd, LEX_STRING *base)
+{
+ String str;
+ char *p;
+
+ str.length(0);
+ /*
+ If the mysql.rpl_slave_pos table could not be loaded, then we cannot
+ easily automatically try to reload it here - we may be inside a statement
+ that already has tables locked and so opening more tables is problematic.
+
+ But if the table is not loaded (eg. missing mysql_upgrade_db or some such),
+ then the slave state must be empty anyway.
+ */
+ if ((rpl_global_gtid_slave_state->loaded &&
+ rpl_append_gtid_state(&str, false)) ||
+ !(p= thd->strmake(str.ptr(), str.length())))
+ {
+ my_error(ER_OUT_OF_RESOURCES, MYF(0));
+ return NULL;
+ }
+
+ return (uchar *)p;
+}
+
+
+static unsigned char opt_gtid_slave_pos_dummy;
+static Sys_var_gtid_slave_pos Sys_gtid_slave_pos(
+ "gtid_slave_pos",
+ "The list of global transaction IDs that were last replicated on the "
+ "server, one for each replication domain.",
+ GLOBAL_VAR(opt_gtid_slave_pos_dummy), NO_CMD_LINE);
+
+
+static Sys_var_mybool Sys_gtid_strict_mode(
+ "gtid_strict_mode",
+ "Enforce strict seq_no ordering of events in the binary log. Slave "
+ "stops with an error if it encounters an event that would cause it to "
+ "generate an out-of-order binlog if executed.",
+ GLOBAL_VAR(opt_gtid_strict_mode),
+ CMD_LINE(OPT_ARG), DEFAULT(FALSE));
+
+
+struct gtid_binlog_state_data { rpl_gtid *list; uint32 list_len; };
+
+bool
+Sys_var_gtid_binlog_state::do_check(THD *thd, set_var *var)
+{
+ String str, *res;
+ struct gtid_binlog_state_data *data;
+ rpl_gtid *list;
+ uint32 list_len;
+
+ DBUG_ASSERT(var->type == OPT_GLOBAL);
+
+ if (!(res= var->value->val_str(&str)))
+ return true;
+ if (thd->in_active_multi_stmt_transaction())
+ {
+ my_error(ER_CANT_DO_THIS_DURING_AN_TRANSACTION, MYF(0));
+ return true;
+ }
+ if (!mysql_bin_log.is_open())
+ {
+ my_error(ER_FLUSH_MASTER_BINLOG_CLOSED, MYF(0));
+ return true;
+ }
+ if (!mysql_bin_log.is_empty_state())
+ {
+ my_error(ER_BINLOG_MUST_BE_EMPTY, MYF(0));
+ return true;
+ }
+ if (res->length() == 0)
+ list= NULL;
+ else if (!(list= gtid_parse_string_to_list(res->ptr(), res->length(),
+ &list_len)))
+ {
+ my_error(ER_INCORRECT_GTID_STATE, MYF(0));
+ return true;
+ }
+ if (!(data= (gtid_binlog_state_data *)my_malloc(sizeof(*data), MYF(0))))
+ {
+ my_free(list);
+ my_error(ER_OUT_OF_RESOURCES, MYF(0));
+ return true;
+ }
+ data->list= list;
+ data->list_len= list_len;
+ var->save_result.ptr= data;
+ return false;
+}
+
+
+bool
+Sys_var_gtid_binlog_state::global_update(THD *thd, set_var *var)
+{
+ bool res;
+
+ DBUG_ASSERT(var->type == OPT_GLOBAL);
+
+ if (!var->value)
+ {
+ my_error(ER_NO_DEFAULT, MYF(0), var->var->name.str);
+ return true;
+ }
+
+ struct gtid_binlog_state_data *data=
+ (struct gtid_binlog_state_data *)var->save_result.ptr;
+ mysql_mutex_unlock(&LOCK_global_system_variables);
+ res= (0 != reset_master(thd, data->list, data->list_len));
+ mysql_mutex_lock(&LOCK_global_system_variables);
+ my_free(data->list);
+ my_free(data);
+ return res;
+}
+
+
+uchar *
+Sys_var_gtid_binlog_state::global_value_ptr(THD *thd, LEX_STRING *base)
+{
+ char buf[512];
+ String str(buf, sizeof(buf), system_charset_info);
+ char *p;
+
+ str.length(0);
+ if ((opt_bin_log && mysql_bin_log.append_state(&str)) ||
+ !(p= thd->strmake(str.ptr(), str.length())))
+ {
+ my_error(ER_OUT_OF_RESOURCES, MYF(0));
+ return NULL;
+ }
+
+ return (uchar *)p;
+}
+
+
+static unsigned char opt_gtid_binlog_state_dummy;
+static Sys_var_gtid_binlog_state Sys_gtid_binlog_state(
+ "gtid_binlog_state",
+ "The internal GTID state of the binlog, used to keep track of all "
+ "GTIDs ever logged to the binlog.",
+ GLOBAL_VAR(opt_gtid_binlog_state_dummy), NO_CMD_LINE);
+
+
+static Sys_var_last_gtid Sys_last_gtid(
+ "last_gtid", "The GTID of the last commit (if binlogging was enabled), "
+ "or the empty string if none.",
+ READ_ONLY sys_var::ONLY_SESSION, NO_CMD_LINE);
+
+
+uchar *
+Sys_var_last_gtid::session_value_ptr(THD *thd, LEX_STRING *base)
+{
+ char buf[10+1+10+1+20+1];
+ String str(buf, sizeof(buf), system_charset_info);
+ char *p;
+ bool first= true;
+
+ str.length(0);
+ if ((thd->last_commit_gtid.seq_no > 0 &&
+ rpl_slave_state_tostring_helper(&str, &thd->last_commit_gtid, &first)) ||
+ !(p= thd->strmake(str.ptr(), str.length())))
+ {
+ my_error(ER_OUT_OF_RESOURCES, MYF(0));
+ return NULL;
+ }
+
+ return (uchar *)p;
+}
+
+
+static bool
+check_slave_parallel_threads(sys_var *self, THD *thd, set_var *var)
+{
+ bool running;
+
+ mysql_mutex_lock(&LOCK_active_mi);
+ running= master_info_index->give_error_if_slave_running();
+ mysql_mutex_unlock(&LOCK_active_mi);
+ if (running)
+ return true;
+
+ return false;
+}
+
+static bool
+fix_slave_parallel_threads(sys_var *self, THD *thd, enum_var_type type)
+{
+ bool err;
+
+ mysql_mutex_unlock(&LOCK_global_system_variables);
+ mysql_mutex_lock(&LOCK_active_mi);
+ err= master_info_index->give_error_if_slave_running();
+ mysql_mutex_unlock(&LOCK_active_mi);
+ mysql_mutex_lock(&LOCK_global_system_variables);
+
+ return err;
+}
+
+
+static Sys_var_ulong Sys_slave_parallel_threads(
+ "slave_parallel_threads",
+ "If non-zero, number of threads to spawn to apply in parallel events "
+ "on the slave that were group-committed on the master or were logged "
+ "with GTID in different replication domains. Note that these threads "
+ "are in addition to the IO and SQL threads, which are always created "
+ "by a replication slave",
+ GLOBAL_VAR(opt_slave_parallel_threads), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(0,16383), DEFAULT(0), BLOCK_SIZE(1), NO_MUTEX_GUARD,
+ NOT_IN_BINLOG, ON_CHECK(check_slave_parallel_threads),
+ ON_UPDATE(fix_slave_parallel_threads));
+
+
+static bool
+check_slave_domain_parallel_threads(sys_var *self, THD *thd, set_var *var)
+{
+ bool running;
+
+ mysql_mutex_lock(&LOCK_active_mi);
+ running= master_info_index->give_error_if_slave_running();
+ mysql_mutex_unlock(&LOCK_active_mi);
+ if (running)
+ return true;
+
+ return false;
+}
+
+static bool
+fix_slave_domain_parallel_threads(sys_var *self, THD *thd, enum_var_type type)
+{
+ bool running;
+
+ mysql_mutex_unlock(&LOCK_global_system_variables);
+ mysql_mutex_lock(&LOCK_active_mi);
+ running= master_info_index->give_error_if_slave_running();
+ mysql_mutex_unlock(&LOCK_active_mi);
+ mysql_mutex_lock(&LOCK_global_system_variables);
+
+ return running ? true : false;
+}
+
+
+static Sys_var_ulong Sys_slave_domain_parallel_threads(
+ "slave_domain_parallel_threads",
+ "Maximum number of parallel threads to use on slave for events in a "
+ "single replication domain. When using multiple domains, this can be "
+ "used to limit a single domain from grabbing all threads and thus "
+ "stalling other domains. The default of 0 means to allow a domain to "
+ "grab as many threads as it wants, up to the value of "
+ "slave_parallel_threads.",
+ GLOBAL_VAR(opt_slave_domain_parallel_threads), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(0,16383), DEFAULT(0), BLOCK_SIZE(1), NO_MUTEX_GUARD,
+ NOT_IN_BINLOG, ON_CHECK(check_slave_domain_parallel_threads),
+ ON_UPDATE(fix_slave_domain_parallel_threads));
+
+
+static Sys_var_ulong Sys_slave_parallel_max_queued(
+ "slave_parallel_max_queued",
+ "Limit on how much memory SQL threads should use per parallel "
+ "replication thread when reading ahead in the relay log looking for "
+ "opportunities for parallel replication. Only used when "
+ "--slave-parallel-threads > 0.",
+ GLOBAL_VAR(opt_slave_parallel_max_queued), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(0,2147483647), DEFAULT(131072), BLOCK_SIZE(1));
+
+
+static bool
+check_gtid_ignore_duplicates(sys_var *self, THD *thd, set_var *var)
+{
+ bool running;
+
+ mysql_mutex_lock(&LOCK_active_mi);
+ running= master_info_index->give_error_if_slave_running();
+ mysql_mutex_unlock(&LOCK_active_mi);
+ if (running)
+ return true;
+
+ return false;
+}
+
+static bool
+fix_gtid_ignore_duplicates(sys_var *self, THD *thd, enum_var_type type)
+{
+ bool running;
+
+ mysql_mutex_unlock(&LOCK_global_system_variables);
+ mysql_mutex_lock(&LOCK_active_mi);
+ running= master_info_index->give_error_if_slave_running();
+ mysql_mutex_unlock(&LOCK_active_mi);
+ mysql_mutex_lock(&LOCK_global_system_variables);
+
+ return running ? true : false;
+}
+
+
+static Sys_var_mybool Sys_gtid_ignore_duplicates(
+ "gtid_ignore_duplicates",
+ "When set, different master connections in multi-source replication are "
+ "allowed to receive and process event groups with the same GTID (when "
+ "using GTID mode). Only one will be applied, any others will be "
+ "ignored. Within a given replication domain, just the sequence number "
+ "will be used to decide whether a given GTID has been already applied; "
+ "this means it is the responsibility of the user to ensure that GTID "
+ "sequence numbers are strictly increasing.",
+ GLOBAL_VAR(opt_gtid_ignore_duplicates), CMD_LINE(OPT_ARG),
+ DEFAULT(FALSE), NO_MUTEX_GUARD,
+ NOT_IN_BINLOG, ON_CHECK(check_gtid_ignore_duplicates),
+ ON_UPDATE(fix_gtid_ignore_duplicates));
+#endif
+
+
+static Sys_var_ulong Sys_binlog_commit_wait_count(
+ "binlog_commit_wait_count",
+ "If non-zero, binlog write will wait at most binlog_commit_wait_usec "
+ "microseconds for at least this many commits to queue up for group "
+ "commit to the binlog. This can reduce I/O on the binlog and provide "
+ "increased opportunity for parallel apply on the slave, but too high "
+ "a value will decrease commit throughput.",
+ GLOBAL_VAR(opt_binlog_commit_wait_count), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(0, ULONG_MAX), DEFAULT(0), BLOCK_SIZE(1));
+
+
+static Sys_var_ulong Sys_binlog_commit_wait_usec(
+ "binlog_commit_wait_usec",
+ "Maximum time, in microseconds, to wait for more commits to queue up "
+ "for binlog group commit. Only takes effect if the value of "
+ "binlog_commit_wait_count is non-zero.",
+ GLOBAL_VAR(opt_binlog_commit_wait_usec), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(0, ULONG_MAX), DEFAULT(100000), BLOCK_SIZE(1));
+
+
static bool fix_max_join_size(sys_var *self, THD *thd, enum_var_type type)
{
SV *sv= type == OPT_GLOBAL ? &global_system_variables : &thd->variables;
@@ -1236,13 +1972,6 @@ static Sys_var_ulong Sys_max_length_for_sort_data(
SESSION_VAR(max_length_for_sort_data), CMD_LINE(REQUIRED_ARG),
VALID_RANGE(4, 8192*1024L), DEFAULT(1024), BLOCK_SIZE(1));
-static Sys_var_harows Sys_sql_max_join_size(
- "sql_max_join_size", "Alias for max_join_size",
- SESSION_VAR(max_join_size), NO_CMD_LINE,
- VALID_RANGE(1, HA_POS_ERROR), DEFAULT(HA_POS_ERROR), BLOCK_SIZE(1),
- NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0),
- ON_UPDATE(fix_max_join_size), DEPRECATED("'@@max_join_size'"));
-
static Sys_var_ulong Sys_max_long_data_size(
"max_long_data_size",
"The maximum BLOB length to send to server from "
@@ -1261,24 +1990,6 @@ static Sys_var_ulong Sys_max_prepared_stmt_count(
VALID_RANGE(0, 1024*1024), DEFAULT(16382), BLOCK_SIZE(1),
&PLock_prepared_stmt_count);
-static bool fix_max_relay_log_size(sys_var *self, THD *thd, enum_var_type type)
-{
-#ifdef HAVE_REPLICATION
- active_mi->rli.relay_log.set_max_size(max_relay_log_size ?
- max_relay_log_size: max_binlog_size);
-#endif
- return false;
-}
-static Sys_var_ulong Sys_max_relay_log_size(
- "max_relay_log_size",
- "If non-zero: relay log will be rotated automatically when the "
- "size exceeds this value; if zero: when the size "
- "exceeds max_binlog_size",
- GLOBAL_VAR(max_relay_log_size), CMD_LINE(REQUIRED_ARG),
- VALID_RANGE(0, 1024L*1024*1024), DEFAULT(0), BLOCK_SIZE(IO_SIZE),
- NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0),
- ON_UPDATE(fix_max_relay_log_size));
-
static Sys_var_ulong Sys_max_sort_length(
"max_sort_length",
"The number of bytes to use when sorting BLOB or TEXT values (only "
@@ -1352,7 +2063,7 @@ static bool check_net_buffer_length(sys_var *self, THD *thd, set_var *var)
val= var->save_result.ulonglong_value;
if (val > (longlong) global_system_variables.max_allowed_packet)
{
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
WARN_OPTION_BELOW_LIMIT, ER(WARN_OPTION_BELOW_LIMIT),
"max_allowed_packet", "net_buffer_length");
}
@@ -1447,13 +2158,42 @@ static Sys_var_ulong Sys_optimizer_prune_level(
SESSION_VAR(optimizer_prune_level), CMD_LINE(REQUIRED_ARG),
VALID_RANGE(0, 1), DEFAULT(1), BLOCK_SIZE(1));
+static Sys_var_ulong Sys_optimizer_selectivity_sampling_limit(
+ "optimizer_selectivity_sampling_limit",
+ "Controls number of record samples to check condition selectivity",
+ SESSION_VAR(optimizer_selectivity_sampling_limit),
+ CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(SELECTIVITY_SAMPLING_THRESHOLD, UINT_MAX),
+ DEFAULT(SELECTIVITY_SAMPLING_LIMIT), BLOCK_SIZE(1));
+
+static Sys_var_ulong Sys_optimizer_use_condition_selectivity(
+ "optimizer_use_condition_selectivity",
+ "Controls selectivity of which conditions the optimizer takes into "
+ "account to calculate cardinality of a partial join when it searches "
+ "for the best execution plan "
+ "Meaning: "
+ "1 - use selectivity of index backed range conditions to calculate "
+ "the cardinality of a partial join if the last joined table is "
+ "accessed by full table scan or an index scan, "
+ "2 - use selectivity of index backed range conditions to calculate "
+ "the cardinality of a partial join in any case, "
+ "3 - additionally always use selectivity of range conditions that are "
+ "not backed by any index to calculate the cardinality of a partial join, "
+ "4 - use histograms to calculate selectivity of range conditions that "
+ "are not backed by any index to calculate the cardinality of "
+ "a partial join."
+ "5 - additionally use selectivity of certain non-range predicates "
+ "calculated on record samples",
+ SESSION_VAR(optimizer_use_condition_selectivity), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(1, 5), DEFAULT(1), BLOCK_SIZE(1));
+
/** Warns about deprecated value 63 */
static bool fix_optimizer_search_depth(sys_var *self, THD *thd,
enum_var_type type)
{
SV *sv= type == OPT_GLOBAL ? &global_system_variables : &thd->variables;
if (sv->optimizer_search_depth == MAX_TABLES+2)
- WARN_DEPRECATED(thd, 6, 0, "optimizer-search-depth=63",
+ WARN_DEPRECATED(thd, 10, 1, "optimizer-search-depth=63",
"a search depth less than 63");
return false;
}
@@ -1496,6 +2236,7 @@ export const char *optimizer_switch_names[]=
"optimize_join_buffer_size",
"table_elimination",
"extended_keys",
+ "exists_to_in",
"default", NullS
};
/** propagates changes to @@engine_condition_pushdown */
@@ -1504,7 +2245,7 @@ static bool fix_optimizer_switch(sys_var *self, THD *thd,
{
SV *sv= (type == OPT_GLOBAL) ? &global_system_variables : &thd->variables;
sv->engine_condition_pushdown=
- test(sv->optimizer_switch & OPTIMIZER_SWITCH_ENGINE_CONDITION_PUSHDOWN);
+ MY_TEST(sv->optimizer_switch & OPTIMIZER_SWITCH_ENGINE_CONDITION_PUSHDOWN);
return false;
}
static Sys_var_flagset Sys_optimizer_switch(
@@ -1537,7 +2278,8 @@ static Sys_var_flagset Sys_optimizer_switch(
"semijoin_with_cache, "
"subquery_cache, "
"table_elimination, "
- "extended_keys "
+ "extended_keys, "
+ "exists_to_in "
"} and val is one of {on, off, default}",
SESSION_VAR(optimizer_switch), CMD_LINE(REQUIRED_ARG),
optimizer_switch_names, DEFAULT(OPTIMIZER_SWITCH_DEFAULT),
@@ -1574,7 +2316,7 @@ static Sys_var_ulong Sys_preload_buff_size(
static Sys_var_uint Sys_protocol_version(
"protocol_version",
"The version of the client/server protocol used by the MySQL server",
- READ_ONLY GLOBAL_VAR(protocol_version), NO_CMD_LINE,
+ READ_ONLY SHOW_VALUE_IN_HELP GLOBAL_VAR(protocol_version), NO_CMD_LINE,
VALID_RANGE(0, ~0), DEFAULT(PROTOCOL_VERSION), BLOCK_SIZE(1));
static Sys_var_proxy_user Sys_proxy_user(
@@ -1889,13 +2631,18 @@ static bool fix_query_cache_size(sys_var *self, THD *thd, enum_var_type type)
requested cache size. See also query_cache_size_arg
*/
if (query_cache_size != new_cache_size)
- push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
ER_WARN_QC_RESIZE, ER(ER_WARN_QC_RESIZE),
query_cache_size, new_cache_size);
query_cache_size= new_cache_size;
return false;
}
+static bool fix_query_cache_limit(sys_var *self, THD *thd, enum_var_type type)
+{
+ query_cache.result_size_limit(query_cache_limit);
+ return false;
+}
static Sys_var_ulonglong Sys_query_cache_size(
"query_cache_size",
"The memory allocated to store results from old queries",
@@ -1907,8 +2654,10 @@ static Sys_var_ulonglong Sys_query_cache_size(
static Sys_var_ulong Sys_query_cache_limit(
"query_cache_limit",
"Don't cache results that are bigger than this",
- GLOBAL_VAR(query_cache.query_cache_limit), CMD_LINE(REQUIRED_ARG),
- VALID_RANGE(0, UINT_MAX), DEFAULT(1024*1024), BLOCK_SIZE(1));
+ GLOBAL_VAR(query_cache_limit), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(0, UINT_MAX), DEFAULT(1024*1024), BLOCK_SIZE(1),
+ NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(NULL),
+ ON_UPDATE(fix_query_cache_limit));
static bool fix_qcache_min_res_unit(sys_var *self, THD *thd, enum_var_type type)
{
@@ -1992,17 +2741,27 @@ static Sys_var_charptr Sys_secure_file_priv(
static bool fix_server_id(sys_var *self, THD *thd, enum_var_type type)
{
- server_id_supplied = 1;
- thd->server_id= server_id;
+ if (type == OPT_GLOBAL)
+ {
+ server_id_supplied = 1;
+ thd->variables.server_id= global_system_variables.server_id;
+ /*
+ Historically, server_id was a global variable that is exported to
+ plugins. Now it is a session variable, and lives in the
+ global_system_variables struct, but we still need to export the
+ value for reading to plugins for backwards compatibility reasons.
+ */
+ ::server_id= global_system_variables.server_id;
+ }
return false;
}
static Sys_var_ulong Sys_server_id(
"server_id",
"Uniquely identifies the server instance in the community of "
"replication partners",
- GLOBAL_VAR(server_id), CMD_LINE(REQUIRED_ARG, OPT_SERVER_ID),
+ SESSION_VAR(server_id), CMD_LINE(REQUIRED_ARG, OPT_SERVER_ID),
VALID_RANGE(0, UINT_MAX32), DEFAULT(0), BLOCK_SIZE(1), NO_MUTEX_GUARD,
- NOT_IN_BINLOG, ON_CHECK(0), ON_UPDATE(fix_server_id));
+ NOT_IN_BINLOG, ON_CHECK(check_has_super), ON_UPDATE(fix_server_id));
static Sys_var_mybool Sys_slave_compressed_protocol(
"slave_compressed_protocol",
@@ -2014,14 +2773,43 @@ static Sys_var_mybool Sys_slave_compressed_protocol(
static const char *slave_exec_mode_names[]= {"STRICT", "IDEMPOTENT", 0};
static Sys_var_enum Slave_exec_mode(
"slave_exec_mode",
- "Modes for how replication events should be executed. Legal values "
+ "How replication events should be executed. Legal values "
"are STRICT (default) and IDEMPOTENT. In IDEMPOTENT mode, "
"replication will not stop for operations that are idempotent. "
+ "For example, in row based replication attempts to delete rows that "
+ "doesn't exist will be ignored. "
"In STRICT mode, replication will stop on any unexpected difference "
"between the master and the slave",
GLOBAL_VAR(slave_exec_mode_options), CMD_LINE(REQUIRED_ARG),
slave_exec_mode_names, DEFAULT(SLAVE_EXEC_MODE_STRICT));
+static Sys_var_enum Slave_ddl_exec_mode(
+ "slave_ddl_exec_mode",
+ "How replication events should be executed. Legal values "
+ "are STRICT and IDEMPOTENT (default). In IDEMPOTENT mode, "
+ "replication will not stop for DDL operations that are idempotent. "
+ "This means that CREATE TABLE is treated as CREATE TABLE OR REPLACE and "
+ "DROP TABLE is treated as DROP TABLE IF EXISTS.",
+ GLOBAL_VAR(slave_ddl_exec_mode_options), CMD_LINE(REQUIRED_ARG),
+ slave_exec_mode_names, DEFAULT(SLAVE_EXEC_MODE_IDEMPOTENT));
+
+#ifdef RBR_TRIGGERS
+static const char *slave_run_triggers_for_rbr_names[]=
+ {"NO", "YES", "LOGGING", 0};
+static Sys_var_enum Slave_run_triggers_for_rbr(
+ "slave_run_triggers_for_rbr",
+ "Modes for how triggers in row-base replication on slave side will be "
+ "executed. Legal values are NO (default), YES and LOGGING. NO means "
+ "that trigger for RBR will not be running on slave. YES and LOGGING "
+ "means that triggers will be running on slave, if there was not "
+ "triggers running on the master for the statement. LOGGING also means "
+ "results of that the executed triggers work will be written to "
+ "the binlog.",
+ GLOBAL_VAR(slave_run_triggers_for_rbr), CMD_LINE(REQUIRED_ARG),
+ slave_run_triggers_for_rbr_names,
+ DEFAULT(SLAVE_RUN_TRIGGERS_FOR_RBR_NO));
+#endif //RBR_TRIGGERS
+
static const char *slave_type_conversions_name[]= {"ALL_LOSSY", "ALL_NON_LOSSY", 0};
static Sys_var_set Slave_type_conversions(
"slave_type_conversions",
@@ -2054,50 +2842,22 @@ static Sys_var_mybool Sys_master_verify_checksum(
static const char *replicate_events_marked_for_skip_names[]= {
"replicate", "filter_on_slave", "filter_on_master", 0
};
-static bool
-replicate_events_marked_for_skip_check(sys_var *self, THD *thd,
- set_var *var)
-{
- int thread_mask;
- DBUG_ENTER("sys_var_replicate_events_marked_for_skip_check");
-
- /* Slave threads must be stopped to change the variable. */
- mysql_mutex_lock(&LOCK_active_mi);
- lock_slave_threads(active_mi);
- init_thread_mask(&thread_mask, active_mi, 0 /*not inverse*/);
- unlock_slave_threads(active_mi);
- mysql_mutex_unlock(&LOCK_active_mi);
- if (thread_mask) // We refuse if any slave thread is running
- {
- my_error(ER_SLAVE_MUST_STOP, MYF(0));
- DBUG_RETURN(true);
- }
- DBUG_RETURN(false);
-}
bool
Sys_var_replicate_events_marked_for_skip::global_update(THD *thd, set_var *var)
{
- bool result;
- int thread_mask;
+ bool result= true; // Assume error
DBUG_ENTER("Sys_var_replicate_events_marked_for_skip::global_update");
- /* Slave threads must be stopped to change the variable. */
+ mysql_mutex_unlock(&LOCK_global_system_variables);
mysql_mutex_lock(&LOCK_active_mi);
- lock_slave_threads(active_mi);
- init_thread_mask(&thread_mask, active_mi, 0 /*not inverse*/);
- if (thread_mask) // We refuse if any slave thread is running
- {
- my_error(ER_SLAVE_MUST_STOP, MYF(0));
- result= true;
- }
- else
+ if (!master_info_index->give_error_if_slave_running())
result= Sys_var_enum::global_update(thd, var);
-
- unlock_slave_threads(active_mi);
mysql_mutex_unlock(&LOCK_active_mi);
+ mysql_mutex_lock(&LOCK_global_system_variables);
DBUG_RETURN(result);
}
+
static Sys_var_replicate_events_marked_for_skip Replicate_events_marked_for_skip
("replicate_events_marked_for_skip",
"Whether the slave should replicate events that were created with "
@@ -2108,8 +2868,7 @@ static Sys_var_replicate_events_marked_for_skip Replicate_events_marked_for_skip
"the slave).",
GLOBAL_VAR(opt_replicate_events_marked_for_skip), CMD_LINE(REQUIRED_ARG),
replicate_events_marked_for_skip_names, DEFAULT(RPL_SKIP_REPLICATE),
- NO_MUTEX_GUARD, NOT_IN_BINLOG,
- ON_CHECK(replicate_events_marked_for_skip_check));
+ NO_MUTEX_GUARD, NOT_IN_BINLOG);
#endif
@@ -2235,7 +2994,9 @@ static Sys_var_set Sys_sql_mode(
static const char *old_mode_names[]=
{
- "NO_DUP_KEY_WARNINGS_WITH_IGNORE", "NO_PROGRESS_INFO",
+ "NO_DUP_KEY_WARNINGS_WITH_IGNORE",
+ "NO_PROGRESS_INFO",
+ "ZERO_DATE_TIME_CAST",
0
};
@@ -2290,6 +3051,19 @@ static Sys_var_charptr Sys_ssl_key(
READ_ONLY GLOBAL_VAR(opt_ssl_key), SSL_OPT(OPT_SSL_KEY),
IN_FS_CHARSET, DEFAULT(0));
+static Sys_var_charptr Sys_ssl_crl(
+ "ssl_crl",
+ "CRL file in PEM format (check OpenSSL docs, implies --ssl)",
+ READ_ONLY GLOBAL_VAR(opt_ssl_crl), SSL_OPT(OPT_SSL_CRL),
+ IN_FS_CHARSET, DEFAULT(0));
+
+static Sys_var_charptr Sys_ssl_crlpath(
+ "ssl_crlpath",
+ "CRL directory (check OpenSSL docs, implies --ssl)",
+ READ_ONLY GLOBAL_VAR(opt_ssl_crlpath), SSL_OPT(OPT_SSL_CRLPATH),
+ IN_FS_CHARSET, DEFAULT(0));
+
+
// why ENUM and not BOOL ?
static const char *updatable_views_with_limit_names[]= {"NO", "YES", 0};
static Sys_var_enum Sys_updatable_views_with_limit(
@@ -2310,21 +3084,33 @@ static Sys_var_mybool Sys_sync_frm(
static char *system_time_zone_ptr;
static Sys_var_charptr Sys_system_time_zone(
"system_time_zone", "The server system time zone",
- READ_ONLY GLOBAL_VAR(system_time_zone_ptr), NO_CMD_LINE,
+ READ_ONLY SHOW_VALUE_IN_HELP GLOBAL_VAR(system_time_zone_ptr),
+ NO_CMD_LINE,
IN_SYSTEM_CHARSET, DEFAULT(system_time_zone));
static Sys_var_ulong Sys_table_def_size(
"table_definition_cache",
"The number of cached table definitions",
- GLOBAL_VAR(table_def_size), CMD_LINE(REQUIRED_ARG),
+ GLOBAL_VAR(tdc_size), CMD_LINE(REQUIRED_ARG),
VALID_RANGE(TABLE_DEF_CACHE_MIN, 512*1024),
DEFAULT(TABLE_DEF_CACHE_DEFAULT), BLOCK_SIZE(1));
+
+static bool fix_table_open_cache(sys_var *, THD *, enum_var_type)
+{
+ mysql_mutex_unlock(&LOCK_global_system_variables);
+ tc_purge();
+ mysql_mutex_lock(&LOCK_global_system_variables);
+ return false;
+}
+
+
static Sys_var_ulong Sys_table_cache_size(
"table_open_cache", "The number of cached open tables",
- GLOBAL_VAR(table_cache_size), CMD_LINE(REQUIRED_ARG),
+ GLOBAL_VAR(tc_size), CMD_LINE(REQUIRED_ARG),
VALID_RANGE(1, 512*1024), DEFAULT(TABLE_OPEN_CACHE_DEFAULT),
- BLOCK_SIZE(1));
+ BLOCK_SIZE(1), NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0),
+ ON_UPDATE(fix_table_open_cache));
static Sys_var_ulong Sys_thread_cache_size(
"thread_cache_size",
@@ -2443,7 +3229,7 @@ static bool check_tx_isolation(sys_var *self, THD *thd, set_var *var)
if (var->type == OPT_DEFAULT && thd->in_active_multi_stmt_transaction())
{
DBUG_ASSERT(thd->in_multi_stmt_transaction_mode());
- my_error(ER_CANT_CHANGE_TX_ISOLATION, MYF(0));
+ my_error(ER_CANT_CHANGE_TX_CHARACTERISTICS, MYF(0));
return TRUE;
}
return FALSE;
@@ -2456,6 +3242,42 @@ static Sys_var_tx_isolation Sys_tx_isolation(
tx_isolation_names, DEFAULT(ISO_REPEATABLE_READ),
NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(check_tx_isolation));
+
+/**
+ Can't change the tx_read_only state if we are already in a
+ transaction.
+*/
+
+static bool check_tx_read_only(sys_var *self, THD *thd, set_var *var)
+{
+ if (var->type == OPT_DEFAULT && thd->in_active_multi_stmt_transaction())
+ {
+ DBUG_ASSERT(thd->in_multi_stmt_transaction_mode());
+ my_error(ER_CANT_CHANGE_TX_CHARACTERISTICS, MYF(0));
+ return true;
+ }
+ return false;
+}
+
+
+bool Sys_var_tx_read_only::session_update(THD *thd, set_var *var)
+{
+ if (var->type == OPT_SESSION && Sys_var_mybool::session_update(thd, var))
+ return true;
+ if (var->type == OPT_DEFAULT || !thd->in_active_multi_stmt_transaction())
+ {
+ // @see Sys_var_tx_isolation::session_update() above for the rules.
+ thd->tx_read_only= var->save_result.ulonglong_value;
+ }
+ return false;
+}
+
+
+static Sys_var_tx_read_only Sys_tx_read_only(
+ "tx_read_only", "Set default transaction access mode to read only.",
+ SESSION_VAR(tx_read_only), NO_CMD_LINE, DEFAULT(0),
+ NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(check_tx_read_only));
+
static Sys_var_ulonglong Sys_tmp_table_size(
"tmp_table_size",
"If an internal in-memory temporary table exceeds this size, MySQL "
@@ -2474,27 +3296,37 @@ static Sys_var_mybool Sys_timed_mutexes(
static char *server_version_ptr;
static Sys_var_charptr Sys_version(
"version", "Server version",
- READ_ONLY GLOBAL_VAR(server_version_ptr), NO_CMD_LINE,
+ READ_ONLY SHOW_VALUE_IN_HELP GLOBAL_VAR(server_version_ptr),
+ NO_CMD_LINE,
IN_SYSTEM_CHARSET, DEFAULT(server_version));
static char *server_version_comment_ptr;
static Sys_var_charptr Sys_version_comment(
"version_comment", "version_comment",
- READ_ONLY GLOBAL_VAR(server_version_comment_ptr), NO_CMD_LINE,
+ READ_ONLY SHOW_VALUE_IN_HELP GLOBAL_VAR(server_version_comment_ptr),
+ NO_CMD_LINE,
IN_SYSTEM_CHARSET, DEFAULT(MYSQL_COMPILATION_COMMENT));
static char *server_version_compile_machine_ptr;
static Sys_var_charptr Sys_version_compile_machine(
"version_compile_machine", "version_compile_machine",
- READ_ONLY GLOBAL_VAR(server_version_compile_machine_ptr), NO_CMD_LINE,
- IN_SYSTEM_CHARSET, DEFAULT(MACHINE_TYPE));
+ READ_ONLY SHOW_VALUE_IN_HELP
+ GLOBAL_VAR(server_version_compile_machine_ptr), NO_CMD_LINE,
+ IN_SYSTEM_CHARSET, DEFAULT(DEFAULT_MACHINE));
static char *server_version_compile_os_ptr;
static Sys_var_charptr Sys_version_compile_os(
"version_compile_os", "version_compile_os",
- READ_ONLY GLOBAL_VAR(server_version_compile_os_ptr), NO_CMD_LINE,
+ READ_ONLY SHOW_VALUE_IN_HELP GLOBAL_VAR(server_version_compile_os_ptr),
+ NO_CMD_LINE,
IN_SYSTEM_CHARSET, DEFAULT(SYSTEM_TYPE));
+static char *malloc_library;
+static Sys_var_charptr Sys_malloc_library(
+ "version_malloc_library", "Version of the used malloc library",
+ READ_ONLY SHOW_VALUE_IN_HELP GLOBAL_VAR(malloc_library), NO_CMD_LINE,
+ IN_SYSTEM_CHARSET, DEFAULT(MALLOC_LIBRARY));
+
static Sys_var_ulong Sys_net_wait_timeout(
"wait_timeout",
"The number of seconds the server waits for activity on a "
@@ -2503,27 +3335,6 @@ static Sys_var_ulong Sys_net_wait_timeout(
VALID_RANGE(1, IF_WIN(INT_MAX32/1000, LONG_TIMEOUT)),
DEFAULT(NET_WAIT_TIMEOUT), BLOCK_SIZE(1));
-/** propagates changes to the relevant flag of @@optimizer_switch */
-static bool fix_engine_condition_pushdown(sys_var *self, THD *thd,
- enum_var_type type)
-{
- SV *sv= (type == OPT_GLOBAL) ? &global_system_variables : &thd->variables;
- if (sv->engine_condition_pushdown)
- sv->optimizer_switch|= OPTIMIZER_SWITCH_ENGINE_CONDITION_PUSHDOWN;
- else
- sv->optimizer_switch&= ~OPTIMIZER_SWITCH_ENGINE_CONDITION_PUSHDOWN;
- return false;
-}
-static Sys_var_mybool Sys_engine_condition_pushdown(
- "engine_condition_pushdown",
- "Push supported query conditions to the storage engine."
- " Deprecated, use --optimizer-switch instead.",
- SESSION_VAR(engine_condition_pushdown),
- CMD_LINE(OPT_ARG, OPT_ENGINE_CONDITION_PUSHDOWN),
- DEFAULT(TRUE), NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(NULL),
- ON_UPDATE(fix_engine_condition_pushdown),
- DEPRECATED("'@@optimizer_switch'"));
-
static Sys_var_plugin Sys_default_storage_engine(
"default_storage_engine", "The default storage engine for new tables",
SESSION_VAR(table_plugin), NO_CMD_LINE,
@@ -2594,10 +3405,10 @@ static bool fix_autocommit(sys_var *self, THD *thd, enum_var_type type)
return false;
}
- if (thd->variables.option_bits & OPTION_AUTOCOMMIT &&
- thd->variables.option_bits & OPTION_NOT_AUTOCOMMIT)
- { // activating autocommit
-
+ if (test_all_bits(thd->variables.option_bits,
+ (OPTION_AUTOCOMMIT | OPTION_NOT_AUTOCOMMIT)))
+ {
+ // activating autocommit
if (trans_commit_stmt(thd) || trans_commit(thd))
{
thd->variables.option_bits&= ~OPTION_AUTOCOMMIT;
@@ -2611,23 +3422,24 @@ static bool fix_autocommit(sys_var *self, THD *thd, enum_var_type type)
Don't close thread tables or release metadata locks: if we do so, we
risk releasing locks/closing tables of expressions used to assign
other variables, as in:
- set @var=my_stored_function1(), @@autocommit=1, @var2=(select max(a)
+ set @var=my_stored_function1(), @@autocommit=1, @var2=(select MY_MAX(a)
from my_table), ...
The locks will be released at statement end anyway, as SET
statement that assigns autocommit is marked to commit
transaction implicitly at the end (@sa stmt_causes_implicitcommit()).
*/
thd->variables.option_bits&=
- ~(OPTION_BEGIN | OPTION_KEEP_LOG | OPTION_NOT_AUTOCOMMIT);
+ ~(OPTION_BEGIN | OPTION_KEEP_LOG | OPTION_NOT_AUTOCOMMIT |
+ OPTION_GTID_BEGIN);
thd->transaction.all.modified_non_trans_table= false;
thd->server_status|= SERVER_STATUS_AUTOCOMMIT;
return false;
}
- if (!(thd->variables.option_bits & OPTION_AUTOCOMMIT) &&
- !(thd->variables.option_bits & OPTION_NOT_AUTOCOMMIT))
- { // disabling autocommit
-
+ if ((thd->variables.option_bits &
+ (OPTION_AUTOCOMMIT |OPTION_NOT_AUTOCOMMIT)) == 0)
+ {
+ // disabling autocommit
thd->transaction.all.modified_non_trans_table= false;
thd->server_status&= ~SERVER_STATUS_AUTOCOMMIT;
thd->variables.option_bits|= OPTION_NOT_AUTOCOMMIT;
@@ -2636,6 +3448,7 @@ static bool fix_autocommit(sys_var *self, THD *thd, enum_var_type type)
return false; // autocommit value wasn't changed
}
+
static Sys_var_bit Sys_autocommit(
"autocommit", "autocommit",
SESSION_VAR(option_bits), NO_CMD_LINE, OPTION_AUTOCOMMIT, DEFAULT(TRUE),
@@ -2647,12 +3460,6 @@ static Sys_var_mybool Sys_big_tables(
"temporary sets on file (Solves most 'table full' errors)",
SESSION_VAR(big_tables), CMD_LINE(OPT_ARG), DEFAULT(FALSE));
-#ifndef TO_BE_DELETED /* Alias for big_tables */
-static Sys_var_mybool Sys_sql_big_tables(
- "sql_big_tables", "alias for big_tables",
- SESSION_VAR(big_tables), NO_CMD_LINE, DEFAULT(FALSE));
-#endif
-
static Sys_var_bit Sys_big_selects(
"sql_big_selects", "sql_big_selects",
SESSION_VAR(option_bits), NO_CMD_LINE, OPTION_BIG_SELECTS,
@@ -2953,7 +3760,7 @@ static Sys_var_session_special Sys_rand_seed2(
static ulonglong read_error_count(THD *thd)
{
- return thd->warning_info->error_count();
+ return thd->get_stmt_da()->error_count();
}
// this really belongs to the SHOW STATUS
static Sys_var_session_special Sys_error_count(
@@ -2965,7 +3772,7 @@ static Sys_var_session_special Sys_error_count(
static ulonglong read_warning_count(THD *thd)
{
- return thd->warning_info->warn_count();
+ return thd->get_stmt_da()->warn_count();
}
// this really belongs to the SHOW STATUS
static Sys_var_session_special Sys_warning_count(
@@ -3062,6 +3869,14 @@ static bool check_log_path(sys_var *self, THD *thd, set_var *var)
if (!path_length)
return true;
+ if (!is_filename_allowed(var->save_result.string_value.str,
+ var->save_result.string_value.length, TRUE))
+ {
+ my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0),
+ self->name.str, var->save_result.string_value.str);
+ return true;
+ }
+
MY_STAT f_stat;
if (my_stat(path, &f_stat, MYF(0)))
@@ -3138,25 +3953,6 @@ static Sys_var_charptr Sys_slow_log_path(
IN_FS_CHARSET, DEFAULT(0), NO_MUTEX_GUARD, NOT_IN_BINLOG,
ON_CHECK(check_log_path), ON_UPDATE(fix_slow_log_file));
-/// @todo deprecate these four legacy have_PLUGIN variables and use I_S instead
-export SHOW_COMP_OPTION have_csv, have_innodb= SHOW_OPTION_DISABLED;
-export SHOW_COMP_OPTION have_ndbcluster, have_partitioning;
-static Sys_var_have Sys_have_csv(
- "have_csv", "have_csv",
- READ_ONLY GLOBAL_VAR(have_csv), NO_CMD_LINE);
-
-static Sys_var_have Sys_have_innodb(
- "have_innodb", "have_innodb",
- READ_ONLY GLOBAL_VAR(have_innodb), NO_CMD_LINE);
-
-static Sys_var_have Sys_have_ndbcluster(
- "have_ndbcluster", "have_ndbcluster",
- READ_ONLY GLOBAL_VAR(have_ndbcluster), NO_CMD_LINE);
-
-static Sys_var_have Sys_have_partition_db(
- "have_partitioning", "have_partitioning",
- READ_ONLY GLOBAL_VAR(have_partitioning), NO_CMD_LINE);
-
static Sys_var_have Sys_have_compress(
"have_compress", "have_compress",
READ_ONLY GLOBAL_VAR(have_compress), NO_CMD_LINE);
@@ -3175,7 +3971,7 @@ static Sys_var_have Sys_have_geometry(
static Sys_var_have Sys_have_openssl(
"have_openssl", "have_openssl",
- READ_ONLY GLOBAL_VAR(have_ssl), NO_CMD_LINE);
+ READ_ONLY GLOBAL_VAR(have_openssl), NO_CMD_LINE);
static Sys_var_have Sys_have_profiling(
"have_profiling", "have_profiling",
@@ -3206,13 +4002,6 @@ static Sys_var_mybool Sys_general_log(
DEFAULT(FALSE), NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0),
ON_UPDATE(fix_log_state));
-// Synonym of "general_log" for consistency with SHOW VARIABLES output
-static Sys_var_mybool Sys_log(
- "log", "Alias for --general-log. Deprecated",
- GLOBAL_VAR(opt_log), NO_CMD_LINE,
- DEFAULT(FALSE), NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0),
- ON_UPDATE(fix_log_state), DEPRECATED("'@@general_log'"));
-
static Sys_var_mybool Sys_slow_query_log(
"slow_query_log",
"Log slow queries to a table or log file. Defaults logging to a file "
@@ -3222,27 +4011,19 @@ static Sys_var_mybool Sys_slow_query_log(
DEFAULT(FALSE), NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0),
ON_UPDATE(fix_log_state));
-/* Synonym of "slow_query_log" for consistency with SHOW VARIABLES output */
-static Sys_var_mybool Sys_log_slow(
- "log_slow_queries",
- "Alias for --slow-query-log. Deprecated",
- GLOBAL_VAR(opt_slow_log), NO_CMD_LINE,
- DEFAULT(FALSE), NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0),
- ON_UPDATE(fix_log_state), DEPRECATED("'@@slow_query_log'"));
-
static bool fix_log_state(sys_var *self, THD *thd, enum_var_type type)
{
bool res;
my_bool *UNINIT_VAR(newvalptr), newval, UNINIT_VAR(oldval);
uint UNINIT_VAR(log_type);
- if (self == &Sys_general_log || self == &Sys_log)
+ if (self == &Sys_general_log)
{
newvalptr= &opt_log;
oldval= logger.get_log_file_handler()->is_open();
log_type= QUERY_LOG_GENERAL;
}
- else if (self == &Sys_slow_query_log || self == &Sys_log_slow)
+ else if (self == &Sys_slow_query_log)
{
newvalptr= &opt_slow_log;
oldval= logger.get_slow_log_file_handler()->is_open();
@@ -3328,76 +4109,52 @@ static Sys_var_mybool Sys_relay_log_recovery(
"processed",
GLOBAL_VAR(relay_log_recovery), CMD_LINE(OPT_ARG), DEFAULT(FALSE));
-bool Sys_var_rpl_filter::do_check(THD *thd, set_var *var)
-{
- bool status;
-
- /*
- We must not be holding LOCK_global_system_variables here, otherwise we can
- deadlock with THD::init() which is invoked from within the slave threads
- with opposite locking order.
- */
- mysql_mutex_assert_not_owner(&LOCK_global_system_variables);
-
- mysql_mutex_lock(&LOCK_active_mi);
- mysql_mutex_lock(&active_mi->rli.run_lock);
- status= active_mi->rli.slave_running;
-
- mysql_mutex_unlock(&active_mi->rli.run_lock);
- mysql_mutex_unlock(&LOCK_active_mi);
-
- if (status)
- my_error(ER_SLAVE_MUST_STOP, MYF(0));
- else
- status= Sys_var_charptr::do_string_check(thd, var, charset(thd));
-
- return status;
-}
-
-void Sys_var_rpl_filter::lock(void)
+bool Sys_var_rpl_filter::global_update(THD *thd, set_var *var)
{
- /*
- Starting a slave thread causes the new thread to attempt to
- acquire LOCK_global_system_variables (in THD::init) while
- LOCK_active_mi is being held by the thread that initiated
- the process. In order to not violate the lock order, unlock
- LOCK_global_system_variables before grabbing LOCK_active_mi.
- */
- mysql_mutex_unlock(&LOCK_global_system_variables);
+ bool result= true; // Assume error
+ Master_info *mi;
+ mysql_mutex_unlock(&LOCK_global_system_variables);
mysql_mutex_lock(&LOCK_active_mi);
- mysql_mutex_lock(&active_mi->rli.run_lock);
-}
+
+ if (!var->base.length) // no base name
+ {
+ mi= master_info_index->
+ get_master_info(&thd->variables.default_master_connection,
+ Sql_condition::WARN_LEVEL_ERROR);
+ }
+ else // has base name
+ {
+ mi= master_info_index->
+ get_master_info(&var->base,
+ Sql_condition::WARN_LEVEL_WARN);
+ }
-void Sys_var_rpl_filter::unlock(void)
-{
- mysql_mutex_unlock(&active_mi->rli.run_lock);
- mysql_mutex_unlock(&LOCK_active_mi);
+ if (mi)
+ {
+ if (mi->rli.slave_running)
+ {
+ my_error(ER_SLAVE_MUST_STOP, MYF(0),
+ mi->connection_name.length,
+ mi->connection_name.str);
+ result= true;
+ }
+ else
+ {
+ result= set_filter_value(var->save_result.string_value.str, mi);
+ }
+ }
+ mysql_mutex_unlock(&LOCK_active_mi);
mysql_mutex_lock(&LOCK_global_system_variables);
+ return result;
}
-bool Sys_var_rpl_filter::global_update(THD *thd, set_var *var)
-{
- bool slave_running, status= false;
-
- lock();
-
- if (! (slave_running= active_mi->rli.slave_running))
- status= set_filter_value(var->save_result.string_value.str);
-
- if (slave_running)
- my_error(ER_SLAVE_MUST_STOP, MYF(0));
-
- unlock();
-
- return slave_running || status;
-}
-
-bool Sys_var_rpl_filter::set_filter_value(const char *value)
+bool Sys_var_rpl_filter::set_filter_value(const char *value, Master_info *mi)
{
bool status= true;
+ Rpl_filter* rpl_filter= mi ? mi->rpl_filter : global_rpl_filter;
switch (opt_id) {
case OPT_REPLICATE_DO_DB:
@@ -3427,10 +4184,33 @@ uchar *Sys_var_rpl_filter::global_value_ptr(THD *thd, LEX_STRING *base)
{
char buf[256];
String tmp(buf, sizeof(buf), &my_charset_bin);
+ uchar *ret;
+ Master_info *mi;
+ Rpl_filter *rpl_filter;
- tmp.length(0);
+ mysql_mutex_unlock(&LOCK_global_system_variables);
+ mysql_mutex_lock(&LOCK_active_mi);
+ if (!base->length) // no base name
+ {
+ mi= master_info_index->
+ get_master_info(&thd->variables.default_master_connection,
+ Sql_condition::WARN_LEVEL_ERROR);
+ }
+ else // has base name
+ {
+ mi= master_info_index->
+ get_master_info(base,
+ Sql_condition::WARN_LEVEL_WARN);
+ }
+ mysql_mutex_lock(&LOCK_global_system_variables);
- lock();
+ if (!mi)
+ {
+ mysql_mutex_unlock(&LOCK_active_mi);
+ return 0;
+ }
+ rpl_filter= mi->rpl_filter;
+ tmp.length(0);
switch (opt_id) {
case OPT_REPLICATE_DO_DB:
@@ -3453,9 +4233,10 @@ uchar *Sys_var_rpl_filter::global_value_ptr(THD *thd, LEX_STRING *base)
break;
}
- unlock();
+ ret= (uchar *) thd->strmake(tmp.ptr(), tmp.length());
+ mysql_mutex_unlock(&LOCK_active_mi);
- return (uchar *) thd->strmake(tmp.ptr(), tmp.length());
+ return ret;
}
static Sys_var_rpl_filter Sys_replicate_do_db(
@@ -3504,72 +4285,127 @@ static Sys_var_charptr Sys_slave_load_tmpdir(
READ_ONLY GLOBAL_VAR(slave_load_tmpdir), CMD_LINE(REQUIRED_ARG),
IN_FS_CHARSET, DEFAULT(0));
-static bool fix_slave_net_timeout(sys_var *self, THD *thd, enum_var_type type)
-{
- DEBUG_SYNC(thd, "fix_slave_net_timeout");
-
- mysql_mutex_unlock(&LOCK_global_system_variables);
- mysql_mutex_lock(&LOCK_active_mi);
- DBUG_PRINT("info", ("slave_net_timeout=%u mi->heartbeat_period=%.3f",
- slave_net_timeout,
- (active_mi? active_mi->heartbeat_period : 0.0)));
- if (active_mi && slave_net_timeout < active_mi->heartbeat_period)
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
- ER_SLAVE_HEARTBEAT_VALUE_OUT_OF_RANGE_MAX,
- ER(ER_SLAVE_HEARTBEAT_VALUE_OUT_OF_RANGE_MAX));
- mysql_mutex_unlock(&LOCK_active_mi);
- mysql_mutex_lock(&LOCK_global_system_variables);
- return false;
-}
static Sys_var_uint Sys_slave_net_timeout(
"slave_net_timeout", "Number of seconds to wait for more data "
- "from a master/slave connection before aborting the read",
+ "from any master/slave connection before aborting the read",
GLOBAL_VAR(slave_net_timeout), CMD_LINE(REQUIRED_ARG),
VALID_RANGE(1, LONG_TIMEOUT), DEFAULT(SLAVE_NET_TIMEOUT), BLOCK_SIZE(1),
NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0),
- ON_UPDATE(fix_slave_net_timeout));
+ ON_UPDATE(0));
+
+
+/*
+ Access a multi_source variable
+ Return 0 + warning if it doesn't exist
+*/
-static bool check_slave_skip_counter(sys_var *self, THD *thd, set_var *var)
+ulonglong Sys_var_multi_source_ulonglong::
+get_master_info_ulonglong_value(THD *thd, ptrdiff_t offset)
{
- bool result= false;
+ Master_info *mi;
+ ulonglong res= 0; // Default value
+ mysql_mutex_unlock(&LOCK_global_system_variables);
mysql_mutex_lock(&LOCK_active_mi);
- mysql_mutex_lock(&active_mi->rli.run_lock);
- if (active_mi->rli.slave_running)
+ mi= master_info_index->
+ get_master_info(&thd->variables.default_master_connection,
+ Sql_condition::WARN_LEVEL_WARN);
+ if (mi)
{
- my_message(ER_SLAVE_MUST_STOP, ER(ER_SLAVE_MUST_STOP), MYF(0));
- result= true;
+ mysql_mutex_lock(&mi->rli.data_lock);
+ res= *((ulonglong*) (((uchar*) mi) + master_info_offset));
+ mysql_mutex_unlock(&mi->rli.data_lock);
}
- mysql_mutex_unlock(&active_mi->rli.run_lock);
- mysql_mutex_unlock(&LOCK_active_mi);
- return result;
+ mysql_mutex_unlock(&LOCK_active_mi);
+ mysql_mutex_lock(&LOCK_global_system_variables);
+ return res;
}
-static bool fix_slave_skip_counter(sys_var *self, THD *thd, enum_var_type type)
+
+
+bool update_multi_source_variable(sys_var *self_var, THD *thd,
+ enum_var_type type)
{
- mysql_mutex_unlock(&LOCK_global_system_variables);
+ Sys_var_multi_source_ulonglong *self= (Sys_var_multi_source_ulonglong*) self_var;
+ bool result= true;
+ Master_info *mi;
+
+ if (type == OPT_GLOBAL)
+ mysql_mutex_unlock(&LOCK_global_system_variables);
mysql_mutex_lock(&LOCK_active_mi);
- mysql_mutex_lock(&active_mi->rli.run_lock);
- /*
- The following test should normally never be true as we test this
- in the check function; To be safe against multiple
- SQL_SLAVE_SKIP_COUNTER request, we do the check anyway
- */
- if (!active_mi->rli.slave_running)
+ mi= master_info_index->
+ get_master_info(&thd->variables.default_master_connection,
+ Sql_condition::WARN_LEVEL_ERROR);
+ if (mi)
{
- mysql_mutex_lock(&active_mi->rli.data_lock);
- active_mi->rli.slave_skip_counter= sql_slave_skip_counter;
- mysql_mutex_unlock(&active_mi->rli.data_lock);
+ mysql_mutex_lock(&mi->rli.run_lock);
+ mysql_mutex_lock(&mi->rli.data_lock);
+ result= self->update_variable(thd, mi);
+ mysql_mutex_unlock(&mi->rli.data_lock);
+ mysql_mutex_unlock(&mi->rli.run_lock);
}
- mysql_mutex_unlock(&active_mi->rli.run_lock);
mysql_mutex_unlock(&LOCK_active_mi);
- mysql_mutex_lock(&LOCK_global_system_variables);
- return 0;
+ if (type == OPT_GLOBAL)
+ mysql_mutex_lock(&LOCK_global_system_variables);
+ return result;
}
-static Sys_var_uint Sys_slave_skip_counter(
- "sql_slave_skip_counter", "sql_slave_skip_counter",
- GLOBAL_VAR(sql_slave_skip_counter), NO_CMD_LINE,
+
+static bool update_slave_skip_counter(sys_var *self, THD *thd, Master_info *mi)
+{
+ if (mi->rli.slave_running)
+ {
+ my_error(ER_SLAVE_MUST_STOP, MYF(0), mi->connection_name.length,
+ mi->connection_name.str);
+ return true;
+ }
+ if (mi->using_gtid != Master_info::USE_GTID_NO &&
+ opt_slave_parallel_threads > 0)
+ {
+ ulong domain_count;
+ mysql_mutex_lock(&rpl_global_gtid_slave_state->LOCK_slave_state);
+ domain_count= rpl_global_gtid_slave_state->count();
+ mysql_mutex_unlock(&rpl_global_gtid_slave_state->LOCK_slave_state);
+ if (domain_count > 1)
+ {
+ /*
+ With domain-based parallel replication, the slave position is
+ multi-dimensional, so the relay log position is not very meaningful.
+ It might not even correspond to the next GTID to execute in _any_
+ domain (the case after error stop). So slave_skip_counter will most
+ likely not do what the user intends. Instead give an error, with a
+ suggestion to instead set @@gtid_slave_pos past the point of error;
+ this works reliably also in the case of multiple domains.
+ */
+ my_error(ER_SLAVE_SKIP_NOT_IN_GTID, MYF(0));
+ return true;
+ }
+ }
+
+ /* The value was stored temporarily in thd */
+ mi->rli.slave_skip_counter= thd->variables.slave_skip_counter;
+ return false;
+}
+
+static Sys_var_multi_source_ulonglong Sys_slave_skip_counter(
+ "sql_slave_skip_counter", "Skip the next N events from the master log",
+ SESSION_VAR(slave_skip_counter), NO_CMD_LINE,
+ MASTER_INFO_VAR(rli.slave_skip_counter),
VALID_RANGE(0, UINT_MAX), DEFAULT(0), BLOCK_SIZE(1),
- NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(check_slave_skip_counter),
- ON_UPDATE(fix_slave_skip_counter));
+ ON_UPDATE(update_slave_skip_counter));
+
+static bool update_max_relay_log_size(sys_var *self, THD *thd, Master_info *mi)
+{
+ mi->rli.max_relay_log_size= thd->variables.max_relay_log_size;
+ mi->rli.relay_log.set_max_size(mi->rli.max_relay_log_size);
+ return false;
+}
+
+static Sys_var_multi_source_ulonglong Sys_max_relay_log_size(
+ "max_relay_log_size",
+ "relay log will be rotated automatically when the size exceeds this "
+ "value. If 0 are startup, it's set to max_binlog_size",
+ SESSION_VAR(max_relay_log_size), CMD_LINE(REQUIRED_ARG),
+ MASTER_INFO_VAR(rli.max_relay_log_size),
+ VALID_RANGE(0, 1024L*1024*1024), DEFAULT(0), BLOCK_SIZE(IO_SIZE),
+ ON_UPDATE(update_max_relay_log_size));
static Sys_var_charptr Sys_slave_skip_errors(
"slave_skip_errors", "Tells the slave thread to continue "
@@ -3662,7 +4498,7 @@ static bool check_locale(sys_var *self, THD *thd, set_var *var)
mysql_mutex_unlock(&LOCK_error_messages);
if (res)
{
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_UNKNOWN_ERROR,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_UNKNOWN_ERROR,
"Can't process error message file for locale '%s'",
locale->name);
return true;
@@ -3689,6 +4525,7 @@ static Sys_var_tz Sys_time_zone(
"time_zone", "time_zone",
SESSION_VAR(time_zone), NO_CMD_LINE,
DEFAULT(&default_tz), NO_MUTEX_GUARD, IN_BINLOG);
+
#ifdef WITH_WSREP
#include "wsrep_var.h"
#include "wsrep_sst.h"
@@ -3890,7 +4727,7 @@ static Sys_var_uint Sys_wsrep_sync_wait(
static const char *wsrep_OSU_method_names[]= { "TOI", "RSU", NullS };
static Sys_var_enum Sys_wsrep_OSU_method(
"wsrep_OSU_method", "Method for Online Schema Upgrade",
- GLOBAL_VAR(wsrep_OSU_method_options), CMD_LINE(OPT_ARG),
+ SESSION_VAR(wsrep_OSU_method), CMD_LINE(OPT_ARG),
wsrep_OSU_method_names, DEFAULT(WSREP_OSU_TOI),
NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0),
ON_UPDATE(0));
@@ -3944,7 +4781,7 @@ static Sys_var_mybool Sys_wsrep_slave_FK_checks(
static Sys_var_mybool Sys_wsrep_slave_UK_checks(
"wsrep_slave_UK_checks", "Should slave thread do "
- "secondary index uniqueness chesks",
+ "secondary index uniqueness checks",
GLOBAL_VAR(wsrep_slave_UK_checks),
CMD_LINE(OPT_ARG), DEFAULT(FALSE));
@@ -3959,6 +4796,22 @@ static Sys_var_mybool Sys_wsrep_dirty_reads(
#endif /* WITH_WSREP */
+static bool fix_host_cache_size(sys_var *, THD *, enum_var_type)
+{
+ hostname_cache_resize((uint) host_cache_size);
+ return false;
+}
+
+static Sys_var_ulong Sys_host_cache_size(
+ "host_cache_size",
+ "How many host names should be cached to avoid resolving.",
+ GLOBAL_VAR(host_cache_size),
+ CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, 65536),
+ DEFAULT(HOST_CACHE_SIZE),
+ BLOCK_SIZE(1),
+ NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(NULL),
+ ON_UPDATE(fix_host_cache_size));
+
static Sys_var_charptr Sys_ignore_db_dirs(
"ignore_db_dirs",
"Specifies a directory to add to the ignore list when collecting "
@@ -4060,6 +4913,46 @@ static Sys_var_set Sys_log_slow_filter(
log_slow_filter_names,
DEFAULT(MAX_SET(array_elements(log_slow_filter_names)-1)));
+static const char *default_regex_flags_names[]=
+{
+ "DOTALL", // (?s) . matches anything including NL
+ "DUPNAMES", // (?J) Allow duplicate names for subpatterns
+ "EXTENDED", // (?x) Ignore white space and # comments
+ "EXTRA", // (?X) extra features (e.g. error on unknown escape character)
+ "MULTILINE", // (?m) ^ and $ match newlines within data
+ "UNGREEDY", // (?U) Invert greediness of quantifiers
+ 0
+};
+static const int default_regex_flags_to_pcre[]=
+{
+ PCRE_DOTALL,
+ PCRE_DUPNAMES,
+ PCRE_EXTENDED,
+ PCRE_EXTRA,
+ PCRE_MULTILINE,
+ PCRE_UNGREEDY,
+ 0
+};
+int default_regex_flags_pcre(const THD *thd)
+{
+ ulonglong src= thd->variables.default_regex_flags;
+ int i, res;
+ for (i= res= 0; default_regex_flags_to_pcre[i]; i++)
+ {
+ if (src & (1 << i))
+ res|= default_regex_flags_to_pcre[i];
+ }
+ return res;
+}
+static Sys_var_set Sys_default_regex_flags(
+ "default_regex_flags",
+ "Default flags for the regex library. "
+ "Syntax: default-regex-flags='[flag[,flag[,flag...]]]'. "
+ "See the manual for the complete list of valid flags",
+ SESSION_VAR(default_regex_flags), CMD_LINE(REQUIRED_ARG),
+ default_regex_flags_names,
+ DEFAULT(0));
+
static Sys_var_ulong Sys_log_slow_rate_limit(
"log_slow_rate_limit",
"Write to slow log every #th slow query. Set to 1 to log everything. "
@@ -4068,11 +4961,12 @@ static Sys_var_ulong Sys_log_slow_rate_limit(
SESSION_VAR(log_slow_rate_limit), CMD_LINE(REQUIRED_ARG),
VALID_RANGE(1, UINT_MAX), DEFAULT(1), BLOCK_SIZE(1));
-static const char *log_slow_verbosity_names[]= { "innodb", "query_plan", 0 };
+static const char *log_slow_verbosity_names[]= { "innodb", "query_plan",
+ "explain", 0 };
static Sys_var_set Sys_log_slow_verbosity(
"log_slow_verbosity",
"log-slow-verbosity=[value[,value ...]] where value is one of "
- "'innodb', 'query_plan'",
+ "'innodb', 'query_plan', 'explain' ",
SESSION_VAR(log_slow_verbosity), CMD_LINE(REQUIRED_ARG),
log_slow_verbosity_names, DEFAULT(LOG_SLOW_VERBOSITY_INIT));
@@ -4114,7 +5008,7 @@ static Sys_var_mybool Sys_binlog_annotate_row_events(
#ifdef HAVE_REPLICATION
static Sys_var_mybool Sys_replicate_annotate_row_events(
"replicate_annotate_row_events",
- "Tells the slave to write annotate rows events recieved from the master "
+ "Tells the slave to write annotate rows events received from the master "
"to its own binary log. Ignored if log_slave_updates is not set",
READ_ONLY GLOBAL_VAR(opt_replicate_annotate_row_events),
CMD_LINE(OPT_ARG), DEFAULT(0));
@@ -4134,6 +5028,32 @@ static Sys_var_ulong Sys_progress_report_time(
SESSION_VAR(progress_report_time), CMD_LINE(REQUIRED_ARG),
VALID_RANGE(0, UINT_MAX), DEFAULT(5), BLOCK_SIZE(1));
+const char *use_stat_tables_modes[] =
+ {"NEVER", "COMPLEMENTARY", "PREFERABLY", 0};
+static Sys_var_enum Sys_optimizer_use_stat_tables(
+ "use_stat_tables",
+ "Specifies how to use system statistics tables. Possible values are "
+ "NEVER, COMPLEMENTARY, PREFERABLY",
+ SESSION_VAR(use_stat_tables), CMD_LINE(REQUIRED_ARG),
+ use_stat_tables_modes, DEFAULT(0));
+
+static Sys_var_ulong Sys_histogram_size(
+ "histogram_size",
+ "Number of bytes used for a histogram. "
+ "If set to 0, no histograms are created by ANALYZE.",
+ SESSION_VAR(histogram_size), CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(0, 255), DEFAULT(0), BLOCK_SIZE(1));
+
+extern const char *histogram_types[];
+static Sys_var_enum Sys_histogram_type(
+ "histogram_type",
+ "Specifies type of the histograms created by ANALYZE. "
+ "Possible values are: "
+ "SINGLE_PREC_HB - single precision height-balanced, "
+ "DOUBLE_PREC_HB - double precision height-balanced.",
+ SESSION_VAR(histogram_type), CMD_LINE(REQUIRED_ARG),
+ histogram_types, DEFAULT(0));
+
static Sys_var_mybool Sys_no_thread_alarm(
"debug_no_thread_alarm",
"Disable system thread alarm calls. Disabling it may be useful "
@@ -4150,7 +5070,7 @@ static Sys_var_mybool Sys_query_cache_strip_comments(
static ulonglong in_transaction(THD *thd)
{
- return test(thd->in_active_multi_stmt_transaction());
+ return MY_TEST(thd->in_active_multi_stmt_transaction());
}
static Sys_var_session_special Sys_in_transaction(
"in_transaction", "Whether there is an active transaction",
@@ -4191,12 +5111,14 @@ static bool check_pseudo_slave_mode(sys_var *self, THD *thd, set_var *var)
#ifndef EMBEDDED_LIBRARY
delete thd->rli_fake;
thd->rli_fake= NULL;
+ delete thd->rgi_fake;
+ thd->rgi_fake= NULL;
#endif
}
else if (previous_val && val)
goto ineffective;
else if (!previous_val && val)
- push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning(thd, Sql_condition::WARN_LEVEL_WARN,
ER_WRONG_VALUE_FOR_VAR,
"'pseudo_slave_mode' is already ON.");
}
@@ -4205,7 +5127,7 @@ static bool check_pseudo_slave_mode(sys_var *self, THD *thd, set_var *var)
if (!previous_val && !val)
goto ineffective;
else if (previous_val && !val)
- push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning(thd, Sql_condition::WARN_LEVEL_WARN,
ER_WRONG_VALUE_FOR_VAR,
"Slave applier execution mode not active, "
"statement ineffective.");
@@ -4213,7 +5135,7 @@ static bool check_pseudo_slave_mode(sys_var *self, THD *thd, set_var *var)
goto end;
ineffective:
- push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning(thd, Sql_condition::WARN_LEVEL_WARN,
ER_WRONG_VALUE_FOR_VAR,
"'pseudo_slave_mode' change was ineffective.");
@@ -4230,3 +5152,13 @@ static Sys_var_mybool Sys_pseudo_slave_mode(
SESSION_ONLY(pseudo_slave_mode), NO_CMD_LINE, DEFAULT(FALSE),
NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(check_pseudo_slave_mode));
+#ifdef HAVE_MMAP
+static Sys_var_ulong Sys_log_tc_size(
+ "log_tc_size",
+ "Size of transaction coordinator log.",
+ READ_ONLY GLOBAL_VAR(opt_tc_log_size),
+ CMD_LINE(REQUIRED_ARG),
+ VALID_RANGE(my_getpagesize() * 3, ULONG_MAX),
+ DEFAULT(my_getpagesize() * 6),
+ BLOCK_SIZE(my_getpagesize()));
+#endif
diff --git a/sql/sys_vars.h b/sql/sys_vars.h
index 3cbd24f1c89..36067c50cc1 100644
--- a/sql/sys_vars.h
+++ b/sql/sys_vars.h
@@ -29,6 +29,7 @@
#include "keycaches.h"
#include "strfunc.h"
#include "tztime.h" // my_tz_find, my_tz_SYSTEM, struct Time_zone
+#include "rpl_mi.h" // For Multi-Source Replication
/*
a set of mostly trivial (as in f(X)=X) defines below to make system variable
@@ -55,6 +56,8 @@
// this means that Sys_var_charptr initial value was malloc()ed
#define PREALLOCATED sys_var::ALLOCATED+
#define PARSED_EARLY sys_var::PARSE_EARLY+
+#define SHOW_VALUE_IN_HELP sys_var::SHOW_VALUE_IN_HELP+
+
/*
Sys_var_bit meaning is reversed, like in
@@foreign_key_checks <-> OPTION_NO_FOREIGN_KEY_CHECKS
@@ -224,6 +227,8 @@ typedef Sys_var_integer<uint, GET_UINT, SHOW_UINT> Sys_var_uint;
typedef Sys_var_integer<ulong, GET_ULONG, SHOW_ULONG> Sys_var_ulong;
typedef Sys_var_integer<ha_rows, GET_HA_ROWS, SHOW_HA_ROWS> Sys_var_harows;
typedef Sys_var_integer<ulonglong, GET_ULL, SHOW_ULONGLONG> Sys_var_ulonglong;
+typedef Sys_var_integer<long, GET_LONG, SHOW_SLONG> Sys_var_long;
+
/**
Helper class for variables that take values from a TYPELIB
@@ -556,6 +561,7 @@ protected:
}
};
+class Master_info;
class Sys_var_rpl_filter: public sys_var
{
private:
@@ -567,14 +573,16 @@ public:
NO_ARG, SHOW_CHAR, 0, NULL, VARIABLE_NOT_IN_BINLOG,
NULL, NULL, NULL), opt_id(getopt_id)
{
- option.var_type= GET_STR;
+ option.var_type= GET_STR | GET_ASK_ADDR;
}
+ bool do_check(THD *thd, set_var *var)
+ {
+ return Sys_var_charptr::do_string_check(thd, var, charset(thd));
+ }
bool check_update_type(Item_result type)
{ return type != STRING_RESULT; }
- bool do_check(THD *thd, set_var *var);
-
void session_save_default(THD *thd, set_var *var)
{ DBUG_ASSERT(FALSE); }
@@ -591,9 +599,7 @@ public:
protected:
uchar *global_value_ptr(THD *thd, LEX_STRING *base);
- bool set_filter_value(const char *value);
- void lock(void);
- void unlock(void);
+ bool set_filter_value(const char *value, Master_info *mi);
};
/**
@@ -637,6 +643,93 @@ public:
}
};
+
+/*
+ A LEX_STRING stored only in thd->variables
+ Only to be used for small buffers
+*/
+
+class Sys_var_session_lexstring: public sys_var
+{
+ size_t max_length;
+public:
+ Sys_var_session_lexstring(const char *name_arg,
+ const char *comment, int flag_args,
+ ptrdiff_t off, size_t size, CMD_LINE getopt,
+ enum charset_enum is_os_charset_arg,
+ const char *def_val, size_t max_length_arg,
+ on_check_function on_check_func=0,
+ on_update_function on_update_func=0)
+ : sys_var(&all_sys_vars, name_arg, comment, flag_args, off, getopt.id,
+ getopt.arg_type, SHOW_CHAR, (intptr)def_val,
+ 0, VARIABLE_NOT_IN_BINLOG, on_check_func, on_update_func,
+ 0),max_length(max_length_arg)
+ {
+ option.var_type= GET_NO_ARG;
+ SYSVAR_ASSERT(scope() == ONLY_SESSION)
+ *const_cast<SHOW_TYPE*>(&show_val_type)= SHOW_LEX_STRING;
+ }
+ bool do_check(THD *thd, set_var *var)
+ {
+ char buff[STRING_BUFFER_USUAL_SIZE];
+ String str(buff, sizeof(buff), system_charset_info), *res;
+
+ if (!(res=var->value->val_str(&str)))
+ {
+ var->save_result.string_value.str= 0; /* NULL */
+ var->save_result.string_value.length= 0;
+ }
+ else
+ {
+ if (res->length() > max_length)
+ {
+ my_error(ER_WRONG_STRING_LENGTH, MYF(0),
+ res->ptr(), name.str, (int) max_length);
+ return true;
+ }
+ var->save_result.string_value.str= thd->strmake(res->ptr(),
+ res->length());
+ var->save_result.string_value.length= res->length();
+ }
+ return false;
+ }
+ bool session_update(THD *thd, set_var *var)
+ {
+ LEX_STRING *tmp= &session_var(thd, LEX_STRING);
+ tmp->length= var->save_result.string_value.length;
+ /* Store as \0 terminated string (just to be safe) */
+ strmake(tmp->str, var->save_result.string_value.str, tmp->length);
+ return false;
+ }
+ bool global_update(THD *thd, set_var *var)
+ {
+ DBUG_ASSERT(FALSE);
+ return false;
+ }
+ void session_save_default(THD *thd, set_var *var)
+ {
+ char *ptr= (char*)(intptr)option.def_value;
+ var->save_result.string_value.str= ptr;
+ var->save_result.string_value.length= strlen(ptr);
+ }
+ void global_save_default(THD *thd, set_var *var)
+ {
+ DBUG_ASSERT(FALSE);
+ }
+ uchar *session_value_ptr(THD *thd, LEX_STRING *base)
+ {
+ return (uchar*) &session_var(thd, LEX_STRING);
+ }
+ uchar *global_value_ptr(THD *thd, LEX_STRING *base)
+ {
+ DBUG_ASSERT(FALSE);
+ return NULL;
+ }
+ bool check_update_type(Item_result type)
+ { return type != STRING_RESULT; }
+};
+
+
#ifndef DBUG_OFF
/**
@@session.dbug and @@global.dbug variables.
@@ -1394,6 +1487,7 @@ public:
};
#endif /* defined(ENABLED_DEBUG_SYNC) */
+
/**
The class for bit variables - a variant of boolean that stores the value
in a bit.
@@ -1847,6 +1941,30 @@ public:
}
};
+
+/**
+ Class representing the tx_read_only system variable for setting
+ default transaction access mode.
+
+ Note that there is a special syntax - SET TRANSACTION READ ONLY
+ (or READ WRITE) that sets the access mode for the next transaction
+ only.
+*/
+
+class Sys_var_tx_read_only: public Sys_var_mybool
+{
+public:
+ Sys_var_tx_read_only(const char *name_arg, const char *comment, int flag_args,
+ ptrdiff_t off, size_t size, CMD_LINE getopt,
+ my_bool def_val, PolyLock *lock,
+ enum binlog_status_enum binlog_status_arg,
+ on_check_function on_check_func)
+ :Sys_var_mybool(name_arg, comment, flag_args, off, size, getopt,
+ def_val, lock, binlog_status_arg, on_check_func)
+ {}
+ virtual bool session_update(THD *thd, set_var *var);
+};
+
/*
Class for replicate_events_marked_for_skip.
We need a custom update function that ensures the slave is stopped when
@@ -1859,25 +1977,312 @@ public:
const char *comment, int flag_args, ptrdiff_t off, size_t size,
CMD_LINE getopt,
const char *values[], uint def_val, PolyLock *lock,
- enum binlog_status_enum binlog_status_arg,
- on_check_function on_check_func)
+ enum binlog_status_enum binlog_status_arg)
:Sys_var_enum(name_arg, comment, flag_args, off, size, getopt,
- values, def_val, lock, binlog_status_arg, on_check_func)
+ values, def_val, lock, binlog_status_arg)
{}
bool global_update(THD *thd, set_var *var);
};
-/****************************************************************************
- Used templates
-****************************************************************************/
-
-#ifdef HAVE_EXPLICIT_TEMPLATE_INSTANTIATION
-template class List<set_var_base>;
-template class List_iterator_fast<set_var_base>;
-template class Sys_var_integer<int, GET_INT, SHOW_SINT>;
-template class Sys_var_integer<uint, GET_UINT, SHOW_INT>;
-template class Sys_var_integer<ulong, GET_ULONG, SHOW_LONG>;
-template class Sys_var_integer<ha_rows, GET_HA_ROWS, SHOW_HA_ROWS>;
-template class Sys_var_integer<ulonglong, GET_ULL, SHOW_LONGLONG>;
-#endif
+/*
+ Class for handing multi-source replication variables
+ Variable values are store in Master_info, but to make it possible to
+ access variable without locks we also store it thd->variables.
+ These can be used as GLOBAL or SESSION, but both points to the same
+ variable. This is to make things compatible with MySQL 5.5 where variables
+ like sql_slave_skip_counter are GLOBAL.
+*/
+
+#define MASTER_INFO_VAR(X) my_offsetof(Master_info, X), sizeof(((Master_info *)0x10)->X)
+class Sys_var_multi_source_ulonglong;
+class Master_info;
+
+typedef bool (*on_multi_source_update_function)(sys_var *self, THD *thd,
+ Master_info *mi);
+bool update_multi_source_variable(sys_var *self,
+ THD *thd, enum_var_type type);
+
+
+class Sys_var_multi_source_ulonglong :public Sys_var_ulonglong
+{
+ ptrdiff_t master_info_offset;
+ on_multi_source_update_function update_multi_source_variable_func;
+public:
+ Sys_var_multi_source_ulonglong(const char *name_arg,
+ const char *comment, int flag_args,
+ ptrdiff_t off, size_t size,
+ CMD_LINE getopt,
+ ptrdiff_t master_info_offset_arg,
+ size_t master_info_arg_size,
+ ulonglong min_val, ulonglong max_val,
+ ulonglong def_val, uint block_size,
+ on_multi_source_update_function on_update_func)
+ :Sys_var_ulonglong(name_arg, comment, flag_args, off, size,
+ getopt, min_val, max_val, def_val, block_size,
+ 0, VARIABLE_NOT_IN_BINLOG, 0, update_multi_source_variable),
+ master_info_offset(master_info_offset_arg),
+ update_multi_source_variable_func(on_update_func)
+ {
+ SYSVAR_ASSERT(master_info_arg_size == size);
+ }
+ bool global_update(THD *thd, set_var *var)
+ {
+ return session_update(thd, var);
+ }
+ void session_save_default(THD *thd, set_var *var)
+ {
+ /* Use value given in variable declaration */
+ global_save_default(thd, var);
+ }
+ uchar *session_value_ptr(THD *thd,LEX_STRING *base)
+ {
+ ulonglong *tmp, res;
+ tmp= (ulonglong*) (((uchar*)&(thd->variables)) + offset);
+ res= get_master_info_ulonglong_value(thd, master_info_offset);
+ *tmp= res;
+ return (uchar*) tmp;
+ }
+ uchar *global_value_ptr(THD *thd, LEX_STRING *base)
+ {
+ return session_value_ptr(thd, base);
+ }
+ ulonglong get_master_info_ulonglong_value(THD *thd, ptrdiff_t offset);
+ bool update_variable(THD *thd, Master_info *mi)
+ {
+ return update_multi_source_variable_func(this, thd, mi);
+ }
+};
+
+
+/**
+ Class for @@global.gtid_current_pos.
+*/
+class Sys_var_gtid_current_pos: public sys_var
+{
+public:
+ Sys_var_gtid_current_pos(const char *name_arg,
+ const char *comment, int flag_args, ptrdiff_t off, size_t size,
+ CMD_LINE getopt)
+ : sys_var(&all_sys_vars, name_arg, comment, flag_args, off, getopt.id,
+ getopt.arg_type, SHOW_CHAR, 0, NULL, VARIABLE_NOT_IN_BINLOG,
+ NULL, NULL, NULL)
+ {
+ option.var_type= GET_STR;
+ }
+ bool do_check(THD *thd, set_var *var)
+ {
+ DBUG_ASSERT(false);
+ return true;
+ }
+ bool session_update(THD *thd, set_var *var)
+ {
+ DBUG_ASSERT(false);
+ return true;
+ }
+ bool global_update(THD *thd, set_var *var)
+ {
+ DBUG_ASSERT(false);
+ return true;
+ }
+ bool check_update_type(Item_result type) {
+ DBUG_ASSERT(false);
+ return false;
+ }
+ void session_save_default(THD *thd, set_var *var)
+ {
+ DBUG_ASSERT(false);
+ }
+ void global_save_default(THD *thd, set_var *var)
+ {
+ DBUG_ASSERT(false);
+ }
+ uchar *session_value_ptr(THD *thd, LEX_STRING *base)
+ {
+ DBUG_ASSERT(false);
+ return NULL;
+ }
+ uchar *global_value_ptr(THD *thd, LEX_STRING *base);
+};
+
+
+/**
+ Class for @@global.gtid_binlog_pos.
+*/
+class Sys_var_gtid_binlog_pos: public sys_var
+{
+public:
+ Sys_var_gtid_binlog_pos(const char *name_arg,
+ const char *comment, int flag_args, ptrdiff_t off, size_t size,
+ CMD_LINE getopt)
+ : sys_var(&all_sys_vars, name_arg, comment, flag_args, off, getopt.id,
+ getopt.arg_type, SHOW_CHAR, 0, NULL, VARIABLE_NOT_IN_BINLOG,
+ NULL, NULL, NULL)
+ {
+ option.var_type= GET_STR;
+ }
+ bool do_check(THD *thd, set_var *var)
+ {
+ DBUG_ASSERT(false);
+ return true;
+ }
+ bool session_update(THD *thd, set_var *var)
+ {
+ DBUG_ASSERT(false);
+ return true;
+ }
+ bool global_update(THD *thd, set_var *var)
+ {
+ DBUG_ASSERT(false);
+ return true;
+ }
+ bool check_update_type(Item_result type) {
+ DBUG_ASSERT(false);
+ return false;
+ }
+ void session_save_default(THD *thd, set_var *var)
+ {
+ DBUG_ASSERT(false);
+ }
+ void global_save_default(THD *thd, set_var *var)
+ {
+ DBUG_ASSERT(false);
+ }
+ uchar *session_value_ptr(THD *thd, LEX_STRING *base)
+ {
+ DBUG_ASSERT(false);
+ return NULL;
+ }
+ uchar *global_value_ptr(THD *thd, LEX_STRING *base);
+};
+
+/**
+ Class for @@global.gtid_slave_pos.
+*/
+class Sys_var_gtid_slave_pos: public sys_var
+{
+public:
+ Sys_var_gtid_slave_pos(const char *name_arg,
+ const char *comment, int flag_args, ptrdiff_t off, size_t size,
+ CMD_LINE getopt)
+ : sys_var(&all_sys_vars, name_arg, comment, flag_args, off, getopt.id,
+ getopt.arg_type, SHOW_CHAR, 0, NULL, VARIABLE_NOT_IN_BINLOG,
+ NULL, NULL, NULL)
+ {
+ option.var_type= GET_STR;
+ }
+ bool do_check(THD *thd, set_var *var);
+ bool session_update(THD *thd, set_var *var)
+ {
+ DBUG_ASSERT(false);
+ return true;
+ }
+ bool global_update(THD *thd, set_var *var);
+ bool check_update_type(Item_result type) { return type != STRING_RESULT; }
+ void session_save_default(THD *thd, set_var *var)
+ {
+ DBUG_ASSERT(false);
+ }
+ void global_save_default(THD *thd, set_var *var)
+ {
+ /* Record the attempt to use default so we can error. */
+ var->value= 0;
+ }
+ uchar *session_value_ptr(THD *thd, LEX_STRING *base)
+ {
+ DBUG_ASSERT(false);
+ return NULL;
+ }
+ uchar *global_value_ptr(THD *thd, LEX_STRING *base);
+};
+
+
+/**
+ Class for @@global.gtid_binlog_state.
+*/
+class Sys_var_gtid_binlog_state: public sys_var
+{
+public:
+ Sys_var_gtid_binlog_state(const char *name_arg,
+ const char *comment, int flag_args, ptrdiff_t off, size_t size,
+ CMD_LINE getopt)
+ : sys_var(&all_sys_vars, name_arg, comment, flag_args, off, getopt.id,
+ getopt.arg_type, SHOW_CHAR, 0, NULL, VARIABLE_NOT_IN_BINLOG,
+ NULL, NULL, NULL)
+ {
+ option.var_type= GET_STR;
+ }
+ bool do_check(THD *thd, set_var *var);
+ bool session_update(THD *thd, set_var *var)
+ {
+ DBUG_ASSERT(false);
+ return true;
+ }
+ bool global_update(THD *thd, set_var *var);
+ bool check_update_type(Item_result type) { return type != STRING_RESULT; }
+ void session_save_default(THD *thd, set_var *var)
+ {
+ DBUG_ASSERT(false);
+ }
+ void global_save_default(THD *thd, set_var *var)
+ {
+ /* Record the attempt to use default so we can error. */
+ var->value= 0;
+ }
+ uchar *session_value_ptr(THD *thd, LEX_STRING *base)
+ {
+ DBUG_ASSERT(false);
+ return NULL;
+ }
+ uchar *global_value_ptr(THD *thd, LEX_STRING *base);
+};
+
+
+/**
+ Class for @@session.last_gtid.
+*/
+class Sys_var_last_gtid: public sys_var
+{
+public:
+ Sys_var_last_gtid(const char *name_arg,
+ const char *comment, int flag_args, CMD_LINE getopt)
+ : sys_var(&all_sys_vars, name_arg, comment, flag_args, 0, getopt.id,
+ getopt.arg_type, SHOW_CHAR, 0, NULL, VARIABLE_NOT_IN_BINLOG,
+ NULL, NULL, NULL)
+ {
+ option.var_type= GET_STR;
+ }
+ bool do_check(THD *thd, set_var *var)
+ {
+ DBUG_ASSERT(false);
+ return true;
+ }
+ bool session_update(THD *thd, set_var *var)
+ {
+ DBUG_ASSERT(false);
+ return true;
+ }
+ bool global_update(THD *thd, set_var *var)
+ {
+ DBUG_ASSERT(false);
+ return true;
+ }
+ bool check_update_type(Item_result type) {
+ DBUG_ASSERT(false);
+ return false;
+ }
+ void session_save_default(THD *thd, set_var *var)
+ {
+ DBUG_ASSERT(false);
+ }
+ void global_save_default(THD *thd, set_var *var)
+ {
+ DBUG_ASSERT(false);
+ }
+ uchar *session_value_ptr(THD *thd, LEX_STRING *base);
+ uchar *global_value_ptr(THD *thd, LEX_STRING *base)
+ {
+ DBUG_ASSERT(false);
+ return NULL;
+ }
+};
diff --git a/sql/table.cc b/sql/table.cc
index b53518a1fa9..5b69ccc23de 100644
--- a/sql/table.cc
+++ b/sql/table.cc
@@ -17,11 +17,9 @@
/* Some general useful functions */
-#include "my_global.h" /* NO_EMBEDDED_ACCESS_CHECKS */
+#include <my_global.h> /* NO_EMBEDDED_ACCESS_CHECKS */
#include "sql_priv.h"
-#include "unireg.h" // REQUIRED: for other includes
#include "table.h"
-#include "frm_crypt.h" // get_crypt_for_frm
#include "key.h" // find_ref_key
#include "sql_table.h" // build_table_filename,
// primary_key_name
@@ -31,13 +29,15 @@
#include "sql_partition.h" // mysql_unpack_partition,
// fix_partition_func, partition_info
#include "sql_acl.h" // *_ACL, acl_getroot_no_password
-#include "sql_base.h" // release_table_share
+#include "sql_base.h"
#include "create_options.h"
#include <m_ctype.h>
#include "my_md5.h"
#include "my_bit.h"
#include "sql_select.h"
#include "sql_derived.h"
+#include "sql_statistics.h"
+#include "discover.h"
#include "mdl.h" // MDL_wait_for_graph_visitor
#ifdef WITH_WSREP
#include "ha_partition.h"
@@ -66,18 +66,12 @@ LEX_STRING parse_vcol_keyword= { C_STRING_WITH_LEN("PARSE_VCOL_EXPR ") };
/* Functions defined in this file */
-void open_table_error(TABLE_SHARE *share, int error, int db_errno,
- myf errortype, int errarg);
-static int open_binary_frm(THD *thd, TABLE_SHARE *share,
- uchar *head, File file);
static void fix_type_pointers(const char ***array, TYPELIB *point_to_type,
uint types, char **names);
static uint find_field(Field **fields, uchar *record, uint start, uint length);
inline bool is_system_table_name(const char *name, uint length);
-static ulong get_form_pos(File file, uchar *head);
-
/**************************************************************************
Object_creation_ctx implementation.
**************************************************************************/
@@ -155,7 +149,7 @@ View_creation_ctx * View_creation_ctx::create(THD *thd,
if (!view->view_client_cs_name.str ||
!view->view_connection_cl_name.str)
{
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
ER_VIEW_NO_CREATION_CTX,
ER(ER_VIEW_NO_CREATION_CTX),
(const char *) view->db,
@@ -189,7 +183,7 @@ View_creation_ctx * View_creation_ctx::create(THD *thd,
(const char *) view->view_client_cs_name.str,
(const char *) view->view_connection_cl_name.str);
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
ER_VIEW_INVALID_CREATION_CTX,
ER(ER_VIEW_INVALID_CREATION_CTX),
(const char *) view->db,
@@ -280,7 +274,7 @@ TABLE_CATEGORY get_table_category(const LEX_STRING *db, const LEX_STRING *name)
/*
- Allocate a setup TABLE_SHARE structure
+ Allocate and setup a TABLE_SHARE structure
SYNOPSIS
alloc_table_share()
@@ -293,8 +287,8 @@ TABLE_CATEGORY get_table_category(const LEX_STRING *db, const LEX_STRING *name)
# Share
*/
-TABLE_SHARE *alloc_table_share(TABLE_LIST *table_list, char *key,
- uint key_length)
+TABLE_SHARE *alloc_table_share(const char *db, const char *table_name,
+ const char *key, uint key_length)
{
MEM_ROOT mem_root;
TABLE_SHARE *share;
@@ -302,13 +296,11 @@ TABLE_SHARE *alloc_table_share(TABLE_LIST *table_list, char *key,
char path[FN_REFLEN];
uint path_length;
DBUG_ENTER("alloc_table_share");
- DBUG_PRINT("enter", ("table: '%s'.'%s'",
- table_list->db, table_list->table_name));
+ DBUG_PRINT("enter", ("table: '%s'.'%s'", db, table_name));
path_length= build_table_filename(path, sizeof(path) - 1,
- table_list->db,
- table_list->table_name, "", 0);
- init_sql_alloc(&mem_root, TABLE_ALLOC_BLOCK_SIZE, 0);
+ db, table_name, "", 0);
+ init_sql_alloc(&mem_root, TABLE_ALLOC_BLOCK_SIZE, 0, MYF(0));
if (multi_alloc_root(&mem_root,
&share, sizeof(*share),
&key_buff, key_length,
@@ -324,26 +316,18 @@ TABLE_SHARE *alloc_table_share(TABLE_LIST *table_list, char *key,
strmov(share->path.str, path);
share->normalized_path.str= share->path.str;
share->normalized_path.length= path_length;
-
- share->set_refresh_version();
-
- /*
- Since alloc_table_share() can be called without any locking (for
- example, ha_create_table... functions), we do not assign a table
- map id here. Instead we assign a value that is not used
- elsewhere, and then assign a table map id inside open_table()
- under the protection of the LOCK_open mutex.
- */
- share->table_map_id= ~0UL;
+ share->table_category= get_table_category(& share->db, & share->table_name);
+ share->open_errno= ENOENT;
share->cached_row_logging_check= -1;
- share->used_tables.empty();
- share->free_tables.empty();
- share->m_flush_tickets.empty();
+ init_sql_alloc(&share->stats_cb.mem_root, TABLE_ALLOC_BLOCK_SIZE, 0, MYF(0));
memcpy((char*) &share->mem_root, (char*) &mem_root, sizeof(mem_root));
+ mysql_mutex_init(key_TABLE_SHARE_LOCK_share,
+ &share->LOCK_share, MY_MUTEX_INIT_SLOW);
mysql_mutex_init(key_TABLE_SHARE_LOCK_ha_data,
&share->LOCK_ha_data, MY_MUTEX_INIT_FAST);
+ tdc_init_share(share);
}
DBUG_RETURN(share);
}
@@ -356,7 +340,7 @@ TABLE_SHARE *alloc_table_share(TABLE_LIST *table_list, char *key,
init_tmp_table_share()
thd thread handle
share Share to fill
- key Table_cache_key, as generated from create_table_def_key.
+ key Table_cache_key, as generated from tdc_create_key.
must start with db name.
key_length Length of key
table_name Table name
@@ -380,7 +364,12 @@ void init_tmp_table_share(THD *thd, TABLE_SHARE *share, const char *key,
DBUG_PRINT("enter", ("table: '%s'.'%s'", key, table_name));
bzero((char*) share, sizeof(*share));
- init_sql_alloc(&share->mem_root, TABLE_ALLOC_BLOCK_SIZE, 0);
+ /*
+ This can't be MY_THREAD_SPECIFIC for slaves as they are freed
+ during cleanup() from Relay_log_info::close_temporary_tables()
+ */
+ init_sql_alloc(&share->mem_root, TABLE_ALLOC_BLOCK_SIZE, 0,
+ MYF(thd->slave_thread ? 0 : MY_THREAD_SPECIFIC));
share->table_category= TABLE_CATEGORY_TEMPORARY;
share->tmp_table= INTERNAL_TMP_TABLE;
share->db.str= (char*) key;
@@ -401,11 +390,6 @@ void init_tmp_table_share(THD *thd, TABLE_SHARE *share, const char *key,
compatibility checks.
*/
share->table_map_id= (ulong) thd->query_id;
-
- share->used_tables.empty();
- share->free_tables.empty();
- share->m_flush_tickets.empty();
-
DBUG_VOID_RETURN;
}
@@ -420,10 +404,28 @@ void TABLE_SHARE::destroy()
{
uint idx;
KEY *info_it;
+ DBUG_ENTER("TABLE_SHARE::destroy");
+ DBUG_PRINT("info", ("db: %s table: %s", db.str, table_name.str));
+
+ if (ha_share)
+ {
+ delete ha_share;
+ ha_share= NULL; // Safety
+ }
+
+ free_root(&stats_cb.mem_root, MYF(0));
+ stats_cb.stats_can_be_read= FALSE;
+ stats_cb.stats_is_read= FALSE;
+ stats_cb.histograms_can_be_read= FALSE;
+ stats_cb.histograms_are_read= FALSE;
- /* The mutex is initialized only for shares that are part of the TDC */
+ /* The mutexes are initialized only for shares that are part of the TDC */
if (tmp_table == NO_TMP_TABLE)
+ {
+ mysql_mutex_destroy(&LOCK_share);
mysql_mutex_destroy(&LOCK_ha_data);
+ tdc_deinit_share(this);
+ }
my_hash_free(&name_hash);
plugin_unlock(NULL, db_plugin);
@@ -439,24 +441,20 @@ void TABLE_SHARE::destroy()
info_it->flags= 0;
}
}
- if (ha_data_destroy)
- {
- ha_data_destroy(ha_data);
- ha_data_destroy= NULL;
- }
+
#ifdef WITH_PARTITION_STORAGE_ENGINE
- if (ha_part_data_destroy)
- {
- ha_part_data_destroy(ha_part_data);
- ha_part_data_destroy= NULL;
- }
+ plugin_unlock(NULL, default_part_plugin);
#endif /* WITH_PARTITION_STORAGE_ENGINE */
+
+ PSI_CALL_release_table_share(m_psi);
+
/*
Make a copy since the share is allocated in its own root,
and free_root() updates its argument after freeing the memory.
*/
MEM_ROOT own_root= mem_root;
free_root(&own_root, MYF(0));
+ DBUG_VOID_RETURN;
}
/*
@@ -471,37 +469,7 @@ void free_table_share(TABLE_SHARE *share)
{
DBUG_ENTER("free_table_share");
DBUG_PRINT("enter", ("table: %s.%s", share->db.str, share->table_name.str));
- DBUG_ASSERT(share->ref_count == 0);
-
- if (share->m_flush_tickets.is_empty())
- {
- /*
- No threads are waiting for this share to be flushed (the
- share is not old, is for a temporary table, or just nobody
- happens to be waiting for it). Destroy it.
- */
- share->destroy();
- }
- else
- {
- Wait_for_flush_list::Iterator it(share->m_flush_tickets);
- Wait_for_flush *ticket;
- /*
- We're about to iterate over a list that is used
- concurrently. Make sure this never happens without a lock.
- */
- mysql_mutex_assert_owner(&LOCK_open);
-
- while ((ticket= it++))
- (void) ticket->get_ctx()->m_wait.set_status(MDL_wait::GRANTED);
- /*
- If there are threads waiting for this share to be flushed,
- the last one to receive the notification will destroy the
- share. At this point the share is removed from the table
- definition cache, so is OK to proceed here without waiting
- for this thread to do the work.
- */
- }
+ share->destroy();
DBUG_VOID_RETURN;
}
@@ -546,6 +514,17 @@ inline bool is_system_table_name(const char *name, uint length)
my_tolower(ci, name[2]) == 'm' &&
my_tolower(ci, name[3]) == 'e') ||
+ /* one of mysql.*_stat tables, but not mysql.innodb* tables*/
+ ((my_tolower(ci, name[length-5]) == 's' &&
+ my_tolower(ci, name[length-4]) == 't' &&
+ my_tolower(ci, name[length-3]) == 'a' &&
+ my_tolower(ci, name[length-2]) == 't' &&
+ my_tolower(ci, name[length-1]) == 's') &&
+ !(my_tolower(ci, name[0]) == 'i' &&
+ my_tolower(ci, name[1]) == 'n' &&
+ my_tolower(ci, name[2]) == 'n' &&
+ my_tolower(ci, name[3]) == 'o')) ||
+
/* mysql.event table */
(my_tolower(ci, name[0]) == 'e' &&
my_tolower(ci, name[1]) == 'v' &&
@@ -558,27 +537,6 @@ inline bool is_system_table_name(const char *name, uint length)
}
-/**
- Check if a string contains path elements
-*/
-
-static bool has_disabled_path_chars(const char *str)
-{
- for (; *str; str++)
- {
- switch (*str) {
- case FN_EXTCHAR:
- case '/':
- case '\\':
- case '~':
- case '@':
- return TRUE;
- }
- }
- return FALSE;
-}
-
-
/*
Read table definition from a binary / text based .frm file
@@ -590,155 +548,114 @@ static bool has_disabled_path_chars(const char *str)
NOTES
This function is called when the table definition is not cached in
- table_def_cache
+ table definition cache
The data is returned in 'share', which is alloced by
alloc_table_share().. The code assumes that share is initialized.
-
- RETURN VALUES
- 0 ok
- 1 Error (see open_table_error)
- 2 Error (see open_table_error)
- 3 Wrong data in .frm file
- 4 Error (see open_table_error)
- 5 Error (see open_table_error: charset unavailable)
- 6 Unknown .frm version
*/
-int open_table_def(THD *thd, TABLE_SHARE *share, uint db_flags)
+enum open_frm_error open_table_def(THD *thd, TABLE_SHARE *share, uint flags)
{
- int error, table_type;
- bool error_given;
+ bool error_given= false;
File file;
- uchar head[64];
+ uchar *buf;
+ uchar head[FRM_HEADER_SIZE];
char path[FN_REFLEN];
- MEM_ROOT **root_ptr, *old_root;
+ size_t frmlen, read_length;
DBUG_ENTER("open_table_def");
DBUG_PRINT("enter", ("table: '%s'.'%s' path: '%s'", share->db.str,
share->table_name.str, share->normalized_path.str));
- error= 1;
- error_given= 0;
+ share->error= OPEN_FRM_OPEN_ERROR;
strxmov(path, share->normalized_path.str, reg_ext, NullS);
- if ((file= mysql_file_open(key_file_frm,
- path, O_RDONLY | O_SHARE, MYF(0))) < 0)
+ if (flags & GTS_FORCE_DISCOVERY)
{
- /*
- We don't try to open 5.0 unencoded name, if
- - non-encoded name contains '@' signs,
- because '@' can be misinterpreted.
- It is not clear if '@' is escape character in 5.1,
- or a normal character in 5.0.
-
- - non-encoded db or table name contain "#mysql50#" prefix.
- This kind of tables must have been opened only by the
- mysql_file_open() above.
- */
- if (has_disabled_path_chars(share->table_name.str) ||
- has_disabled_path_chars(share->db.str) ||
- !strncmp(share->db.str, MYSQL50_TABLE_NAME_PREFIX,
- MYSQL50_TABLE_NAME_PREFIX_LENGTH) ||
- !strncmp(share->table_name.str, MYSQL50_TABLE_NAME_PREFIX,
- MYSQL50_TABLE_NAME_PREFIX_LENGTH))
- goto err_not_open;
-
- /* Try unencoded 5.0 name */
- uint length;
- strxnmov(path, sizeof(path)-1,
- mysql_data_home, "/", share->db.str, "/",
- share->table_name.str, reg_ext, NullS);
- length= unpack_filename(path, path) - reg_ext_length;
- /*
- The following is a safety test and should never fail
- as the old file name should never be longer than the new one.
- */
- DBUG_ASSERT(length <= share->normalized_path.length);
- /*
- If the old and the new names have the same length,
- then table name does not have tricky characters,
- so no need to check the old file name.
- */
- if (length == share->normalized_path.length ||
- ((file= mysql_file_open(key_file_frm,
- path, O_RDONLY | O_SHARE, MYF(0))) < 0))
- goto err_not_open;
+ DBUG_ASSERT(flags & GTS_TABLE);
+ DBUG_ASSERT(flags & GTS_USE_DISCOVERY);
+ mysql_file_delete_with_symlink(key_file_frm, path, MYF(0));
+ file= -1;
+ }
+ else
+ file= mysql_file_open(key_file_frm, path, O_RDONLY | O_SHARE, MYF(0));
- /* Unencoded 5.0 table name found */
- path[length]= '\0'; // Remove .frm extension
- strmov(share->normalized_path.str, path);
- share->normalized_path.length= length;
+ if (file < 0)
+ {
+ if ((flags & GTS_TABLE) && (flags & GTS_USE_DISCOVERY))
+ {
+ ha_discover_table(thd, share);
+ error_given= true;
+ }
+ goto err_not_open;
}
- error= 4;
- if (mysql_file_read(file, head, 64, MYF(MY_NABP)))
+ if (mysql_file_read(file, head, sizeof(head), MYF(MY_NABP)))
+ {
+ share->error = my_errno == HA_ERR_FILE_TOO_SHORT
+ ? OPEN_FRM_CORRUPTED : OPEN_FRM_READ_ERROR;
goto err;
+ }
- if (head[0] == (uchar) 254 && head[1] == 1)
+ if (memcmp(head, STRING_WITH_LEN("TYPE=VIEW\n")) == 0)
{
- if (head[2] == FRM_VER || head[2] == FRM_VER+1 ||
- (head[2] >= FRM_VER+3 && head[2] <= FRM_VER+4))
- {
- /* Open view only */
- if (db_flags & OPEN_VIEW_ONLY)
- {
- error_given= 1;
- goto err;
- }
- table_type= 1;
- }
- else
- {
- error= 6; // Unkown .frm version
- goto err;
- }
+ share->is_view= 1;
+ share->error= flags & GTS_VIEW ? OPEN_FRM_OK : OPEN_FRM_NOT_A_TABLE;
+ goto err;
}
- else if (memcmp(head, STRING_WITH_LEN("TYPE=")) == 0)
+ if (!is_binary_frm_header(head))
{
- error= 5;
- if (memcmp(head+5,"VIEW",4) == 0)
- {
- share->is_view= 1;
- if (db_flags & OPEN_VIEW)
- error= 0;
- }
+ /* No handling of text based files yet */
+ share->error = OPEN_FRM_CORRUPTED;
goto err;
}
- else
+ if (!(flags & GTS_TABLE))
+ {
+ share->error = OPEN_FRM_NOT_A_VIEW;
+ goto err;
+ }
+
+ frmlen= uint4korr(head+10);
+ set_if_smaller(frmlen, FRM_MAX_SIZE); // safety
+
+ if (!(buf= (uchar*)my_malloc(frmlen, MYF(MY_THREAD_SPECIFIC|MY_WME))))
goto err;
- /* No handling of text based files yet */
- if (table_type == 1)
+ memcpy(buf, head, sizeof(head));
+
+ read_length= mysql_file_read(file, buf + sizeof(head),
+ frmlen - sizeof(head), MYF(MY_WME));
+ if (read_length == 0 || read_length == (size_t)-1)
{
- root_ptr= my_pthread_getspecific_ptr(MEM_ROOT**, THR_MALLOC);
- old_root= *root_ptr;
- *root_ptr= &share->mem_root;
- error= open_binary_frm(thd, share, head, file);
- *root_ptr= old_root;
- error_given= 1;
+ share->error = OPEN_FRM_READ_ERROR;
+ my_free(buf);
+ goto err;
}
+ mysql_file_close(file, MYF(MY_WME));
+
+ frmlen= read_length + sizeof(head);
- share->table_category= get_table_category(& share->db, & share->table_name);
+ share->init_from_binary_frm_image(thd, false, buf, frmlen);
+ error_given= true; // init_from_binary_frm_image has already called my_error()
+ my_free(buf);
- if (!error)
- thd->status_var.opened_shares++;
+ goto err_not_open;
err:
mysql_file_close(file, MYF(MY_WME));
err_not_open:
- if (error && !error_given)
+ if (share->error && !error_given)
{
- share->error= error;
- open_table_error(share, error, (share->open_errno= my_errno), 0);
+ share->open_errno= my_errno;
+ open_table_error(share, share->error, share->open_errno);
}
- DBUG_RETURN(error);
+ DBUG_RETURN(share->error);
}
-
-static bool create_key_infos(uchar *strpos, uint keys, KEY *keyinfo,
- uint new_frm_ver,
- uint &ext_key_parts, TABLE_SHARE *share, uint len,
+static bool create_key_infos(const uchar *strpos, const uchar *frm_image_end,
+ uint keys, KEY *keyinfo,
+ uint new_frm_ver, uint &ext_key_parts,
+ TABLE_SHARE *share, uint len,
KEY *first_keyinfo, char* &keynames)
{
uint i, j, n_length;
@@ -773,25 +690,29 @@ static bool create_key_infos(uchar *strpos, uint keys, KEY *keyinfo,
{
if (new_frm_ver >= 3)
{
+ if (strpos + 8 >= frm_image_end)
+ return 1;
keyinfo->flags= (uint) uint2korr(strpos) ^ HA_NOSAME;
keyinfo->key_length= (uint) uint2korr(strpos+2);
- keyinfo->key_parts= (uint) strpos[4];
+ keyinfo->user_defined_key_parts= (uint) strpos[4];
keyinfo->algorithm= (enum ha_key_alg) strpos[5];
keyinfo->block_size= uint2korr(strpos+6);
strpos+=8;
}
else
{
+ if (strpos + 4 >= frm_image_end)
+ return 1;
keyinfo->flags= ((uint) strpos[0]) ^ HA_NOSAME;
keyinfo->key_length= (uint) uint2korr(strpos+1);
- keyinfo->key_parts= (uint) strpos[3];
+ keyinfo->user_defined_key_parts= (uint) strpos[3];
keyinfo->algorithm= HA_KEY_ALG_UNDEF;
strpos+=4;
}
if (i == 0)
{
- ext_key_parts+= (share->use_ext_keys ? first_keyinfo->key_parts*(keys-1) : 0);
+ ext_key_parts+= (share->use_ext_keys ? first_keyinfo->user_defined_key_parts*(keys-1) : 0);
n_length=keys * sizeof(KEY) + ext_key_parts * sizeof(KEY_PART_INFO);
if (!(keyinfo= (KEY*) alloc_root(&share->mem_root,
n_length + len)))
@@ -804,10 +725,10 @@ static bool create_key_infos(uchar *strpos, uint keys, KEY *keyinfo,
sizeof(ulong) * ext_key_parts)))
return 1;
first_key_part= key_part;
- first_key_parts= first_keyinfo->key_parts;
+ first_key_parts= first_keyinfo->user_defined_key_parts;
keyinfo->flags= first_keyinfo->flags;
keyinfo->key_length= first_keyinfo->key_length;
- keyinfo->key_parts= first_keyinfo->key_parts;
+ keyinfo->user_defined_key_parts= first_keyinfo->user_defined_key_parts;
keyinfo->algorithm= first_keyinfo->algorithm;
if (new_frm_ver >= 3)
keyinfo->block_size= first_keyinfo->block_size;
@@ -815,8 +736,10 @@ static bool create_key_infos(uchar *strpos, uint keys, KEY *keyinfo,
keyinfo->key_part= key_part;
keyinfo->rec_per_key= rec_per_key;
- for (j=keyinfo->key_parts ; j-- ; key_part++)
+ for (j=keyinfo->user_defined_key_parts ; j-- ; key_part++)
{
+ if (strpos + (new_frm_ver >= 1 ? 9 : 7) >= frm_image_end)
+ return 1;
*rec_per_key++=0;
key_part->fieldnr= (uint16) (uint2korr(strpos) & FIELD_NR_MASK);
key_part->offset= (uint) uint2korr(strpos+2)-1;
@@ -841,16 +764,21 @@ static bool create_key_infos(uchar *strpos, uint keys, KEY *keyinfo,
}
key_part->store_length=key_part->length;
}
- keyinfo->ext_key_parts= keyinfo->key_parts;
+
+ /*
+ Add primary key to end of extended keys for non unique keys for
+ storage engines that supports it.
+ */
+ keyinfo->ext_key_parts= keyinfo->user_defined_key_parts;
keyinfo->ext_key_flags= keyinfo->flags;
keyinfo->ext_key_part_map= 0;
- if (share->use_ext_keys && i)
+ if (share->use_ext_keys && i && !(keyinfo->flags & HA_NOSAME))
{
for (j= 0;
j < first_key_parts && keyinfo->ext_key_parts < MAX_REF_PARTS;
j++)
{
- uint key_parts= keyinfo->key_parts;
+ uint key_parts= keyinfo->user_defined_key_parts;
KEY_PART_INFO* curr_key_part= keyinfo->key_part;
KEY_PART_INFO* curr_key_part_end= curr_key_part+key_parts;
for ( ; curr_key_part < curr_key_part_end; curr_key_part++)
@@ -872,20 +800,28 @@ static bool create_key_infos(uchar *strpos, uint keys, KEY *keyinfo,
share->ext_key_parts+= keyinfo->ext_key_parts;
}
keynames=(char*) key_part;
- strpos+= (strmov(keynames, (char *) strpos) - keynames)+1;
+ strpos+= strnmov(keynames, (char *) strpos, frm_image_end - strpos) - keynames;
+ if (*strpos++) // key names are \0-terminated
+ return 1;
//reading index comments
for (keyinfo= share->key_info, i=0; i < keys; i++, keyinfo++)
{
if (keyinfo->flags & HA_USES_COMMENT)
{
+ if (strpos + 2 >= frm_image_end)
+ return 1;
keyinfo->comment.length= uint2korr(strpos);
- keyinfo->comment.str= strmake_root(&share->mem_root, (char*) strpos+2,
+ strpos+= 2;
+
+ if (strpos + keyinfo->comment.length >= frm_image_end)
+ return 1;
+ keyinfo->comment.str= strmake_root(&share->mem_root, (char*) strpos,
keyinfo->comment.length);
- strpos+= 2 + keyinfo->comment.length;
+ strpos+= keyinfo->comment.length;
}
- DBUG_ASSERT(test(keyinfo->flags & HA_USES_COMMENT) ==
- (keyinfo->comment.length > 0));
+ DBUG_ASSERT(MY_TEST(keyinfo->flags & HA_USES_COMMENT) ==
+ (keyinfo->comment.length > 0));
}
share->keys= keys; // do it *after* all key_info's are initialized
@@ -893,11 +829,13 @@ static bool create_key_infos(uchar *strpos, uint keys, KEY *keyinfo,
return 0;
}
+
/** ensures that the enum value (read from frm) is within limits
if not - issues a warning and resets the value to 0
(that is, 0 is assumed to be a default value)
*/
+
static uint enum_value_with_check(THD *thd, TABLE_SHARE *share,
const char *name, uint value, uint limit)
{
@@ -910,31 +848,77 @@ static uint enum_value_with_check(THD *thd, TABLE_SHARE *share,
}
-/*
- Read data from a binary .frm file from MySQL 3.23 - 5.0 into TABLE_SHARE
+/**
+ Check if a collation has changed number
+
+ @param mysql_version
+ @param current collation number
+
+ @retval new collation number (same as current collation number of no change)
+*/
+
+static uint upgrade_collation(ulong mysql_version, uint cs_number)
+{
+ if (mysql_version >= 50300 && mysql_version <= 50399)
+ {
+ switch (cs_number) {
+ case 149: return MY_PAGE2_COLLATION_ID_UCS2; // ucs2_crotian_ci
+ case 213: return MY_PAGE2_COLLATION_ID_UTF8; // utf8_crotian_ci
+ }
+ }
+ if ((mysql_version >= 50500 && mysql_version <= 50599) ||
+ (mysql_version >= 100000 && mysql_version <= 100005))
+ {
+ switch (cs_number) {
+ case 149: return MY_PAGE2_COLLATION_ID_UCS2; // ucs2_crotian_ci
+ case 213: return MY_PAGE2_COLLATION_ID_UTF8; // utf8_crotian_ci
+ case 214: return MY_PAGE2_COLLATION_ID_UTF32; // utf32_croatian_ci
+ case 215: return MY_PAGE2_COLLATION_ID_UTF16; // utf16_croatian_ci
+ case 245: return MY_PAGE2_COLLATION_ID_UTF8MB4;// utf8mb4_croatian_ci
+ }
+ }
+ return cs_number;
+}
+
+
+/**
+ Read data from a binary .frm file image into a TABLE_SHARE
+
+ @note
+ frm bytes at the following offsets are unused in MariaDB 10.0:
+
+ 8..9 (used to be the number of "form names")
+ 28..29 (used to be key_info_length)
+
+ They're still set, for compatibility reasons, but never read.
+
+ 42..46 are unused since 5.0 (were for RAID support)
+ Also, there're few unused bytes in forminfo.
+
*/
-static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
- File file)
+int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
+ const uchar *frm_image,
+ size_t frm_length)
{
- int error, errarg= 0;
+ TABLE_SHARE *share= this;
uint new_frm_ver, field_pack_length, new_field_pack_flag;
uint interval_count, interval_parts, read_length, int_length;
uint db_create_options, keys, key_parts, n_length;
- uint key_info_length, com_length, null_bit_pos;
+ uint com_length, null_bit_pos;
uint extra_rec_buf_length;
uint i;
bool use_hash;
char *keynames, *names, *comment_pos;
- uchar forminfo[288];
- uchar *record;
- uchar *disk_buff, *strpos, *null_flags, *null_pos;
- ulong pos, record_offset;
+ const uchar *forminfo, *extra2;
+ const uchar *frm_image_end = frm_image + frm_length;
+ uchar *record, *null_flags, *null_pos;
+ const uchar *disk_buff, *strpos;
+ ulong pos, record_offset;
ulong rec_buff_length;
handler *handler_file= 0;
KEY *keyinfo;
KEY_PART_INFO *key_part= NULL;
- SQL_CRYPT *crypted=0;
Field **field_ptr, *reg_field;
const char **interval_array;
enum legacy_db_type legacy_db_type;
@@ -942,80 +926,160 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
bool null_bits_are_used;
uint vcol_screen_length, UNINIT_VAR(options_len);
char *vcol_screen_pos;
- uchar *UNINIT_VAR(options);
- uchar *extra_segment_buff= 0;
+ const uchar *options= 0;
KEY first_keyinfo;
uint len;
uint ext_key_parts= 0;
+ plugin_ref se_plugin= 0;
keyinfo= &first_keyinfo;
share->ext_key_parts= 0;
- DBUG_ENTER("open_binary_frm");
+ MEM_ROOT **root_ptr, *old_root;
+ DBUG_ENTER("TABLE_SHARE::init_from_binary_frm_image");
+
+ root_ptr= my_pthread_getspecific_ptr(MEM_ROOT**, THR_MALLOC);
+ old_root= *root_ptr;
+ *root_ptr= &share->mem_root;
- new_field_pack_flag= head[27];
- new_frm_ver= (head[2] - FRM_VER);
+ if (write && write_frm_image(frm_image, frm_length))
+ goto err;
+
+ if (frm_length < FRM_HEADER_SIZE + FRM_FORMINFO_SIZE)
+ goto err;
+
+ new_field_pack_flag= frm_image[27];
+ new_frm_ver= (frm_image[2] - FRM_VER);
field_pack_length= new_frm_ver < 2 ? 11 : 17;
- disk_buff= 0;
- error= 3;
- /* Position of the form in the form file. */
- if (!(pos= get_form_pos(file, head)))
- goto err; /* purecov: inspected */
+ /* Length of the MariaDB extra2 segment in the form file. */
+ len = uint2korr(frm_image+4);
+ extra2= frm_image + 64;
- mysql_file_seek(file,pos,MY_SEEK_SET,MYF(0));
- if (mysql_file_read(file, forminfo,288,MYF(MY_NABP)))
+ if (*extra2 != '/') // old frm had '/' there
+ {
+ const uchar *e2end= extra2 + len;
+ while (extra2 + 3 < e2end)
+ {
+ uchar type= *extra2++;
+ size_t length= *extra2++;
+ if (!length)
+ {
+ if (extra2 + 2 >= e2end)
+ goto err;
+ length= uint2korr(extra2);
+ extra2+= 2;
+ if (length < 256)
+ goto err;
+ }
+ if (extra2 + length > e2end)
+ goto err;
+ switch (type) {
+ case EXTRA2_TABLEDEF_VERSION:
+ if (tabledef_version.str) // see init_from_sql_statement_string()
+ {
+ if (length != tabledef_version.length ||
+ memcmp(extra2, tabledef_version.str, length))
+ goto err;
+ }
+ else
+ {
+ tabledef_version.length= length;
+ tabledef_version.str= (uchar*)memdup_root(&mem_root, extra2, length);
+ if (!tabledef_version.str)
+ goto err;
+ }
+ break;
+ case EXTRA2_ENGINE_TABLEOPTS:
+ if (options)
+ goto err;
+ /* remember but delay parsing until we have read fields and keys */
+ options= extra2;
+ options_len= length;
+ break;
+ case EXTRA2_DEFAULT_PART_ENGINE:
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+ {
+ LEX_STRING name= { (char*)extra2, length };
+ share->default_part_plugin= ha_resolve_by_name(NULL, &name);
+ if (!share->default_part_plugin)
+ goto err;
+ }
+#endif
+ break;
+ default:
+ /* abort frm parsing if it's an unknown but important extra2 value */
+ if (type >= EXTRA2_ENGINE_IMPORTANT)
+ goto err;
+ }
+ extra2+= length;
+ }
+ if (extra2 != e2end)
+ goto err;
+ }
+
+ if (frm_length < FRM_HEADER_SIZE + len ||
+ !(pos= uint4korr(frm_image + FRM_HEADER_SIZE + len)))
+ goto err;
+
+ forminfo= frm_image + pos;
+ if (forminfo + FRM_FORMINFO_SIZE >= frm_image_end)
goto err;
- share->frm_version= head[2];
+
+ share->frm_version= frm_image[2];
/*
Check if .frm file created by MySQL 5.0. In this case we want to
display CHAR fields as CHAR and not as VARCHAR.
We do it this way as we want to keep the old frm version to enable
MySQL 4.1 to read these files.
*/
- if (share->frm_version == FRM_VER_TRUE_VARCHAR -1 && head[33] == 5)
+ if (share->frm_version == FRM_VER_TRUE_VARCHAR -1 && frm_image[33] == 5)
share->frm_version= FRM_VER_TRUE_VARCHAR;
#ifdef WITH_PARTITION_STORAGE_ENGINE
- /*
- Yuck! Double-bad. Doesn't work with dynamic engine codes.
- And doesn't lock the plugin. Fixed in 10.0.4
- */
- compile_time_assert(MYSQL_VERSION_ID < 100000);
- if (*(head+61) &&
- !(share->default_part_db_type=
- ha_checktype(thd, (enum legacy_db_type) (uint) *(head+61), 1, 0)))
- goto err;
- DBUG_PRINT("info", ("default_part_db_type = %u", head[61]));
+ if (frm_image[61] && !share->default_part_plugin)
+ {
+ enum legacy_db_type db_type= (enum legacy_db_type) (uint) frm_image[61];
+ share->default_part_plugin=
+ ha_lock_engine(NULL, ha_checktype(thd, db_type, 1, 0));
+ if (!share->default_part_plugin)
+ goto err;
+ }
#endif
- legacy_db_type= (enum legacy_db_type) (uint) *(head+3);
- DBUG_ASSERT(share->db_plugin == NULL);
+ legacy_db_type= (enum legacy_db_type) (uint) frm_image[3];
/*
if the storage engine is dynamic, no point in resolving it by its
dynamically allocated legacy_db_type. We will resolve it later by name.
*/
if (legacy_db_type > DB_TYPE_UNKNOWN &&
legacy_db_type < DB_TYPE_FIRST_DYNAMIC)
- share->db_plugin= ha_lock_engine(NULL,
- ha_checktype(thd, legacy_db_type, 0, 0));
- share->db_create_options= db_create_options= uint2korr(head+30);
+ se_plugin= ha_lock_engine(NULL, ha_checktype(thd, legacy_db_type, 0, 0));
+ share->db_create_options= db_create_options= uint2korr(frm_image+30);
share->db_options_in_use= share->db_create_options;
- share->mysql_version= uint4korr(head+51);
+ share->mysql_version= uint4korr(frm_image+51);
share->null_field_first= 0;
- if (!head[32]) // New frm file in 3.23
+ if (!frm_image[32]) // New frm file in 3.23
{
- share->avg_row_length= uint4korr(head+34);
+ uint cs_org= (((uint) frm_image[41]) << 8) + (uint) frm_image[38];
+ uint cs_new= upgrade_collation(share->mysql_version, cs_org);
+ if (cs_org != cs_new)
+ share->incompatible_version|= HA_CREATE_USED_CHARSET;
+
+ share->avg_row_length= uint4korr(frm_image+34);
share->transactional= (ha_choice)
- enum_value_with_check(thd, share, "transactional", (head[39] & 3), HA_CHOICE_MAX);
+ enum_value_with_check(thd, share, "transactional", frm_image[39] & 3, HA_CHOICE_MAX);
share->page_checksum= (ha_choice)
- enum_value_with_check(thd, share, "page_checksum", (head[39] >> 2) & 3, HA_CHOICE_MAX);
- share->row_type= (row_type)
- enum_value_with_check(thd, share, "row_format", head[40], ROW_TYPE_MAX);
- share->table_charset= get_charset((((uint) head[41]) << 8) +
- (uint) head[38],MYF(0));
+ enum_value_with_check(thd, share, "page_checksum", (frm_image[39] >> 2) & 3, HA_CHOICE_MAX);
+ share->row_type= (enum row_type)
+ enum_value_with_check(thd, share, "row_format", frm_image[40], ROW_TYPE_MAX);
+
+ if (cs_new && !(share->table_charset= get_charset(cs_new, MYF(MY_WME))))
+ goto err;
share->null_field_first= 1;
+ share->stats_sample_pages= uint2korr(frm_image+42);
+ share->stats_auto_recalc= (enum_stats_auto_recalc)(frm_image[44]);
}
if (!share->table_charset)
{
- /* unknown charset in head[38] or pre-3.23 frm */
+ /* unknown charset in frm_image[38] or pre-3.23 frm */
if (use_mb(default_charset_info))
{
/* Warn that we may be changing the size of character columns */
@@ -1026,18 +1090,17 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
}
share->table_charset= default_charset_info;
}
+
share->db_record_offset= 1;
- if (db_create_options & HA_OPTION_LONG_BLOB_PTR)
- share->blob_ptr_size= portable_sizeof_char_ptr;
- error=4;
- share->max_rows= uint4korr(head+18);
- share->min_rows= uint4korr(head+22);
+ share->max_rows= uint4korr(frm_image+18);
+ share->min_rows= uint4korr(frm_image+22);
/* Read keyinformation */
- key_info_length= (uint) uint2korr(head+28);
- mysql_file_seek(file, (ulong) uint2korr(head+6), MY_SEEK_SET, MYF(0));
- if (read_string(file,(uchar**) &disk_buff,key_info_length))
- goto err; /* purecov: inspected */
+ disk_buff= frm_image + uint2korr(frm_image+6);
+
+ if (disk_buff + 6 >= frm_image_end)
+ goto err;
+
if (disk_buff[0] & 0x80)
{
keys= (disk_buff[1] << 7) | (disk_buff[0] & 0x7f);
@@ -1054,36 +1117,29 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
len= (uint) uint2korr(disk_buff+4);
- share->reclength = uint2korr((head+16));
+ share->reclength = uint2korr(frm_image+16);
share->stored_rec_length= share->reclength;
- if (*(head+26) == 1)
+ if (frm_image[26] == 1)
share->system= 1; /* one-record-database */
-#ifdef HAVE_CRYPTED_FRM
- else if (*(head+26) == 2)
- {
- crypted= get_crypt_for_frm();
- share->crypted= 1;
- }
-#endif
- record_offset= (ulong) (uint2korr(head+6)+
- ((uint2korr(head+14) == 0xffff ?
- uint4korr(head+47) : uint2korr(head+14))));
+ record_offset= (ulong) (uint2korr(frm_image+6)+
+ ((uint2korr(frm_image+14) == 0xffff ?
+ uint4korr(frm_image+47) : uint2korr(frm_image+14))));
+
+ if (record_offset + share->reclength >= frm_length)
+ goto err;
- if ((n_length= uint4korr(head+55)))
+ if ((n_length= uint4korr(frm_image+55)))
{
/* Read extra data segment */
- uchar *next_chunk, *buff_end;
+ const uchar *next_chunk, *buff_end;
DBUG_PRINT("info", ("extra segment size is %u bytes", n_length));
- if (!(extra_segment_buff= (uchar*) my_malloc(n_length + 1, MYF(MY_WME))))
- goto err;
- next_chunk= extra_segment_buff;
- if (mysql_file_pread(file, extra_segment_buff,
- n_length, record_offset + share->reclength,
- MYF(MY_NABP)))
- {
+ next_chunk= frm_image + record_offset + share->reclength;
+ buff_end= next_chunk + n_length;
+
+ if (buff_end >= frm_image_end)
goto err;
- }
+
share->connect_string.length= uint2korr(next_chunk);
if (!(share->connect_string.str= strmake_root(&share->mem_root,
(char*) next_chunk + 2,
@@ -1093,7 +1149,6 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
goto err;
}
next_chunk+= share->connect_string.length + 2;
- buff_end= extra_segment_buff + n_length;
if (next_chunk + 2 < buff_end)
{
uint str_db_type_length= uint2korr(next_chunk);
@@ -1102,12 +1157,9 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
name.length= str_db_type_length;
plugin_ref tmp_plugin= ha_resolve_by_name(thd, &name);
- if (tmp_plugin != NULL && !plugin_equals(tmp_plugin, share->db_plugin))
+ if (tmp_plugin != NULL && !plugin_equals(tmp_plugin, se_plugin))
{
- if (legacy_db_type > DB_TYPE_UNKNOWN &&
- legacy_db_type < DB_TYPE_FIRST_DYNAMIC &&
- legacy_db_type != ha_legacy_type(
- plugin_data(tmp_plugin, handlerton *)))
+ if (se_plugin)
{
/* bad file, legacy_db_type did not match the name */
sql_print_warning("%s.frm is inconsistent: engine typecode %d, engine name %s (%d)",
@@ -1117,14 +1169,11 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
}
/*
tmp_plugin is locked with a local lock.
- we unlock the old value of share->db_plugin before
+ we unlock the old value of se_plugin before
replacing it with a globally locked version of tmp_plugin
*/
- plugin_unlock(NULL, share->db_plugin);
- share->db_plugin= my_plugin_lock(NULL, tmp_plugin);
- DBUG_PRINT("info", ("setting dbtype to '%.*s' (%d)",
- str_db_type_length, next_chunk + 2,
- ha_legacy_type(share->db_type())));
+ plugin_unlock(NULL, se_plugin);
+ se_plugin= plugin_lock(NULL, tmp_plugin);
}
#ifdef WITH_PARTITION_STORAGE_ENGINE
else if (str_db_type_length == 9 &&
@@ -1133,28 +1182,23 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
/*
Use partition handler
tmp_plugin is locked with a local lock.
- we unlock the old value of share->db_plugin before
+ we unlock the old value of se_plugin before
replacing it with a globally locked version of tmp_plugin
*/
/* Check if the partitioning engine is ready */
if (!plugin_is_ready(&name, MYSQL_STORAGE_ENGINE_PLUGIN))
{
- error= 8;
my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0),
"--skip-partition");
goto err;
}
- 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())));
+ plugin_unlock(NULL, se_plugin);
+ se_plugin= ha_lock_engine(NULL, partition_hton);
}
#endif
else if (!tmp_plugin)
{
/* purecov: begin inspected */
- error= 8;
name.str[name.length]=0;
my_error(ER_UNKNOWN_STORAGE_ENGINE, MYF(0), name.str);
goto err;
@@ -1163,10 +1207,10 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
next_chunk+= str_db_type_length + 2;
}
- share->set_use_ext_keys_flag(share->db_type()->flags & HTON_EXTENDED_KEYS);
+ share->set_use_ext_keys_flag(plugin_hton(se_plugin)->flags & HTON_SUPPORTS_EXTENDED_KEYS);
- if (create_key_infos(disk_buff + 6, keys, keyinfo, new_frm_ver,
- ext_key_parts,
+ if (create_key_infos(disk_buff + 6, frm_image_end, keys, keyinfo,
+ new_frm_ver, ext_key_parts,
share, len, &first_keyinfo, keynames))
goto err;
@@ -1246,12 +1290,10 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
DBUG_ASSERT(next_chunk <= buff_end);
- if (share->db_create_options & HA_OPTION_TEXT_CREATE_OPTIONS)
+ if (share->db_create_options & HA_OPTION_TEXT_CREATE_OPTIONS_legacy)
{
- /*
- store options position, but skip till the time we will
- know number of fields
- */
+ if (options)
+ goto err;
options_len= uint4korr(next_chunk);
options= next_chunk + 4;
next_chunk+= options_len + 4;
@@ -1260,35 +1302,27 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
}
else
{
- if (create_key_infos(disk_buff + 6, keys, keyinfo, new_frm_ver,
- ext_key_parts,
+ if (create_key_infos(disk_buff + 6, frm_image_end, keys, keyinfo,
+ new_frm_ver, ext_key_parts,
share, len, &first_keyinfo, keynames))
goto err;
}
- share->key_block_size= uint2korr(head+62);
+ share->key_block_size= uint2korr(frm_image+62);
- error=4;
- extra_rec_buf_length= uint2korr(head+59);
+ if (share->db_plugin && !plugin_equals(share->db_plugin, se_plugin))
+ goto err; // wrong engine (someone changed the frm under our feet?)
+
+ extra_rec_buf_length= uint2korr(frm_image+59);
rec_buff_length= ALIGN_SIZE(share->reclength + 1 + extra_rec_buf_length);
share->rec_buff_length= rec_buff_length;
if (!(record= (uchar *) alloc_root(&share->mem_root,
rec_buff_length)))
goto err; /* purecov: inspected */
share->default_values= record;
- if (mysql_file_pread(file, record, (size_t) share->reclength,
- record_offset, MYF(MY_NABP)))
- goto err; /* purecov: inspected */
+ memcpy(record, frm_image + record_offset, share->reclength);
- mysql_file_seek(file, pos+288, MY_SEEK_SET, MYF(0));
-#ifdef HAVE_CRYPTED_FRM
- if (crypted)
- {
- crypted->decode((char*) forminfo+256,288-256);
- if (sint2korr(forminfo+284) != 0) // Should be 0
- goto err; // Wrong password
- }
-#endif
+ disk_buff= frm_image + pos + FRM_FORMINFO_SIZE;
share->fields= uint2korr(forminfo+258);
pos= uint2korr(forminfo+260); /* Length of all screens */
@@ -1300,6 +1334,7 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
com_length= uint2korr(forminfo+284);
vcol_screen_length= uint2korr(forminfo+286);
share->vfields= 0;
+ share->default_fields= 0;
share->stored_fields= share->fields;
if (forminfo[46] != (uchar)255)
{
@@ -1325,16 +1360,6 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
read_length=(uint) (share->fields * field_pack_length +
pos+ (uint) (n_length+int_length+com_length+
vcol_screen_length));
- if (read_string(file,(uchar**) &disk_buff,read_length))
- goto err; /* purecov: inspected */
-#ifdef HAVE_CRYPTED_FRM
- if (crypted)
- {
- crypted->decode((char*) disk_buff,read_length);
- delete crypted;
- crypted=0;
- }
-#endif
strpos= disk_buff+pos;
share->intervals= (TYPELIB*) (field_ptr+share->fields+1);
@@ -1382,14 +1407,17 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
/* Allocate handler */
if (!(handler_file= get_new_handler(share, thd->mem_root,
- share->db_type())))
+ plugin_hton(se_plugin))))
+ goto err;
+
+ if (handler_file->set_ha_share_ref(&share->ha_share))
goto err;
record= share->default_values-1; /* Fieldstart = 1 */
null_bits_are_used= share->null_fields != 0;
if (share->null_field_first)
{
- null_flags= null_pos= (uchar*) record+1;
+ null_flags= null_pos= record+1;
null_bit_pos= (db_create_options & HA_OPTION_PACK_RECORD) ? 0 : 1;
/*
null_bytes below is only correct under the condition that
@@ -1402,8 +1430,7 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
else
{
share->null_bytes= (share->null_fields+7)/8;
- null_flags= null_pos= (uchar*) (record + 1 +share->reclength -
- share->null_bytes);
+ null_flags= null_pos= record + 1 + share->reclength - share->null_bytes;
null_bit_pos= 0;
}
#endif
@@ -1445,19 +1472,29 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
geom_type= (Field::geometry_type) strpos[14];
charset= &my_charset_bin;
#else
- error= 4; // unsupported field type
goto err;
#endif
}
else
{
- uint csid= strpos[14] + (((uint) strpos[11]) << 8);
- if (!csid)
+ uint cs_org= strpos[14] + (((uint) strpos[11]) << 8);
+ uint cs_new= upgrade_collation(share->mysql_version, cs_org);
+ if (cs_org != cs_new)
+ share->incompatible_version|= HA_CREATE_USED_CHARSET;
+ if (!cs_new)
charset= &my_charset_bin;
- else if (!(charset= get_charset(csid, MYF(0))))
+ else if (!(charset= get_charset(cs_new, MYF(0))))
{
- error= 5; // Unknown or unavailable charset
- errarg= (int) csid;
+ const char *csname= get_charset_name((uint) cs_new);
+ char tmp[10];
+ if (!csname || csname[0] =='?')
+ {
+ my_snprintf(tmp, sizeof(tmp), "#%d", cs_new);
+ csname= tmp;
+ }
+ my_printf_error(ER_UNKNOWN_COLLATION,
+ "Unknown collation '%s' in table '%-.64s' definition",
+ MYF(0), csname, share->table_name.str);
goto err;
}
}
@@ -1501,10 +1538,8 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
if (opt_interval_id)
interval_nr= (uint)vcol_screen_pos[3];
else if ((uint)vcol_screen_pos[0] != 1)
- {
- error= 4;
goto err;
- }
+
fld_stored_in_db= (bool) (uint) vcol_screen_pos[2];
vcol_expr_length= vcol_info_length -
(uint)(FRM_VCOL_HEADER_SIZE(opt_interval_id));
@@ -1578,7 +1613,7 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
"Please do \"ALTER TABLE '%s' FORCE\" to fix it!",
share->fieldnames.type_names[i], share->table_name.str,
share->table_name.str);
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_CRASHED_ON_USAGE,
"Found incompatible DECIMAL field '%s' in %s; "
"Please do \"ALTER TABLE '%s' FORCE\" to fix it!",
@@ -1603,10 +1638,8 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
(TYPELIB*) 0),
share->fieldnames.type_names[i]);
if (!reg_field) // Not supported field type
- {
- error= 4;
- goto err; /* purecov: inspected */
- }
+ goto err;
+
reg_field->field_index= i;
reg_field->comment=comment;
@@ -1631,29 +1664,18 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
if (reg_field->unireg_check == Field::NEXT_NUMBER)
share->found_next_number_field= field_ptr;
- if (share->timestamp_field == reg_field)
- share->timestamp_field_offset= i;
- if (use_hash)
- {
- if (my_hash_insert(&share->name_hash,
- (uchar*) field_ptr))
- {
- /*
- Set return code 8 here to indicate that an error has
- occurred but that the error message already has been
- sent (OOM).
- */
- error= 8;
- goto err;
- }
- }
+ if (use_hash && my_hash_insert(&share->name_hash, (uchar*) field_ptr))
+ goto err;
if (!reg_field->stored_in_db)
{
share->stored_fields--;
if (share->stored_rec_length>=recpos)
share->stored_rec_length= recpos-1;
}
+ if (reg_field->has_insert_default_function() ||
+ reg_field->has_update_default_function())
+ ++share->default_fields;
}
*field_ptr=0; // End marker
/* Sanity checks: */
@@ -1664,10 +1686,44 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
if (key_parts)
{
uint add_first_key_parts= 0;
- uint primary_key=(uint) (find_type(primary_key_name, &share->keynames,
- FIND_TYPE_NO_PREFIX) - 1);
longlong ha_option= handler_file->ha_table_flags();
keyinfo= share->key_info;
+ uint primary_key= my_strcasecmp(system_charset_info, share->keynames.type_names[0],
+ primary_key_name) ? MAX_KEY : 0;
+
+ if (primary_key >= MAX_KEY && keyinfo->flags & HA_NOSAME)
+ {
+ /*
+ If the UNIQUE key doesn't have NULL columns and is not a part key
+ declare this as a primary key.
+ */
+ primary_key= 0;
+ key_part= keyinfo->key_part;
+ for (i=0 ; i < keyinfo->user_defined_key_parts ;i++)
+ {
+ DBUG_ASSERT(key_part[i].fieldnr > 0);
+ // Table field corresponding to the i'th key part.
+ Field *table_field= share->field[key_part[i].fieldnr - 1];
+
+ /*
+ If the key column is of NOT NULL BLOB type, then it
+ will definitly have key prefix. And if key part prefix size
+ is equal to the BLOB column max size, then we can promote
+ it to primary key.
+ */
+ if (!table_field->real_maybe_null() &&
+ table_field->type() == MYSQL_TYPE_BLOB &&
+ table_field->field_length == key_part[i].length)
+ continue;
+
+ if (table_field->real_maybe_null() ||
+ table_field->key_length() != key_part[i].length)
+ {
+ primary_key= MAX_KEY; // Can't be used
+ break;
+ }
+ }
+ }
if (share->use_ext_keys)
{
@@ -1678,12 +1734,12 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
}
else
{
- add_first_key_parts= first_keyinfo.key_parts;
+ add_first_key_parts= first_keyinfo.user_defined_key_parts;
/*
Do not add components of the primary key starting from
the major component defined over the beginning of a field.
*/
- for (i= 0; i < first_keyinfo.key_parts; i++)
+ for (i= 0; i < first_keyinfo.user_defined_key_parts; i++)
{
uint fieldnr= keyinfo[0].key_part[i].fieldnr;
if (share->field[fieldnr-1]->key_length() !=
@@ -1722,7 +1778,7 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
Do not extend the key that contains a component
defined over the beginning of a field.
*/
- for (i= 0; i < keyinfo->key_parts; i++)
+ for (i= 0; i < keyinfo->user_defined_key_parts; i++)
{
uint fieldnr= keyinfo->key_part[i].fieldnr;
if (share->field[fieldnr-1]->key_length() !=
@@ -1733,11 +1789,11 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
}
}
- if (add_first_key_parts < keyinfo->ext_key_parts-keyinfo->key_parts)
+ if (add_first_key_parts < keyinfo->ext_key_parts-keyinfo->user_defined_key_parts)
{
share->ext_key_parts-= keyinfo->ext_key_parts;
key_part_map ext_key_part_map= keyinfo->ext_key_part_map;
- keyinfo->ext_key_parts= keyinfo->key_parts;
+ keyinfo->ext_key_parts= keyinfo->user_defined_key_parts;
keyinfo->ext_key_flags= keyinfo->flags;
keyinfo->ext_key_part_map= 0;
for (i= 0; i < add_first_key_parts; i++)
@@ -1762,43 +1818,9 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
if (share->key_info[key].flags & HA_FULLTEXT)
share->key_info[key].algorithm= HA_KEY_ALG_FULLTEXT;
- if (primary_key >= MAX_KEY && (keyinfo->flags & HA_NOSAME))
- {
- /*
- If the UNIQUE key doesn't have NULL columns and is not a part key
- declare this as a primary key.
- */
- primary_key=key;
- key_part= keyinfo->key_part;
- for (i=0 ; i < keyinfo->key_parts ;i++)
- {
- DBUG_ASSERT(key_part[i].fieldnr > 0);
- // Table field corresponding to the i'th key part.
- Field *table_field= share->field[key_part[i].fieldnr - 1];
-
- /*
- If the key column is of NOT NULL BLOB type, then it
- will definitly have key prefix. And if key part prefix size
- is equal to the BLOB column max size, then we can promote
- it to primary key.
- */
- if (!table_field->real_maybe_null() &&
- table_field->type() == MYSQL_TYPE_BLOB &&
- table_field->field_length == key_part[i].length)
- continue;
-
- if (table_field->real_maybe_null() ||
- table_field->key_length() != key_part[i].length)
- {
- primary_key= MAX_KEY; // Can't be used
- break;
- }
- }
- }
-
key_part= keyinfo->key_part;
uint key_parts= share->use_ext_keys ? keyinfo->ext_key_parts :
- keyinfo->key_parts;
+ keyinfo->user_defined_key_parts;
for (i=0; i < key_parts; key_part++, i++)
{
Field *field;
@@ -1808,10 +1830,8 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
(uint) key_part->offset,
(uint) key_part->length);
if (!key_part->fieldnr)
- {
- error= 4; // Wrong file
goto err;
- }
+
field= key_part->field= share->field[key_part->fieldnr-1];
key_part->type= field->key_type();
if (field->null_ptr)
@@ -1840,7 +1860,7 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
if (i == 0 && key != primary_key)
field->flags |= (((keyinfo->flags & HA_NOSAME) &&
- (keyinfo->key_parts == 1)) ?
+ (keyinfo->user_defined_key_parts == 1)) ?
UNIQUE_KEY_FLAG : MULTIPLE_KEY_FLAG);
if (i == 0)
field->key_start.set_bit(key);
@@ -1851,7 +1871,7 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
{
share->keys_for_keyread.set_bit(key);
field->part_of_key.set_bit(key);
- if (i < keyinfo->key_parts)
+ if (i < keyinfo->user_defined_key_parts)
field->part_of_key_not_clustered.set_bit(key);
}
if (handler_file->index_flags(key, i, 1) & HA_READ_ORDER)
@@ -1897,7 +1917,7 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
"Please do \"ALTER TABLE '%s' FORCE \" to fix it!",
share->table_name.str,
share->table_name.str);
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_CRASHED_ON_USAGE,
"Found wrong key definition in %s; "
"Please do \"ALTER TABLE '%s' FORCE\" to fix "
@@ -1925,7 +1945,7 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
keyinfo->usable_key_parts= usable_parts; // Filesort
set_if_bigger(share->max_key_length,keyinfo->key_length+
- keyinfo->key_parts);
+ keyinfo->user_defined_key_parts);
share->total_key_length+= keyinfo->key_length;
/*
MERGE tables do not have unique indexes. But every key could be
@@ -1943,7 +1963,7 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
If we are using an integer as the primary key then allow the user to
refer to it as '_rowid'
*/
- if (share->key_info[primary_key].key_parts == 1)
+ if (share->key_info[primary_key].user_defined_key_parts == 1)
{
Field *field= share->key_info[primary_key].key_part[0].field;
if (field && field->result_type() == INT_RESULT)
@@ -1959,8 +1979,6 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
}
else
share->primary_key= MAX_KEY;
- my_free(disk_buff);
- disk_buff=0;
if (new_field_pack_flag <= 1)
{
/* Old file format with default as not null */
@@ -1969,7 +1987,7 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
null_length, 255);
}
- if (share->db_create_options & HA_OPTION_TEXT_CREATE_OPTIONS)
+ if (options)
{
DBUG_ASSERT(options_len);
if (engine_table_options_frm_read(options, options_len, share))
@@ -1986,13 +2004,8 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
share->default_values, reg_field,
&share->next_number_key_offset,
&share->next_number_keypart)) < 0)
- {
- /* Wrong field definition */
- error= 4;
- goto err;
- }
- else
- reg_field->flags |= AUTO_INCREMENT_FLAG;
+ goto err; // Wrong field definition
+ reg_field->flags |= AUTO_INCREMENT_FLAG;
}
if (share->blob_fields)
@@ -2028,7 +2041,7 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
if (!(bitmaps= (my_bitmap_map*) alloc_root(&share->mem_root,
share->column_bitmap_size)))
goto err;
- bitmap_init(&share->all_set, bitmaps, share->fields, FALSE);
+ my_bitmap_init(&share->all_set, bitmaps, share->fields, FALSE);
bitmap_set_all(&share->all_set);
delete handler_file;
@@ -2036,34 +2049,187 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head,
if (use_hash)
(void) my_hash_check(&share->name_hash);
#endif
- my_free(extra_segment_buff);
- DBUG_RETURN (0);
+
+ share->db_plugin= se_plugin;
+ share->error= OPEN_FRM_OK;
+ thd->status_var.opened_shares++;
+ *root_ptr= old_root;
+ DBUG_RETURN(0);
err:
- share->error= error;
+ share->error= OPEN_FRM_CORRUPTED;
share->open_errno= my_errno;
- share->errarg= errarg;
- my_free(disk_buff);
- my_free(extra_segment_buff);
- delete crypted;
delete handler_file;
+ plugin_unlock(0, se_plugin);
my_hash_free(&share->name_hash);
- if (share->ha_data_destroy)
+
+ if (!thd->is_error())
+ open_table_error(share, OPEN_FRM_CORRUPTED, share->open_errno);
+
+ *root_ptr= old_root;
+ DBUG_RETURN(HA_ERR_NOT_A_TABLE);
+}
+
+
+static bool sql_unusable_for_discovery(THD *thd, handlerton *engine,
+ const char *sql)
+{
+ LEX *lex= thd->lex;
+ HA_CREATE_INFO *create_info= &lex->create_info;
+
+ // ... not CREATE TABLE
+ if (lex->sql_command != SQLCOM_CREATE_TABLE)
+ return 1;
+ // ... create like
+ if (create_info->options & HA_LEX_CREATE_TABLE_LIKE)
+ return 1;
+ // ... create select
+ if (lex->select_lex.item_list.elements)
+ return 1;
+ // ... temporary
+ if (create_info->tmp_table())
+ return 1;
+ // ... if exists
+ if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS)
+ return 1;
+
+ // XXX error out or rather ignore the following:
+ // ... partitioning
+ if (lex->part_info)
+ return 1;
+ // ... union
+ if (create_info->used_fields & HA_CREATE_USED_UNION)
+ return 1;
+ // ... index/data directory
+ if (create_info->data_file_name || create_info->index_file_name)
+ return 1;
+ // ... engine
+ if (create_info->db_type && create_info->db_type != engine)
+ return 1;
+
+ return 0;
+}
+
+int TABLE_SHARE::init_from_sql_statement_string(THD *thd, bool write,
+ const char *sql, size_t sql_length)
+{
+ ulonglong saved_mode= thd->variables.sql_mode;
+ CHARSET_INFO *old_cs= thd->variables.character_set_client;
+ Parser_state parser_state;
+ bool error;
+ char *sql_copy;
+ handler *file;
+ LEX *old_lex;
+ Query_arena *arena, backup;
+ LEX tmp_lex;
+ KEY *unused1;
+ uint unused2;
+ handlerton *hton= plugin_hton(db_plugin);
+ LEX_CUSTRING frm= {0,0};
+
+ DBUG_ENTER("TABLE_SHARE::init_from_sql_statement_string");
+
+ /*
+ Ouch. Parser may *change* the string it's working on.
+ Currently (2013-02-26) it is used to permanently disable
+ conditional comments.
+ Anyway, let's copy the caller's string...
+ */
+ if (!(sql_copy= thd->strmake(sql, sql_length)))
+ DBUG_RETURN(HA_ERR_OUT_OF_MEM);
+
+ if (parser_state.init(thd, sql_copy, sql_length))
+ DBUG_RETURN(HA_ERR_OUT_OF_MEM);
+
+ thd->variables.sql_mode= MODE_NO_ENGINE_SUBSTITUTION | MODE_NO_DIR_IN_CREATE;
+ thd->variables.character_set_client= system_charset_info;
+ tmp_disable_binlog(thd);
+ old_lex= thd->lex;
+ thd->lex= &tmp_lex;
+
+ arena= thd->stmt_arena;
+ if (arena->is_conventional())
+ arena= 0;
+ else
+ thd->set_n_backup_active_arena(arena, &backup);
+
+ lex_start(thd);
+
+ if ((error= parse_sql(thd, & parser_state, NULL) ||
+ sql_unusable_for_discovery(thd, hton, sql_copy)))
+ goto ret;
+
+ thd->lex->create_info.db_type= hton;
+
+ if (tabledef_version.str)
+ thd->lex->create_info.tabledef_version= tabledef_version;
+
+ promote_first_timestamp_column(&thd->lex->alter_info.create_list);
+ file= mysql_create_frm_image(thd, db.str, table_name.str,
+ &thd->lex->create_info, &thd->lex->alter_info,
+ C_ORDINARY_CREATE, &unused1, &unused2, &frm);
+ error|= file == 0;
+ delete file;
+
+ if (frm.str)
{
- share->ha_data_destroy(share->ha_data);
- share->ha_data_destroy= NULL;
+ option_list= 0; // cleanup existing options ...
+ option_struct= 0; // ... if it's an assisted discovery
+ error= init_from_binary_frm_image(thd, write, frm.str, frm.length);
}
-#ifdef WITH_PARTITION_STORAGE_ENGINE
- if (share->ha_part_data_destroy)
+
+ret:
+ my_free(const_cast<uchar*>(frm.str));
+ lex_end(thd->lex);
+ thd->lex= old_lex;
+ if (arena)
+ thd->restore_active_arena(arena, &backup);
+ reenable_binlog(thd);
+ thd->variables.sql_mode= saved_mode;
+ thd->variables.character_set_client= old_cs;
+ if (thd->is_error() || error)
{
- share->ha_part_data_destroy(share->ha_part_data);
- share->ha_data_destroy= NULL;
+ thd->clear_error();
+ my_error(ER_SQL_DISCOVER_ERROR, MYF(0),
+ plugin_name(db_plugin)->str, db.str, table_name.str,
+ sql_copy);
+ DBUG_RETURN(HA_ERR_GENERIC);
}
-#endif /* WITH_PARTITION_STORAGE_ENGINE */
+ DBUG_RETURN(0);
+}
+
+bool TABLE_SHARE::write_frm_image(const uchar *frm, size_t len)
+{
+ return writefrm(normalized_path.str, db.str, table_name.str, false, frm, len);
+}
+
+
+bool TABLE_SHARE::read_frm_image(const uchar **frm, size_t *len)
+{
+ if (IF_PARTITIONING(partition_info_str, 0)) // cannot discover a partition
+ {
+ DBUG_ASSERT(db_type()->discover_table == 0);
+ return 1;
+ }
+
+ if (frm_image)
+ {
+ *frm= frm_image->str;
+ *len= frm_image->length;
+ frm_image->str= 0; // pass the ownership to the caller
+ frm_image= 0;
+ return 0;
+ }
+ return readfrm(normalized_path.str, frm, len);
+}
+
+
+void TABLE_SHARE::free_frm_image(const uchar *frm)
+{
+ if (frm)
+ my_free(const_cast<uchar*>(frm));
+}
- open_table_error(share, error, share->open_errno, errarg);
- DBUG_RETURN(error);
-} /* open_binary_frm */
/*
@brief
@@ -2376,15 +2542,16 @@ end:
7 Table definition has changed in engine
*/
-int open_table_from_share(THD *thd, TABLE_SHARE *share, const char *alias,
- uint db_stat, uint prgflag, uint ha_open_flags,
- TABLE *outparam, bool is_create_table)
+enum open_frm_error open_table_from_share(THD *thd, TABLE_SHARE *share,
+ const char *alias, uint db_stat, uint prgflag,
+ uint ha_open_flags, TABLE *outparam,
+ bool is_create_table)
{
- int error;
+ enum open_frm_error error;
uint records, i, bitmap_size;
bool error_reported= FALSE;
uchar *record, *bitmaps;
- Field **field_ptr, **vfield_ptr;
+ Field **field_ptr, **UNINIT_VAR(vfield_ptr), **UNINIT_VAR(dfield_ptr);
uint8 save_context_analysis_only= thd->lex->context_analysis_only;
DBUG_ENTER("open_table_from_share");
DBUG_PRINT("enter",("name: '%s.%s' form: 0x%lx", share->db.str,
@@ -2392,14 +2559,21 @@ int open_table_from_share(THD *thd, TABLE_SHARE *share, const char *alias,
thd->lex->context_analysis_only&= ~CONTEXT_ANALYSIS_ONLY_VIEW; // not a view
- error= 1;
+ error= OPEN_FRM_ERROR_ALREADY_ISSUED; // for OOM errors below
bzero((char*) outparam, sizeof(*outparam));
outparam->in_use= thd;
outparam->s= share;
outparam->db_stat= db_stat;
outparam->write_row_record= NULL;
- init_sql_alloc(&outparam->mem_root, TABLE_ALLOC_BLOCK_SIZE, 0);
+ if (share->incompatible_version &&
+ !(ha_open_flags & (HA_OPEN_FOR_ALTER | HA_OPEN_FOR_REPAIR)))
+ {
+ /* one needs to run mysql_upgrade on the table */
+ error= OPEN_FRM_NEEDS_REBUILD;
+ goto err;
+ }
+ init_sql_alloc(&outparam->mem_root, TABLE_ALLOC_BLOCK_SIZE, 0, MYF(0));
if (outparam->alias.copy(alias, strlen(alias), table_alias_charset))
goto err;
@@ -2415,13 +2589,15 @@ int open_table_from_share(THD *thd, TABLE_SHARE *share, const char *alias,
if (!(outparam->file= get_new_handler(share, &outparam->mem_root,
share->db_type())))
goto err;
+
+ if (outparam->file->set_ha_share_ref(&share->ha_share))
+ goto err;
}
else
{
DBUG_ASSERT(!db_stat);
}
- error= 4;
outparam->reginfo.lock_type= TL_UNLOCK;
outparam->current_lock= F_UNLCK;
records=0;
@@ -2473,9 +2649,6 @@ int open_table_from_share(THD *thd, TABLE_SHARE *share, const char *alias,
if (share->found_next_number_field)
outparam->found_next_number_field=
outparam->field[(uint) (share->found_next_number_field - share->field)];
- if (share->timestamp_field)
- outparam->timestamp_field= (Field_timestamp*) outparam->field[share->timestamp_field_offset];
-
/* Fix key->name and key_part->field */
if (share->key_parts)
@@ -2503,7 +2676,7 @@ int open_table_from_share(THD *thd, TABLE_SHARE *share, const char *alias,
key_info->key_part= key_part;
key_part_end= key_part + (share->use_ext_keys ? key_info->ext_key_parts :
- key_info->key_parts) ;
+ key_info->user_defined_key_parts) ;
for ( ; key_part < key_part_end; key_part++)
{
Field *field= key_part->field= outparam->field[key_part->fieldnr - 1];
@@ -2521,16 +2694,14 @@ int open_table_from_share(THD *thd, TABLE_SHARE *share, const char *alias,
}
}
if (!share->use_ext_keys)
- key_part+= key_info->ext_key_parts - key_info->key_parts;
+ key_part+= key_info->ext_key_parts - key_info->user_defined_key_parts;
}
}
/*
- Process virtual columns, if any.
+ Process virtual and default columns, if any.
*/
- if (!share->vfields)
- outparam->vfield= NULL;
- else
+ if (share->vfields)
{
if (!(vfield_ptr = (Field **) alloc_root(&outparam->mem_root,
(uint) ((share->vfields+1)*
@@ -2538,10 +2709,24 @@ int open_table_from_share(THD *thd, TABLE_SHARE *share, const char *alias,
goto err;
outparam->vfield= vfield_ptr;
+ }
+
+ if (share->default_fields)
+ {
+ if (!(dfield_ptr = (Field **) alloc_root(&outparam->mem_root,
+ (uint) ((share->default_fields+1)*
+ sizeof(Field*)))))
+ goto err;
+ outparam->default_field= dfield_ptr;
+ }
+
+ if (share->vfields || share->default_fields)
+ {
+ /* Reuse the same loop both for virtual and default fields. */
for (field_ptr= outparam->field; *field_ptr; field_ptr++)
{
- if ((*field_ptr)->vcol_info)
+ if (share->vfields && (*field_ptr)->vcol_info)
{
if (unpack_vcol_info_from_frm(thd,
&outparam->mem_root,
@@ -2550,13 +2735,20 @@ int open_table_from_share(THD *thd, TABLE_SHARE *share, const char *alias,
&(*field_ptr)->vcol_info->expr_str,
&error_reported))
{
- error= 4; // in case no error is reported
+ error= OPEN_FRM_CORRUPTED;
goto err;
}
*(vfield_ptr++)= *field_ptr;
}
+ if (share->default_fields &&
+ ((*field_ptr)->has_insert_default_function() ||
+ (*field_ptr)->has_update_default_function()))
+ *(dfield_ptr++)= *field_ptr;
}
- *vfield_ptr= 0; // End marker
+ if (share->vfields)
+ *vfield_ptr= 0; // End marker
+ if (share->default_fields)
+ *dfield_ptr= 0; // End marker
}
#ifdef WITH_PARTITION_STORAGE_ENGINE
@@ -2585,7 +2777,7 @@ int open_table_from_share(THD *thd, TABLE_SHARE *share, const char *alias,
tmp= mysql_unpack_partition(thd, share->partition_info_str,
share->partition_info_str_len,
outparam, is_create_table,
- share->default_part_db_type,
+ plugin_hton(share->default_part_plugin),
&work_part_info_used);
if (tmp)
{
@@ -2595,8 +2787,9 @@ int open_table_from_share(THD *thd, TABLE_SHARE *share, const char *alias,
}
outparam->part_info->is_auto_partitioned= share->auto_partitioned;
DBUG_PRINT("info", ("autopartitioned: %u", share->auto_partitioned));
- /* we should perform the fix_partition_func in either local or
- caller's arena depending on work_part_info_used value
+ /*
+ We should perform the fix_partition_func in either local or
+ caller's arena depending on work_part_info_used value.
*/
if (!work_part_info_used)
tmp= fix_partition_func(thd, outparam, is_create_table);
@@ -2639,68 +2832,65 @@ partititon_err:
/* Allocate bitmaps */
bitmap_size= share->column_bitmap_size;
- if (!(bitmaps= (uchar*) alloc_root(&outparam->mem_root, bitmap_size*5)))
+ if (!(bitmaps= (uchar*) alloc_root(&outparam->mem_root, bitmap_size*6)))
goto err;
- bitmap_init(&outparam->def_read_set,
+ my_bitmap_init(&outparam->def_read_set,
(my_bitmap_map*) bitmaps, share->fields, FALSE);
- bitmap_init(&outparam->def_write_set,
+ my_bitmap_init(&outparam->def_write_set,
(my_bitmap_map*) (bitmaps+bitmap_size), share->fields, FALSE);
- bitmap_init(&outparam->def_vcol_set,
+ my_bitmap_init(&outparam->def_vcol_set,
(my_bitmap_map*) (bitmaps+bitmap_size*2), share->fields, FALSE);
- bitmap_init(&outparam->tmp_set,
+ my_bitmap_init(&outparam->tmp_set,
(my_bitmap_map*) (bitmaps+bitmap_size*3), share->fields, FALSE);
- bitmap_init(&outparam->eq_join_set,
+ my_bitmap_init(&outparam->eq_join_set,
(my_bitmap_map*) (bitmaps+bitmap_size*4), share->fields, FALSE);
+ my_bitmap_init(&outparam->cond_set,
+ (my_bitmap_map*) (bitmaps+bitmap_size*5), share->fields, FALSE);
outparam->default_column_bitmaps();
+ outparam->cond_selectivity= 1.0;
+
/* The table struct is now initialized; Open the table */
- error= 2;
if (db_stat)
{
- int ha_err;
- if ((ha_err= (outparam->file->
- ha_open(outparam, share->normalized_path.str,
- (db_stat & HA_READ_ONLY ? O_RDONLY : O_RDWR),
- (db_stat & HA_OPEN_TEMPORARY ? HA_OPEN_TMP_TABLE :
- ((db_stat & HA_WAIT_IF_LOCKED) ||
- (specialflag & SPECIAL_WAIT_IF_LOCKED)) ?
- HA_OPEN_WAIT_IF_LOCKED :
- (db_stat & (HA_ABORT_IF_LOCKED | HA_GET_INFO)) ?
- HA_OPEN_ABORT_IF_LOCKED :
- HA_OPEN_IGNORE_IF_LOCKED) | ha_open_flags))))
+ if (db_stat & HA_OPEN_TEMPORARY)
+ ha_open_flags|= HA_OPEN_TMP_TABLE;
+ else if ((db_stat & HA_WAIT_IF_LOCKED) ||
+ (specialflag & SPECIAL_WAIT_IF_LOCKED))
+ ha_open_flags|= HA_OPEN_WAIT_IF_LOCKED;
+ else if (db_stat & (HA_ABORT_IF_LOCKED | HA_GET_INFO))
+ ha_open_flags|= HA_OPEN_ABORT_IF_LOCKED;
+ else
+ ha_open_flags|= HA_OPEN_IGNORE_IF_LOCKED;
+
+ int ha_err= outparam->file->ha_open(outparam, share->normalized_path.str,
+ (db_stat & HA_READ_ONLY ? O_RDONLY : O_RDWR),
+ ha_open_flags);
+ if (ha_err)
{
+ share->open_errno= ha_err;
/* Set a flag if the table is crashed and it can be auto. repaired */
share->crashed= (outparam->file->auto_repair(ha_err) &&
!(ha_open_flags & HA_OPEN_FOR_REPAIR));
+ outparam->file->print_error(ha_err, MYF(0));
+ error_reported= TRUE;
- switch (ha_err)
- {
- case HA_ERR_NO_SUCH_TABLE:
- /*
- The table did not exists in storage engine, use same error message
- as if the .frm file didn't exist
- */
- error= 1;
- my_errno= ENOENT;
- break;
- case EMFILE:
- /*
- Too many files opened, use same error message as if the .frm
- file can't open
- */
- DBUG_PRINT("error", ("open file: %s failed, too many files opened (errno: %d)",
- share->normalized_path.str, ha_err));
- error= 1;
- my_errno= EMFILE;
- break;
- default:
- outparam->file->print_error(ha_err, MYF(0));
- error_reported= TRUE;
- if (ha_err == HA_ERR_TABLE_DEF_CHANGED)
- error= 7;
- break;
- }
- goto err; /* purecov: inspected */
+ if (ha_err == HA_ERR_TABLE_DEF_CHANGED)
+ error= OPEN_FRM_DISCOVER;
+
+ /*
+ We're here, because .frm file was successfully opened.
+
+ But if the table doesn't exist in the engine and the engine
+ supports discovery, we force rediscover to discover
+ the fact that table doesn't in fact exist and remove
+ the stray .frm file.
+ */
+ if (share->db_type()->discover_table &&
+ (ha_err == ENOENT || ha_err == HA_ERR_NO_SUCH_TABLE))
+ error= OPEN_FRM_DISCOVER;
+
+ goto err;
}
}
@@ -2708,19 +2898,32 @@ partititon_err:
bzero((char*) bitmaps, bitmap_size*3);
#endif
- outparam->no_replicate= outparam->file &&
- test(outparam->file->ha_table_flags() &
- HA_HAS_OWN_BINLOGGING);
+ if (share->table_category == TABLE_CATEGORY_LOG)
+ {
+ outparam->no_replicate= TRUE;
+ }
+ else if (outparam->file)
+ {
+ handler::Table_flags flags= outparam->file->ha_table_flags();
+ outparam->no_replicate= ! MY_TEST(flags & (HA_BINLOG_STMT_CAPABLE
+ | HA_BINLOG_ROW_CAPABLE))
+ || MY_TEST(flags & HA_HAS_OWN_BINLOGGING);
+ }
+ else
+ {
+ outparam->no_replicate= FALSE;
+ }
+
/* Increment the opened_tables counter, only when open flags set. */
if (db_stat)
thd->status_var.opened_tables++;
thd->lex->context_analysis_only= save_context_analysis_only;
- DBUG_RETURN (0);
+ DBUG_RETURN (OPEN_FRM_OK);
err:
if (! error_reported)
- open_table_error(share, error, my_errno, 0);
+ open_table_error(share, error, my_errno);
delete outparam->file;
#ifdef WITH_PARTITION_STORAGE_ENGINE
if (outparam->part_info)
@@ -2772,6 +2975,7 @@ int closefrm(register TABLE *table, bool free_share)
#ifdef WITH_PARTITION_STORAGE_ENGINE
if (table->part_info)
{
+ /* Allocated through table->mem_root, freed below */
free_items(table->part_info->item_free_list);
table->part_info->item_free_list= 0;
table->part_info= 0;
@@ -2780,7 +2984,7 @@ int closefrm(register TABLE *table, bool free_share)
if (free_share)
{
if (table->s->tmp_table == NO_TMP_TABLE)
- release_table_share(table->s);
+ tdc_release_share(table->s);
else
free_table_share(table->s);
}
@@ -2831,158 +3035,18 @@ void free_field_buffers_larger_than(TABLE *table, uint32 size)
}
}
-/**
- Find where a form starts.
-
- @param head The start of the form file.
-
- @remark If formname is NULL then only formnames is read.
-
- @retval The form position.
-*/
-
-static ulong get_form_pos(File file, uchar *head)
-{
- uchar *pos, *buf;
- uint names, length;
- ulong ret_value=0;
- DBUG_ENTER("get_form_pos");
-
- names= uint2korr(head+8);
-
- if (!(names= uint2korr(head+8)))
- DBUG_RETURN(0);
-
- length= uint2korr(head+4);
-
- mysql_file_seek(file, 64L, MY_SEEK_SET, MYF(0));
-
- if (!(buf= (uchar*) my_malloc(length+names*4, MYF(MY_WME))))
- DBUG_RETURN(0);
-
- if (mysql_file_read(file, buf, length+names*4, MYF(MY_NABP)))
- {
- my_free(buf);
- DBUG_RETURN(0);
- }
-
- pos= buf+length;
- ret_value= uint4korr(pos);
-
- my_free(buf);
-
- DBUG_RETURN(ret_value);
-}
-
-
-/*
- Read string from a file with malloc
-
- NOTES:
- We add an \0 at end of the read string to make reading of C strings easier
-*/
-
-int read_string(File file, uchar**to, size_t length)
-{
- DBUG_ENTER("read_string");
-
- my_free(*to);
- if (!(*to= (uchar*) my_malloc(length+1,MYF(MY_WME))) ||
- mysql_file_read(file, *to, length, MYF(MY_NABP)))
- {
- my_free(*to); /* purecov: inspected */
- *to= 0; /* purecov: inspected */
- DBUG_RETURN(1); /* purecov: inspected */
- }
- *((char*) *to+length)= '\0';
- DBUG_RETURN (0);
-} /* read_string */
-
-
- /* Add a new form to a form file */
+/* error message when opening a form file */
-ulong make_new_entry(File file, uchar *fileinfo, TYPELIB *formnames,
- const char *newname)
+void open_table_error(TABLE_SHARE *share, enum open_frm_error error,
+ int db_errno)
{
- uint i,bufflength,maxlength,n_length,length,names;
- ulong endpos,newpos;
- uchar buff[IO_SIZE];
- uchar *pos;
- DBUG_ENTER("make_new_entry");
-
- length=(uint) strlen(newname)+1;
- n_length=uint2korr(fileinfo+4);
- maxlength=uint2korr(fileinfo+6);
- names=uint2korr(fileinfo+8);
- newpos=uint4korr(fileinfo+10);
-
- if (64+length+n_length+(names+1)*4 > maxlength)
- { /* Expand file */
- newpos+=IO_SIZE;
- int4store(fileinfo+10,newpos);
- /* Copy from file-end */
- endpos= (ulong) mysql_file_seek(file, 0L, MY_SEEK_END, MYF(0));
- bufflength= (uint) (endpos & (IO_SIZE-1)); /* IO_SIZE is a power of 2 */
-
- while (endpos > maxlength)
- {
- mysql_file_seek(file, (ulong) (endpos-bufflength), MY_SEEK_SET, MYF(0));
- if (mysql_file_read(file, buff, bufflength, MYF(MY_NABP+MY_WME)))
- DBUG_RETURN(0L);
- mysql_file_seek(file, (ulong) (endpos-bufflength+IO_SIZE), MY_SEEK_SET,
- MYF(0));
- if ((mysql_file_write(file, buff, bufflength, MYF(MY_NABP+MY_WME))))
- DBUG_RETURN(0);
- endpos-=bufflength; bufflength=IO_SIZE;
- }
- bzero(buff,IO_SIZE); /* Null new block */
- mysql_file_seek(file, (ulong) maxlength, MY_SEEK_SET, MYF(0));
- if (mysql_file_write(file, buff, bufflength, MYF(MY_NABP+MY_WME)))
- DBUG_RETURN(0L);
- maxlength+=IO_SIZE; /* Fix old ref */
- int2store(fileinfo+6,maxlength);
- for (i=names, pos= (uchar*) *formnames->type_names+n_length-1; i-- ;
- pos+=4)
- {
- endpos=uint4korr(pos)+IO_SIZE;
- int4store(pos,endpos);
- }
- }
-
- if (n_length == 1 )
- { /* First name */
- length++;
- (void) strxmov((char*) buff,"/",newname,"/",NullS);
- }
- else
- (void) strxmov((char*) buff,newname,"/",NullS); /* purecov: inspected */
- mysql_file_seek(file, 63L+(ulong) n_length, MY_SEEK_SET, MYF(0));
- if (mysql_file_write(file, buff, (size_t) length+1, MYF(MY_NABP+MY_WME)) ||
- (names && mysql_file_write(file,
- (uchar*) (*formnames->type_names+n_length-1),
- names*4, MYF(MY_NABP+MY_WME))) ||
- mysql_file_write(file, fileinfo+10, 4, MYF(MY_NABP+MY_WME)))
- DBUG_RETURN(0L); /* purecov: inspected */
-
- int2store(fileinfo+8,names+1);
- int2store(fileinfo+4,n_length+length);
- (void) mysql_file_chsize(file, newpos, 0, MYF(MY_WME));/* Append file with '\0' */
- DBUG_RETURN(newpos);
-} /* make_new_entry */
-
-
- /* error message when opening a form file */
-
-void open_table_error(TABLE_SHARE *share, int error, int db_errno, int errarg)
-{
- int err_no;
char buff[FN_REFLEN];
- myf errortype= ME_ERROR+ME_WAITTANG; // Write fatals error to log
+ const myf errortype= ME_ERROR+ME_WAITTANG; // Write fatals error to log
DBUG_ENTER("open_table_error");
+ DBUG_PRINT("info", ("error: %d db_errno: %d", error, db_errno));
switch (error) {
- case 7:
- case 1:
+ case OPEN_FRM_OPEN_ERROR:
/*
Test if file didn't exists. We have to also test for EINVAL as this
may happen on windows when opening a file with a not legal file name
@@ -2996,55 +3060,35 @@ void open_table_error(TABLE_SHARE *share, int error, int db_errno, int errarg)
errortype, buff, db_errno);
}
break;
- case 2:
- {
- handler *file= 0;
- const char *datext= "";
-
- if (share->db_type() != NULL)
- {
- if ((file= get_new_handler(share, current_thd->mem_root,
- share->db_type())))
- {
- if (!(datext= *file->bas_ext()))
- datext= "";
- }
- }
- err_no= (db_errno == ENOENT) ? ER_FILE_NOT_FOUND : (db_errno == EAGAIN) ?
- ER_FILE_USED : ER_CANT_OPEN_FILE;
- strxmov(buff, share->normalized_path.str, datext, NullS);
- my_error(err_no,errortype, buff, db_errno);
- delete file;
+ case OPEN_FRM_OK:
+ DBUG_ASSERT(0); // open_table_error() is never called for this one
break;
- }
- case 5:
- {
- const char *csname= get_charset_name((uint) errarg);
- char tmp[10];
- if (!csname || csname[0] =='?')
- {
- my_snprintf(tmp, sizeof(tmp), "#%d", errarg);
- csname= tmp;
- }
- my_printf_error(ER_UNKNOWN_COLLATION,
- "Unknown collation '%s' in table '%-.64s' definition",
- MYF(0), csname, share->table_name.str);
+ case OPEN_FRM_ERROR_ALREADY_ISSUED:
break;
- }
- case 6:
- strxmov(buff, share->normalized_path.str, reg_ext, NullS);
- my_printf_error(ER_NOT_FORM_FILE,
- "Table '%-.64s' was created with a different version "
- "of MySQL and cannot be read",
- MYF(0), buff);
+ case OPEN_FRM_NOT_A_VIEW:
+ my_error(ER_WRONG_OBJECT, MYF(0), share->db.str,
+ share->table_name.str, "VIEW");
+ break;
+ case OPEN_FRM_NOT_A_TABLE:
+ my_error(ER_WRONG_OBJECT, MYF(0), share->db.str,
+ share->table_name.str, "TABLE");
break;
- case 8:
+ case OPEN_FRM_DISCOVER:
+ DBUG_ASSERT(0); // open_table_error() is never called for this one
break;
- default: /* Better wrong error than none */
- case 4:
+ case OPEN_FRM_CORRUPTED:
strxmov(buff, share->normalized_path.str, reg_ext, NullS);
my_error(ER_NOT_FORM_FILE, errortype, buff);
break;
+ case OPEN_FRM_READ_ERROR:
+ strxmov(buff, share->normalized_path.str, reg_ext, NullS);
+ my_error(ER_ERROR_ON_READ, errortype, buff, db_errno);
+ break;
+ case OPEN_FRM_NEEDS_REBUILD:
+ strxnmov(buff, sizeof(buff)-1,
+ share->db.str, ".", share->table_name.str, NullS);
+ my_error(ER_TABLE_NEEDS_REBUILD, errortype, buff);
+ break;
}
DBUG_VOID_RETURN;
} /* open_table_error */
@@ -3148,28 +3192,6 @@ static uint find_field(Field **fields, uchar *record, uint start, uint length)
}
- /* Check that the integer is in the internal */
-
-int set_zone(register int nr, int min_zone, int max_zone)
-{
- if (nr<=min_zone)
- return (min_zone);
- if (nr>=max_zone)
- return (max_zone);
- return (nr);
-} /* set_zone */
-
- /* Adjust number to next larger disk buffer */
-
-ulong next_io_size(register ulong pos)
-{
- reg2 ulong offset;
- if ((offset= pos & (IO_SIZE-1)))
- return pos-offset+IO_SIZE;
- return pos;
-} /* next_io_size */
-
-
/*
Store an SQL quoted string.
@@ -3232,22 +3254,12 @@ void append_unescaped(String *res, const char *pos, uint length)
}
- /* Create a .frm file */
-
-File create_frm(THD *thd, const char *name, const char *db,
- const char *table, uint reclength, uchar *fileinfo,
- HA_CREATE_INFO *create_info, uint keys, KEY *key_info)
+void prepare_frm_header(THD *thd, uint reclength, uchar *fileinfo,
+ HA_CREATE_INFO *create_info, uint keys, KEY *key_info)
{
- register File file;
- ulong length;
- uchar fill[IO_SIZE];
- int create_flags= O_RDWR | O_TRUNC;
ulong key_comment_total_bytes= 0;
uint i;
- DBUG_ENTER("create_frm");
-
- if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
- create_flags|= O_EXCL | O_NOFOLLOW;
+ DBUG_ENTER("prepare_frm_header");
/* Fix this when we have new .frm files; Current limit is 4G rows (TODO) */
if (create_info->max_rows > UINT_MAX32)
@@ -3255,101 +3267,76 @@ File create_frm(THD *thd, const char *name, const char *db,
if (create_info->min_rows > UINT_MAX32)
create_info->min_rows= UINT_MAX32;
- if ((file= mysql_file_create(key_file_frm,
- name, CREATE_MODE, create_flags, MYF(0))) >= 0)
- {
- uint key_length, tmp_key_length, tmp, csid;
- bzero((char*) fileinfo,64);
- /* header */
- fileinfo[0]=(uchar) 254;
- fileinfo[1]= 1;
- fileinfo[2]= FRM_VER+3+ test(create_info->varchar);
+ uint key_length, tmp_key_length, tmp, csid;
+ bzero((char*) fileinfo, FRM_HEADER_SIZE);
+ /* header */
+ fileinfo[0]=(uchar) 254;
+ fileinfo[1]= 1;
+ fileinfo[2]= FRM_VER + 3 + MY_TEST(create_info->varchar);
- fileinfo[3]= (uchar) ha_legacy_type(
- ha_checktype(thd,ha_legacy_type(create_info->db_type),0,0));
- fileinfo[4]=1;
- int2store(fileinfo+6,IO_SIZE); /* Next block starts here */
- /*
- Keep in sync with pack_keys() in unireg.cc
- For each key:
- 8 bytes for the key header
- 9 bytes for each key-part (MAX_REF_PARTS)
- NAME_LEN bytes for the name
- 1 byte for the NAMES_SEP_CHAR (before the name)
- For all keys:
- 6 bytes for the header
- 1 byte for the NAMES_SEP_CHAR (after the last name)
- 9 extra bytes (padding for safety? alignment?)
- */
- for (i= 0; i < keys; i++)
- {
- DBUG_ASSERT(test(key_info[i].flags & HA_USES_COMMENT) ==
- (key_info[i].comment.length > 0));
- if (key_info[i].flags & HA_USES_COMMENT)
- key_comment_total_bytes += 2 + key_info[i].comment.length;
- }
+ fileinfo[3]= (uchar) ha_legacy_type(
+ ha_checktype(thd,ha_legacy_type(create_info->db_type),0,0));
- key_length= keys * (8 + MAX_REF_PARTS * 9 + NAME_LEN + 1) + 16
- + key_comment_total_bytes;
-
- length= next_io_size((ulong) (IO_SIZE+key_length+reclength+
- create_info->extra_size));
- int4store(fileinfo+10,length);
- tmp_key_length= (key_length < 0xffff) ? key_length : 0xffff;
- int2store(fileinfo+14,tmp_key_length);
- int2store(fileinfo+16,reclength);
- int4store(fileinfo+18,create_info->max_rows);
- int4store(fileinfo+22,create_info->min_rows);
- /* fileinfo[26] is set in mysql_create_frm() */
- fileinfo[27]=2; // Use long pack-fields
- /* fileinfo[28 & 29] is set to key_info_length in mysql_create_frm() */
- create_info->table_options|=HA_OPTION_LONG_BLOB_PTR; // Use portable blob pointers
- int2store(fileinfo+30,create_info->table_options);
- fileinfo[32]=0; // No filename anymore
- fileinfo[33]=5; // Mark for 5.0 frm file
- int4store(fileinfo+34,create_info->avg_row_length);
- csid= (create_info->default_table_charset ?
- create_info->default_table_charset->number : 0);
- fileinfo[38]= (uchar) csid;
- fileinfo[39]= (uchar) ((uint) create_info->transactional |
- ((uint) create_info->page_checksum << 2));
- fileinfo[40]= (uchar) create_info->row_type;
- /* Next few bytes where for RAID support */
- fileinfo[41]= (uchar) (csid >> 8);
- fileinfo[42]= 0;
- fileinfo[43]= 0;
- fileinfo[44]= 0;
- fileinfo[45]= 0;
- fileinfo[46]= 0;
- int4store(fileinfo+47, key_length);
- tmp= MYSQL_VERSION_ID; // Store to avoid warning from int4store
- int4store(fileinfo+51, tmp);
- int4store(fileinfo+55, create_info->extra_size);
- /*
- 59-60 is reserved for extra_rec_buf_length,
- 61 for default_part_db_type
- */
- int2store(fileinfo+62, create_info->key_block_size);
- bzero(fill,IO_SIZE);
- for (; length > IO_SIZE ; length-= IO_SIZE)
- {
- if (mysql_file_write(file, fill, IO_SIZE, MYF(MY_WME | MY_NABP)))
- {
- (void) mysql_file_close(file, MYF(0));
- (void) mysql_file_delete(key_file_frm, name, MYF(0));
- return(-1);
- }
- }
- }
- else
- {
- if (my_errno == ENOENT)
- my_error(ER_BAD_DB_ERROR,MYF(0),db);
- else
- my_error(ER_CANT_CREATE_TABLE,MYF(0),table,my_errno);
- }
- DBUG_RETURN(file);
-} /* create_frm */
+ /*
+ Keep in sync with pack_keys() in unireg.cc
+ For each key:
+ 8 bytes for the key header
+ 9 bytes for each key-part (MAX_REF_PARTS)
+ NAME_LEN bytes for the name
+ 1 byte for the NAMES_SEP_CHAR (before the name)
+ For all keys:
+ 6 bytes for the header
+ 1 byte for the NAMES_SEP_CHAR (after the last name)
+ 9 extra bytes (padding for safety? alignment?)
+ */
+ for (i= 0; i < keys; i++)
+ {
+ DBUG_ASSERT(MY_TEST(key_info[i].flags & HA_USES_COMMENT) ==
+ (key_info[i].comment.length > 0));
+ if (key_info[i].flags & HA_USES_COMMENT)
+ key_comment_total_bytes += 2 + key_info[i].comment.length;
+ }
+
+ key_length= keys * (8 + MAX_REF_PARTS * 9 + NAME_LEN + 1) + 16
+ + key_comment_total_bytes;
+
+ int2store(fileinfo+8,1);
+ tmp_key_length= (key_length < 0xffff) ? key_length : 0xffff;
+ int2store(fileinfo+14,tmp_key_length);
+ int2store(fileinfo+16,reclength);
+ int4store(fileinfo+18,create_info->max_rows);
+ int4store(fileinfo+22,create_info->min_rows);
+ /* fileinfo[26] is set in mysql_create_frm() */
+ fileinfo[27]=2; // Use long pack-fields
+ /* fileinfo[28 & 29] is set to key_info_length in mysql_create_frm() */
+ create_info->table_options|=HA_OPTION_LONG_BLOB_PTR; // Use portable blob pointers
+ int2store(fileinfo+30,create_info->table_options);
+ fileinfo[32]=0; // No filename anymore
+ fileinfo[33]=5; // Mark for 5.0 frm file
+ int4store(fileinfo+34,create_info->avg_row_length);
+ csid= (create_info->default_table_charset ?
+ create_info->default_table_charset->number : 0);
+ fileinfo[38]= (uchar) csid;
+ fileinfo[39]= (uchar) ((uint) create_info->transactional |
+ ((uint) create_info->page_checksum << 2));
+ fileinfo[40]= (uchar) create_info->row_type;
+ /* Bytes 41-46 were for RAID support; now reused for other purposes */
+ fileinfo[41]= (uchar) (csid >> 8);
+ int2store(fileinfo+42, create_info->stats_sample_pages & 0xffff);
+ fileinfo[44]= (uchar) create_info->stats_auto_recalc;
+ fileinfo[45]= 0;
+ fileinfo[46]= 0;
+ int4store(fileinfo+47, key_length);
+ tmp= MYSQL_VERSION_ID; // Store to avoid warning from int4store
+ int4store(fileinfo+51, tmp);
+ int4store(fileinfo+55, create_info->extra_size);
+ /*
+ 59-60 is reserved for extra_rec_buf_length,
+ 61 for default_part_db_type
+ */
+ int2store(fileinfo+62, create_info->key_block_size);
+ DBUG_VOID_RETURN;
+} /* prepare_fileinfo */
void update_create_info_from_table(HA_CREATE_INFO *create_info, TABLE *table)
@@ -3378,7 +3365,7 @@ rename_file_ext(const char * from,const char * to,const char * ext)
char from_b[FN_REFLEN],to_b[FN_REFLEN];
(void) strxmov(from_b,from,ext,NullS);
(void) strxmov(to_b,to,ext,NullS);
- return (mysql_file_rename(key_file_frm, from_b, to_b, MYF(MY_WME)));
+ return mysql_file_rename(key_file_frm, from_b, to_b, MYF(0));
}
@@ -3410,7 +3397,7 @@ bool get_field(MEM_ROOT *mem, Field *field, String *res)
}
if (!(to= strmake_root(mem, str.ptr(), length)))
length= 0; // Safety fix
- res->set(to, length, ((Field_str*)field)->charset());
+ res->set(to, length, field->charset());
return 0;
}
@@ -3468,15 +3455,34 @@ uint calculate_key_len(TABLE *table, uint key, const uchar *buf,
return length;
}
+#ifndef DBUG_OFF
+/**
+ Verifies that database/table name is in lowercase, when it should be
+
+ This is supposed to be used only inside DBUG_ASSERT()
+*/
+bool ok_for_lower_case_names(const char *name)
+{
+ if (!lower_case_table_names || !name)
+ return true;
+
+ char buf[SAFE_NAME_LEN];
+ strmake_buf(buf, name);
+ my_casedn_str(files_charset_info, buf);
+ return strcmp(name, buf) == 0;
+}
+#endif
+
/*
Check if database name is valid
SYNPOSIS
check_db_name()
- org_name Name of database and length
+ org_name Name of database
NOTES
- If lower_case_table_names is set then database is converted to lower case
+ If lower_case_table_names is set to 1 then database name is converted
+ to lower case
RETURN
0 ok
@@ -3498,9 +3504,12 @@ bool check_db_name(LEX_STRING *org_name)
if (!name_length || name_length > NAME_LEN)
return 1;
- if (lower_case_table_names && name != any_db)
- my_casedn_str(files_charset_info, name);
-
+ if (lower_case_table_names == 1 && name != any_db)
+ {
+ org_name->length= name_length= my_casedn_str(files_charset_info, name);
+ if (check_for_path_chars)
+ org_name->length+= MYSQL50_TABLE_NAME_PREFIX_LENGTH;
+ }
if (db_name_is_in_ignore_db_dirs_list(name))
return 1;
@@ -3647,9 +3656,9 @@ Table_check_intact::check(TABLE *table, const TABLE_FIELD_DEF *table_def)
}
else if (MYSQL_VERSION_ID == table->s->mysql_version)
{
- report_error(ER_COL_COUNT_DOESNT_MATCH_CORRUPTED,
- ER(ER_COL_COUNT_DOESNT_MATCH_CORRUPTED),
- table->alias.c_ptr(),
+ report_error(ER_COL_COUNT_DOESNT_MATCH_CORRUPTED_V2,
+ ER(ER_COL_COUNT_DOESNT_MATCH_CORRUPTED_V2),
+ table->s->db.str, table->s->table_name.str,
table_def->count, table->s->fields);
DBUG_RETURN(TRUE);
}
@@ -3749,6 +3758,46 @@ Table_check_intact::check(TABLE *table, const TABLE_FIELD_DEF *table_def)
}
}
+ if (table_def->primary_key_parts)
+ {
+ if (table->s->primary_key == MAX_KEY)
+ {
+ report_error(0, "Incorrect definition of table %s.%s: "
+ "missing primary key.", table->s->db.str,
+ table->alias.c_ptr());
+ error= TRUE;
+ }
+ else
+ {
+ KEY *pk= &table->s->key_info[table->s->primary_key];
+ if (pk->user_defined_key_parts != table_def->primary_key_parts)
+ {
+ report_error(0, "Incorrect definition of table %s.%s: "
+ "Expected primary key to have %u columns, but instead "
+ "found %u columns.", table->s->db.str,
+ table->alias.c_ptr(), table_def->primary_key_parts,
+ pk->user_defined_key_parts);
+ error= TRUE;
+ }
+ else
+ {
+ for (i= 0; i < pk->user_defined_key_parts; ++i)
+ {
+ if (table_def->primary_key_columns[i] + 1 != pk->key_part[i].fieldnr)
+ {
+ report_error(0, "Incorrect definition of table %s.%s: Expected "
+ "primary key part %u to refer to column %u, but "
+ "instead found column %u.", table->s->db.str,
+ table->alias.c_ptr(), i + 1,
+ table_def->primary_key_columns[i] + 1,
+ pk->key_part[i].fieldnr);
+ error= TRUE;
+ }
+ }
+ }
+ }
+ }
+
if (! error)
table->s->table_field_def_cache= table_def;
@@ -3797,16 +3846,17 @@ bool TABLE_SHARE::visit_subgraph(Wait_for_flush *wait_for_flush,
bool result= TRUE;
/*
- To protect used_tables list from being concurrently modified
- while we are iterating through it we acquire LOCK_open.
+ To protect all_tables list from being concurrently modified
+ while we are iterating through it we increment tdc.all_tables_refs.
This does not introduce deadlocks in the deadlock detector
- because we won't try to acquire LOCK_open while
+ because we won't try to acquire tdc.LOCK_table_share while
holding a write-lock on MDL_lock::m_rwlock.
*/
- if (gvisitor->m_lock_open_count++ == 0)
- mysql_mutex_lock(&LOCK_open);
+ mysql_mutex_lock(&tdc.LOCK_table_share);
+ tdc.all_tables_refs++;
+ mysql_mutex_unlock(&tdc.LOCK_table_share);
- I_P_List_iterator <TABLE, TABLE_share> tables_it(used_tables);
+ All_share_tables_list::Iterator tables_it(tdc.all_tables);
/*
In case of multiple searches running in parallel, avoid going
@@ -3824,6 +3874,7 @@ bool TABLE_SHARE::visit_subgraph(Wait_for_flush *wait_for_flush,
while ((table= tables_it++))
{
+ DBUG_ASSERT(table->in_use && tdc.flushed);
if (gvisitor->inspect_edge(&table->in_use->mdl_context))
{
goto end_leave_node;
@@ -3833,6 +3884,7 @@ bool TABLE_SHARE::visit_subgraph(Wait_for_flush *wait_for_flush,
tables_it.rewind();
while ((table= tables_it++))
{
+ DBUG_ASSERT(table->in_use && tdc.flushed);
if (table->in_use->mdl_context.visit_subgraph(gvisitor))
{
goto end_leave_node;
@@ -3845,8 +3897,10 @@ end_leave_node:
gvisitor->leave_node(src_ctx);
end:
- if (gvisitor->m_lock_open_count-- == 1)
- mysql_mutex_unlock(&LOCK_open);
+ mysql_mutex_lock(&tdc.LOCK_table_share);
+ if (!--tdc.all_tables_refs)
+ mysql_cond_broadcast(&tdc.COND_release);
+ mysql_mutex_unlock(&tdc.LOCK_table_share);
return result;
}
@@ -3860,10 +3914,15 @@ end:
@param abstime Timeout for waiting as absolute time value.
@param deadlock_weight Weight of this wait for deadlock detector.
- @pre LOCK_open is write locked, the share is used (has
- non-zero reference count), is marked for flush and
+ @pre LOCK_table_share is locked, the share is marked for flush and
this connection does not reference the share.
- LOCK_open will be unlocked temporarily during execution.
+ LOCK_table_share will be unlocked temporarily during execution.
+
+ It may happen that another FLUSH TABLES thread marked this share
+ for flush, but didn't yet purge it from table definition cache.
+ In this case we may start waiting for a table share that has no
+ references (ref_count == 0). We do this with assumption that this
+ another FLUSH TABLES thread is about to purge this share.
@retval FALSE - Success.
@retval TRUE - Error (OOM, deadlock, timeout, etc...).
@@ -3876,41 +3935,40 @@ bool TABLE_SHARE::wait_for_old_version(THD *thd, struct timespec *abstime,
Wait_for_flush ticket(mdl_context, this, deadlock_weight);
MDL_wait::enum_wait_status wait_status;
- mysql_mutex_assert_owner(&LOCK_open);
- /*
- We should enter this method only when share's version is not
- up to date and the share is referenced. Otherwise our
- thread will never be woken up from wait.
- */
- DBUG_ASSERT(version != refresh_version && ref_count != 0);
+ mysql_mutex_assert_owner(&tdc.LOCK_table_share);
+ DBUG_ASSERT(tdc.flushed);
- m_flush_tickets.push_front(&ticket);
+ tdc.m_flush_tickets.push_front(&ticket);
mdl_context->m_wait.reset_status();
- mysql_mutex_unlock(&LOCK_open);
+ mysql_mutex_unlock(&tdc.LOCK_table_share);
mdl_context->will_wait_for(&ticket);
mdl_context->find_deadlock();
wait_status= mdl_context->m_wait.timed_wait(thd, abstime, TRUE,
- "Waiting for table flush");
+ &stage_waiting_for_table_flush);
mdl_context->done_waiting_for();
- mysql_mutex_lock(&LOCK_open);
+ mysql_mutex_lock(&tdc.LOCK_table_share);
- m_flush_tickets.remove(&ticket);
+ tdc.m_flush_tickets.remove(&ticket);
- if (m_flush_tickets.is_empty() && ref_count == 0)
+ if (tdc.m_flush_tickets.is_empty() && tdc.ref_count == 0)
{
/*
If our thread was the last one using the share,
we must destroy it here.
*/
+ mysql_mutex_unlock(&tdc.LOCK_table_share);
destroy();
}
+ else
+ mysql_mutex_unlock(&tdc.LOCK_table_share);
+
/*
In cases when our wait was aborted by KILL statement,
@@ -3955,7 +4013,7 @@ bool TABLE_SHARE::wait_for_old_version(THD *thd, struct timespec *abstime,
void TABLE::init(THD *thd, TABLE_LIST *tl)
{
- DBUG_ASSERT(s->ref_count > 0 || s->tmp_table != NO_TMP_TABLE);
+ DBUG_ASSERT(s->tdc.ref_count > 0 || s->tmp_table != NO_TMP_TABLE);
if (thd->lex->need_correct_ident())
alias_name_used= my_strcasecmp(table_alias_charset,
@@ -3977,25 +4035,27 @@ void TABLE::init(THD *thd, TABLE_LIST *tl)
insert_values= 0;
fulltext_searched= 0;
file->ft_handler= 0;
-#ifdef WITH_WSREP
- if (file->ht->db_type == DB_TYPE_PARTITION_DB)
- {
- ((ha_partition*)file)->wsrep_reset_files();
- }
-#endif
reginfo.impossible_range= 0;
created= TRUE;
+ cond_selectivity= 1.0;
+ cond_selectivity_sampling_explain= NULL;
+#ifdef HAVE_REPLICATION
+ /* used in RBR Triggers */
+ master_had_triggers= 0;
+#endif
/* Catch wrong handling of the auto_increment_field_not_null. */
DBUG_ASSERT(!auto_increment_field_not_null);
auto_increment_field_not_null= FALSE;
- if (timestamp_field)
- timestamp_field_type= timestamp_field->get_auto_set_type();
-
pos_in_table_list= tl;
clear_column_bitmaps();
+ for (Field **f_ptr= field ; *f_ptr ; f_ptr++)
+ {
+ (*f_ptr)->next_equal_field= NULL;
+ (*f_ptr)->cond_selectivity= 1.0;
+ }
DBUG_ASSERT(key_read == 0);
@@ -4084,7 +4144,8 @@ void TABLE::reset_item_list(List<Item> *item_list) const
void TABLE_LIST::calc_md5(char *buffer)
{
uchar digest[16];
- MY_MD5_HASH(digest, (uchar *) select_stmt.str, select_stmt.length);
+ compute_md5_hash((char*) digest, select_stmt.str,
+ select_stmt.length);
sprintf((char *) buffer,
"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
digest[0], digest[1], digest[2], digest[3],
@@ -4476,27 +4537,32 @@ void TABLE_LIST::hide_view_error(THD *thd)
return;
/* Hide "Unknown column" or "Unknown function" error */
DBUG_ASSERT(thd->is_error());
+ switch (thd->get_stmt_da()->sql_errno()) {
+ case ER_BAD_FIELD_ERROR:
+ case ER_SP_DOES_NOT_EXIST:
+ case ER_FUNC_INEXISTENT_NAME_COLLISION:
+ case ER_PROCACCESS_DENIED_ERROR:
+ case ER_COLUMNACCESS_DENIED_ERROR:
+ case ER_TABLEACCESS_DENIED_ERROR:
+ case ER_TABLE_NOT_LOCKED:
+ case ER_NO_SUCH_TABLE:
+ {
+ TABLE_LIST *top= top_table();
+ thd->clear_error();
+ my_error(ER_VIEW_INVALID, MYF(0),
+ top->view_db.str, top->view_name.str);
+ break;
+ }
- if (thd->stmt_da->sql_errno() == ER_BAD_FIELD_ERROR ||
- thd->stmt_da->sql_errno() == ER_SP_DOES_NOT_EXIST ||
- thd->stmt_da->sql_errno() == ER_FUNC_INEXISTENT_NAME_COLLISION ||
- thd->stmt_da->sql_errno() == ER_PROCACCESS_DENIED_ERROR ||
- thd->stmt_da->sql_errno() == ER_COLUMNACCESS_DENIED_ERROR ||
- thd->stmt_da->sql_errno() == ER_TABLEACCESS_DENIED_ERROR ||
- thd->stmt_da->sql_errno() == ER_TABLE_NOT_LOCKED ||
- thd->stmt_da->sql_errno() == ER_NO_SUCH_TABLE)
- {
- TABLE_LIST *top= top_table();
- thd->clear_error();
- my_error(ER_VIEW_INVALID, MYF(0), top->view_db.str, top->view_name.str);
- }
- else if (thd->stmt_da->sql_errno() == ER_NO_DEFAULT_FOR_FIELD)
- {
- TABLE_LIST *top= top_table();
- thd->clear_error();
- // TODO: make correct error message
- my_error(ER_NO_DEFAULT_FOR_VIEW_FIELD, MYF(0),
- top->view_db.str, top->view_name.str);
+ case ER_NO_DEFAULT_FOR_FIELD:
+ {
+ TABLE_LIST *top= top_table();
+ thd->clear_error();
+ // TODO: make correct error message
+ my_error(ER_NO_DEFAULT_FOR_VIEW_FIELD, MYF(0),
+ top->view_db.str, top->view_name.str);
+ break;
+ }
}
}
@@ -4572,7 +4638,7 @@ int TABLE_LIST::view_check_option(THD *thd, bool ignore_failure)
TABLE_LIST *main_view= top_table();
if (ignore_failure)
{
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_VIEW_CHECK_FAILED, ER(ER_VIEW_CHECK_FAILED),
main_view->view_db.str, main_view->view_name.str);
return(VIEW_CHECK_SKIP);
@@ -4651,23 +4717,26 @@ bool TABLE_LIST::check_single_table(TABLE_LIST **table_arg,
bool TABLE_LIST::set_insert_values(MEM_ROOT *mem_root)
{
+ DBUG_ENTER("set_insert_values");
if (table)
{
+ DBUG_PRINT("info", ("setting insert_value for table"));
if (!table->insert_values &&
!(table->insert_values= (uchar *)alloc_root(mem_root,
table->s->rec_buff_length)))
- return TRUE;
+ DBUG_RETURN(TRUE);
}
else
{
+ DBUG_PRINT("info", ("setting insert_value for view"));
DBUG_ASSERT(is_view_or_derived() && is_merged_derived());
for (TABLE_LIST *tbl= (TABLE_LIST*)view->select_lex.table_list.first;
tbl;
tbl= tbl->next_local)
if (tbl->set_insert_values(mem_root))
- return TRUE;
+ DBUG_RETURN(TRUE);
}
- return FALSE;
+ DBUG_RETURN(FALSE);
}
@@ -4840,7 +4909,7 @@ void TABLE_LIST::register_want_access(ulong want_access)
Load security context information for this view
SYNOPSIS
- TABLE_LIST::prepare_view_securety_context()
+ TABLE_LIST::prepare_view_security_context()
thd [in] thread handler
RETURN
@@ -4849,9 +4918,9 @@ void TABLE_LIST::register_want_access(ulong want_access)
*/
#ifndef NO_EMBEDDED_ACCESS_CHECKS
-bool TABLE_LIST::prepare_view_securety_context(THD *thd)
+bool TABLE_LIST::prepare_view_security_context(THD *thd)
{
- DBUG_ENTER("TABLE_LIST::prepare_view_securety_context");
+ DBUG_ENTER("TABLE_LIST::prepare_view_security_context");
DBUG_PRINT("enter", ("table: %s", alias));
DBUG_ASSERT(!prelocking_placeholder && view);
@@ -4865,7 +4934,7 @@ bool TABLE_LIST::prepare_view_securety_context(THD *thd)
if ((thd->lex->sql_command == SQLCOM_SHOW_CREATE) ||
(thd->lex->sql_command == SQLCOM_SHOW_FIELDS))
{
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
ER_NO_SUCH_USER,
ER(ER_NO_SUCH_USER),
definer.user.str, definer.host.str);
@@ -4894,6 +4963,7 @@ bool TABLE_LIST::prepare_view_securety_context(THD *thd)
}
}
DBUG_RETURN(FALSE);
+
}
#endif
@@ -4958,7 +5028,7 @@ bool TABLE_LIST::prepare_security(THD *thd)
Security_context *save_security_ctx= thd->security_ctx;
DBUG_ASSERT(!prelocking_placeholder);
- if (prepare_view_securety_context(thd))
+ if (prepare_view_security_context(thd))
DBUG_RETURN(TRUE);
thd->security_ctx= find_view_security_context(thd);
while ((tbl= tb++))
@@ -5252,6 +5322,12 @@ Item *create_view_field(THD *thd, TABLE_LIST *view, Item **field_ref,
item->maybe_null= TRUE;
/* Save item in case we will need to fall back to materialization. */
view->used_items.push_front(item);
+ /*
+ If we create this reference on persistent memory then it should be
+ present in persistent list
+ */
+ if (thd->mem_root == thd->stmt_arena->mem_root)
+ view->persistent_used_items.push_front(item);
DBUG_RETURN(item);
}
@@ -5661,7 +5737,7 @@ void TABLE::mark_columns_used_by_index_no_reset(uint index,
{
KEY_PART_INFO *key_part= key_info[index].key_part;
KEY_PART_INFO *key_part_end= (key_part +
- key_info[index].key_parts);
+ key_info[index].user_defined_key_parts);
for (;key_part != key_part_end; key_part++)
{
bitmap_set_bit(bitmap, key_part->fieldnr-1);
@@ -5936,6 +6012,51 @@ void TABLE::mark_virtual_columns_for_write(bool insert_fl)
/**
+ Check if a table has a default function either for INSERT or UPDATE-like
+ operation
+ @retval true there is a default function
+ @retval false there is no default function
+*/
+
+bool TABLE::has_default_function(bool is_update)
+{
+ Field **dfield_ptr, *dfield;
+ bool res= false;
+ for (dfield_ptr= default_field; *dfield_ptr; dfield_ptr++)
+ {
+ dfield= (*dfield_ptr);
+ if (is_update)
+ res= dfield->has_update_default_function();
+ else
+ res= dfield->has_insert_default_function();
+ if (res)
+ return res;
+ }
+ return res;
+}
+
+
+/**
+ Add all fields that have a default function to the table write set.
+*/
+
+void TABLE::mark_default_fields_for_write()
+{
+ Field **dfield_ptr, *dfield;
+ enum_sql_command cmd= in_use->lex->sql_command;
+ for (dfield_ptr= default_field; *dfield_ptr; dfield_ptr++)
+ {
+ dfield= (*dfield_ptr);
+ if (((sql_command_flags[cmd] & CF_INSERTS_DATA) &&
+ dfield->has_insert_default_function()) ||
+ ((sql_command_flags[cmd] & CF_UPDATES_DATA) &&
+ dfield->has_update_default_function()))
+ bitmap_set_bit(write_set, dfield->field_index);
+ }
+}
+
+
+/**
@brief
Allocate space for keys
@@ -5975,6 +6096,7 @@ bool TABLE::alloc_keys(uint key_count)
void TABLE::create_key_part_by_field(KEY_PART_INFO *key_part_info,
Field *field, uint fieldnr)
{
+ DBUG_ASSERT(field->field_index + 1 == (int)fieldnr);
key_part_info->null_bit= field->null_bit;
key_part_info->null_offset= (uint) (field->null_ptr -
(uchar*) record[0]);
@@ -6109,12 +6231,13 @@ bool TABLE::add_tmp_key(uint key, uint key_parts,
return TRUE;
keyinfo= key_info + key;
keyinfo->key_part= key_part_info;
- keyinfo->usable_key_parts= keyinfo->key_parts = key_parts;
- keyinfo->ext_key_parts= keyinfo->key_parts;
+ keyinfo->usable_key_parts= keyinfo->user_defined_key_parts = key_parts;
+ keyinfo->ext_key_parts= keyinfo->user_defined_key_parts;
keyinfo->key_length=0;
keyinfo->algorithm= HA_KEY_ALG_UNDEF;
keyinfo->flags= HA_GENERATED_KEY;
keyinfo->ext_key_flags= keyinfo->flags;
+ keyinfo->is_statistics_from_stat_tables= FALSE;
if (unique)
keyinfo->flags|= HA_NOSAME;
sprintf(buf, "key%i", key);
@@ -6125,6 +6248,8 @@ bool TABLE::add_tmp_key(uint key, uint key_parts,
if (!keyinfo->rec_per_key)
return TRUE;
bzero(keyinfo->rec_per_key, sizeof(ulong)*key_parts);
+ keyinfo->read_stats= NULL;
+ keyinfo->collected_stats= NULL;
for (i= 0; i < key_parts; i++)
{
@@ -6185,9 +6310,9 @@ bool TABLE::is_filled_at_execution()
do not have a corresponding table reference. Such tables are filled
during execution.
*/
- return test(!pos_in_table_list ||
- pos_in_table_list->jtbm_subselect ||
- pos_in_table_list->is_active_sjm());
+ return MY_TEST(!pos_in_table_list ||
+ pos_in_table_list->jtbm_subselect ||
+ pos_in_table_list->is_active_sjm());
}
@@ -6208,7 +6333,7 @@ bool TABLE::is_filled_at_execution()
uint TABLE::actual_n_key_parts(KEY *keyinfo)
{
return optimizer_flag(in_use, OPTIMIZER_SWITCH_EXTENDED_KEYS) ?
- keyinfo->ext_key_parts : keyinfo->key_parts;
+ keyinfo->ext_key_parts : keyinfo->user_defined_key_parts;
}
@@ -6525,7 +6650,7 @@ bool TABLE::update_const_key_parts(COND *conds)
for (uint index= 0; index < s->keys; index++)
{
KEY_PART_INFO *keyinfo= key_info[index].key_part;
- KEY_PART_INFO *keyinfo_end= keyinfo + key_info[index].key_parts;
+ KEY_PART_INFO *keyinfo_end= keyinfo + key_info[index].user_defined_key_parts;
for (key_part_map part_map= (key_part_map)1;
keyinfo < keyinfo_end;
@@ -6609,6 +6734,135 @@ int update_virtual_fields(THD *thd, TABLE *table,
DBUG_RETURN(0);
}
+
+/**
+ Update all DEFAULT and/or ON INSERT fields.
+
+ @details
+ Compute and set the default value of all fields with a default function.
+ There are two kinds of default functions - one is used for INSERT-like
+ operations, the other for UPDATE-like operations. Depending on the field
+ definition and the current operation one or the other kind of update
+ function is evaluated.
+
+ @retval
+ 0 Success
+ @retval
+ >0 Error occurred when storing a virtual field value
+*/
+
+int TABLE::update_default_fields()
+{
+ DBUG_ENTER("update_default_fields");
+ Field **dfield_ptr, *dfield;
+ int res= 0;
+ enum_sql_command cmd= in_use->lex->sql_command;
+
+ DBUG_ASSERT(default_field);
+
+ /* Iterate over fields with default functions in the table */
+ for (dfield_ptr= default_field; *dfield_ptr; dfield_ptr++)
+ {
+ dfield= (*dfield_ptr);
+ /*
+ If an explicit default value for a filed overrides the default,
+ do not update the field with its automatic default value.
+ */
+ if (!(dfield->flags & HAS_EXPLICIT_VALUE))
+ {
+ if (sql_command_flags[cmd] & CF_INSERTS_DATA)
+ res= dfield->evaluate_insert_default_function();
+ if (sql_command_flags[cmd] & CF_UPDATES_DATA)
+ res= dfield->evaluate_update_default_function();
+ if (res)
+ DBUG_RETURN(res);
+ }
+ }
+ DBUG_RETURN(res);
+}
+
+void TABLE::reset_default_fields()
+{
+ if (default_field)
+ for (Field **df= default_field; *df; df++)
+ (*df)->flags&= ~HAS_EXPLICIT_VALUE;
+}
+
+/*
+ Prepare triggers for INSERT-like statement.
+
+ SYNOPSIS
+ prepare_triggers_for_insert_stmt_or_event()
+
+ NOTE
+ Prepare triggers for INSERT-like statement by marking fields
+ used by triggers and inform handlers that batching of UPDATE/DELETE
+ cannot be done if there are BEFORE UPDATE/DELETE triggers.
+*/
+
+void TABLE::prepare_triggers_for_insert_stmt_or_event()
+{
+ if (triggers)
+ {
+ if (triggers->has_triggers(TRG_EVENT_DELETE,
+ TRG_ACTION_AFTER))
+ {
+ /*
+ The table has AFTER DELETE triggers that might access to
+ subject table and therefore might need delete to be done
+ immediately. So we turn-off the batching.
+ */
+ (void) file->extra(HA_EXTRA_DELETE_CANNOT_BATCH);
+ }
+ if (triggers->has_triggers(TRG_EVENT_UPDATE,
+ TRG_ACTION_AFTER))
+ {
+ /*
+ The table has AFTER UPDATE triggers that might access to subject
+ table and therefore might need update to be done immediately.
+ So we turn-off the batching.
+ */
+ (void) file->extra(HA_EXTRA_UPDATE_CANNOT_BATCH);
+ }
+ }
+}
+
+
+bool TABLE::prepare_triggers_for_delete_stmt_or_event()
+{
+ if (triggers &&
+ triggers->has_triggers(TRG_EVENT_DELETE,
+ TRG_ACTION_AFTER))
+ {
+ /*
+ The table has AFTER DELETE triggers that might access to subject table
+ and therefore might need delete to be done immediately. So we turn-off
+ the batching.
+ */
+ (void) file->extra(HA_EXTRA_DELETE_CANNOT_BATCH);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+bool TABLE::prepare_triggers_for_update_stmt_or_event()
+{
+ if (triggers &&
+ triggers->has_triggers(TRG_EVENT_UPDATE,
+ TRG_ACTION_AFTER))
+ {
+ /*
+ The table has AFTER UPDATE triggers that might access to subject
+ table and therefore might need update to be done immediately.
+ So we turn-off the batching.
+ */
+ (void) file->extra(HA_EXTRA_UPDATE_CANNOT_BATCH);
+ return TRUE;
+ }
+ return FALSE;
+}
+
/*
@brief Reset const_table flag
@@ -6649,15 +6903,17 @@ void TABLE_LIST::reset_const_table()
bool TABLE_LIST::handle_derived(LEX *lex, uint phases)
{
- SELECT_LEX_UNIT *unit= get_unit();
- if (unit)
+ SELECT_LEX_UNIT *unit;
+ DBUG_ENTER("handle_derived");
+ DBUG_PRINT("enter", ("phases: 0x%x", phases));
+ if ((unit= get_unit()))
{
for (SELECT_LEX *sl= unit->first_select(); sl; sl= sl->next_select())
if (sl->handle_derived(lex, phases))
- return TRUE;
- return mysql_handle_single_derived(lex, this, phases);
+ DBUG_RETURN(TRUE);
+ DBUG_RETURN(mysql_handle_single_derived(lex, this, phases));
}
- return FALSE;
+ DBUG_RETURN(FALSE);
}
@@ -6820,6 +7076,7 @@ int TABLE_LIST::fetch_number_of_rows()
{
table->file->stats.records= ((select_union*)derived->result)->records;
set_if_bigger(table->file->stats.records, 2);
+ table->used_stat_records= table->file->stats.records;
}
else
error= table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK);
@@ -6937,11 +7194,11 @@ uint TABLE_SHARE::actual_n_key_parts(THD *thd)
}
-/*****************************************************************************
-** Instansiate templates
-*****************************************************************************/
+double KEY::actual_rec_per_key(uint i)
+{
+ if (rec_per_key == 0)
+ return 0;
+ return (is_statistics_from_stat_tables ?
+ read_stats->get_avg_frequency(i) : (double) rec_per_key[i]);
+}
-#ifdef HAVE_EXPLICIT_TEMPLATE_INSTANTIATION
-template class List<String>;
-template class List_iterator<String>;
-#endif
diff --git a/sql/table.h b/sql/table.h
index 17fdd4aba15..39faa8b9765 100644
--- a/sql/table.h
+++ b/sql/table.h
@@ -29,6 +29,7 @@
#include "handler.h" /* row_type, ha_choice, handler */
#include "mysql_com.h" /* enum_field_types */
#include "thr_lock.h" /* thr_lock_type */
+#include "filesort_utils.h"
/* Structs that defines the TABLE */
@@ -45,6 +46,7 @@ struct TABLE_LIST;
class ACL_internal_schema_access;
class ACL_internal_table_access;
class Field;
+class Table_statistics;
/*
Used to identify NESTED_JOIN structures within a join (applicable only to
@@ -192,10 +194,20 @@ private:
/* Order clause list element */
+typedef int (*fast_field_copier)(Field *to, Field *from);
+
+
typedef struct st_order {
struct st_order *next;
Item **item; /* Point at item in select fields */
Item *item_ptr; /* Storage for initial item */
+ /*
+ Reference to the function we are trying to optimize copy to
+ a temporary table
+ */
+ fast_field_copier fast_field_copier_func;
+ /* Field for which above optimizer function setup */
+ Field *fast_field_copier_setup;
int counter; /* position in SELECT list, correct
only if counter_used is true*/
bool asc; /* true if ascending */
@@ -249,7 +261,8 @@ typedef struct st_grant_info
@details The version of this copy is found in GRANT_INFO::version.
*/
- GRANT_TABLE *grant_table;
+ GRANT_TABLE *grant_table_user;
+ GRANT_TABLE *grant_table_role;
/**
@brief Used for cache invalidation when caching privilege information.
@@ -306,11 +319,13 @@ enum enum_vcol_update_mode
VCOL_UPDATE_ALL
};
-typedef struct st_filesort_info
+class Filesort_info
{
+ /// Buffer for sorting keys.
+ Filesort_buffer filesort_buffer;
+
+public:
IO_CACHE *io_cache; /* If sorted through filesort */
- uchar **sort_keys; /* Buffer for sorting keys */
- uint keys; /* Number of key pointers in buffer */
uchar *buffpek; /* Buffer for buffpek structures */
uint buffpek_len; /* Max number of buffpeks in the buffer */
uchar *addon_buf; /* Pointer to a buffer if sorted with fields */
@@ -319,28 +334,40 @@ typedef struct st_filesort_info
void (*unpack)(struct st_sort_addon_field *, uchar *, uchar *); /* To unpack back */
uchar *record_pointers; /* If sorted in memory */
ha_rows found_records; /* How many records in sort */
-} FILESORT_INFO;
+ /** Sort filesort_buffer */
+ void sort_buffer(Sort_param *param, uint count)
+ { filesort_buffer.sort_buffer(param, count); }
-/*
- Values in this enum are used to indicate how a tables TIMESTAMP field
- should be treated. It can be set to the current timestamp on insert or
- update or both.
- WARNING: The values are used for bit operations. If you change the
- enum, you must keep the bitwise relation of the values. For example:
- (int) TIMESTAMP_AUTO_SET_ON_BOTH must be equal to
- (int) TIMESTAMP_AUTO_SET_ON_INSERT | (int) TIMESTAMP_AUTO_SET_ON_UPDATE.
- We use an enum here so that the debugger can display the value names.
-*/
-enum timestamp_auto_set_type
-{
- TIMESTAMP_NO_AUTO_SET= 0, TIMESTAMP_AUTO_SET_ON_INSERT= 1,
- TIMESTAMP_AUTO_SET_ON_UPDATE= 2, TIMESTAMP_AUTO_SET_ON_BOTH= 3
+ /**
+ Accessors for Filesort_buffer (which @c).
+ */
+ uchar *get_record_buffer(uint idx)
+ { return filesort_buffer.get_record_buffer(idx); }
+
+ uchar **get_sort_keys()
+ { return filesort_buffer.get_sort_keys(); }
+
+ uchar **alloc_sort_buffer(uint num_records, uint record_length)
+ { return filesort_buffer.alloc_sort_buffer(num_records, record_length); }
+
+ bool check_sort_buffer_properties(uint num_records, uint record_length)
+ {
+ return filesort_buffer.check_sort_buffer_properties(num_records,
+ record_length);
+ }
+
+ void free_sort_buffer()
+ { filesort_buffer.free_sort_buffer(); }
+
+ void init_record_pointers()
+ { filesort_buffer.init_record_pointers(); }
+
+ size_t sort_buffer_size() const
+ { return filesort_buffer.sort_buffer_size(); }
};
-#define clear_timestamp_auto_bits(_target_, _bits_) \
- (_target_)= (enum timestamp_auto_set_type)((int)(_target_) & ~(int)(_bits_))
-class Field_timestamp;
+
class Field_blob;
class Table_triggers_list;
@@ -462,8 +489,7 @@ TABLE_CATEGORY get_table_category(const LEX_STRING *db,
struct TABLE_share;
-
-extern ulong refresh_version;
+struct All_share_tables;
typedef struct st_table_field_type
{
@@ -477,22 +503,11 @@ typedef struct st_table_field_def
{
uint count;
const TABLE_FIELD_TYPE *field;
+ uint primary_key_parts;
+ const uint *primary_key_columns;
} TABLE_FIELD_DEF;
-#ifdef WITH_PARTITION_STORAGE_ENGINE
-/**
- Partition specific ha_data struct.
-*/
-typedef struct st_ha_data_partition
-{
- bool auto_inc_initialized;
- mysql_mutex_t LOCK_auto_inc; /**< protecting auto_inc val */
- ulonglong next_auto_inc_val; /**< first non reserved value */
-} HA_DATA_PARTITION;
-#endif
-
-
class Table_check_intact
{
protected:
@@ -546,6 +561,35 @@ typedef I_P_List <Wait_for_flush,
Wait_for_flush_list;
+enum open_frm_error {
+ OPEN_FRM_OK = 0,
+ OPEN_FRM_OPEN_ERROR,
+ OPEN_FRM_READ_ERROR,
+ OPEN_FRM_CORRUPTED,
+ OPEN_FRM_DISCOVER,
+ OPEN_FRM_ERROR_ALREADY_ISSUED,
+ OPEN_FRM_NOT_A_VIEW,
+ OPEN_FRM_NOT_A_TABLE,
+ OPEN_FRM_NEEDS_REBUILD
+};
+
+/**
+ Control block to access table statistics loaded
+ from persistent statistical tables
+*/
+
+struct TABLE_STATISTICS_CB
+{
+ MEM_ROOT mem_root; /* MEM_ROOT to allocate statistical data for the table */
+ Table_statistics *table_stats; /* Structure to access the statistical data */
+ bool stats_can_be_read; /* Memory for statistical data is allocated */
+ bool stats_is_read; /* Statistical data for table has been read
+ from statistical tables */
+ bool histograms_can_be_read;
+ bool histograms_are_read;
+};
+
+
/**
This structure is shared between different table objects. There is one
instance of table share per one table in the database.
@@ -565,14 +609,36 @@ struct TABLE_SHARE
TYPELIB fieldnames; /* Pointer to fieldnames */
TYPELIB *intervals; /* pointer to interval info */
mysql_mutex_t LOCK_ha_data; /* To protect access to ha_data */
- TABLE_SHARE *next, **prev; /* Link to unused shares */
+ mysql_mutex_t LOCK_share; /* To protect TABLE_SHARE */
- /*
- Doubly-linked (back-linked) lists of used and unused TABLE objects
- for this share.
- */
- I_P_List <TABLE, TABLE_share> used_tables;
- I_P_List <TABLE, TABLE_share> free_tables;
+ typedef I_P_List <TABLE, TABLE_share> TABLE_list;
+ typedef I_P_List <TABLE, All_share_tables> All_share_tables_list;
+ struct
+ {
+ /**
+ Protects ref_count, m_flush_tickets, all_tables, free_tables, flushed,
+ all_tables_refs.
+ */
+ mysql_mutex_t LOCK_table_share;
+ mysql_cond_t COND_release;
+ TABLE_SHARE *next, **prev; /* Link to unused shares */
+ uint ref_count; /* How many TABLE objects uses this */
+ uint all_tables_refs; /* Number of refs to all_tables */
+ /**
+ List of tickets representing threads waiting for the share to be flushed.
+ */
+ Wait_for_flush_list m_flush_tickets;
+ /*
+ Doubly-linked (back-linked) lists of used and unused TABLE objects
+ for this share.
+ */
+ All_share_tables_list all_tables;
+ TABLE_list free_tables;
+ ulong version;
+ bool flushed;
+ } tdc;
+
+ LEX_CUSTRING tabledef_version;
engine_option_value *option_list; /* text options for table */
ha_table_option_struct *option_struct; /* structure with parsed options */
@@ -580,10 +646,11 @@ struct TABLE_SHARE
/* The following is copied to each TABLE on OPEN */
Field **field;
Field **found_next_number_field;
- Field *timestamp_field; /* Used only during open */
KEY *key_info; /* data of keys in database */
uint *blob_field; /* Index to blobs in Field arrray*/
+ TABLE_STATISTICS_CB stats_cb;
+
uchar *default_values; /* row with default values */
LEX_STRING comment; /* Comment about table */
CHARSET_INFO *table_charset; /* Default charset of string fields */
@@ -614,7 +681,7 @@ struct TABLE_SHARE
key_map keys_for_keyread;
ha_rows min_rows, max_rows; /* create information */
ulong avg_row_length; /* create information */
- ulong version, mysql_version;
+ ulong mysql_version; /* 0 if .frm is created before 5.0 */
ulong reclength; /* Recordlength */
/* Stored record length. No generated-only virtual fields are included */
ulong stored_rec_length;
@@ -622,8 +689,8 @@ struct TABLE_SHARE
plugin_ref db_plugin; /* storage engine plugin */
inline handlerton *db_type() const /* table_type for handler */
{
- // DBUG_ASSERT(db_plugin);
- return db_plugin ? plugin_data(db_plugin, handlerton*) : NULL;
+ return is_view ? view_pseudo_hton :
+ db_plugin ? plugin_hton(db_plugin) : NULL;
}
enum row_type row_type; /* How rows are stored */
enum tmp_table_type tmp_table;
@@ -633,9 +700,10 @@ struct TABLE_SHARE
/** Per-page checksums or not. */
enum ha_choice page_checksum;
- uint ref_count; /* How many TABLE objects uses this */
- uint blob_ptr_size; /* 4 or 8 */
uint key_block_size; /* create key_block_size, if used */
+ uint stats_sample_pages; /* number of pages to sample during
+ stats estimation, if used, otherwise 0. */
+ enum_stats_auto_recalc stats_auto_recalc; /* Automatic recalc of stats. */
uint null_bytes, last_null_bit_pos;
/*
Same as null_bytes, except that if there is only a 'delete-marker' in
@@ -652,7 +720,6 @@ struct TABLE_SHARE
uint uniques; /* Number of UNIQUE index */
uint null_fields; /* number of null fields */
uint blob_fields; /* number of blob fields */
- uint timestamp_field_offset; /* Field number for timestamp field */
uint varchar_fields; /* number of varchar fields */
uint db_create_options; /* Create options from database */
uint db_options_in_use; /* Options in use */
@@ -663,10 +730,12 @@ struct TABLE_SHARE
uint next_number_index; /* autoincrement key number */
uint next_number_key_offset; /* autoinc keypart offset in a key */
uint next_number_keypart; /* autoinc keypart number in a key */
- uint error, open_errno, errarg; /* error from open_table_def() */
+ enum open_frm_error error; /* error from open_table_def() */
+ uint open_errno; /* error from open_table_def() */
uint column_bitmap_size;
uchar frm_version;
uint vfields; /* Number of computed (virtual) fields */
+ uint default_fields; /* Number of default fields */
bool use_ext_keys; /* Extended keys can be used */
bool null_field_first;
bool system; /* Set if system table (one record) */
@@ -675,9 +744,17 @@ struct TABLE_SHARE
bool is_view;
bool deleting; /* going to delete this table */
bool can_cmp_whole_record;
+ bool table_creation_was_logged;
ulong table_map_id; /* for row-based replication */
/*
+ Things that are incompatible between the stored version and the
+ current version. This is a set of HA_CREATE... bits that can be used
+ to modify create_info->used_fields for ALTER TABLE.
+ */
+ ulong incompatible_version;
+
+ /*
Cache for row-based replication table share checks that does not
need to be repeated. Possible values are: -1 when cache value is
not calculated yet, 0 when table *shall not* be replicated, 1 when
@@ -685,13 +762,16 @@ struct TABLE_SHARE
*/
int cached_row_logging_check;
+ /* Name of the tablespace used for this table */
+ char *tablespace;
+
#ifdef WITH_PARTITION_STORAGE_ENGINE
/* filled in when reading from frm */
bool auto_partitioned;
char *partition_info_str;
uint partition_info_str_len;
uint partition_info_buffer_size;
- handlerton *default_part_db_type;
+ plugin_ref default_part_plugin;
#endif
/**
@@ -706,25 +786,12 @@ struct TABLE_SHARE
*/
const TABLE_FIELD_DEF *table_field_def_cache;
- /** place to store storage engine specific data */
- void *ha_data;
- void (*ha_data_destroy)(void *); /* An optional destructor for ha_data */
-
-#ifdef WITH_PARTITION_STORAGE_ENGINE
- /** place to store partition specific data, LOCK_ha_data hold while init. */
- HA_DATA_PARTITION *ha_part_data;
- /* Destructor for ha_part_data */
- void (*ha_part_data_destroy)(HA_DATA_PARTITION *);
-#endif
+ /** Main handler's share */
+ Handler_share *ha_share;
/** Instrumentation for this table share. */
PSI_table_share *m_psi;
- /**
- List of tickets representing threads waiting for the share to be flushed.
- */
- Wait_for_flush_list m_flush_tickets;
-
/*
Set share's table cache key and update its db and table name appropriately.
@@ -793,42 +860,6 @@ struct TABLE_SHARE
return table_map_id;
}
- /** Is this table share being expelled from the table definition cache? */
- inline bool has_old_version() const
- {
- return version != refresh_version;
- }
- inline bool protected_against_usage() const
- {
- return version == 0;
- }
- inline void protect_against_usage()
- {
- version= 0;
- }
- /*
- This is used only for the case of locked tables, as we want to
- allow one to do SHOW commands on them even after ALTER or REPAIR
- */
- inline void allow_access_to_protected_table()
- {
- DBUG_ASSERT(version == 0);
- version= 1;
- }
- /*
- Remove from table definition cache at close.
- Table can still be opened by SHOW
- */
- inline void remove_from_cache_at_close()
- {
- if (version != 0) /* Don't remove protection */
- version= 1;
- }
- inline void set_refresh_version()
- {
- version= refresh_version;
- }
-
/**
Convert unrelated members of TABLE_SHARE to one enum
representing its type.
@@ -850,7 +881,7 @@ struct TABLE_SHARE
}
/**
Return a table metadata version.
- * for base tables, we return table_map_id.
+ * for base tables and views, we return table_map_id.
It is assigned from a global counter incremented for each
new table loaded into the table definition cache (TDC).
* for temporary tables it's table_map_id again. But for
@@ -859,7 +890,7 @@ struct TABLE_SHARE
counter incremented for every new SQL statement. Since
temporary tables are thread-local, each temporary table
gets a unique id.
- * for everything else (views, information schema tables),
+ * for everything else (e.g. information schema tables),
the version id is zero.
This choice of version id is a large compromise
@@ -874,8 +905,8 @@ struct TABLE_SHARE
version id of a temporary table is never compared with
a version id of a view, and vice versa.
- Secondly, for base tables, we know that each DDL flushes the
- respective share from the TDC. This ensures that whenever
+ Secondly, for base tables and views, we know that each DDL flushes
+ the respective share from the TDC. This ensures that whenever
a table is altered or dropped and recreated, it gets a new
version id.
Unfortunately, since elements of the TDC are also flushed on
@@ -896,26 +927,6 @@ struct TABLE_SHARE
Metadata of information schema tables never changes.
Thus we can safely assume 0 for a good enough version id.
- Views are a special and tricky case. A view is always inlined
- into the parse tree of a prepared statement at prepare.
- Thus, when we execute a prepared statement, the parse tree
- will not get modified even if the view is replaced with another
- view. Therefore, we can safely choose 0 for version id of
- views and effectively never invalidate a prepared statement
- when a view definition is altered. Note, that this leads to
- wrong binary log in statement-based replication, since we log
- prepared statement execution in form Query_log_events
- containing conventional statements. But since there is no
- metadata locking for views, the very same problem exists for
- conventional statements alone, as reported in Bug#25144. The only
- difference between prepared and conventional execution is,
- effectively, that for prepared statements the race condition
- window is much wider.
- In 6.0 we plan to support view metadata locking (WL#3726) and
- extend table definition cache to cache views (WL#4298).
- When this is done, views will be handled in the same fashion
- as the base tables.
-
Finally, by taking into account table type, we always
track that a change has taken place when a view is replaced
with a base table, a base table is replaced with a temporary
@@ -925,7 +936,7 @@ struct TABLE_SHARE
*/
ulong get_table_ref_version() const
{
- return (tmp_table == SYSTEM_TMP_TABLE || is_view) ? 0 : table_map_id;
+ return (tmp_table == SYSTEM_TMP_TABLE) ? 0 : table_map_id;
}
bool visit_subgraph(Wait_for_flush *waiting_ticket,
@@ -942,6 +953,43 @@ struct TABLE_SHARE
}
uint actual_n_key_parts(THD *thd);
+
+ LEX_CUSTRING *frm_image; ///< only during CREATE TABLE (@sa ha_create_table)
+
+ /*
+ populates TABLE_SHARE from the table description in the binary frm image.
+ if 'write' is true, this frm image is also written into a corresponding
+ frm file, that serves as a persistent metadata cache to avoid
+ discovering the table over and over again
+ */
+ int init_from_binary_frm_image(THD *thd, bool write,
+ const uchar *frm_image, size_t frm_length);
+
+ /*
+ populates TABLE_SHARE from the table description, specified as the
+ complete CREATE TABLE sql statement.
+ if 'write' is true, this frm image is also written into a corresponding
+ frm file, that serves as a persistent metadata cache to avoid
+ discovering the table over and over again
+ */
+ int init_from_sql_statement_string(THD *thd, bool write,
+ const char *sql, size_t sql_length);
+ /*
+ writes the frm image to an frm file, corresponding to this table
+ */
+ bool write_frm_image(const uchar *frm_image, size_t frm_length);
+
+ bool write_frm_image(void)
+ { return frm_image ? write_frm_image(frm_image->str, frm_image->length) : 0; }
+
+ /*
+ returns an frm image for this table.
+ the memory is allocated and must be freed later
+ */
+ bool read_frm_image(const uchar **frm_image, size_t *frm_length);
+
+ /* frees the memory allocated in read_frm_image */
+ void free_frm_image(const uchar *frm);
};
@@ -953,6 +1001,7 @@ enum index_hint_type
INDEX_HINT_FORCE
};
+struct st_cond_statistic;
#define CHECK_ROW_FOR_NULLS_TO_REJECT (1 << 0)
#define REJECT_ROW_DUE_TO_NULL_FIELDS (1 << 1)
@@ -970,17 +1019,18 @@ struct TABLE
private:
/**
- Links for the lists of used/unused TABLE objects for this share.
+ Links for the list of all TABLE objects for this share.
Declared as private to avoid direct manipulation with those objects.
One should use methods of I_P_List template instead.
*/
- TABLE *share_next, **share_prev;
-
- friend struct TABLE_share;
+ TABLE *share_all_next, **share_all_prev;
+ friend struct All_share_tables;
public:
THD *in_use; /* Which thread uses this */
+ /* Time when table was released to table cache. Valid for unused tables. */
+ ulonglong tc_time;
Field **field; /* Pointer to fields */
uchar *record[2]; /* Pointer to records */
@@ -1013,8 +1063,9 @@ public:
Field *next_number_field; /* Set if next_number is activated */
Field *found_next_number_field; /* Set on open */
- Field_timestamp *timestamp_field;
Field **vfield; /* Pointer to virtual fields*/
+ /* Fields that are updated automatically on INSERT or UPDATE. */
+ Field **default_field;
/* Table's triggers, 0 if there are no of them */
Table_triggers_list *triggers;
@@ -1024,9 +1075,9 @@ public:
ORDER *group;
String alias; /* alias or table name */
uchar *null_flags;
- my_bitmap_map *bitmap_init_value;
MY_BITMAP def_read_set, def_write_set, def_vcol_set, tmp_set;
MY_BITMAP eq_join_set; /* used to mark equi-joined fields */
+ MY_BITMAP cond_set; /* used to mark fields from sargable conditions*/
MY_BITMAP *read_set, *write_set, *vcol_set; /* Active column sets */
/*
The ID of the query that opened and is using this table. Has different
@@ -1048,13 +1099,26 @@ public:
*/
query_id_t query_id;
+ /*
+ This structure is used for statistical data on the table that
+ is collected by the function collect_statistics_for_table
+ */
+ Table_statistics *collected_stats;
+
+ /* The estimate of the number of records in the table used by optimizer */
+ ha_rows used_stat_records;
+
/*
For each key that has quick_keys.is_set(key) == TRUE: estimate of #records
and max #key parts that range access would use.
*/
ha_rows quick_rows[MAX_KEY];
- /* Bitmaps of key parts that =const for the entire join. */
+ /*
+ Bitmaps of key parts that =const for the duration of join execution. If
+ we're in a subquery, then the constant may be different across subquery
+ re-executions.
+ */
key_part_map const_key_parts[MAX_KEY];
uint quick_key_parts[MAX_KEY];
@@ -1070,19 +1134,9 @@ public:
*/
ha_rows quick_condition_rows;
- /*
- If this table has TIMESTAMP field with auto-set property (pointed by
- timestamp_field member) then this variable indicates during which
- operations (insert only/on update/in both cases) we should set this
- field to current timestamp. If there are no such field in this table
- or we should not automatically set its value during execution of current
- statement then the variable contains TIMESTAMP_NO_AUTO_SET (i.e. 0).
-
- Value of this variable is set for each statement in open_table() and
- if needed cleared later in statement processing code (see mysql_update()
- as example).
- */
- timestamp_auto_set_type timestamp_field_type;
+ double cond_selectivity;
+ List<st_cond_statistic> *cond_selectivity_sampling_explain;
+
table_map map; /* ID bit of table (1,2,4,8,16...) */
uint lock_position; /* Position in MYSQL_LOCK.table */
@@ -1152,7 +1206,12 @@ public:
See TABLE_LIST::process_index_hints().
*/
bool force_index_group;
- bool distinct,const_table,no_rows, used_for_duplicate_elimination;
+ /*
+ TRUE<=> this table was created with create_tmp_table(... distinct=TRUE..)
+ call
+ */
+ bool distinct;
+ bool const_table,no_rows, used_for_duplicate_elimination;
/**
Forces DYNAMIC Aria row format for internal temporary tables.
*/
@@ -1164,6 +1223,9 @@ public:
*/
bool key_read;
bool no_keyread;
+ /**
+ If set, indicate that the table is not replicated by the server.
+ */
bool locked_by_logger;
bool no_replicate;
bool locked_by_name;
@@ -1182,11 +1244,15 @@ public:
bool get_fields_in_item_tree; /* Signal to fix_field */
bool m_needs_reopen;
bool created; /* For tmp tables. TRUE <=> tmp table was actually created.*/
+#ifdef HAVE_REPLICATION
+ /* used in RBR Triggers */
+ bool master_had_triggers;
+#endif
REGINFO reginfo; /* field connections */
MEM_ROOT mem_root;
GRANT_INFO grant;
- FILESORT_INFO sort;
+ Filesort_info sort;
/*
The arena which the items for expressions from the table definition
are associated with.
@@ -1197,9 +1263,12 @@ public:
Query_arena *expr_arena;
#ifdef WITH_PARTITION_STORAGE_ENGINE
partition_info *part_info; /* Partition related information */
- bool no_partitions_used; /* If true, all partitions have been pruned away */
+ /* If true, all partitions have been pruned away */
+ bool all_partitions_pruned_away;
#endif
uint max_keys; /* Size of allocated key_info array. */
+ bool stats_is_read; /* Persistent statistics is read for the table */
+ bool histograms_are_read;
MDL_ticket *mdl_ticket;
void init(THD *thd, TABLE_LIST *tl);
@@ -1217,6 +1286,8 @@ public:
void mark_columns_needed_for_insert(void);
bool mark_virtual_col(Field *field);
void mark_virtual_columns_for_write(bool insert_fl);
+ void mark_default_fields_for_write();
+ bool has_default_function(bool is_update);
inline void column_bitmaps_set(MY_BITMAP *read_set_arg,
MY_BITMAP *write_set_arg)
{
@@ -1303,8 +1374,19 @@ public:
}
bool update_const_key_parts(COND *conds);
+
+ my_ptrdiff_t default_values_offset() const
+ { return (my_ptrdiff_t) (s->default_values - record[0]); }
+
uint actual_n_key_parts(KEY *keyinfo);
ulong actual_key_flags(KEY *keyinfo);
+ int update_default_fields();
+ void reset_default_fields();
+ inline ha_rows stat_records() { return used_stat_records; }
+
+ void prepare_triggers_for_insert_stmt_or_event();
+ bool prepare_triggers_for_delete_stmt_or_event();
+ bool prepare_triggers_for_update_stmt_or_event();
};
@@ -1317,11 +1399,24 @@ struct TABLE_share
{
static inline TABLE **next_ptr(TABLE *l)
{
- return &l->share_next;
+ return &l->next;
}
static inline TABLE ***prev_ptr(TABLE *l)
{
- return &l->share_prev;
+ return (TABLE ***) &l->prev;
+ }
+};
+
+
+struct All_share_tables
+{
+ static inline TABLE **next_ptr(TABLE *l)
+ {
+ return &l->share_all_next;
+ }
+ static inline TABLE ***prev_ptr(TABLE *l)
+ {
+ return &l->share_all_prev;
}
};
@@ -1584,7 +1679,7 @@ struct TABLE_LIST
/**
Prepare TABLE_LIST that consists of one table instance to use in
- simple_open_and_lock_tables
+ open_and_lock_tables
*/
inline void init_one_table(const char *db_name_arg,
size_t db_length_arg,
@@ -1886,7 +1981,7 @@ struct TABLE_LIST
Indicates that if TABLE_LIST object corresponds to the table/view
which requires special handling.
*/
- enum
+ enum enum_open_strategy
{
/* Normal open. */
OPEN_NORMAL= 0,
@@ -1898,7 +1993,6 @@ struct TABLE_LIST
/* For transactional locking. */
int lock_timeout; /* NOWAIT or WAIT [X] */
bool lock_transactional; /* If transactional lock requested. */
- bool internal_tmp_table;
/** TRUE if an alias for this table was specified in the SQL. */
bool is_alias;
/** TRUE if the table is referred to in the statement using a fully
@@ -1968,6 +2062,11 @@ struct TABLE_LIST
MDL_request mdl_request;
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+ /* List to carry partition names from PARTITION (...) clause in statement */
+ List<String> *partition_names;
+#endif /* WITH_PARTITION_STORAGE_ENGINE */
+
void calc_md5(char *buffer);
int view_check_option(THD *thd, bool ignore_failure);
bool create_field_translation(THD *thd);
@@ -2027,7 +2126,7 @@ struct TABLE_LIST
bool prepare_security(THD *thd);
#ifndef NO_EMBEDDED_ACCESS_CHECKS
Security_context *find_view_security_context(THD *thd);
- bool prepare_view_securety_context(THD *thd);
+ bool prepare_view_security_context(THD *thd);
#endif
/*
Cleanup for re-execution in a prepared statement or a stored
@@ -2145,7 +2244,7 @@ struct TABLE_LIST
@brief Returns the name of the database that the referenced table belongs
to.
*/
- char *get_db_name() { return view != NULL ? view_db.str : db; }
+ char *get_db_name() const { return view != NULL ? view_db.str : db; }
/**
@brief Returns the name of the table that this TABLE_LIST represents.
@@ -2153,9 +2252,9 @@ struct TABLE_LIST
@details The unqualified table name or view name for a table or view,
respectively.
*/
- char *get_table_name() { return view != NULL ? view_name.str : table_name; }
+ char *get_table_name() const { return view != NULL ? view_name.str : table_name; }
bool is_active_sjm();
- bool is_jtbm() { return test(jtbm_subselect!=NULL); }
+ bool is_jtbm() { return MY_TEST(jtbm_subselect != NULL); }
st_select_lex_unit *get_unit();
st_select_lex *get_single_select();
void wrap_into_nested_join(List<TABLE_LIST> &join_list);
@@ -2185,9 +2284,9 @@ private:
#else
inline void set_check_merged() {}
#endif
- /** See comments for set_metadata_id() */
+ /** See comments for set_table_ref_id() */
enum enum_table_ref_type m_table_ref_type;
- /** See comments for set_metadata_id() */
+ /** See comments for set_table_ref_id() */
ulong m_table_ref_version;
};
@@ -2441,26 +2540,38 @@ static inline void dbug_tmp_restore_column_maps(MY_BITMAP *read_set,
#endif
}
+bool ok_for_lower_case_names(const char *names);
-size_t max_row_length(TABLE *table, const uchar *data);
+enum get_table_share_flags {
+ GTS_TABLE = 1,
+ GTS_VIEW = 2,
+ GTS_NOLOCK = 4,
+ GTS_USE_DISCOVERY = 8,
+ GTS_FORCE_DISCOVERY = 16
+};
+size_t max_row_length(TABLE *table, const uchar *data);
void init_mdl_requests(TABLE_LIST *table_list);
-int open_table_from_share(THD *thd, TABLE_SHARE *share, const char *alias,
- uint db_stat, uint prgflag, uint ha_open_flags,
- TABLE *outparam, bool is_create_table);
+enum open_frm_error open_table_from_share(THD *thd, TABLE_SHARE *share,
+ const char *alias, uint db_stat, uint prgflag,
+ uint ha_open_flags, TABLE *outparam,
+ bool is_create_table);
bool unpack_vcol_info_from_frm(THD *thd, MEM_ROOT *mem_root,
TABLE *table, Field *field,
LEX_STRING *vcol_expr, bool *error_reported);
-TABLE_SHARE *alloc_table_share(TABLE_LIST *table_list, char *key,
- uint key_length);
+TABLE_SHARE *alloc_table_share(const char *db, const char *table_name,
+ const char *key, uint key_length);
void init_tmp_table_share(THD *thd, TABLE_SHARE *share, const char *key,
uint key_length,
const char *table_name, const char *path);
void free_table_share(TABLE_SHARE *share);
-int open_table_def(THD *thd, TABLE_SHARE *share, uint db_flags);
-void open_table_error(TABLE_SHARE *share, int error, int db_errno, int errarg);
+enum open_frm_error open_table_def(THD *thd, TABLE_SHARE *share,
+ uint flags = GTS_TABLE);
+
+void open_table_error(TABLE_SHARE *share, enum open_frm_error error,
+ int db_errno);
void update_create_info_from_table(HA_CREATE_INFO *info, TABLE *form);
bool check_and_convert_db_name(LEX_STRING *db, bool preserve_lettercase);
bool check_db_name(LEX_STRING *db);
@@ -2470,21 +2581,28 @@ int rename_file_ext(const char * from,const char * to,const char * ext);
char *get_field(MEM_ROOT *mem, Field *field);
bool get_field(MEM_ROOT *mem, Field *field, class String *res);
+bool validate_comment_length(THD *thd, LEX_STRING *comment, size_t max_len,
+ uint err_code, const char *name);
+
int closefrm(TABLE *table, bool free_share);
-int read_string(File file, uchar* *to, size_t length);
void free_blobs(TABLE *table);
void free_field_buffers_larger_than(TABLE *table, uint32 size);
-int set_zone(int nr,int min_zone,int max_zone);
ulong get_form_pos(File file, uchar *head, TYPELIB *save_names);
-ulong make_new_entry(File file,uchar *fileinfo,TYPELIB *formnames,
- const char *newname);
-ulong next_io_size(ulong pos);
void append_unescaped(String *res, const char *pos, uint length);
-File create_frm(THD *thd, const char *name, const char *db,
- const char *table, uint reclength, uchar *fileinfo,
- HA_CREATE_INFO *create_info, uint keys, KEY *key_info);
+void prepare_frm_header(THD *thd, uint reclength, uchar *fileinfo,
+ HA_CREATE_INFO *create_info, uint keys, KEY *key_info);
char *fn_rext(char *name);
+/* Check that the integer is in the internal */
+static inline int set_zone(int nr,int min_zone,int max_zone)
+{
+ if (nr <= min_zone)
+ return min_zone;
+ if (nr >= max_zone)
+ return max_zone;
+ return nr;
+}
+
/* performance schema */
extern LEX_STRING PERFORMANCE_SCHEMA_DB_NAME;
diff --git a/sql/table_cache.cc b/sql/table_cache.cc
new file mode 100644
index 00000000000..097f37d26d8
--- /dev/null
+++ b/sql/table_cache.cc
@@ -0,0 +1,1220 @@
+/* Copyright (c) 2000, 2012, Oracle and/or its affiliates.
+ Copyright (c) 2010, 2011 Monty Program Ab
+ Copyright (C) 2013 Sergey Vojtovich and MariaDB Foundation
+
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+/**
+ @file
+ Table definition cache and table cache implementation.
+
+ Table definition cache actions:
+ - add new TABLE_SHARE object to cache (tdc_acquire_share())
+ - acquire TABLE_SHARE object from cache (tdc_acquire_share())
+ - release TABLE_SHARE object to cache (tdc_release_share())
+ - purge unused TABLE_SHARE objects from cache (tdc_purge())
+ - remove TABLE_SHARE object from cache (tdc_remove_table())
+ - get number of TABLE_SHARE objects in cache (tdc_records())
+
+ Table cache actions:
+ - add new TABLE object to cache (tc_add_table())
+ - acquire TABLE object from cache (tc_acquire_table())
+ - release TABLE object to cache (tc_release_table())
+ - purge unused TABLE objects from cache (tc_purge())
+ - purge unused TABLE objects of a table from cache (tdc_remove_table())
+ - get number of TABLE objects in cache (tc_records())
+
+ Dependencies:
+ - intern_close_table(): frees TABLE object
+ - kill_delayed_threads_for_table()
+ - close_cached_tables(): flush tables on shutdown
+ - alloc_table_share()
+ - free_table_share()
+
+ Table cache invariants:
+ - TABLE_SHARE::free_tables shall not contain objects with TABLE::in_use != 0
+ - TABLE_SHARE::free_tables shall not receive new objects if
+ TABLE_SHARE::tdc.flushed is true
+*/
+
+#include "my_global.h"
+#include "hash.h"
+#include "table.h"
+#include "sql_base.h"
+
+/** Configuration. */
+ulong tdc_size; /**< Table definition cache threshold for LRU eviction. */
+ulong tc_size; /**< Table cache threshold for LRU eviction. */
+
+/** Data collections. */
+static HASH tdc_hash; /**< Collection of TABLE_SHARE objects. */
+/** Collection of unused TABLE_SHARE objects. */
+static TABLE_SHARE *oldest_unused_share, end_of_unused_share;
+
+static int64 tdc_version; /* Increments on each reload */
+static int64 last_table_id;
+static bool tdc_inited;
+
+static int32 tc_count; /**< Number of TABLE objects in table cache. */
+
+
+/**
+ Protects unused shares list.
+
+ TABLE_SHARE::tdc.prev
+ TABLE_SHARE::tdc.next
+ oldest_unused_share
+ end_of_unused_share
+*/
+
+static mysql_mutex_t LOCK_unused_shares;
+static mysql_rwlock_t LOCK_tdc; /**< Protects tdc_hash. */
+my_atomic_rwlock_t LOCK_tdc_atomics; /**< Protects tdc_version. */
+
+#ifdef HAVE_PSI_INTERFACE
+static PSI_mutex_key key_LOCK_unused_shares, key_TABLE_SHARE_LOCK_table_share;
+static PSI_mutex_info all_tc_mutexes[]=
+{
+ { &key_LOCK_unused_shares, "LOCK_unused_shares", PSI_FLAG_GLOBAL },
+ { &key_TABLE_SHARE_LOCK_table_share, "TABLE_SHARE::tdc.LOCK_table_share", 0 }
+};
+
+static PSI_rwlock_key key_rwlock_LOCK_tdc;
+static PSI_rwlock_info all_tc_rwlocks[]=
+{
+ { &key_rwlock_LOCK_tdc, "LOCK_tdc", PSI_FLAG_GLOBAL }
+};
+
+
+static PSI_cond_key key_TABLE_SHARE_COND_release;
+static PSI_cond_info all_tc_conds[]=
+{
+ { &key_TABLE_SHARE_COND_release, "TABLE_SHARE::tdc.COND_release", 0 }
+};
+
+
+static void init_tc_psi_keys(void)
+{
+ const char *category= "sql";
+ int count;
+
+ count= array_elements(all_tc_mutexes);
+ mysql_mutex_register(category, all_tc_mutexes, count);
+
+ count= array_elements(all_tc_rwlocks);
+ mysql_rwlock_register(category, all_tc_rwlocks, count);
+
+ count= array_elements(all_tc_conds);
+ mysql_cond_register(category, all_tc_conds, count);
+}
+#endif
+
+
+/*
+ Auxiliary routines for manipulating with per-share all/unused lists
+ and tc_count counter.
+ Responsible for preserving invariants between those lists, counter
+ and TABLE::in_use member.
+ In fact those routines implement sort of implicit table cache as
+ part of table definition cache.
+*/
+
+
+/**
+ Get number of TABLE objects (used and unused) in table cache.
+*/
+
+uint tc_records(void)
+{
+ uint count;
+ my_atomic_rwlock_rdlock(&LOCK_tdc_atomics);
+ count= my_atomic_load32(&tc_count);
+ my_atomic_rwlock_rdunlock(&LOCK_tdc_atomics);
+ return count;
+}
+
+
+/**
+ Remove TABLE object from table cache.
+
+ - decrement tc_count
+ - remove object from TABLE_SHARE::tdc.all_tables
+*/
+
+static void tc_remove_table(TABLE *table)
+{
+ my_atomic_rwlock_wrlock(&LOCK_tdc_atomics);
+ my_atomic_add32(&tc_count, -1);
+ my_atomic_rwlock_wrunlock(&LOCK_tdc_atomics);
+ table->s->tdc.all_tables.remove(table);
+}
+
+
+/**
+ Wait for MDL deadlock detector to complete traversing tdc.all_tables.
+
+ Must be called before updating TABLE_SHARE::tdc.all_tables.
+*/
+
+static void tc_wait_for_mdl_deadlock_detector(TABLE_SHARE *share)
+{
+ while (share->tdc.all_tables_refs)
+ mysql_cond_wait(&share->tdc.COND_release, &share->tdc.LOCK_table_share);
+}
+
+
+/**
+ Get last element of tdc.free_tables.
+*/
+
+static TABLE *tc_free_tables_back(TABLE_SHARE *share)
+{
+ TABLE_SHARE::TABLE_list::Iterator it(share->tdc.free_tables);
+ TABLE *entry, *last= 0;
+ while ((entry= it++))
+ last= entry;
+ return last;
+}
+
+
+/**
+ Free all unused TABLE objects.
+
+ While locked:
+ - remove unused objects from TABLE_SHARE::tdc.free_tables and
+ TABLE_SHARE::tdc.all_tables
+ - decrement tc_count
+
+ While unlocked:
+ - free resources related to unused objects
+
+ @note This is called by 'handle_manager' when one wants to
+ periodicly flush all not used tables.
+*/
+
+void tc_purge(bool mark_flushed)
+{
+ TABLE_SHARE *share;
+ TABLE *table;
+ TDC_iterator tdc_it;
+ TABLE_SHARE::TABLE_list purge_tables;
+
+ tdc_it.init();
+ while ((share= tdc_it.next()))
+ {
+ mysql_mutex_lock(&share->tdc.LOCK_table_share);
+ tc_wait_for_mdl_deadlock_detector(share);
+
+ if (mark_flushed)
+ share->tdc.flushed= true;
+ while ((table= share->tdc.free_tables.pop_front()))
+ {
+ tc_remove_table(table);
+ purge_tables.push_front(table);
+ }
+ mysql_mutex_unlock(&share->tdc.LOCK_table_share);
+ }
+ tdc_it.deinit();
+
+ while ((table= purge_tables.pop_front()))
+ intern_close_table(table);
+}
+
+
+/**
+ Add new TABLE object to table cache.
+
+ @pre TABLE object is used by caller.
+
+ Added object cannot be evicted or acquired.
+
+ While locked:
+ - add object to TABLE_SHARE::tdc.all_tables
+ - increment tc_count
+ - evict LRU object from table cache if we reached threshold
+
+ While unlocked:
+ - free evicted object
+*/
+
+void tc_add_table(THD *thd, TABLE *table)
+{
+ bool need_purge;
+ DBUG_ASSERT(table->in_use == thd);
+ mysql_mutex_lock(&table->s->tdc.LOCK_table_share);
+ tc_wait_for_mdl_deadlock_detector(table->s);
+ table->s->tdc.all_tables.push_front(table);
+ mysql_mutex_unlock(&table->s->tdc.LOCK_table_share);
+
+ /* If we have too many TABLE instances around, try to get rid of them */
+ my_atomic_rwlock_wrlock(&LOCK_tdc_atomics);
+ need_purge= my_atomic_add32(&tc_count, 1) >= (int32) tc_size;
+ my_atomic_rwlock_wrunlock(&LOCK_tdc_atomics);
+
+ if (need_purge)
+ {
+ TABLE_SHARE *purge_share= 0;
+ TABLE_SHARE *share;
+ TABLE *entry;
+ ulonglong UNINIT_VAR(purge_time);
+ TDC_iterator tdc_it;
+
+ tdc_it.init();
+ while ((share= tdc_it.next()))
+ {
+ mysql_mutex_lock(&share->tdc.LOCK_table_share);
+ if ((entry= tc_free_tables_back(share)) &&
+ (!purge_share || entry->tc_time < purge_time))
+ {
+ purge_share= share;
+ purge_time= entry->tc_time;
+ }
+ mysql_mutex_unlock(&share->tdc.LOCK_table_share);
+ }
+
+ if (purge_share)
+ {
+ mysql_mutex_lock(&purge_share->tdc.LOCK_table_share);
+ tc_wait_for_mdl_deadlock_detector(purge_share);
+ tdc_it.deinit();
+ /*
+ It may happen that oldest table was acquired meanwhile. In this case
+ just go ahead, number of objects in table cache will normalize
+ eventually.
+ */
+ if ((entry= tc_free_tables_back(purge_share)) &&
+ entry->tc_time == purge_time)
+ {
+ entry->s->tdc.free_tables.remove(entry);
+ tc_remove_table(entry);
+ mysql_mutex_unlock(&purge_share->tdc.LOCK_table_share);
+ intern_close_table(entry);
+ }
+ else
+ mysql_mutex_unlock(&purge_share->tdc.LOCK_table_share);
+ }
+ else
+ tdc_it.deinit();
+ }
+}
+
+
+/**
+ Acquire TABLE object from table cache.
+
+ @pre share must be protected against removal.
+
+ Acquired object cannot be evicted or acquired again.
+
+ While locked:
+ - pop object from TABLE_SHARE::tdc.free_tables
+
+ While unlocked:
+ - mark object used by thd
+
+ @return TABLE object, or NULL if no unused objects.
+*/
+
+static TABLE *tc_acquire_table(THD *thd, TABLE_SHARE *share)
+{
+ TABLE *table;
+
+ mysql_mutex_lock(&share->tdc.LOCK_table_share);
+ table= share->tdc.free_tables.pop_front();
+ if (table)
+ {
+ DBUG_ASSERT(!table->in_use);
+ table->in_use= thd;
+ /* The ex-unused table must be fully functional. */
+ DBUG_ASSERT(table->db_stat && table->file);
+ /* The children must be detached from the table. */
+ DBUG_ASSERT(!table->file->extra(HA_EXTRA_IS_ATTACHED_CHILDREN));
+ }
+ mysql_mutex_unlock(&share->tdc.LOCK_table_share);
+ return table;
+}
+
+
+/**
+ Release TABLE object to table cache.
+
+ @pre object is used by caller.
+
+ Released object may be evicted or acquired again.
+
+ While locked:
+ - if object is marked for purge, decrement tc_count
+ - add object to TABLE_SHARE::tdc.free_tables
+ - evict LRU object from table cache if we reached threshold
+
+ While unlocked:
+ - mark object not in use by any thread
+ - free evicted/purged object
+
+ @note Another thread may mark share for purge any moment (even
+ after version check). It means to-be-purged object may go to
+ unused lists. This other thread is expected to call tc_purge(),
+ which is synchronized with us on TABLE_SHARE::tdc.LOCK_table_share.
+
+ @return
+ @retval true object purged
+ @retval false object released
+*/
+
+bool tc_release_table(TABLE *table)
+{
+ DBUG_ASSERT(table->in_use);
+ DBUG_ASSERT(table->file);
+
+ if (table->needs_reopen() || tc_records() > tc_size)
+ {
+ mysql_mutex_lock(&table->s->tdc.LOCK_table_share);
+ goto purge;
+ }
+
+ table->tc_time= my_interval_timer();
+
+ mysql_mutex_lock(&table->s->tdc.LOCK_table_share);
+ if (table->s->tdc.flushed)
+ goto purge;
+ /*
+ in_use doesn't really need mutex protection, but must be reset after
+ checking tdc.flushed and before this table appears in free_tables.
+ Resetting in_use is needed only for print_cached_tables() and
+ list_open_tables().
+ */
+ table->in_use= 0;
+ /* Add table to the list of unused TABLE objects for this share. */
+ table->s->tdc.free_tables.push_front(table);
+ mysql_mutex_unlock(&table->s->tdc.LOCK_table_share);
+ return false;
+
+purge:
+ tc_wait_for_mdl_deadlock_detector(table->s);
+ tc_remove_table(table);
+ mysql_mutex_unlock(&table->s->tdc.LOCK_table_share);
+ table->in_use= 0;
+ intern_close_table(table);
+ return true;
+}
+
+
+extern "C" uchar *tdc_key(const uchar *record, size_t *length,
+ my_bool not_used __attribute__((unused)))
+{
+ TABLE_SHARE *entry= (TABLE_SHARE*) record;
+ *length= entry->table_cache_key.length;
+ return (uchar*) entry->table_cache_key.str;
+}
+
+
+/**
+ Delete share from hash and free share object.
+
+ @return
+ @retval 0 Success
+ @retval 1 Share is referenced
+*/
+
+static int tdc_delete_share_from_hash(TABLE_SHARE *share)
+{
+ DBUG_ENTER("tdc_delete_share_from_hash");
+ mysql_rwlock_wrlock(&LOCK_tdc);
+ mysql_mutex_lock(&share->tdc.LOCK_table_share);
+ if (--share->tdc.ref_count)
+ {
+ mysql_cond_broadcast(&share->tdc.COND_release);
+ mysql_mutex_unlock(&share->tdc.LOCK_table_share);
+ mysql_rwlock_unlock(&LOCK_tdc);
+ DBUG_RETURN(1);
+ }
+ my_hash_delete(&tdc_hash, (uchar*) share);
+ /* Notify PFS early, while still locked. */
+ PSI_CALL_release_table_share(share->m_psi);
+ share->m_psi= 0;
+ mysql_rwlock_unlock(&LOCK_tdc);
+
+ if (share->tdc.m_flush_tickets.is_empty())
+ {
+ /* No threads are waiting for this share to be flushed, destroy it. */
+ mysql_mutex_unlock(&share->tdc.LOCK_table_share);
+ free_table_share(share);
+ }
+ else
+ {
+ Wait_for_flush_list::Iterator it(share->tdc.m_flush_tickets);
+ Wait_for_flush *ticket;
+ while ((ticket= it++))
+ (void) ticket->get_ctx()->m_wait.set_status(MDL_wait::GRANTED);
+ /*
+ If there are threads waiting for this share to be flushed,
+ the last one to receive the notification will destroy the
+ share. At this point the share is removed from the table
+ definition cache, so is OK to proceed here without waiting
+ for this thread to do the work.
+ */
+ mysql_mutex_unlock(&share->tdc.LOCK_table_share);
+ }
+ DBUG_RETURN(0);
+}
+
+
+/**
+ Initialize table definition cache.
+
+ @retval 0 Success
+ @retval !0 Error
+*/
+
+int tdc_init(void)
+{
+ DBUG_ENTER("tdc_init");
+#ifdef HAVE_PSI_INTERFACE
+ init_tc_psi_keys();
+#endif
+ tdc_inited= true;
+ mysql_mutex_init(key_LOCK_unused_shares, &LOCK_unused_shares,
+ MY_MUTEX_INIT_FAST);
+ mysql_rwlock_init(key_rwlock_LOCK_tdc, &LOCK_tdc);
+ my_atomic_rwlock_init(&LOCK_tdc_atomics);
+ oldest_unused_share= &end_of_unused_share;
+ end_of_unused_share.tdc.prev= &oldest_unused_share;
+ tdc_version= 1L; /* Increments on each reload */
+ DBUG_RETURN(my_hash_init(&tdc_hash, &my_charset_bin, tdc_size, 0, 0, tdc_key,
+ 0, 0));
+}
+
+
+/**
+ Notify table definition cache that process of shutting down server
+ has started so it has to keep number of TABLE and TABLE_SHARE objects
+ minimal in order to reduce number of references to pluggable engines.
+*/
+
+void tdc_start_shutdown(void)
+{
+ DBUG_ENTER("table_def_start_shutdown");
+ if (tdc_inited)
+ {
+ /*
+ Ensure that TABLE and TABLE_SHARE objects which are created for
+ tables that are open during process of plugins' shutdown are
+ immediately released. This keeps number of references to engine
+ plugins minimal and allows shutdown to proceed smoothly.
+ */
+ tdc_size= 0;
+ tc_size= 0;
+ /* Free all cached but unused TABLEs and TABLE_SHAREs. */
+ close_cached_tables(NULL, NULL, FALSE, LONG_TIMEOUT);
+ }
+ DBUG_VOID_RETURN;
+}
+
+
+/**
+ Deinitialize table definition cache.
+*/
+
+void tdc_deinit(void)
+{
+ DBUG_ENTER("tdc_deinit");
+ if (tdc_inited)
+ {
+ tdc_inited= false;
+ my_hash_free(&tdc_hash);
+ my_atomic_rwlock_destroy(&LOCK_tdc_atomics);
+ mysql_rwlock_destroy(&LOCK_tdc);
+ mysql_mutex_destroy(&LOCK_unused_shares);
+ }
+ DBUG_VOID_RETURN;
+}
+
+
+/**
+ Get number of cached table definitions.
+
+ @return Number of cached table definitions
+*/
+
+ulong tdc_records(void)
+{
+ ulong records;
+ DBUG_ENTER("tdc_records");
+ mysql_rwlock_rdlock(&LOCK_tdc);
+ records= tdc_hash.records;
+ mysql_rwlock_unlock(&LOCK_tdc);
+ DBUG_RETURN(records);
+}
+
+
+void tdc_purge(bool all)
+{
+ DBUG_ENTER("tdc_purge");
+ while (all || tdc_records() > tdc_size)
+ {
+ TABLE_SHARE *share;
+
+ mysql_mutex_lock(&LOCK_unused_shares);
+ if (!oldest_unused_share->tdc.next)
+ {
+ mysql_mutex_unlock(&LOCK_unused_shares);
+ break;
+ }
+
+ share= oldest_unused_share;
+ *share->tdc.prev= share->tdc.next;
+ share->tdc.next->tdc.prev= share->tdc.prev;
+ /* Concurrent thread may start using share again, reset prev and next. */
+ share->tdc.prev= 0;
+ share->tdc.next= 0;
+ mysql_mutex_lock(&share->tdc.LOCK_table_share);
+ share->tdc.ref_count++;
+ mysql_mutex_unlock(&share->tdc.LOCK_table_share);
+ mysql_mutex_unlock(&LOCK_unused_shares);
+
+ tdc_delete_share_from_hash(share);
+ }
+ DBUG_VOID_RETURN;
+}
+
+
+/**
+ Prepeare table share for use with table definition cache.
+*/
+
+void tdc_init_share(TABLE_SHARE *share)
+{
+ DBUG_ENTER("tdc_init_share");
+ mysql_mutex_init(key_TABLE_SHARE_LOCK_table_share,
+ &share->tdc.LOCK_table_share, MY_MUTEX_INIT_FAST);
+ mysql_cond_init(key_TABLE_SHARE_COND_release, &share->tdc.COND_release, 0);
+ share->tdc.m_flush_tickets.empty();
+ share->tdc.all_tables.empty();
+ share->tdc.free_tables.empty();
+ tdc_assign_new_table_id(share);
+ share->tdc.version= tdc_refresh_version();
+ share->tdc.flushed= false;
+ share->tdc.all_tables_refs= 0;
+ DBUG_VOID_RETURN;
+}
+
+
+/**
+ Release table definition cache specific resources of table share.
+*/
+
+void tdc_deinit_share(TABLE_SHARE *share)
+{
+ DBUG_ENTER("tdc_deinit_share");
+ DBUG_ASSERT(share->tdc.ref_count == 0);
+ DBUG_ASSERT(share->tdc.m_flush_tickets.is_empty());
+ DBUG_ASSERT(share->tdc.all_tables.is_empty());
+ DBUG_ASSERT(share->tdc.free_tables.is_empty());
+ DBUG_ASSERT(share->tdc.all_tables_refs == 0);
+ mysql_cond_destroy(&share->tdc.COND_release);
+ mysql_mutex_destroy(&share->tdc.LOCK_table_share);
+ DBUG_VOID_RETURN;
+}
+
+
+/**
+ Lock table share.
+
+ Find table share with given db.table_name in table definition cache. Return
+ locked table share if found.
+
+ Locked table share means:
+ - table share is protected against removal from table definition cache
+ - no other thread can acquire/release table share
+
+ Caller is expected to unlock table share with tdc_unlock_share().
+
+ @retval 0 Share not found
+ @retval !0 Pointer to locked table share
+*/
+
+TABLE_SHARE *tdc_lock_share(const char *db, const char *table_name)
+{
+ char key[MAX_DBKEY_LENGTH];
+ uint key_length;
+
+ DBUG_ENTER("tdc_lock_share");
+ key_length= tdc_create_key(key, db, table_name);
+
+ mysql_rwlock_rdlock(&LOCK_tdc);
+ TABLE_SHARE* share= (TABLE_SHARE*) my_hash_search(&tdc_hash,
+ (uchar*) key, key_length);
+ if (share && !share->error)
+ mysql_mutex_lock(&share->tdc.LOCK_table_share);
+ else
+ share= 0;
+ mysql_rwlock_unlock(&LOCK_tdc);
+ DBUG_RETURN(share);
+}
+
+
+/**
+ Unlock share locked by tdc_lock_share().
+*/
+
+void tdc_unlock_share(TABLE_SHARE *share)
+{
+ DBUG_ENTER("tdc_unlock_share");
+ mysql_mutex_unlock(&share->tdc.LOCK_table_share);
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+ Get TABLE_SHARE for a table.
+
+ tdc_acquire_share()
+ thd Thread handle
+ table_list Table that should be opened
+ key Table cache key
+ key_length Length of key
+ flags operation: what to open table or view
+
+ IMPLEMENTATION
+ Get a table definition from the table definition cache.
+ If it doesn't exist, create a new from the table definition file.
+
+ RETURN
+ 0 Error
+ # Share for table
+*/
+
+TABLE_SHARE *tdc_acquire_share(THD *thd, const char *db, const char *table_name,
+ const char *key, uint key_length,
+ my_hash_value_type hash_value, uint flags,
+ TABLE **out_table)
+{
+ TABLE_SHARE *share;
+ bool was_unused;
+ DBUG_ENTER("tdc_acquire_share");
+
+ mysql_rwlock_rdlock(&LOCK_tdc);
+ share= (TABLE_SHARE*) my_hash_search_using_hash_value(&tdc_hash, hash_value,
+ (uchar*) key,
+ key_length);
+ if (!share)
+ {
+ TABLE_SHARE *new_share;
+ mysql_rwlock_unlock(&LOCK_tdc);
+
+ if (!(new_share= alloc_table_share(db, table_name, key, key_length)))
+ DBUG_RETURN(0);
+ new_share->error= OPEN_FRM_OPEN_ERROR;
+
+ mysql_rwlock_wrlock(&LOCK_tdc);
+ share= (TABLE_SHARE*) my_hash_search_using_hash_value(&tdc_hash, hash_value,
+ (uchar*) key,
+ key_length);
+ if (!share)
+ {
+ bool need_purge;
+
+ share= new_share;
+ mysql_mutex_lock(&share->tdc.LOCK_table_share);
+ if (my_hash_insert(&tdc_hash, (uchar*) share))
+ {
+ mysql_mutex_unlock(&share->tdc.LOCK_table_share);
+ mysql_rwlock_unlock(&LOCK_tdc);
+ free_table_share(share);
+ DBUG_RETURN(0);
+ }
+ need_purge= tdc_hash.records > tdc_size;
+ mysql_rwlock_unlock(&LOCK_tdc);
+
+ /* note that tdc_acquire_share() *always* uses discovery */
+ open_table_def(thd, share, flags | GTS_USE_DISCOVERY);
+ share->tdc.ref_count++;
+ mysql_mutex_unlock(&share->tdc.LOCK_table_share);
+
+ if (share->error)
+ {
+ tdc_delete_share_from_hash(share);
+ DBUG_RETURN(0);
+ }
+ else if (need_purge)
+ tdc_purge(false);
+ if (out_table)
+ *out_table= 0;
+ share->m_psi= PSI_CALL_get_table_share(false, share);
+ goto end;
+ }
+ free_table_share(new_share);
+ }
+
+ /* cannot force discovery of a cached share */
+ DBUG_ASSERT(!(flags & GTS_FORCE_DISCOVERY));
+
+ if (out_table && (flags & GTS_TABLE))
+ {
+ if ((*out_table= tc_acquire_table(thd, share)))
+ {
+ mysql_rwlock_unlock(&LOCK_tdc);
+ DBUG_ASSERT(!(flags & GTS_NOLOCK));
+ DBUG_ASSERT(!share->error);
+ DBUG_ASSERT(!share->is_view);
+ DBUG_RETURN(share);
+ }
+ }
+
+ mysql_mutex_lock(&share->tdc.LOCK_table_share);
+ mysql_rwlock_unlock(&LOCK_tdc);
+
+ /*
+ We found an existing table definition. Return it if we didn't get
+ an error when reading the table definition from file.
+ */
+ if (share->error)
+ {
+ open_table_error(share, share->error, share->open_errno);
+ goto err;
+ }
+
+ if (share->is_view && !(flags & GTS_VIEW))
+ {
+ open_table_error(share, OPEN_FRM_NOT_A_TABLE, ENOENT);
+ goto err;
+ }
+ if (!share->is_view && !(flags & GTS_TABLE))
+ {
+ open_table_error(share, OPEN_FRM_NOT_A_VIEW, ENOENT);
+ goto err;
+ }
+
+ was_unused= !share->tdc.ref_count;
+ share->tdc.ref_count++;
+ mysql_mutex_unlock(&share->tdc.LOCK_table_share);
+ if (was_unused)
+ {
+ mysql_mutex_lock(&LOCK_unused_shares);
+ if (share->tdc.prev)
+ {
+ /*
+ Share was not used before and it was in the old_unused_share list
+ Unlink share from this list
+ */
+ DBUG_PRINT("info", ("Unlinking from not used list"));
+ *share->tdc.prev= share->tdc.next;
+ share->tdc.next->tdc.prev= share->tdc.prev;
+ share->tdc.next= 0;
+ share->tdc.prev= 0;
+ }
+ mysql_mutex_unlock(&LOCK_unused_shares);
+ }
+
+end:
+ DBUG_PRINT("exit", ("share: 0x%lx ref_count: %u",
+ (ulong) share, share->tdc.ref_count));
+ if (flags & GTS_NOLOCK)
+ {
+ tdc_release_share(share);
+ /*
+ if GTS_NOLOCK is requested, the returned share pointer cannot be used,
+ the share it points to may go away any moment.
+ But perhaps the caller is only interested to know whether a share or
+ table existed?
+ Let's return an invalid pointer here to catch dereferencing attempts.
+ */
+ share= (TABLE_SHARE*) 1;
+ }
+ DBUG_RETURN(share);
+
+err:
+ mysql_mutex_unlock(&share->tdc.LOCK_table_share);
+ DBUG_RETURN(0);
+}
+
+
+/**
+ Release table share acquired by tdc_acquire_share().
+*/
+
+void tdc_release_share(TABLE_SHARE *share)
+{
+ DBUG_ENTER("tdc_release_share");
+
+ mysql_mutex_lock(&share->tdc.LOCK_table_share);
+ DBUG_PRINT("enter",
+ ("share: 0x%lx table: %s.%s ref_count: %u version: %lu",
+ (ulong) share, share->db.str, share->table_name.str,
+ share->tdc.ref_count, share->tdc.version));
+ DBUG_ASSERT(share->tdc.ref_count);
+
+ if (share->tdc.ref_count > 1)
+ {
+ share->tdc.ref_count--;
+ if (!share->is_view)
+ mysql_cond_broadcast(&share->tdc.COND_release);
+ mysql_mutex_unlock(&share->tdc.LOCK_table_share);
+ DBUG_VOID_RETURN;
+ }
+ mysql_mutex_unlock(&share->tdc.LOCK_table_share);
+
+ mysql_mutex_lock(&LOCK_unused_shares);
+ mysql_mutex_lock(&share->tdc.LOCK_table_share);
+ if (share->tdc.flushed)
+ {
+ mysql_mutex_unlock(&share->tdc.LOCK_table_share);
+ mysql_mutex_unlock(&LOCK_unused_shares);
+ tdc_delete_share_from_hash(share);
+ DBUG_VOID_RETURN;
+ }
+ if (--share->tdc.ref_count)
+ {
+ mysql_mutex_unlock(&share->tdc.LOCK_table_share);
+ mysql_mutex_unlock(&LOCK_unused_shares);
+ DBUG_VOID_RETURN;
+ }
+ /* Link share last in used_table_share list */
+ DBUG_PRINT("info", ("moving share to unused list"));
+ DBUG_ASSERT(share->tdc.next == 0);
+ share->tdc.prev= end_of_unused_share.tdc.prev;
+ *end_of_unused_share.tdc.prev= share;
+ end_of_unused_share.tdc.prev= &share->tdc.next;
+ share->tdc.next= &end_of_unused_share;
+ mysql_mutex_unlock(&share->tdc.LOCK_table_share);
+ mysql_mutex_unlock(&LOCK_unused_shares);
+
+ /* Delete the least used share to preserve LRU order. */
+ tdc_purge(false);
+ DBUG_VOID_RETURN;
+}
+
+
+static TABLE_SHARE *tdc_delete_share(const char *db, const char *table_name)
+{
+ TABLE_SHARE *share;
+ DBUG_ENTER("tdc_delete_share");
+
+ while ((share= tdc_lock_share(db, table_name)))
+ {
+ share->tdc.ref_count++;
+ if (share->tdc.ref_count > 1)
+ {
+ tdc_unlock_share(share);
+ DBUG_RETURN(share);
+ }
+ tdc_unlock_share(share);
+
+ mysql_mutex_lock(&LOCK_unused_shares);
+ if (share->tdc.prev)
+ {
+ *share->tdc.prev= share->tdc.next;
+ share->tdc.next->tdc.prev= share->tdc.prev;
+ /* Concurrent thread may start using share again, reset prev and next. */
+ share->tdc.prev= 0;
+ share->tdc.next= 0;
+ }
+ mysql_mutex_unlock(&LOCK_unused_shares);
+
+ if (!tdc_delete_share_from_hash(share))
+ break;
+ }
+ DBUG_RETURN(0);
+}
+
+
+/**
+ Remove all or some (depending on parameter) instances of TABLE and
+ TABLE_SHARE from the table definition cache.
+
+ @param thd Thread context
+ @param remove_type Type of removal:
+ TDC_RT_REMOVE_ALL - remove all TABLE instances and
+ TABLE_SHARE instance. There
+ should be no used TABLE objects
+ and caller should have exclusive
+ metadata lock on the table.
+ TDC_RT_REMOVE_NOT_OWN - remove all TABLE instances
+ except those that belong to
+ this thread. There should be
+ no TABLE objects used by other
+ threads and caller should have
+ exclusive metadata lock on the
+ table.
+ TDC_RT_REMOVE_UNUSED - remove all unused TABLE
+ instances (if there are no
+ used instances will also
+ remove TABLE_SHARE).
+ TDC_RT_REMOVE_NOT_OWN_KEEP_SHARE -
+ remove all TABLE instances
+ except those that belong to
+ this thread, but don't mark
+ TABLE_SHARE as old. There
+ should be no TABLE objects
+ used by other threads and
+ caller should have exclusive
+ metadata lock on the table.
+ @param db Name of database
+ @param table_name Name of table
+ @param kill_delayed_threads If TRUE, kill INSERT DELAYED threads
+
+ @note It assumes that table instances are already not used by any
+ (other) thread (this should be achieved by using meta-data locks).
+*/
+
+bool tdc_remove_table(THD *thd, enum_tdc_remove_table_type remove_type,
+ const char *db, const char *table_name,
+ bool kill_delayed_threads)
+{
+ TABLE *table;
+ TABLE_SHARE *share;
+ bool found= false;
+ DBUG_ENTER("tdc_remove_table");
+ DBUG_PRINT("enter",("name: %s remove_type: %d", table_name, remove_type));
+
+ DBUG_ASSERT(remove_type == TDC_RT_REMOVE_UNUSED ||
+ thd->mdl_context.is_lock_owner(MDL_key::TABLE, db, table_name,
+ MDL_EXCLUSIVE));
+
+ if ((share= tdc_delete_share(db, table_name)))
+ {
+ I_P_List <TABLE, TABLE_share> purge_tables;
+ uint my_refs= 1;
+
+ mysql_mutex_lock(&share->tdc.LOCK_table_share);
+ tc_wait_for_mdl_deadlock_detector(share);
+ /*
+ Mark share flushed in order to ensure that it gets
+ automatically deleted once it is no longer referenced.
+
+ Note that code in TABLE_SHARE::wait_for_old_version() assumes that
+ marking share flushed is followed by purge of unused table
+ shares.
+ */
+ if (remove_type != TDC_RT_REMOVE_NOT_OWN_KEEP_SHARE)
+ share->tdc.flushed= true;
+
+ while ((table= share->tdc.free_tables.pop_front()))
+ {
+ tc_remove_table(table);
+ purge_tables.push_front(table);
+ }
+ if (kill_delayed_threads)
+ kill_delayed_threads_for_table(share);
+
+ if (remove_type == TDC_RT_REMOVE_NOT_OWN ||
+ remove_type == TDC_RT_REMOVE_NOT_OWN_KEEP_SHARE)
+ {
+ TABLE_SHARE::All_share_tables_list::Iterator it(share->tdc.all_tables);
+ while ((table= it++))
+ {
+ my_refs++;
+ DBUG_ASSERT(table->in_use == thd);
+ }
+ }
+ DBUG_ASSERT(share->tdc.all_tables.is_empty() || remove_type != TDC_RT_REMOVE_ALL);
+ mysql_mutex_unlock(&share->tdc.LOCK_table_share);
+
+ while ((table= purge_tables.pop_front()))
+ intern_close_table(table);
+
+ if (remove_type != TDC_RT_REMOVE_UNUSED)
+ {
+ /*
+ Even though current thread holds exclusive metadata lock on this share
+ (asserted above), concurrent FLUSH TABLES threads may be in process of
+ closing unused table instances belonging to this share. E.g.:
+ thr1 (FLUSH TABLES): table= share->tdc.free_tables.pop_front();
+ thr1 (FLUSH TABLES): share->tdc.all_tables.remove(table);
+ thr2 (ALTER TABLE): tdc_remove_table();
+ thr1 (FLUSH TABLES): intern_close_table(table);
+
+ Current remove type assumes that all table instances (except for those
+ that are owned by current thread) must be closed before
+ thd_remove_table() returns. Wait for such tables now.
+
+ intern_close_table() decrements ref_count and signals COND_release. When
+ ref_count drops down to number of references owned by current thread
+ waiting is completed.
+
+ Unfortunately TABLE_SHARE::wait_for_old_version() cannot be used here
+ because it waits for all table instances, whereas we have to wait only
+ for those that are not owned by current thread.
+ */
+ mysql_mutex_lock(&share->tdc.LOCK_table_share);
+ while (share->tdc.ref_count > my_refs)
+ mysql_cond_wait(&share->tdc.COND_release, &share->tdc.LOCK_table_share);
+ mysql_mutex_unlock(&share->tdc.LOCK_table_share);
+ }
+
+ tdc_release_share(share);
+
+ found= true;
+ }
+ DBUG_ASSERT(found || remove_type != TDC_RT_REMOVE_NOT_OWN_KEEP_SHARE);
+ DBUG_RETURN(found);
+}
+
+
+/**
+ Check if table's share is being removed from the table definition
+ cache and, if yes, wait until the flush is complete.
+
+ @param thd Thread context.
+ @param table_list Table which share should be checked.
+ @param timeout Timeout for waiting.
+ @param deadlock_weight Weight of this wait for deadlock detector.
+
+ @retval 0 Success. Share is up to date or has been flushed.
+ @retval 1 Error (OOM, was killed, the wait resulted
+ in a deadlock or timeout). Reported.
+*/
+
+int tdc_wait_for_old_version(THD *thd, const char *db, const char *table_name,
+ ulong wait_timeout, uint deadlock_weight,
+ ulong refresh_version)
+{
+ TABLE_SHARE *share;
+ int res= FALSE;
+
+ if ((share= tdc_lock_share(db, table_name)))
+ {
+ if (share->tdc.flushed && refresh_version > share->tdc.version)
+ {
+ struct timespec abstime;
+ set_timespec(abstime, wait_timeout);
+ res= share->wait_for_old_version(thd, &abstime, deadlock_weight);
+ }
+ else
+ tdc_unlock_share(share);
+ }
+ return res;
+}
+
+
+ulong tdc_refresh_version(void)
+{
+ my_atomic_rwlock_rdlock(&LOCK_tdc_atomics);
+ ulong v= my_atomic_load64(&tdc_version);
+ my_atomic_rwlock_rdunlock(&LOCK_tdc_atomics);
+ return v;
+}
+
+
+ulong tdc_increment_refresh_version(void)
+{
+ my_atomic_rwlock_wrlock(&LOCK_tdc_atomics);
+ ulong v= my_atomic_add64(&tdc_version, 1);
+ my_atomic_rwlock_wrunlock(&LOCK_tdc_atomics);
+ DBUG_PRINT("tcache", ("incremented global refresh_version to: %lu", v));
+ return v + 1;
+}
+
+
+/**
+ Initialize table definition cache iterator.
+*/
+
+void TDC_iterator::init(void)
+{
+ DBUG_ENTER("TDC_iterator::init");
+ idx= 0;
+ mysql_rwlock_rdlock(&LOCK_tdc);
+ DBUG_VOID_RETURN;
+}
+
+
+/**
+ Deinitialize table definition cache iterator.
+*/
+
+void TDC_iterator::deinit(void)
+{
+ DBUG_ENTER("TDC_iterator::deinit");
+ mysql_rwlock_unlock(&LOCK_tdc);
+ DBUG_VOID_RETURN;
+}
+
+
+/**
+ Get next TABLE_SHARE object from table definition cache.
+
+ Object is protected against removal from table definition cache.
+
+ @note Returned TABLE_SHARE is not guaranteed to be fully initialized:
+ tdc_acquire_share() added new share, but didn't open it yet. If caller
+ needs fully initializer share, it must lock table share mutex.
+*/
+
+TABLE_SHARE *TDC_iterator::next(void)
+{
+ TABLE_SHARE *share= 0;
+ DBUG_ENTER("TDC_iterator::next");
+ if (idx < tdc_hash.records)
+ {
+ share= (TABLE_SHARE*) my_hash_element(&tdc_hash, idx);
+ idx++;
+ }
+ DBUG_RETURN(share);
+}
+
+
+/*
+ Function to assign a new table map id to a table share.
+
+ PARAMETERS
+
+ share - Pointer to table share structure
+
+ DESCRIPTION
+
+ We are intentionally not checking that share->mutex is locked
+ since this function should only be called when opening a table
+ share and before it is entered into the table definition cache
+ (meaning that it cannot be fetched by another thread, even
+ accidentally).
+
+ PRE-CONDITION(S)
+
+ share is non-NULL
+ last_table_id_lock initialized (tdc_inited)
+
+ POST-CONDITION(S)
+
+ share->table_map_id is given a value that with a high certainty is
+ not used by any other table (the only case where a table id can be
+ reused is on wrap-around, which means more than 4 billion table
+ share opens have been executed while one table was open all the
+ time).
+
+ share->table_map_id is not ~0UL.
+*/
+
+void tdc_assign_new_table_id(TABLE_SHARE *share)
+{
+ ulong tid;
+ DBUG_ENTER("assign_new_table_id");
+ DBUG_ASSERT(share);
+ DBUG_ASSERT(tdc_inited);
+
+ /*
+ There is one reserved number that cannot be used. Remember to
+ change this when 6-byte global table id's are introduced.
+ */
+ do
+ {
+ my_atomic_rwlock_wrlock(&LOCK_tdc_atomics);
+ tid= my_atomic_add64(&last_table_id, 1);
+ my_atomic_rwlock_wrunlock(&LOCK_tdc_atomics);
+ } while (unlikely(tid == ~0UL));
+
+ share->table_map_id= tid;
+ DBUG_PRINT("info", ("table_id= %lu", share->table_map_id));
+ DBUG_VOID_RETURN;
+}
diff --git a/sql/table_cache.h b/sql/table_cache.h
new file mode 100644
index 00000000000..ea3822f9f68
--- /dev/null
+++ b/sql/table_cache.h
@@ -0,0 +1,137 @@
+/* Copyright (c) 2000, 2012, Oracle and/or its affiliates.
+ Copyright (c) 2010, 2011 Monty Program Ab
+ Copyright (C) 2013 Sergey Vojtovich and MariaDB Foundation
+
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+
+enum enum_tdc_remove_table_type
+{
+ TDC_RT_REMOVE_ALL,
+ TDC_RT_REMOVE_NOT_OWN,
+ TDC_RT_REMOVE_UNUSED,
+ TDC_RT_REMOVE_NOT_OWN_KEEP_SHARE
+};
+
+extern ulong tdc_size;
+extern ulong tc_size;
+
+extern int tdc_init(void);
+extern void tdc_start_shutdown(void);
+extern void tdc_deinit(void);
+extern ulong tdc_records(void);
+extern void tdc_purge(bool all);
+extern void tdc_init_share(TABLE_SHARE *share);
+extern void tdc_deinit_share(TABLE_SHARE *share);
+extern TABLE_SHARE *tdc_lock_share(const char *db, const char *table_name);
+extern void tdc_unlock_share(TABLE_SHARE *share);
+extern TABLE_SHARE *tdc_acquire_share(THD *thd, const char *db,
+ const char *table_name,
+ const char *key, uint key_length,
+ my_hash_value_type hash_value,
+ uint flags, TABLE **out_table);
+extern void tdc_release_share(TABLE_SHARE *share);
+extern bool tdc_remove_table(THD *thd, enum_tdc_remove_table_type remove_type,
+ const char *db, const char *table_name,
+ bool kill_delayed_threads);
+extern int tdc_wait_for_old_version(THD *thd, const char *db,
+ const char *table_name,
+ ulong wait_timeout, uint deadlock_weight,
+ ulong refresh_version= ULONG_MAX);
+extern ulong tdc_refresh_version(void);
+extern ulong tdc_increment_refresh_version(void);
+extern void tdc_assign_new_table_id(TABLE_SHARE *share);
+
+extern uint tc_records(void);
+extern void tc_purge(bool mark_flushed= false);
+extern void tc_add_table(THD *thd, TABLE *table);
+extern bool tc_release_table(TABLE *table);
+
+/**
+ Create a table cache key for non-temporary table.
+
+ @param key Buffer for key (must be at least NAME_LEN*2+2 bytes).
+ @param db Database name.
+ @param table_name Table name.
+
+ @return Length of key.
+*/
+
+inline uint tdc_create_key(char *key, const char *db, const char *table_name)
+{
+ /*
+ In theory caller should ensure that both db and table_name are
+ not longer than NAME_LEN bytes. In practice we play safe to avoid
+ buffer overruns.
+ */
+ return (uint) (strmake(strmake(key, db, NAME_LEN) + 1, table_name,
+ NAME_LEN) - key + 1);
+}
+
+/**
+ Convenience helper: call tdc_acquire_share() without out_table.
+*/
+
+static inline TABLE_SHARE *tdc_acquire_share(THD *thd, const char *db,
+ const char *table_name,
+ const char *key,
+ uint key_length, uint flags)
+{
+ return tdc_acquire_share(thd, db, table_name, key, key_length,
+ my_hash_sort(&my_charset_bin, (uchar*) key,
+ key_length), flags, 0);
+}
+
+
+/**
+ Convenience helper: call tdc_acquire_share() without precomputed cache key.
+*/
+
+static inline TABLE_SHARE *tdc_acquire_share(THD *thd, const char *db,
+ const char *table_name, uint flags)
+{
+ char key[MAX_DBKEY_LENGTH];
+ uint key_length;
+ key_length= tdc_create_key(key, db, table_name);
+ return tdc_acquire_share(thd, db, table_name, key, key_length, flags);
+}
+
+
+/**
+ Convenience helper: call tdc_acquire_share() reusing the MDL cache key.
+
+ @note lifetime of the returned TABLE_SHARE is limited by the
+ lifetime of the TABLE_LIST object!!!
+*/
+
+uint get_table_def_key(const TABLE_LIST *table_list, const char **key);
+
+static inline TABLE_SHARE *tdc_acquire_share_shortlived(THD *thd, TABLE_LIST *tl,
+ uint flags)
+{
+ const char *key;
+ uint key_length= get_table_def_key(tl, &key);
+ return tdc_acquire_share(thd, tl->db, tl->table_name, key, key_length,
+ tl->mdl_request.key.tc_hash_value(), flags, 0);
+}
+
+
+class TDC_iterator
+{
+ ulong idx;
+public:
+ void init(void);
+ void deinit(void);
+ TABLE_SHARE *next(void);
+};
diff --git a/sql/thr_malloc.cc b/sql/thr_malloc.cc
index cedcbefc26f..9786f1a6942 100644
--- a/sql/thr_malloc.cc
+++ b/sql/thr_malloc.cc
@@ -17,6 +17,7 @@
/* Mallocs for used in threads */
+#include <my_global.h>
#include "sql_priv.h"
#include "unireg.h"
#include "thr_malloc.h"
@@ -46,10 +47,7 @@ extern "C" {
returned in the error packet.
- SHOW ERROR/SHOW WARNINGS may be empty.
*/
- thd->stmt_da->set_error_status(thd,
- ER_OUT_OF_RESOURCES,
- ER(ER_OUT_OF_RESOURCES),
- NULL);
+ thd->get_stmt_da()->set_error_status(ER_OUT_OF_RESOURCES);
}
}
@@ -61,9 +59,10 @@ extern "C" {
}
}
-void init_sql_alloc(MEM_ROOT *mem_root, uint block_size, uint pre_alloc)
+void init_sql_alloc(MEM_ROOT *mem_root, uint block_size, uint pre_alloc,
+ myf my_flags)
{
- init_alloc_root(mem_root, block_size, pre_alloc);
+ init_alloc_root(mem_root, block_size, pre_alloc, my_flags);
mem_root->error_handler=sql_alloc_error_handler;
}
@@ -133,7 +132,7 @@ char *sql_strmake_with_convert(const char *str, size_t arg_length,
if ((from_cs == &my_charset_bin) || (to_cs == &my_charset_bin))
{
// Safety if to_cs->mbmaxlen > 0
- new_length= min(arg_length, max_res_length);
+ new_length= MY_MIN(arg_length, max_res_length);
memcpy(pos, str, new_length);
}
else
diff --git a/sql/thr_malloc.h b/sql/thr_malloc.h
index 81b7d3cc238..0b17c5cdaf1 100644
--- a/sql/thr_malloc.h
+++ b/sql/thr_malloc.h
@@ -20,7 +20,8 @@
typedef struct st_mem_root MEM_ROOT;
-void init_sql_alloc(MEM_ROOT *root, uint block_size, uint pre_alloc_size);
+void init_sql_alloc(MEM_ROOT *root, uint block_size, uint pre_alloc_size,
+ myf my_flags);
void *sql_alloc(size_t);
void *sql_calloc(size_t);
char *sql_strdup(const char *str);
diff --git a/sql/threadpool.h b/sql/threadpool.h
index c080e5ba343..bcbdca47808 100644
--- a/sql/threadpool.h
+++ b/sql/threadpool.h
@@ -27,6 +27,7 @@ extern uint threadpool_oversubscribe; /* Maximum active threads in group */
/* Common thread pool routines, suitable for different implementations */
+extern void threadpool_cleanup_connection(THD *thd);
extern void threadpool_remove_connection(THD *thd);
extern int threadpool_process_request(THD *thd);
extern int threadpool_add_connection(THD *thd);
diff --git a/sql/threadpool_common.cc b/sql/threadpool_common.cc
index 9e0cb07b86c..5bcea767aae 100644
--- a/sql/threadpool_common.cc
+++ b/sql/threadpool_common.cc
@@ -168,22 +168,28 @@ int threadpool_add_connection(THD *thd)
return retval;
}
+/*
+ threadpool_cleanup_connection() does the bulk of connection shutdown work.
+ Usually called from threadpool_remove_connection(), but rarely it might
+ be called also in the main polling thread if connection initialization fails.
+*/
+void threadpool_cleanup_connection(THD *thd)
+{
+ thd->net.reading_or_writing = 0;
+ end_connection(thd);
+ close_connection(thd, 0);
+ unlink_thd(thd);
+ mysql_cond_broadcast(&COND_thread_count);
+}
+
void threadpool_remove_connection(THD *thd)
{
-
Worker_thread_context worker_context;
worker_context.save();
-
thread_attach(thd);
- thd->net.reading_or_writing= 0;
-
- end_connection(thd);
- close_connection(thd, 0);
-
- unlink_thd(thd);
- mysql_cond_broadcast(&COND_thread_count);
+ threadpool_cleanup_connection(thd);
/*
Free resources associated with this connection:
mysys thread_var and PSI thread.
diff --git a/sql/threadpool_unix.cc b/sql/threadpool_unix.cc
index df1a05b3ebf..89a2036cb10 100644
--- a/sql/threadpool_unix.cc
+++ b/sql/threadpool_unix.cc
@@ -19,6 +19,9 @@
#include <sql_class.h>
#include <my_pthread.h>
#include <scheduler.h>
+
+#ifdef HAVE_POOL_OF_THREADS
+
#include <sql_connect.h>
#include <mysqld.h>
#include <debug_sync.h>
@@ -1252,7 +1255,7 @@ void tp_add_connection(THD *thd)
else
{
/* Allocation failed */
- threadpool_remove_connection(thd);
+ threadpool_cleanup_connection(thd);
}
DBUG_VOID_RETURN;
}
@@ -1366,7 +1369,7 @@ static int change_group(connection_t *c,
thread_group_t *new_group)
{
int ret= 0;
- int fd = c->thd->net.vio->sd;
+ int fd= mysql_socket_getfd(c->thd->net.vio->mysql_socket);
DBUG_ASSERT(c->thread_group == old_group);
@@ -1394,7 +1397,7 @@ static int change_group(connection_t *c,
static int start_io(connection_t *connection)
{
- int fd = connection->thd->net.vio->sd;
+ int fd = mysql_socket_getfd(connection->thd->net.vio->mysql_socket);
/*
Usually, connection will stay in the same group for the entire
@@ -1514,7 +1517,7 @@ static void *worker_main(void *param)
bool tp_init()
{
DBUG_ENTER("tp_init");
- threadpool_max_size= max(threadpool_size, 128);
+ threadpool_max_size= MY_MAX(threadpool_size, 128);
all_groups= (thread_group_t *)
my_malloc(sizeof(thread_group_t) * threadpool_max_size, MYF(MY_WME|MY_ZEROFILL));
if (!all_groups)
@@ -1631,7 +1634,7 @@ int tp_get_idle_thread_count()
Delay in microseconds, after which "pool blocked" message is printed.
(30 sec == 30 Mio usec)
*/
-#define BLOCK_MSG_DELAY 30*1000000
+#define BLOCK_MSG_DELAY (30*1000000)
#define MAX_THREADS_REACHED_MSG \
"Threadpool could not create additional thread to handle queries, because the \
@@ -1678,3 +1681,5 @@ static void print_pool_blocked_message(bool max_threads_reached)
msg_written= true;
}
}
+
+#endif /* HAVE_POOL_OF_THREADS */
diff --git a/sql/threadpool_win.cc b/sql/threadpool_win.cc
index 72e03da2453..4be51f3d6e9 100644
--- a/sql/threadpool_win.cc
+++ b/sql/threadpool_win.cc
@@ -255,7 +255,7 @@ int init_io(connection_t *connection, THD *thd)
{
case VIO_TYPE_SSL:
case VIO_TYPE_TCPIP:
- connection->handle= (HANDLE)vio->sd;
+ connection->handle= (HANDLE)mysql_socket_getfd(connection->thd->net.vio->mysql_socket);
break;
case VIO_TYPE_NAMEDPIPE:
connection->handle= (HANDLE)vio->hPipe;
@@ -342,7 +342,7 @@ int start_io(connection_t *connection, PTP_CALLBACK_INSTANCE instance)
if (vio->type == VIO_TYPE_TCPIP || vio->type == VIO_TYPE_SSL)
{
/* Start async io (sockets). */
- if (WSARecv(vio->sd , &buf, 1, &num_bytes, &flags,
+ if (WSARecv(mysql_socket_getfd(vio->mysql_socket) , &buf, 1, &num_bytes, &flags,
overlapped, NULL) == 0)
{
retval= last_error= 0;
@@ -667,7 +667,7 @@ void tp_add_connection(THD *thd)
if(!con)
{
tp_log_warning("Allocation failed", "tp_add_connection");
- threadpool_remove_connection(thd);
+ threadpool_cleanup_connection(thd);
return;
}
@@ -685,7 +685,7 @@ void tp_add_connection(THD *thd)
else
{
/* Likely memory pressure */
- login_callback(NULL, con, NULL); /* deletes connection if something goes wrong */
+ threadpool_cleanup_connection(thd);
}
}
diff --git a/sql/transaction.cc b/sql/transaction.cc
index c293c651c04..f478b4a18f7 100644
--- a/sql/transaction.cc
+++ b/sql/transaction.cc
@@ -18,10 +18,12 @@
#pragma implementation // gcc: Class implementation
#endif
+#include <my_global.h>
#include "sql_priv.h"
#include "transaction.h"
#include "rpl_handler.h"
#include "debug_sync.h" // DEBUG_SYNC
+#include "sql_acl.h"
/* Conditions under which the transaction state must not change. */
static bool trans_check(THD *thd)
@@ -140,15 +142,24 @@ bool trans_begin(THD *thd, uint flags)
#ifdef WITH_WSREP
wsrep_register_hton(thd, TRUE);
#endif /* WITH_WSREP */
- thd->server_status&= ~SERVER_STATUS_IN_TRANS;
- res= test(ha_commit_trans(thd, TRUE));
+ thd->server_status&=
+ ~(SERVER_STATUS_IN_TRANS | SERVER_STATUS_IN_TRANS_READONLY);
+ DBUG_PRINT("info", ("clearing SERVER_STATUS_IN_TRANS"));
+ res= MY_TEST(ha_commit_trans(thd, TRUE));
#ifdef WITH_WSREP
wsrep_post_commit(thd, TRUE);
#endif /* WITH_WSREP */
}
thd->variables.option_bits&= ~(OPTION_BEGIN | OPTION_KEEP_LOG);
+
+ /*
+ The following set should not be needed as the flag should always be 0
+ when we come here. We should at some point change this to an assert.
+ */
thd->transaction.all.modified_non_trans_table= FALSE;
+ thd->has_waiter= false;
+ thd->waiting_on_group_commit= false;
if (res)
DBUG_RETURN(TRUE);
@@ -159,6 +170,29 @@ bool trans_begin(THD *thd, uint flags)
*/
thd->mdl_context.release_transactional_locks();
+ // The RO/RW options are mutually exclusive.
+ DBUG_ASSERT(!((flags & MYSQL_START_TRANS_OPT_READ_ONLY) &&
+ (flags & MYSQL_START_TRANS_OPT_READ_WRITE)));
+ if (flags & MYSQL_START_TRANS_OPT_READ_ONLY)
+ thd->tx_read_only= true;
+ else if (flags & MYSQL_START_TRANS_OPT_READ_WRITE)
+ {
+ /*
+ Explicitly starting a RW transaction when the server is in
+ read-only mode, is not allowed unless the user has SUPER priv.
+ Implicitly starting a RW transaction is allowed for backward
+ compatibility.
+ */
+ const bool user_is_super=
+ MY_TEST(thd->security_ctx->master_access & SUPER_ACL);
+ if (opt_readonly && !user_is_super)
+ {
+ my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--read-only");
+ DBUG_RETURN(true);
+ }
+ thd->tx_read_only= false;
+ }
+
#ifdef WITH_WSREP
thd->wsrep_PA_safe= true;
if (WSREP_CLIENT(thd) && wsrep_sync_wait(thd))
@@ -167,11 +201,15 @@ bool trans_begin(THD *thd, uint flags)
thd->variables.option_bits|= OPTION_BEGIN;
thd->server_status|= SERVER_STATUS_IN_TRANS;
+ if (thd->tx_read_only)
+ thd->server_status|= SERVER_STATUS_IN_TRANS_READONLY;
+ DBUG_PRINT("info", ("setting SERVER_STATUS_IN_TRANS"));
+ /* ha_start_consistent_snapshot() relies on OPTION_BEGIN flag set. */
if (flags & MYSQL_START_TRANS_OPT_WITH_CONS_SNAPSHOT)
res= ha_start_consistent_snapshot(thd);
- DBUG_RETURN(test(res));
+ DBUG_RETURN(MY_TEST(res));
}
@@ -195,24 +233,26 @@ bool trans_commit(THD *thd)
#ifdef WITH_WSREP
wsrep_register_hton(thd, TRUE);
#endif /* WITH_WSREP */
- thd->server_status&= ~SERVER_STATUS_IN_TRANS;
+ thd->server_status&=
+ ~(SERVER_STATUS_IN_TRANS | SERVER_STATUS_IN_TRANS_READONLY);
+ DBUG_PRINT("info", ("clearing SERVER_STATUS_IN_TRANS"));
res= ha_commit_trans(thd, TRUE);
#ifdef WITH_WSREP
wsrep_post_commit(thd, TRUE);
#endif /* WITH_WSREP */
- if (res)
/*
if res is non-zero, then ha_commit_trans has rolled back the
transaction, so the hooks for rollback will be called.
*/
- RUN_HOOK(transaction, after_rollback, (thd, FALSE));
+ if (res)
+ (void) RUN_HOOK(transaction, after_rollback, (thd, FALSE));
else
- RUN_HOOK(transaction, after_commit, (thd, FALSE));
+ (void) RUN_HOOK(transaction, after_commit, (thd, FALSE));
thd->variables.option_bits&= ~(OPTION_BEGIN | OPTION_KEEP_LOG);
thd->transaction.all.modified_non_trans_table= FALSE;
thd->lex->start_transaction_opt= 0;
- DBUG_RETURN(test(res));
+ DBUG_RETURN(MY_TEST(res));
}
@@ -235,6 +275,10 @@ bool trans_commit_implicit(THD *thd)
if (trans_check(thd))
DBUG_RETURN(TRUE);
+ if (thd->variables.option_bits & OPTION_GTID_BEGIN)
+ DBUG_PRINT("error", ("OPTION_GTID_BEGIN is set. "
+ "Master and slave will have different GTID values"));
+
if (thd->in_multi_stmt_transaction_mode() ||
(thd->variables.option_bits & OPTION_TABLE_LOCK))
{
@@ -244,8 +288,10 @@ bool trans_commit_implicit(THD *thd)
#ifdef WITH_WSREP
wsrep_register_hton(thd, TRUE);
#endif /* WITH_WSREP */
- thd->server_status&= ~SERVER_STATUS_IN_TRANS;
- res= test(ha_commit_trans(thd, TRUE));
+ thd->server_status&=
+ ~(SERVER_STATUS_IN_TRANS | SERVER_STATUS_IN_TRANS_READONLY);
+ DBUG_PRINT("info", ("clearing SERVER_STATUS_IN_TRANS"));
+ res= MY_TEST(ha_commit_trans(thd, TRUE));
#ifdef WITH_WSREP
wsrep_post_commit(thd, TRUE);
#endif /* WITH_WSREP */
@@ -256,11 +302,12 @@ bool trans_commit_implicit(THD *thd)
/*
Upon implicit commit, reset the current transaction
- isolation level. We do not care about
+ isolation level and access mode. We do not care about
@@session.completion_type since it's documented
to not have any effect on implicit commit.
*/
thd->tx_isolation= (enum_tx_isolation) thd->variables.tx_isolation;
+ thd->tx_read_only= thd->variables.tx_read_only;
DBUG_RETURN(res);
}
@@ -278,24 +325,29 @@ bool trans_commit_implicit(THD *thd)
bool trans_rollback(THD *thd)
{
int res;
- DBUG_ENTER("trans_rollback");
+ DBUG_ENTER("trans_rollback");
+
#ifdef WITH_WSREP
thd->wsrep_PA_safe= true;
#endif /* WITH_WSREP */
- if (trans_check(thd))
+ if (trans_check(thd))
DBUG_RETURN(TRUE);
#ifdef WITH_WSREP
wsrep_register_hton(thd, TRUE);
#endif /* WITH_WSREP */
- thd->server_status&= ~SERVER_STATUS_IN_TRANS;
+ thd->server_status&=
+ ~(SERVER_STATUS_IN_TRANS | SERVER_STATUS_IN_TRANS_READONLY);
+ DBUG_PRINT("info", ("clearing SERVER_STATUS_IN_TRANS"));
res= ha_rollback_trans(thd, TRUE);
- RUN_HOOK(transaction, after_rollback, (thd, FALSE));
+ (void) RUN_HOOK(transaction, after_rollback, (thd, FALSE));
thd->variables.option_bits&= ~(OPTION_BEGIN | OPTION_KEEP_LOG);
+ /* Reset the binlog transaction marker */
+ thd->variables.option_bits&= ~OPTION_GTID_BEGIN;
thd->transaction.all.modified_non_trans_table= FALSE;
thd->lex->start_transaction_opt= 0;
- DBUG_RETURN(test(res));
+ DBUG_RETURN(MY_TEST(res));
}
@@ -327,9 +379,6 @@ bool trans_rollback_implicit(THD *thd)
*/
DBUG_ASSERT(thd->transaction.stmt.is_empty() && !thd->in_sub_stmt);
-#ifdef WITH_WSREP
- wsrep_register_hton(thd, true);
-#endif /* WITH_WSREP */
thd->server_status&= ~SERVER_STATUS_IN_TRANS;
DBUG_PRINT("info", ("clearing SERVER_STATUS_IN_TRANS"));
res= ha_rollback_trans(thd, true);
@@ -344,7 +393,7 @@ bool trans_rollback_implicit(THD *thd)
/* Rollback should clear transaction_rollback_request flag. */
DBUG_ASSERT(! thd->transaction_rollback_request);
- DBUG_RETURN(test(res));
+ DBUG_RETURN(MY_TEST(res));
}
@@ -382,28 +431,27 @@ bool trans_commit_stmt(THD *thd)
#endif /* WITH_WSREP */
res= ha_commit_trans(thd, FALSE);
if (! thd->in_active_multi_stmt_transaction())
-#ifdef WITH_WSREP
{
-#endif /* WITH_WSREP */
thd->tx_isolation= (enum_tx_isolation) thd->variables.tx_isolation;
+ thd->tx_read_only= thd->variables.tx_read_only;
#ifdef WITH_WSREP
wsrep_post_commit(thd, FALSE);
- }
#endif /* WITH_WSREP */
+ }
}
- if (res)
/*
if res is non-zero, then ha_commit_trans has rolled back the
transaction, so the hooks for rollback will be called.
*/
- RUN_HOOK(transaction, after_rollback, (thd, FALSE));
+ if (res)
+ (void) RUN_HOOK(transaction, after_rollback, (thd, FALSE));
else
- RUN_HOOK(transaction, after_commit, (thd, FALSE));
+ (void) RUN_HOOK(transaction, after_commit, (thd, FALSE));
thd->transaction.stmt.reset();
- DBUG_RETURN(test(res));
+ DBUG_RETURN(MY_TEST(res));
}
@@ -434,10 +482,13 @@ bool trans_rollback_stmt(THD *thd)
#endif /* WITH_WSREP */
ha_rollback_trans(thd, FALSE);
if (! thd->in_active_multi_stmt_transaction())
+ {
thd->tx_isolation= (enum_tx_isolation) thd->variables.tx_isolation;
+ thd->tx_read_only= thd->variables.tx_read_only;
+ }
}
- RUN_HOOK(transaction, after_rollback, (thd, FALSE));
+ (void) RUN_HOOK(transaction, after_rollback, (thd, FALSE));
thd->transaction.stmt.reset();
@@ -568,28 +619,47 @@ bool trans_rollback_to_savepoint(THD *thd, LEX_STRING name)
DBUG_RETURN(TRUE);
}
+ /**
+ Checking whether it is safe to release metadata locks acquired after
+ savepoint, if rollback to savepoint is successful.
+
+ Whether it is safe to release MDL after rollback to savepoint depends
+ on storage engines participating in transaction:
+
+ - InnoDB doesn't release any row-locks on rollback to savepoint so it
+ is probably a bad idea to release MDL as well.
+ - Binary log implementation in some cases (e.g when non-transactional
+ tables involved) may choose not to remove events added after savepoint
+ from transactional cache, but instead will write them to binary
+ log accompanied with ROLLBACK TO SAVEPOINT statement. Since the real
+ write happens at the end of transaction releasing MDL on tables
+ mentioned in these events (i.e. acquired after savepoint and before
+ rollback ot it) can break replication, as concurrent DROP TABLES
+ statements will be able to drop these tables before events will get
+ into binary log,
+
+ For backward-compatibility reasons we always release MDL if binary
+ logging is off.
+ */
+ bool mdl_can_safely_rollback_to_savepoint=
+ (!(mysql_bin_log.is_open() && thd->variables.sql_log_bin) ||
+ ha_rollback_to_savepoint_can_release_mdl(thd));
+
if (ha_rollback_to_savepoint(thd, sv))
res= TRUE;
else if (((thd->variables.option_bits & OPTION_KEEP_LOG) ||
thd->transaction.all.modified_non_trans_table) &&
!thd->slave_thread)
- push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning(thd, Sql_condition::WARN_LEVEL_WARN,
ER_WARNING_NOT_COMPLETE_ROLLBACK,
ER(ER_WARNING_NOT_COMPLETE_ROLLBACK));
thd->transaction.savepoints= sv;
- /*
- Release metadata locks that were acquired during this savepoint unit
- unless binlogging is on. Releasing locks with binlogging on can break
- replication as it allows other connections to drop these tables before
- rollback to savepoint is written to the binlog.
- */
- bool binlog_on= mysql_bin_log.is_open() && thd->variables.sql_log_bin;
- if (!res && !binlog_on)
+ if (!res && mdl_can_safely_rollback_to_savepoint)
thd->mdl_context.rollback_to_savepoint(sv->mdl_savepoint);
- DBUG_RETURN(test(res));
+ DBUG_RETURN(MY_TEST(res));
}
@@ -624,7 +694,7 @@ bool trans_release_savepoint(THD *thd, LEX_STRING name)
thd->transaction.savepoints= sv->prev;
- DBUG_RETURN(test(res));
+ DBUG_RETURN(MY_TEST(res));
}
@@ -791,7 +861,7 @@ bool trans_xa_commit(THD *thd)
wsrep_register_hton(thd, TRUE);
#endif /* WITH_WSREP */
int r= ha_commit_trans(thd, TRUE);
- if ((res= test(r)))
+ if ((res= MY_TEST(r)))
my_error(r == 1 ? ER_XA_RBROLLBACK : ER_XAER_RMERR, MYF(0));
#ifdef WITH_WSREP
wsrep_post_commit(thd, TRUE);
@@ -824,7 +894,7 @@ bool trans_xa_commit(THD *thd)
{
DEBUG_SYNC(thd, "trans_xa_commit_after_acquire_commit_lock");
- res= test(ha_commit_one_phase(thd, 1));
+ res= MY_TEST(ha_commit_one_phase(thd, 1));
if (res)
my_error(ER_XAER_RMERR, MYF(0));
}
@@ -837,7 +907,9 @@ bool trans_xa_commit(THD *thd)
thd->variables.option_bits&= ~(OPTION_BEGIN | OPTION_KEEP_LOG);
thd->transaction.all.modified_non_trans_table= FALSE;
- thd->server_status&= ~SERVER_STATUS_IN_TRANS;
+ thd->server_status&=
+ ~(SERVER_STATUS_IN_TRANS | SERVER_STATUS_IN_TRANS_READONLY);
+ DBUG_PRINT("info", ("clearing SERVER_STATUS_IN_TRANS"));
xid_cache_delete(&thd->transaction.xid_state);
thd->transaction.xid_state.xa_state= XA_NOTR;
@@ -871,7 +943,7 @@ bool trans_xa_rollback(THD *thd)
ha_commit_or_rollback_by_xid(thd->lex->xid, 0);
xid_cache_delete(xs);
}
- DBUG_RETURN(thd->stmt_da->is_error());
+ DBUG_RETURN(thd->get_stmt_da()->is_error());
}
if (xa_state != XA_IDLE && xa_state != XA_PREPARED && xa_state != XA_ROLLBACK_ONLY)
@@ -884,7 +956,9 @@ bool trans_xa_rollback(THD *thd)
thd->variables.option_bits&= ~(OPTION_BEGIN | OPTION_KEEP_LOG);
thd->transaction.all.modified_non_trans_table= FALSE;
- thd->server_status&= ~SERVER_STATUS_IN_TRANS;
+ thd->server_status&=
+ ~(SERVER_STATUS_IN_TRANS | SERVER_STATUS_IN_TRANS_READONLY);
+ DBUG_PRINT("info", ("clearing SERVER_STATUS_IN_TRANS"));
xid_cache_delete(&thd->transaction.xid_state);
thd->transaction.xid_state.xa_state= XA_NOTR;
diff --git a/sql/tztime.cc b/sql/tztime.cc
index 88169a41915..bea1d9c5d28 100644
--- a/sql/tztime.cc
+++ b/sql/tztime.cc
@@ -180,7 +180,7 @@ tz_load(const char *name, TIME_ZONE_INFO *sp, MEM_ROOT *storage)
uchar buf[sizeof(struct tzhead) + sizeof(my_time_t) * TZ_MAX_TIMES +
TZ_MAX_TIMES + sizeof(TRAN_TYPE_INFO) * TZ_MAX_TYPES +
#ifdef ABBR_ARE_USED
- max(TZ_MAX_CHARS + 1, (2 * (MY_TZNAME_MAX + 1))) +
+ MY_MAX(TZ_MAX_CHARS + 1, (2 * (MY_TZNAME_MAX + 1))) +
#endif
sizeof(LS_INFO) * TZ_MAX_LEAPS];
} u;
@@ -409,7 +409,7 @@ prepare_tz_info(TIME_ZONE_INFO *sp, MEM_ROOT *storage)
Let us choose end_t as point before next time type change or leap
second correction.
*/
- end_t= min((next_trans_idx < sp->timecnt) ? sp->ats[next_trans_idx] - 1:
+ end_t= MY_MIN((next_trans_idx < sp->timecnt) ? sp->ats[next_trans_idx] - 1:
MY_TIME_T_MAX,
(next_leap_idx < sp->leapcnt) ?
sp->lsis[next_leap_idx].ls_trans - 1: MY_TIME_T_MAX);
@@ -1641,7 +1641,7 @@ my_tz_init(THD *org_thd, const char *default_tzname, my_bool bootstrap)
my_hash_free(&tz_names);
goto end;
}
- init_sql_alloc(&tz_storage, 32 * 1024, 0);
+ init_sql_alloc(&tz_storage, 32 * 1024, 0, MYF(0));
mysql_mutex_init(key_tz_LOCK, &tz_LOCK, MY_MUTEX_INIT_FAST);
tz_inited= 1;
@@ -1694,7 +1694,8 @@ my_tz_init(THD *org_thd, const char *default_tzname, my_bool bootstrap)
MYSQL_OPEN_IGNORE_FLUSH | MYSQL_LOCK_IGNORE_TIMEOUT))
{
sql_print_warning("Can't open and lock time zone table: %s "
- "trying to live without them", thd->stmt_da->message());
+ "trying to live without them",
+ thd->get_stmt_da()->message());
/* We will try emulate that everything is ok */
return_val= time_zone_tables_exist= 0;
goto end_with_setting_default_tz;
@@ -1802,11 +1803,7 @@ end:
if (org_thd)
org_thd->store_globals(); /* purecov: inspected */
else
- {
- /* Remember that we don't have a THD */
- my_pthread_setspecific_ptr(THR_THD, 0);
my_pthread_setspecific_ptr(THR_MALLOC, 0);
- }
default_tz= default_tz_name ? global_system_variables.time_zone
: my_tz_SYSTEM;
@@ -1880,7 +1877,7 @@ tz_load_from_open_tables(const String *tz_name, TABLE_LIST *tz_tables)
uchar types[TZ_MAX_TIMES];
TRAN_TYPE_INFO ttis[TZ_MAX_TYPES];
#ifdef ABBR_ARE_USED
- char chars[max(TZ_MAX_CHARS + 1, (2 * (MY_TZNAME_MAX + 1)))];
+ char chars[MY_MAX(TZ_MAX_CHARS + 1, (2 * (MY_TZNAME_MAX + 1)))];
#endif
/*
Used as a temporary tz_info until we decide that we actually want to
@@ -1931,7 +1928,7 @@ tz_load_from_open_tables(const String *tz_name, TABLE_LIST *tz_tables)
field->store((longlong) tzid, TRUE);
DBUG_ASSERT(field->key_length() <= sizeof(keybuff));
field->get_key_image(keybuff,
- min(field->key_length(), sizeof(keybuff)),
+ MY_MIN(field->key_length(), sizeof(keybuff)),
Field::itRAW);
if (table->file->ha_index_init(0, 1))
goto end;
@@ -1964,7 +1961,7 @@ tz_load_from_open_tables(const String *tz_name, TABLE_LIST *tz_tables)
field->store((longlong) tzid, TRUE);
DBUG_ASSERT(field->key_length() <= sizeof(keybuff));
field->get_key_image(keybuff,
- min(field->key_length(), sizeof(keybuff)),
+ MY_MIN(field->key_length(), sizeof(keybuff)),
Field::itRAW);
if (table->file->ha_index_init(0, 1))
goto end;
@@ -2032,7 +2029,7 @@ tz_load_from_open_tables(const String *tz_name, TABLE_LIST *tz_tables)
/*
At last we are doing the same thing for records in
- mysql.time_zone_transition table. Here we additionaly need records
+ mysql.time_zone_transition table. Here we additionally need records
in ascending order by index scan also satisfies us.
*/
table= tz_tables->table;
@@ -2514,12 +2511,13 @@ scan_tz_dir(char * name_end, uint symlink_recursion_level, uint verbose)
char *name_end_tmp;
uint i;
- if (!(cur_dir= my_dir(fullname, MYF(MY_WANT_STAT))))
+ /* Sort directory data, to pass mtr tests on different platforms. */
+ if (!(cur_dir= my_dir(fullname, MYF(MY_WANT_STAT|MY_WANT_SORT))))
return 1;
name_end= strmake(name_end, "/", FN_REFLEN - (name_end - fullname));
- for (i= 0; i < cur_dir->number_off_files; i++)
+ for (i= 0; i < cur_dir->number_of_files; i++)
{
if (cur_dir->dir_entry[i].name[0] != '.' &&
strcmp(cur_dir->dir_entry[i].name, "Factory"))
@@ -2571,7 +2569,7 @@ scan_tz_dir(char * name_end, uint symlink_recursion_level, uint verbose)
}
else if (MY_S_ISREG(cur_dir->dir_entry[i].mystat->st_mode))
{
- init_alloc_root(&tz_storage, 32768, 0);
+ init_alloc_root(&tz_storage, 32768, 0, MYF(MY_THREAD_SPECIFIC));
if (!tz_load(fullname, &tz_info, &tz_storage))
print_tz_as_sql(root_name_end + 1, &tz_info);
else
@@ -2744,7 +2742,7 @@ main(int argc, char **argv)
First argument is timezonefile.
The second is timezonename if opt_leap is not given
*/
- init_alloc_root(&tz_storage, 32768, 0);
+ init_alloc_root(&tz_storage, 32768, 0, MYF(0));
if (tz_load(argv[0], &tz_info, &tz_storage))
{
@@ -2820,7 +2818,7 @@ main(int argc, char **argv)
MY_INIT(argv[0]);
- init_alloc_root(&tz_storage, 32768, 0);
+ init_alloc_root(&tz_storage, 32768, MYF(0));
/* let us set some well known timezone */
setenv("TZ", "MET", 1);
@@ -2832,7 +2830,7 @@ main(int argc, char **argv)
if (TYPE_SIGNED(time_t))
{
t= -100;
- localtime_negative= test(localtime_r(&t, &tmp) != 0);
+ localtime_negative= MY_TEST(localtime_r(&t, &tmp) != 0);
printf("localtime_r %s negative params \
(time_t=%d is %d-%d-%d %d:%d:%d)\n",
(localtime_negative ? "supports" : "doesn't support"), (int)t,
diff --git a/sql/uniques.cc b/sql/uniques.cc
index fe3e329cda6..c755293035b 100644
--- a/sql/uniques.cc
+++ b/sql/uniques.cc
@@ -1,4 +1,5 @@
-/* Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved.
+/* Copyright (c) 2001, 2010, Oracle and/or its affiliates.
+ Copyright (c) 2010, 2015, MariaDB
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
@@ -30,6 +31,7 @@
deletes in disk order.
*/
+#include <my_global.h>
#include "sql_priv.h"
#include "unireg.h"
#include "sql_sort.h"
@@ -86,11 +88,13 @@ Unique::Unique(qsort_cmp2 comp_func, void * comp_func_fixed_arg,
full_size= size;
if (min_dupl_count_arg)
full_size+= sizeof(element_count);
+ with_counters= MY_TEST(min_dupl_count_arg);
my_b_clear(&file);
- init_tree(&tree, (ulong) (max_in_memory_size / 16), 0, size, comp_func, 0,
- NULL, comp_func_fixed_arg);
+ init_tree(&tree, (ulong) (max_in_memory_size / 16), 0, size, comp_func,
+ NULL, comp_func_fixed_arg, MYF(MY_THREAD_SPECIFIC));
/* If the following fail's the next add will also fail */
- my_init_dynamic_array(&file_ptrs, sizeof(BUFFPEK), 16, 16);
+ my_init_dynamic_array(&file_ptrs, sizeof(BUFFPEK), 16, 16,
+ MYF(MY_THREAD_SPECIFIC));
/*
If you change the following, change it in get_max_elements function, too.
*/
@@ -427,6 +431,22 @@ static int buffpek_compare(void *arg, uchar *key_ptr1, uchar *key_ptr2)
C_MODE_END
+inline
+element_count get_counter_from_merged_element(void *ptr, uint ofs)
+{
+ element_count cnt;
+ memcpy((uchar *) &cnt, (uchar *) ptr + ofs, sizeof(element_count));
+ return cnt;
+}
+
+
+inline
+void put_counter_into_merged_element(void *ptr, uint ofs, element_count cnt)
+{
+ memcpy((uchar *) ptr + ofs, (uchar *) &cnt, sizeof(element_count));
+}
+
+
/*
DESCRIPTION
@@ -456,6 +476,8 @@ C_MODE_END
file file with all trees dumped. Trees in the file
must contain sorted unique values. Cache must be
initialized in read mode.
+ with counters take into account counters for equal merged
+ elements
RETURN VALUE
0 ok
<> 0 error
@@ -465,7 +487,7 @@ static bool merge_walk(uchar *merge_buffer, ulong merge_buffer_size,
uint key_length, BUFFPEK *begin, BUFFPEK *end,
tree_walk_action walk_action, void *walk_action_arg,
qsort_cmp2 compare, void *compare_arg,
- IO_CACHE *file)
+ IO_CACHE *file, bool with_counters)
{
BUFFPEK_COMPARE_CONTEXT compare_context = { compare, compare_arg };
QUEUE queue;
@@ -484,6 +506,8 @@ static bool merge_walk(uchar *merge_buffer, ulong merge_buffer_size,
uint bytes_read; /* to hold return value of read_to_buffer */
BUFFPEK *top;
int res= 1;
+ uint cnt_ofs= key_length - (with_counters ? sizeof(element_count) : 0);
+ element_count cnt;
/*
Invariant: queue must contain top element from each tree, until a tree
is not completely walked through.
@@ -542,9 +566,17 @@ static bool merge_walk(uchar *merge_buffer, ulong merge_buffer_size,
/* new top has been obtained; if old top is unique, apply the action */
if (compare(compare_arg, old_key, top->key))
{
- if (walk_action(old_key, 1, walk_action_arg))
+ cnt= with_counters ?
+ get_counter_from_merged_element(old_key, cnt_ofs) : 1;
+ if (walk_action(old_key, cnt, walk_action_arg))
goto end;
}
+ else if (with_counters)
+ {
+ cnt= get_counter_from_merged_element(top->key, cnt_ofs);
+ cnt+= get_counter_from_merged_element(old_key, cnt_ofs);
+ put_counter_into_merged_element(top->key, cnt_ofs, cnt);
+ }
}
/*
Applying walk_action to the tail of the last tree: this is safe because
@@ -555,7 +587,10 @@ static bool merge_walk(uchar *merge_buffer, ulong merge_buffer_size,
{
do
{
- if (walk_action(top->key, 1, walk_action_arg))
+
+ cnt= with_counters ?
+ get_counter_from_merged_element(top->key, cnt_ofs) : 1;
+ if (walk_action(top->key, cnt, walk_action_arg))
goto end;
top->key+= key_length;
}
@@ -608,9 +643,9 @@ bool Unique::walk(TABLE *table, tree_walk_action action, void *walk_action_arg)
if (flush_io_cache(&file) || reinit_io_cache(&file, READ_CACHE, 0L, 0, 0))
return 1;
size_t buff_sz= (max_in_memory_size / full_size + 1) * full_size;
- if (!(merge_buffer = (uchar *)my_malloc(buff_sz, MYF(MY_WME))))
+ if (!(merge_buffer = (uchar *)my_malloc(buff_sz, MYF(MY_THREAD_SPECIFIC|MY_WME))))
return 1;
- if (buff_sz < (ulong) (full_size * (file_ptrs.elements + 1)))
+ if (buff_sz < full_size * (file_ptrs.elements + 1UL))
res= merge(table, merge_buffer, buff_sz >= full_size * MERGEBUFF2) ;
if (!res)
@@ -619,7 +654,7 @@ bool Unique::walk(TABLE *table, tree_walk_action action, void *walk_action_arg)
(BUFFPEK *) file_ptrs.buffer,
(BUFFPEK *) file_ptrs.buffer + file_ptrs.elements,
action, walk_action_arg,
- tree.compare, tree.custom_arg, &file);
+ tree.compare, tree.custom_arg, &file, with_counters);
}
my_free(merge_buffer);
return res;
@@ -644,7 +679,6 @@ bool Unique::walk(TABLE *table, tree_walk_action action, void *walk_action_arg)
bool Unique::merge(TABLE *table, uchar *buff, bool without_last_merge)
{
- SORTPARAM sort_param;
IO_CACHE *outfile= table->sort.io_cache;
BUFFPEK *file_ptr= (BUFFPEK*) file_ptrs.buffer;
uint maxbuffer= file_ptrs.elements - 1;
@@ -654,7 +688,7 @@ bool Unique::merge(TABLE *table, uchar *buff, bool without_last_merge)
/* Open cached file if it isn't open */
if (!outfile)
outfile= table->sort.io_cache= (IO_CACHE*) my_malloc(sizeof(IO_CACHE),
- MYF(MY_ZEROFILL));
+ MYF(MY_THREAD_SPECIFIC|MY_ZEROFILL));
if (!outfile ||
(! my_b_inited(outfile) &&
open_cached_file(outfile,mysql_tmpdir,TEMP_PREFIX,READ_RECORD_BUFFER,
@@ -662,6 +696,7 @@ bool Unique::merge(TABLE *table, uchar *buff, bool without_last_merge)
return 1;
reinit_io_cache(outfile,WRITE_CACHE,0L,0,0);
+ Sort_param sort_param;
bzero((char*) &sort_param,sizeof(sort_param));
sort_param.max_rows= elements;
sort_param.sort_form= table;
@@ -669,10 +704,12 @@ bool Unique::merge(TABLE *table, uchar *buff, bool without_last_merge)
full_size;
sort_param.min_dupl_count= min_dupl_count;
sort_param.res_length= 0;
- sort_param.keys= (uint) (max_in_memory_size / sort_param.sort_length);
+ sort_param.max_keys_per_buffer=
+ (uint) (max_in_memory_size / sort_param.sort_length);
sort_param.not_killable= 1;
- sort_param.unique_buff= buff + (sort_param.keys * sort_param.sort_length);
+ sort_param.unique_buff= buff +(sort_param.max_keys_per_buffer *
+ sort_param.sort_length);
sort_param.compare= (qsort2_cmp) buffpek_compare;
sort_param.cmp_context.key_compare= tree.compare;
@@ -722,7 +759,7 @@ bool Unique::get(TABLE *table)
{
/* Whole tree is in memory; Don't use disk if you don't need to */
if ((record_pointers=table->sort.record_pointers= (uchar*)
- my_malloc(size * tree.elements_in_tree, MYF(0))))
+ my_malloc(size * tree.elements_in_tree, MYF(MY_THREAD_SPECIFIC))))
{
tree_walk_action action= min_dupl_count ?
(tree_walk_action) unique_intersect_write_to_ptrs :
@@ -738,7 +775,7 @@ bool Unique::get(TABLE *table)
if (flush())
return 1;
size_t buff_sz= (max_in_memory_size / full_size + 1) * full_size;
- if (!(sort_buffer= (uchar*) my_malloc(buff_sz, MYF(MY_WME))))
+ if (!(sort_buffer= (uchar*) my_malloc(buff_sz, MYF(MY_THREAD_SPECIFIC|MY_WME))))
return 1;
if (merge(table, sort_buffer, FALSE))
diff --git a/sql/unireg.cc b/sql/unireg.cc
index 528c3025c57..12d3c265a86 100644
--- a/sql/unireg.cc
+++ b/sql/unireg.cc
@@ -1,5 +1,6 @@
/*
Copyright (c) 2000, 2011, Oracle and/or its affiliates.
+ Copyright (c) 2009, 2013, Monty Program Ab.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -24,151 +25,107 @@
str is a (long) to record position where 0 is the first position.
*/
+#include <my_global.h>
#include "sql_priv.h"
#include "unireg.h"
#include "sql_partition.h" // struct partition_info
-#include "sql_table.h" // check_duplicate_warning
#include "sql_class.h" // THD, Internal_error_handler
#include "create_options.h"
+#include "discover.h"
#include <m_ctype.h>
-#include <assert.h>
#define FCOMP 17 /* Bytes for a packed field */
/* threshold for safe_alloca */
#define ALLOCA_THRESHOLD 2048
-static uchar * pack_screens(List<Create_field> &create_fields,
- uint *info_length, uint *screens, bool small_file);
-static uint pack_keys(uchar *keybuff,uint key_count, KEY *key_info,
- ulong data_offset);
-static bool pack_header(uchar *forminfo,enum legacy_db_type table_type,
- List<Create_field> &create_fields,
- uint info_length, uint screens, uint table_options,
- ulong data_offset, handler *file);
+static uint pack_keys(uchar *,uint, KEY *, ulong);
+static bool pack_header(THD *, uchar *, List<Create_field> &, uint, ulong, handler *);
static uint get_interval_id(uint *,List<Create_field> &, Create_field *);
-static bool pack_fields(File file, List<Create_field> &create_fields,
- ulong data_offset);
-static bool make_empty_rec(THD *thd, int file, enum legacy_db_type table_type,
- uint table_options,
- List<Create_field> &create_fields,
- uint reclength, ulong data_offset,
- handler *handler);
-
-/**
- An interceptor to hijack ER_TOO_MANY_FIELDS error from
- pack_screens and retry again without UNIREG screens.
+static bool pack_fields(uchar *, List<Create_field> &, ulong);
+static size_t packed_fields_length(List<Create_field> &);
+static bool make_empty_rec(THD *, uchar *, uint, List<Create_field> &, uint, ulong);
- XXX: what is a UNIREG screen?
+/*
+ write the length as
+ if ( 0 < length <= 255) one byte
+ if (256 < length <= 65535) zero byte, then two bytes, low-endian
*/
+static uchar *extra2_write_len(uchar *pos, size_t len)
+{
+ if (len <= 255)
+ *pos++= len;
+ else
+ {
+ /*
+ At the moment we support options_len up to 64K.
+ We can easily extend it in the future, if the need arises.
+ */
+ DBUG_ASSERT(len <= 65535);
+ int2store(pos + 1, len);
+ pos+= 3;
+ }
+ return pos;
+}
-struct Pack_header_error_handler: public Internal_error_handler
+static uchar *extra2_write(uchar *pos, enum extra2_frm_value_type type,
+ LEX_STRING *str)
{
- virtual bool handle_condition(THD *thd,
- uint sql_errno,
- const char* sqlstate,
- MYSQL_ERROR::enum_warning_level level,
- const char* msg,
- MYSQL_ERROR ** cond_hdl);
- bool is_handled;
- Pack_header_error_handler() :is_handled(FALSE) {}
-};
-
-
-bool
-Pack_header_error_handler::
-handle_condition(THD *,
- uint sql_errno,
- const char*,
- MYSQL_ERROR::enum_warning_level,
- const char*,
- MYSQL_ERROR ** cond_hdl)
+ *pos++ = type;
+ pos= extra2_write_len(pos, str->length);
+ memcpy(pos, str->str, str->length);
+ return pos + str->length;
+}
+
+static uchar *extra2_write(uchar *pos, enum extra2_frm_value_type type,
+ LEX_CUSTRING *str)
{
- *cond_hdl= NULL;
- is_handled= (sql_errno == ER_TOO_MANY_FIELDS);
- return is_handled;
+ return extra2_write(pos, type, reinterpret_cast<LEX_STRING *>(str));
}
-/*
+/**
Create a frm (table definition) file
- SYNOPSIS
- mysql_create_frm()
- thd Thread handler
- file_name Path for file (including database and .frm)
- db Name of database
- table Name of table
- create_info create info parameters
- create_fields Fields to create
- keys number of keys to create
- key_info Keys to create
- db_file Handler to use. May be zero, in which case we use
- create_info->db_type
- RETURN
- false ok
- true error
+ @param thd Thread handler
+ @param table Name of table
+ @param create_info create info parameters
+ @param create_fields Fields to create
+ @param keys number of keys to create
+ @param key_info Keys to create
+ @param db_file Handler to use.
+
+ @return the generated frm image as a LEX_CUSTRING,
+ or null LEX_CUSTRING (str==0) in case of an error.
*/
-bool mysql_create_frm(THD *thd, const char *file_name,
- const char *db, const char *table,
- HA_CREATE_INFO *create_info,
- List<Create_field> &create_fields,
- uint keys, KEY *key_info,
- handler *db_file)
+LEX_CUSTRING build_frm_image(THD *thd, const char *table,
+ HA_CREATE_INFO *create_info,
+ List<Create_field> &create_fields,
+ uint keys, KEY *key_info, handler *db_file)
{
LEX_STRING str_db_type;
- uint reclength, info_length, screens, key_info_length, maxlength, tmp_len, i;
+ uint reclength, key_info_length, i;
ulong key_buff_length;
- File file;
ulong filepos, data_offset;
uint options_len;
- uchar fileinfo[64],forminfo[288],*keybuff;
- uchar *screen_buff;
- char buff[128];
-#ifdef WITH_PARTITION_STORAGE_ENGINE
- partition_info *part_info= thd->work_part_info;
-#endif
- Pack_header_error_handler pack_header_error_handler;
+ uchar fileinfo[FRM_HEADER_SIZE],forminfo[FRM_FORMINFO_SIZE];
+ const partition_info *part_info= IF_PARTITIONING(thd->work_part_info, 0);
int error;
- DBUG_ENTER("mysql_create_frm");
-
- DBUG_ASSERT(*fn_rext((char*)file_name)); // Check .frm extension
-
- if (!(screen_buff=pack_screens(create_fields,&info_length,&screens,0)))
- DBUG_RETURN(1);
- DBUG_ASSERT(db_file != NULL);
+ uchar *frm_ptr, *pos;
+ LEX_CUSTRING frm= {0,0};
+ DBUG_ENTER("build_frm_image");
/* If fixed row records, we need one bit to check for deleted rows */
if (!(create_info->table_options & HA_OPTION_PACK_RECORD))
create_info->null_bits++;
data_offset= (create_info->null_bits + 7) / 8;
- thd->push_internal_handler(&pack_header_error_handler);
-
- error= pack_header(forminfo, ha_legacy_type(create_info->db_type),
- create_fields,info_length,
- screens, create_info->table_options,
+ error= pack_header(thd, forminfo, create_fields, create_info->table_options,
data_offset, db_file);
- thd->pop_internal_handler();
-
if (error)
- {
- my_free(screen_buff);
- if (! pack_header_error_handler.is_handled)
- DBUG_RETURN(1);
-
- // Try again without UNIREG screens (to get more columns)
- if (!(screen_buff=pack_screens(create_fields,&info_length,&screens,1)))
- DBUG_RETURN(1);
- if (pack_header(forminfo, ha_legacy_type(create_info->db_type),
- create_fields,info_length,
- screens, create_info->table_options, data_offset, db_file))
- {
- my_free(screen_buff);
- DBUG_RETURN(1);
- }
- }
+ DBUG_RETURN(frm);
+
reclength=uint2korr(forminfo+266);
/* Calculate extra data segment length */
@@ -184,12 +141,8 @@ bool mysql_create_frm(THD *thd, const char *file_name,
=> Total 6 byte
*/
create_info->extra_size+= 6;
-#ifdef WITH_PARTITION_STORAGE_ENGINE
if (part_info)
- {
create_info->extra_size+= part_info->part_info_len;
- }
-#endif
for (i= 0; i < keys; i++)
{
@@ -201,234 +154,164 @@ bool mysql_create_frm(THD *thd, const char *file_name,
create_fields,
keys, key_info);
DBUG_PRINT("info", ("Options length: %u", options_len));
- if (options_len)
- {
- create_info->table_options|= HA_OPTION_TEXT_CREATE_OPTIONS;
- create_info->extra_size+= (options_len + 4);
- }
- else
- create_info->table_options&= ~HA_OPTION_TEXT_CREATE_OPTIONS;
-
- /*
- This gives us the byte-position of the character at
- (character-position, not byte-position) TABLE_COMMENT_MAXLEN.
- The trick here is that character-positions start at 0, so the last
- character in a maximum-allowed length string would be at char-pos
- MAXLEN-1; charpos MAXLEN will be the position of the terminator.
- Consequently, bytepos(charpos(MAXLEN)) should be equal to
- comment[length] (which should also be the terminator, or at least
- the first byte after the payload in the strict sense). If this is
- not so (bytepos(charpos(MAXLEN)) comes /before/ the end of the
- string), the string is too long.
-
- For additional credit, realise that UTF-8 has 1-3 bytes before 6.0,
- and 1-4 bytes in 6.0 (6.0 also has UTF-32).
- */
- tmp_len= system_charset_info->cset->charpos(system_charset_info,
- create_info->comment.str,
- create_info->comment.str +
- create_info->comment.length,
- TABLE_COMMENT_MAXLEN);
- if (tmp_len < create_info->comment.length)
- {
- char *real_table_name= (char*) table;
- List_iterator<Create_field> it(create_fields);
- Create_field *field;
- while ((field=it++))
- {
- if (field->field && field->field->table &&
- (real_table_name= field->field->table->s->table_name.str))
- break;
- }
- if ((thd->variables.sql_mode &
- (MODE_STRICT_TRANS_TABLES | MODE_STRICT_ALL_TABLES)))
- {
- my_error(ER_TOO_LONG_TABLE_COMMENT, MYF(0),
- real_table_name, static_cast<ulong>(TABLE_COMMENT_MAXLEN));
- my_free(screen_buff);
- DBUG_RETURN(1);
- }
- char warn_buff[MYSQL_ERRMSG_SIZE];
- my_snprintf(warn_buff, sizeof(warn_buff), ER(ER_TOO_LONG_TABLE_COMMENT),
- real_table_name, static_cast<ulong>(TABLE_COMMENT_MAXLEN));
- /* do not push duplicate warnings */
- if (!check_duplicate_warning(current_thd, warn_buff, strlen(warn_buff)))
- push_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
- ER_TOO_LONG_TABLE_COMMENT, warn_buff);
- create_info->comment.length= tmp_len;
- }
+ if (validate_comment_length(thd, &create_info->comment, TABLE_COMMENT_MAXLEN,
+ ER_TOO_LONG_TABLE_COMMENT, table))
+ DBUG_RETURN(frm);
/*
If table comment is longer than TABLE_COMMENT_INLINE_MAXLEN bytes,
store the comment in an extra segment (up to TABLE_COMMENT_MAXLEN bytes).
- Pre 6.0, the limit was 60 characters, with no extra segment-handling.
+ Pre 5.5, the limit was 60 characters, with no extra segment-handling.
*/
if (create_info->comment.length > TABLE_COMMENT_INLINE_MAXLEN)
{
forminfo[46]=255;
create_info->extra_size+= 2 + create_info->comment.length;
}
- else{
+ else
+ {
strmake((char*) forminfo+47, create_info->comment.str ?
create_info->comment.str : "", create_info->comment.length);
forminfo[46]=(uchar) create_info->comment.length;
}
- if ((file=create_frm(thd, file_name, db, table, reclength, fileinfo,
- create_info, keys, key_info)) < 0)
+ if (!create_info->tabledef_version.str)
{
- my_free(screen_buff);
- DBUG_RETURN(1);
+ uchar *to= (uchar*) thd->alloc(MY_UUID_SIZE);
+ if (unlikely(!to))
+ DBUG_RETURN(frm);
+ my_uuid(to);
+ create_info->tabledef_version.str= to;
+ create_info->tabledef_version.length= MY_UUID_SIZE;
}
+ DBUG_ASSERT(create_info->tabledef_version.length > 0);
+ DBUG_ASSERT(create_info->tabledef_version.length <= 255);
+
+ prepare_frm_header(thd, reclength, fileinfo, create_info, keys, key_info);
+
+ /* one byte for a type, one or three for a length */
+ uint extra2_size= 1 + 1 + create_info->tabledef_version.length;
+ if (options_len)
+ extra2_size+= 1 + (options_len > 255 ? 3 : 1) + options_len;
+
+ if (part_info)
+ extra2_size+= 1 + 1 + hton_name(part_info->default_engine_type)->length;
key_buff_length= uint4korr(fileinfo+47);
- keybuff=(uchar*) my_malloc(key_buff_length, MYF(0));
- key_info_length= pack_keys(keybuff, keys, key_info, data_offset);
- /*
- Ensure that there are no forms in this newly created form file.
- Even if the form file exists, create_frm must truncate it to
- ensure one form per form file.
- */
- DBUG_ASSERT(uint2korr(fileinfo+8) == 0);
+ frm.length= FRM_HEADER_SIZE; // fileinfo;
+ frm.length+= extra2_size + 4; // mariadb extra2 frm segment
- if (!(filepos= make_new_entry(file, fileinfo, NULL, "")))
- goto err;
- maxlength=(uint) next_io_size((ulong) (uint2korr(forminfo)+1000));
- int2store(forminfo+2,maxlength);
- int4store(fileinfo+10,(ulong) (filepos+maxlength));
- fileinfo[26]= (uchar) test((create_info->max_rows == 1) &&
- (create_info->min_rows == 1) && (keys == 0));
+ int2store(fileinfo+4, extra2_size);
+ int2store(fileinfo+6, frm.length); // Position to key information
+ frm.length+= key_buff_length;
+ frm.length+= reclength; // row with default values
+ frm.length+= create_info->extra_size;
+
+ filepos= frm.length;
+ frm.length+= FRM_FORMINFO_SIZE; // forminfo
+ frm.length+= packed_fields_length(create_fields);
+
+ if (frm.length > FRM_MAX_SIZE)
+ {
+ my_error(ER_TABLE_DEFINITION_TOO_BIG, MYF(0), table);
+ DBUG_RETURN(frm);
+ }
+
+ frm_ptr= (uchar*) my_malloc(frm.length, MYF(MY_WME | MY_ZEROFILL |
+ MY_THREAD_SPECIFIC));
+ if (!frm_ptr)
+ DBUG_RETURN(frm);
+
+ /* write the extra2 segment */
+ pos = frm_ptr + 64;
+ compile_time_assert(EXTRA2_TABLEDEF_VERSION != '/');
+ pos= extra2_write(pos, EXTRA2_TABLEDEF_VERSION,
+ &create_info->tabledef_version);
+
+ if (part_info)
+ pos= extra2_write(pos, EXTRA2_DEFAULT_PART_ENGINE,
+ hton_name(part_info->default_engine_type));
+
+ if (options_len)
+ {
+ *pos++= EXTRA2_ENGINE_TABLEOPTS;
+ pos= extra2_write_len(pos, options_len);
+ pos= engine_table_options_frm_image(pos, create_info->option_list,
+ create_fields, keys, key_info);
+ }
+
+ int4store(pos, filepos); // end of the extra2 segment
+ pos+= 4;
+
+ DBUG_ASSERT(pos == frm_ptr + uint2korr(fileinfo+6));
+ key_info_length= pack_keys(pos, keys, key_info, data_offset);
+
+ int2store(forminfo+2, frm.length - filepos);
+ int4store(fileinfo+10, frm.length);
+ fileinfo[26]= (uchar) MY_TEST((create_info->max_rows == 1) &&
+ (create_info->min_rows == 1) && (keys == 0));
int2store(fileinfo+28,key_info_length);
-#ifdef WITH_PARTITION_STORAGE_ENGINE
if (part_info)
{
fileinfo[61]= (uchar) ha_legacy_type(part_info->default_engine_type);
DBUG_PRINT("info", ("part_db_type = %d", fileinfo[61]));
}
-#endif
- int2store(fileinfo+59,db_file->extra_rec_buf_length());
- if (mysql_file_pwrite(file, fileinfo, 64, 0L, MYF_RW) ||
- mysql_file_pwrite(file, keybuff, key_info_length,
- (ulong) uint2korr(fileinfo+6), MYF_RW))
- goto err;
- mysql_file_seek(file,
- (ulong) uint2korr(fileinfo+6) + (ulong) key_buff_length,
- MY_SEEK_SET, MYF(0));
- if (make_empty_rec(thd,file,ha_legacy_type(create_info->db_type),
- create_info->table_options,
- create_fields,reclength, data_offset, db_file))
- goto err;
+ int2store(fileinfo+59,db_file->extra_rec_buf_length());
- int2store(buff, create_info->connect_string.length);
- if (mysql_file_write(file, (const uchar*)buff, 2, MYF(MY_NABP)) ||
- mysql_file_write(file, (const uchar*)create_info->connect_string.str,
- create_info->connect_string.length, MYF(MY_NABP)))
- goto err;
+ memcpy(frm_ptr, fileinfo, FRM_HEADER_SIZE);
- int2store(buff, str_db_type.length);
- if (mysql_file_write(file, (const uchar*)buff, 2, MYF(MY_NABP)) ||
- mysql_file_write(file, (const uchar*)str_db_type.str,
- str_db_type.length, MYF(MY_NABP)))
+ pos+= key_buff_length;
+ if (make_empty_rec(thd, pos, create_info->table_options, create_fields,
+ reclength, data_offset))
goto err;
-#ifdef WITH_PARTITION_STORAGE_ENGINE
+ pos+= reclength;
+ int2store(pos, create_info->connect_string.length);
+ pos+= 2;
+ memcpy(pos, create_info->connect_string.str, create_info->connect_string.length);
+ pos+= create_info->connect_string.length;
+ int2store(pos, str_db_type.length);
+ pos+= 2;
+ memcpy(pos, str_db_type.str, str_db_type.length);
+ pos+= str_db_type.length;
+
if (part_info)
{
char auto_partitioned= part_info->is_auto_partitioned ? 1 : 0;
- int4store(buff, part_info->part_info_len);
- if (mysql_file_write(file, (const uchar*)buff, 4, MYF_RW) ||
- mysql_file_write(file, (const uchar*)part_info->part_info_string,
- part_info->part_info_len + 1, MYF_RW) ||
- mysql_file_write(file, (const uchar*)&auto_partitioned, 1, MYF_RW))
- goto err;
+ int4store(pos, part_info->part_info_len);
+ pos+= 4;
+ memcpy(pos, part_info->part_info_string, part_info->part_info_len + 1);
+ pos+= part_info->part_info_len + 1;
+ *pos++= auto_partitioned;
}
else
-#endif
{
- bzero((uchar*) buff, 6);
- if (mysql_file_write(file, (uchar*) buff, 6, MYF_RW))
- goto err;
+ pos+= 6;
}
for (i= 0; i < keys; i++)
{
if (key_info[i].parser_name)
{
- if (mysql_file_write(file, (const uchar*)key_info[i].parser_name->str,
- key_info[i].parser_name->length + 1, MYF(MY_NABP)))
- goto err;
+ memcpy(pos, key_info[i].parser_name->str, key_info[i].parser_name->length + 1);
+ pos+= key_info[i].parser_name->length + 1;
}
}
- if (forminfo[46] == (uchar)255)
+ if (forminfo[46] == (uchar)255) // New style MySQL 5.5 table comment
{
- uchar comment_length_buff[2];
- int2store(comment_length_buff,create_info->comment.length);
- if (mysql_file_write(file, comment_length_buff, 2, MYF(MY_NABP)) ||
- mysql_file_write(file, (uchar*) create_info->comment.str,
- create_info->comment.length, MYF(MY_NABP)))
- goto err;
+ int2store(pos, create_info->comment.length);
+ pos+=2;
+ memcpy(pos, create_info->comment.str, create_info->comment.length);
+ pos+= create_info->comment.length;
}
- if (options_len)
- {
- uchar *optbuff= (uchar *)my_safe_alloca(options_len + 4, ALLOCA_THRESHOLD);
- my_bool error;
- DBUG_PRINT("info", ("Create options length: %u", options_len));
- if (!optbuff)
- goto err;
- int4store(optbuff, options_len);
- engine_table_options_frm_image(optbuff + 4,
- create_info->option_list,
- create_fields,
- keys, key_info);
- error= my_write(file, optbuff, options_len + 4, MYF_RW);
- my_safe_afree(optbuff, options_len + 4, ALLOCA_THRESHOLD);
- if (error)
- goto err;
- }
-
- mysql_file_seek(file, filepos, MY_SEEK_SET, MYF(0));
- if (mysql_file_write(file, forminfo, 288, MYF_RW) ||
- mysql_file_write(file, screen_buff, info_length, MYF_RW) ||
- pack_fields(file, create_fields, data_offset))
+ memcpy(frm_ptr + filepos, forminfo, 288);
+ if (pack_fields(frm_ptr + filepos + 288, create_fields, data_offset))
goto err;
-#ifdef HAVE_CRYPTED_FRM
- if (create_info->password)
- {
- char tmp=2,*disk_buff=0;
- SQL_CRYPT *crypted=new SQL_CRYPT(create_info->password);
- if (!crypted || mysql_file_pwrite(file, &tmp, 1, 26, MYF_RW))// Mark crypted
- goto err;
- uint read_length=uint2korr(forminfo)-256;
- mysql_file_seek(file, filepos+256, MY_SEEK_SET, MYF(0));
- if (read_string(file,(uchar**) &disk_buff,read_length))
- goto err;
- crypted->encode(disk_buff,read_length);
- delete crypted;
- if (mysql_file_pwrite(file, disk_buff, read_length, filepos+256, MYF_RW))
- {
- my_free(disk_buff);
- goto err;
- }
- my_free(disk_buff);
- }
-#endif
-
- my_free(screen_buff);
- my_free(keybuff);
-
- if (opt_sync_frm && !(create_info->options & HA_LEX_CREATE_TMP_TABLE) &&
- (mysql_file_sync(file, MYF(MY_WME)) ||
- my_sync_dir_by_file(file_name, MYF(MY_WME))))
- goto err2;
-
- if (mysql_file_close(file, MYF(MY_WME)))
- goto err3;
-
{
/*
Restore all UCS2 intervals.
@@ -445,149 +328,68 @@ bool mysql_create_frm(THD *thd, const char *file_name,
}
}
}
- DBUG_RETURN(0);
+
+ frm.str= frm_ptr;
+ DBUG_RETURN(frm);
err:
- my_free(screen_buff);
- my_free(keybuff);
-err2:
- (void) mysql_file_close(file, MYF(MY_WME));
-err3:
- mysql_file_delete(key_file_frm, file_name, MYF(0));
- DBUG_RETURN(1);
-} /* mysql_create_frm */
+ my_free(frm_ptr);
+ DBUG_RETURN(frm);
+}
-/*
+/**
Create a frm (table definition) file and the tables
- SYNOPSIS
- rea_create_table()
- thd Thread handler
- path Name of file (including database, without .frm)
- db Data base name
- table_name Table name
- create_info create info parameters
- create_fields Fields to create
- keys number of keys to create
- key_info Keys to create
- file Handler to use
-
- RETURN
- 0 ok
- 1 error
+ @param thd Thread handler
+ @param frm Binary frm image of the table to create
+ @param path Name of file (including database, without .frm)
+ @param db Data base name
+ @param table_name Table name
+ @param create_info create info parameters
+ @param file Handler to use or NULL if only frm needs to be created
+
+ @retval 0 ok
+ @retval 1 error
*/
-int rea_create_table(THD *thd, const char *path,
- const char *db, const char *table_name,
- HA_CREATE_INFO *create_info,
- List<Create_field> &create_fields,
- uint keys, KEY *key_info, handler *file)
+int rea_create_table(THD *thd, LEX_CUSTRING *frm,
+ const char *path, const char *db, const char *table_name,
+ HA_CREATE_INFO *create_info, handler *file,
+ bool no_ha_create_table)
{
DBUG_ENTER("rea_create_table");
- char frm_name[FN_REFLEN];
- strxmov(frm_name, path, reg_ext, NullS);
- if (mysql_create_frm(thd, frm_name, db, table_name, create_info,
- create_fields, keys, key_info, file))
-
- DBUG_RETURN(1);
+ // TODO don't write frm for temp tables
+ if (no_ha_create_table || create_info->tmp_table())
+ {
+ if (writefrm(path, db, table_name, true, frm->str, frm->length))
+ goto err_frm;
+ }
- // Make sure mysql_create_frm din't remove extension
- DBUG_ASSERT(*fn_rext(frm_name));
if (thd->variables.keep_files_on_create)
create_info->options|= HA_CREATE_KEEP_FILES;
- if (!create_info->frm_only &&
- (file->ha_create_handler_files(path, NULL, CHF_CREATE_FLAG,
- create_info) ||
- ha_create_table(thd, path, db, table_name, create_info, 0)))
- goto err_handler;
- DBUG_RETURN(0);
-
-err_handler:
- (void) file->ha_create_handler_files(path, NULL, CHF_DELETE_FLAG, create_info);
- mysql_file_delete(key_file_frm, frm_name, MYF(0));
- DBUG_RETURN(1);
-} /* rea_create_table */
-
-
- /* Pack screens to a screen for save in a form-file */
-
-static uchar *pack_screens(List<Create_field> &create_fields,
- uint *info_length, uint *screens,
- bool small_file)
-{
- reg1 uint i;
- uint row,start_row,end_row,fields_on_screen;
- uint length,cols;
- uchar *info,*pos,*start_screen;
- uint fields=create_fields.elements;
- List_iterator<Create_field> it(create_fields);
- DBUG_ENTER("pack_screens");
-
- start_row=4; end_row=22; cols=80; fields_on_screen=end_row+1-start_row;
- *screens=(fields-1)/fields_on_screen+1;
- length= (*screens) * (SC_INFO_LENGTH+ (cols>> 1)+4);
+ if (file->ha_create_partitioning_metadata(path, NULL, CHF_CREATE_FLAG))
+ goto err_part;
- Create_field *field;
- while ((field=it++))
- length+=(uint) strlen(field->field_name)+1+TE_INFO_LENGTH+cols/2;
-
- if (!(info=(uchar*) my_malloc(length,MYF(MY_WME))))
- DBUG_RETURN(0);
-
- start_screen=0;
- row=end_row;
- pos=info;
- it.rewind();
- for (i=0 ; i < fields ; i++)
+ if (!no_ha_create_table)
{
- Create_field *cfield=it++;
- if (row++ == end_row)
- {
- if (i)
- {
- length=(uint) (pos-start_screen);
- int2store(start_screen,length);
- start_screen[2]=(uchar) (fields_on_screen+1);
- start_screen[3]=(uchar) (fields_on_screen);
- }
- row=start_row;
- start_screen=pos;
- pos+=4;
- pos[0]= (uchar) start_row-2; /* Header string */
- pos[1]= (uchar) (cols >> 2);
- pos[2]= (uchar) (cols >> 1) +1;
- strfill((char *) pos+3,(uint) (cols >> 1),' ');
- pos+=(cols >> 1)+4;
- }
- length=(uint) strlen(cfield->field_name);
- if (length > cols-3)
- length=cols-3;
-
- if (!small_file)
- {
- pos[0]=(uchar) row;
- pos[1]=0;
- pos[2]=(uchar) (length+1);
- pos=(uchar*) strmake((char*) pos+3,cfield->field_name,length)+1;
- }
- cfield->row=(uint8) row;
- cfield->col=(uint8) (length+1);
- cfield->sc_length=(uint8) min(cfield->length,cols-(length+2));
+ if (ha_create_table(thd, path, db, table_name, create_info, frm))
+ goto err_part;
}
- length=(uint) (pos-start_screen);
- int2store(start_screen,length);
- start_screen[2]=(uchar) (row-start_row+2);
- start_screen[3]=(uchar) (row-start_row+1);
- *info_length=(uint) (pos-info);
- DBUG_RETURN(info);
-} /* pack_screens */
+ DBUG_RETURN(0);
+
+err_part:
+ file->ha_create_partitioning_metadata(path, NULL, CHF_DELETE_FLAG);
+err_frm:
+ deletefrm(path);
+ DBUG_RETURN(1);
+} /* rea_create_table */
- /* Pack keyinfo and keynames to keybuff for save in form-file. */
+/* Pack keyinfo and keynames to keybuff for save in form-file. */
static uint pack_keys(uchar *keybuff, uint key_count, KEY *keyinfo,
ulong data_offset)
@@ -604,15 +406,15 @@ static uint pack_keys(uchar *keybuff, uint key_count, KEY *keyinfo,
{
int2store(pos, (key->flags ^ HA_NOSAME));
int2store(pos+2,key->key_length);
- pos[4]= (uchar) key->key_parts;
+ pos[4]= (uchar) key->user_defined_key_parts;
pos[5]= (uchar) key->algorithm;
int2store(pos+6, key->block_size);
pos+=8;
- key_parts+=key->key_parts;
+ key_parts+=key->user_defined_key_parts;
DBUG_PRINT("loop", ("flags: %lu key_parts: %d key_part: 0x%lx",
- key->flags, key->key_parts,
+ key->flags, key->user_defined_key_parts,
(long) key->key_part));
- for (key_part=key->key_part,key_part_end=key_part+key->key_parts ;
+ for (key_part=key->key_part,key_part_end=key_part+key->user_defined_key_parts ;
key_part != key_part_end ;
key_part++)
@@ -670,12 +472,11 @@ static uint pack_keys(uchar *keybuff, uint key_count, KEY *keyinfo,
} /* pack_keys */
- /* Make formheader */
+/* Make formheader */
-static bool pack_header(uchar *forminfo, enum legacy_db_type table_type,
- List<Create_field> &create_fields,
- uint info_length, uint screens, uint table_options,
- ulong data_offset, handler *file)
+static bool pack_header(THD *thd, uchar *forminfo,
+ List<Create_field> &create_fields,
+ uint table_options, ulong data_offset, handler *file)
{
uint length,int_count,int_length,no_empty, int_parts;
uint time_stamp_pos,null_fields;
@@ -694,45 +495,23 @@ static bool pack_header(uchar *forminfo, enum legacy_db_type table_type,
com_length=vcol_info_length=0;
n_length=2L;
- /* Check fields */
-
+ /* Check fields */
List_iterator<Create_field> it(create_fields);
Create_field *field;
while ((field=it++))
{
- uint tmp_len= system_charset_info->cset->charpos(system_charset_info,
- field->comment.str,
- field->comment.str +
- field->comment.length,
- COLUMN_COMMENT_MAXLEN);
- if (tmp_len < field->comment.length)
- {
- if ((current_thd->variables.sql_mode &
- (MODE_STRICT_TRANS_TABLES | MODE_STRICT_ALL_TABLES)))
- {
- my_error(ER_TOO_LONG_FIELD_COMMENT, MYF(0), field->field_name,
- static_cast<ulong>(COLUMN_COMMENT_MAXLEN));
- DBUG_RETURN(1);
- }
- char warn_buff[MYSQL_ERRMSG_SIZE];
- my_snprintf(warn_buff, sizeof(warn_buff), ER(ER_TOO_LONG_FIELD_COMMENT),
- field->field_name,
- static_cast<ulong>(COLUMN_COMMENT_MAXLEN));
- /* do not push duplicate warnings */
- if (!check_duplicate_warning(current_thd, warn_buff, strlen(warn_buff)))
- push_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
- ER_TOO_LONG_FIELD_COMMENT, warn_buff);
- field->comment.length= tmp_len;
- }
+ if (validate_comment_length(thd, &field->comment, COLUMN_COMMENT_MAXLEN,
+ ER_TOO_LONG_FIELD_COMMENT, field->field_name))
+ DBUG_RETURN(1);
+
if (field->vcol_info)
{
uint col_expr_maxlen= field->virtual_col_expr_maxlen();
- tmp_len=
- system_charset_info->cset->charpos(system_charset_info,
- field->vcol_info->expr_str.str,
- field->vcol_info->expr_str.str +
- field->vcol_info->expr_str.length,
- col_expr_maxlen);
+ uint tmp_len= my_charpos(system_charset_info,
+ field->vcol_info->expr_str.str,
+ field->vcol_info->expr_str.str +
+ field->vcol_info->expr_str.length,
+ col_expr_maxlen);
if (tmp_len < field->vcol_info->expr_str.length)
{
@@ -747,7 +526,7 @@ static bool pack_header(uchar *forminfo, enum legacy_db_type table_type,
expressions saved in the frm file for virtual columns.
*/
vcol_info_length+= field->vcol_info->expr_str.length+
- FRM_VCOL_HEADER_SIZE(field->interval!=NULL);
+ FRM_VCOL_HEADER_SIZE(field->interval);
}
totlength+= field->length;
@@ -824,16 +603,15 @@ static bool pack_header(uchar *forminfo, enum legacy_db_type table_type,
}
int_length+=int_count*2; // 255 prefix + 0 suffix
- /* Save values in forminfo */
-
+ /* Save values in forminfo */
if (reclength > (ulong) file->max_record_length())
{
my_error(ER_TOO_BIG_ROWSIZE, MYF(0), static_cast<long>(file->max_record_length()));
DBUG_RETURN(1);
}
/* Hack to avoid bugs with small static rows in MySQL */
- reclength=max(file->min_record_length(table_options),reclength);
- if (info_length+(ulong) create_fields.elements*FCOMP+288+
+ reclength=MY_MAX(file->min_record_length(table_options),reclength);
+ if ((ulong) create_fields.elements*FCOMP+FRM_FORMINFO_SIZE+
n_length+int_length+com_length+vcol_info_length > 65535L ||
int_count > 255)
{
@@ -841,13 +619,13 @@ static bool pack_header(uchar *forminfo, enum legacy_db_type table_type,
DBUG_RETURN(1);
}
- bzero((char*)forminfo,288);
- length=(info_length+create_fields.elements*FCOMP+288+n_length+int_length+
+ bzero((char*)forminfo,FRM_FORMINFO_SIZE);
+ length=(create_fields.elements*FCOMP+FRM_FORMINFO_SIZE+n_length+int_length+
com_length+vcol_info_length);
int2store(forminfo,length);
- forminfo[256] = (uint8) screens;
+ forminfo[256] = 0;
int2store(forminfo+258,create_fields.elements);
- int2store(forminfo+260,info_length);
+ int2store(forminfo+260,0);
int2store(forminfo+262,totlength);
int2store(forminfo+264,no_empty);
int2store(forminfo+266,reclength);
@@ -861,13 +639,11 @@ static bool pack_header(uchar *forminfo, enum legacy_db_type table_type,
int2store(forminfo+282,null_fields);
int2store(forminfo+284,com_length);
int2store(forminfo+286,vcol_info_length);
- /* forminfo+288 is free to use for additional information */
DBUG_RETURN(0);
} /* pack_header */
- /* get each unique interval each own id */
-
+/* get each unique interval each own id */
static uint get_interval_id(uint *int_count,List<Create_field> &create_fields,
Create_field *last_field)
{
@@ -894,29 +670,57 @@ static uint get_interval_id(uint *int_count,List<Create_field> &create_fields,
}
- /* Save fields, fieldnames and intervals */
+static size_t packed_fields_length(List<Create_field> &create_fields)
+{
+ Create_field *field;
+ size_t length= 0;
+ DBUG_ENTER("packed_fields_length");
+
+ List_iterator<Create_field> it(create_fields);
+ uint int_count=0;
+ while ((field=it++))
+ {
+ if (field->interval_id > int_count)
+ {
+ int_count= field->interval_id;
+ length++;
+ for (int i=0; field->interval->type_names[i]; i++)
+ {
+ length+= field->interval->type_lengths[i];
+ length++;
+ }
+ length++;
+ }
+ if (field->vcol_info)
+ {
+ length+= field->vcol_info->expr_str.length +
+ FRM_VCOL_HEADER_SIZE(field->interval);
+ }
+ length+= FCOMP;
+ length+= strlen(field->field_name)+1;
+ length+= field->comment.length;
+ }
+ length++;
+ length++;
+ DBUG_RETURN(length);
+}
+
+/* Save fields, fieldnames and intervals */
-static bool pack_fields(File file, List<Create_field> &create_fields,
+static bool pack_fields(uchar *buff, List<Create_field> &create_fields,
ulong data_offset)
{
- reg2 uint i;
uint int_count, comment_length= 0, vcol_info_length=0;
- uchar buff[MAX_FIELD_WIDTH];
Create_field *field;
DBUG_ENTER("pack_fields");
- /* Write field info */
-
+ /* Write field info */
List_iterator<Create_field> it(create_fields);
-
int_count=0;
while ((field=it++))
{
uint recpos;
uint cur_vcol_expr_len= 0;
- buff[0]= (uchar) field->row;
- buff[1]= (uchar) field->col;
- buff[2]= (uchar) field->sc_length;
int2store(buff+3, field->length);
/* The +1 is here becasue the col offset in .frm file have offset 1 */
recpos= field->offset+1 + (uint) data_offset;
@@ -950,40 +754,29 @@ static bool pack_fields(File file, List<Create_field> &create_fields,
the additional data saved for the virtual field
*/
buff[12]= cur_vcol_expr_len= field->vcol_info->expr_str.length +
- FRM_VCOL_HEADER_SIZE(field->interval!=NULL);
- vcol_info_length+= cur_vcol_expr_len +
- FRM_VCOL_HEADER_SIZE(field->interval!=NULL);
+ FRM_VCOL_HEADER_SIZE(field->interval);
+ vcol_info_length+= cur_vcol_expr_len;
buff[13]= (uchar) MYSQL_TYPE_VIRTUAL;
}
int2store(buff+15, field->comment.length);
comment_length+= field->comment.length;
set_if_bigger(int_count,field->interval_id);
- if (mysql_file_write(file, buff, FCOMP, MYF_RW))
- DBUG_RETURN(1);
+ buff+= FCOMP;
}
- /* Write fieldnames */
- buff[0]=(uchar) NAMES_SEP_CHAR;
- if (mysql_file_write(file, buff, 1, MYF_RW))
- DBUG_RETURN(1);
- i=0;
+ /* Write fieldnames */
+ *buff++= NAMES_SEP_CHAR;
it.rewind();
while ((field=it++))
{
- char *pos= strmov((char*) buff,field->field_name);
- * (uchar*) pos++= (uchar) NAMES_SEP_CHAR;
- if (i == create_fields.elements-1)
- *pos++=0;
- if (mysql_file_write(file, buff, (size_t) (pos-(char*) buff), MYF_RW))
- DBUG_RETURN(1);
- i++;
+ buff= (uchar*)strmov((char*) buff, field->field_name);
+ *buff++=NAMES_SEP_CHAR;
}
+ *buff++= 0;
- /* Write intervals */
+ /* Write intervals */
if (int_count)
{
- String tmp((char*) buff,sizeof(buff), &my_charset_bin);
- tmp.length(0);
it.rewind();
int_count=0;
while ((field=it++))
@@ -1025,34 +818,30 @@ static bool pack_fields(File file, List<Create_field> &create_fields,
}
int_count= field->interval_id;
- tmp.append(sep);
- for (const char **pos=field->interval->type_names ; *pos ; pos++)
+ *buff++= sep;
+ for (int i=0; field->interval->type_names[i]; i++)
{
- tmp.append(*pos);
- tmp.append(sep);
+ memcpy(buff, field->interval->type_names[i], field->interval->type_lengths[i]);
+ buff+= field->interval->type_lengths[i];
+ *buff++= sep;
}
- tmp.append('\0'); // End of intervall
+ *buff++= 0;
+
}
}
- if (mysql_file_write(file, (uchar*) tmp.ptr(), tmp.length(), MYF_RW))
- DBUG_RETURN(1);
}
if (comment_length)
{
it.rewind();
- int_count=0;
while ((field=it++))
{
- if (field->comment.length)
- if (mysql_file_write(file, (uchar*) field->comment.str,
- field->comment.length, MYF_RW))
- DBUG_RETURN(1);
+ memcpy(buff, field->comment.str, field->comment.length);
+ buff+= field->comment.length;
}
}
if (vcol_info_length)
{
it.rewind();
- int_count=0;
while ((field=it++))
{
/*
@@ -1065,18 +854,13 @@ static bool pack_fields(File file, List<Create_field> &create_fields,
*/
if (field->vcol_info && field->vcol_info->expr_str.length)
{
- buff[0]= (uchar)(1 + test(field->interval_id));
- buff[1]= (uchar) field->sql_type;
- buff[2]= (uchar) field->stored_in_db;
- if (field->interval_id)
- buff[3]= (uchar) field->interval_id;
- if (my_write(file, buff, 3 + test(field->interval_id), MYF_RW))
- DBUG_RETURN(1);
- if (my_write(file,
- (uchar*) field->vcol_info->expr_str.str,
- field->vcol_info->expr_str.length,
- MYF_RW))
- DBUG_RETURN(1);
+ *buff++= (uchar) (1 + MY_TEST(field->interval));
+ *buff++= (uchar) field->sql_type;
+ *buff++= (uchar) field->stored_in_db;
+ if (field->interval)
+ *buff++= (uchar) field->interval_id;
+ memcpy(buff, field->vcol_info->expr_str.str, field->vcol_info->expr_str.length);
+ buff+= field->vcol_info->expr_str.length;
}
}
}
@@ -1084,19 +868,16 @@ static bool pack_fields(File file, List<Create_field> &create_fields,
}
- /* save an empty record on start of formfile */
+/* save an empty record on start of formfile */
-static bool make_empty_rec(THD *thd, File file,enum legacy_db_type table_type,
- uint table_options,
+static bool make_empty_rec(THD *thd, uchar *buff, uint table_options,
List<Create_field> &create_fields,
- uint reclength,
- ulong data_offset,
- handler *handler)
+ uint reclength, ulong data_offset)
{
int error= 0;
Field::utype type;
uint null_count;
- uchar *buff,*null_pos;
+ uchar *null_pos;
TABLE table;
TABLE_SHARE share;
Create_field *field;
@@ -1108,13 +889,7 @@ static bool make_empty_rec(THD *thd, File file,enum legacy_db_type table_type,
bzero((char*) &share, sizeof(share));
table.s= &share;
- if (!(buff=(uchar*) my_malloc((size_t) reclength,MYF(MY_WME | MY_ZEROFILL))))
- {
- DBUG_RETURN(1);
- }
-
table.in_use= thd;
- table.s->blob_ptr_size= portable_sizeof_char_ptr;
null_count=0;
if (!(table_options & HA_OPTION_PACK_RECORD))
@@ -1198,10 +973,7 @@ static bool make_empty_rec(THD *thd, File file,enum legacy_db_type table_type,
if (null_count & 7)
*(null_pos + null_count / 8)|= ~(((uchar) 1 << (null_count & 7)) - 1);
- error= mysql_file_write(file, buff, (size_t) reclength, MYF_RW) != 0;
-
err:
- my_free(buff);
thd->count_cuted_fields= old_count_cuted_fields;
DBUG_RETURN(error);
} /* make_empty_rec */
diff --git a/sql/unireg.h b/sql/unireg.h
index a35c25f92ee..b13dd494c74 100644
--- a/sql/unireg.h
+++ b/sql/unireg.h
@@ -18,13 +18,10 @@
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */
-#include "my_global.h" /* ulonglong */
-#include "mysql_version.h" /* FRM_VER */
+#include <mysql_version.h> /* FRM_VER */
/* Extra functions used by unireg library */
-typedef struct st_ha_create_information HA_CREATE_INFO;
-
#ifndef NO_ALARM_LOOP
#define NO_ALARM_LOOP /* lib5 and popen can't use alarm */
#endif
@@ -88,7 +85,7 @@ typedef struct st_ha_create_information HA_CREATE_INFO;
#define READ_ALL 1 /* openfrm: Read all parameters */
#define CHANGE_FRM 2 /* openfrm: open .frm as O_RDWR */
#define READ_KEYINFO 4 /* L{s nyckeldata fr}n filen */
-#define EXTRA_RECORD 8 /* Reservera plats f|r extra record */
+#define EXTRA_RECORD 8 /* Reserve space for an extra record */
#define DONT_OPEN_TABLES 8 /* Don't open database-files (frd) */
#define DONT_OPEN_MASTER_REG 16 /* Don't open first reg-file (prt) */
#define EXTRA_LONG_RECORD 16 /* Plats f|r dubbel s|k-record */
@@ -113,32 +110,36 @@ typedef struct st_ha_create_information HA_CREATE_INFO;
The flag means that we need to process tables only to get necessary data.
Views are not processed.
*/
-#define OPEN_TABLE_ONLY OPEN_FRM_FILE_ONLY*2
+#define OPEN_TABLE_ONLY (OPEN_FRM_FILE_ONLY*2)
/**
This flag is used in function get_all_tables() which fills
I_S tables with data which are retrieved from frm files and storage engine
The flag means that we need to process views only to get necessary data.
Tables are not processed.
*/
-#define OPEN_VIEW_ONLY OPEN_TABLE_ONLY*2
+#define OPEN_VIEW_ONLY (OPEN_TABLE_ONLY*2)
/**
This flag is used in function get_all_tables() which fills
I_S tables with data which are retrieved from frm files and storage engine.
The flag means that we need to open a view using
open_normal_and_derived_tables() function.
*/
-#define OPEN_VIEW_FULL OPEN_VIEW_ONLY*2
+#define OPEN_VIEW_FULL (OPEN_VIEW_ONLY*2)
/**
This flag is used in function get_all_tables() which fills
I_S tables with data which are retrieved from frm files and storage engine.
The flag means that I_S table uses optimization algorithm.
*/
-#define OPTIMIZE_I_S_TABLE OPEN_VIEW_FULL*2
+#define OPTIMIZE_I_S_TABLE (OPEN_VIEW_FULL*2)
+/**
+ This flag is used to instruct tdc_open_view() to check metadata version.
+*/
+#define CHECK_METADATA_VERSION (OPEN_TRIGGER_ONLY*2)
/*
The flag means that we need to process trigger files only.
*/
-#define OPEN_TRIGGER_ONLY OPTIMIZE_I_S_TABLE*2
+#define OPEN_TRIGGER_ONLY (OPTIMIZE_I_S_TABLE*2)
#define SC_INFO_LENGTH 4 /* Form format constant */
#define TE_INFO_LENGTH 3
@@ -169,15 +170,46 @@ typedef struct st_ha_create_information HA_CREATE_INFO;
#include "sql_list.h" /* List<> */
#include "field.h" /* Create_field */
-bool mysql_create_frm(THD *thd, const char *file_name,
- const char *db, const char *table,
- HA_CREATE_INFO *create_info,
- List<Create_field> &create_field,
- uint key_count,KEY *key_info,handler *db_type);
-int rea_create_table(THD *thd, const char *path,
- const char *db, const char *table_name,
- HA_CREATE_INFO *create_info,
- List<Create_field> &create_field,
- uint key_count,KEY *key_info,
- handler *file);
+/*
+ Types of values in the MariaDB extra2 frm segment.
+ Each value is written as
+ type: 1 byte
+ length: 1 byte (1..255) or \0 and 2 bytes.
+ binary value of the 'length' bytes.
+
+ Older MariaDB servers can ignore values of unknown types if
+ the type code is less than 128 (EXTRA2_ENGINE_IMPORTANT).
+ Otherwise older (but newer than 10.0.1) servers are required
+ to report an error.
+*/
+enum extra2_frm_value_type {
+ EXTRA2_TABLEDEF_VERSION=0,
+ EXTRA2_DEFAULT_PART_ENGINE=1,
+
+#define EXTRA2_ENGINE_IMPORTANT 128
+
+ EXTRA2_ENGINE_TABLEOPTS=128,
+};
+
+int rea_create_table(THD *thd, LEX_CUSTRING *frm,
+ const char *path, const char *db, const char *table_name,
+ HA_CREATE_INFO *create_info, handler *file,
+ bool no_ha_create_table);
+LEX_CUSTRING build_frm_image(THD *thd, const char *table,
+ HA_CREATE_INFO *create_info,
+ List<Create_field> &create_fields,
+ uint keys, KEY *key_info, handler *db_file);
+
+#define FRM_HEADER_SIZE 64
+#define FRM_FORMINFO_SIZE 288
+#define FRM_MAX_SIZE (512*1024)
+
+static inline bool is_binary_frm_header(uchar *head)
+{
+ return head[0] == 254
+ && head[1] == 1
+ && head[2] >= FRM_VER
+ && head[2] <= FRM_VER+4;
+}
+
#endif
diff --git a/sql/wsrep_applier.cc b/sql/wsrep_applier.cc
index 3736431cd30..90c84f1c2cc 100644
--- a/sql/wsrep_applier.cc
+++ b/sql/wsrep_applier.cc
@@ -1,4 +1,4 @@
-/* Copyright (C) 2013 Codership Oy <info@codership.com>
+/* Copyright (C) 2013-2015 Codership Oy <info@codership.com>
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
@@ -13,11 +13,13 @@
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+#include "wsrep_applier.h"
#include "wsrep_priv.h"
#include "wsrep_binlog.h" // wsrep_dump_rbr_buf()
+#include "wsrep_xid.h"
-#include "log_event.h" // EVENT_LEN_OFFSET, etc.
-#include "wsrep_applier.h"
+#include "log_event.h" // class THD, EVENT_LEN_OFFSET, etc.
+#include "debug_sync.h"
/*
read the first event from (*buf). The size of the (*buf) is (*buf_len).
@@ -43,8 +45,7 @@ static Log_event* wsrep_read_log_event(
goto err;
}
- res= Log_event::read_log_event(buf, data_len, &error, description_event,
- FALSE);
+ res= Log_event::read_log_event(buf, data_len, &error, description_event, true);
err:
if (!res)
@@ -80,7 +81,7 @@ wsrep_get_apply_format(THD* thd)
{
return (Format_description_log_event*) thd->wsrep_apply_format;
}
- return thd->wsrep_rli->relay_log.description_event_for_exec;
+ return thd->wsrep_rgi->rli->relay_log.description_event_for_exec;
}
static wsrep_cb_status_t wsrep_apply_events(THD* thd,
@@ -114,7 +115,6 @@ static wsrep_cb_status_t wsrep_apply_events(THD* thd,
while(buf_len)
{
int exec_res;
- int error = 0;
Log_event* ev= wsrep_read_log_event(&buf, &buf_len,
wsrep_get_apply_format(thd));
@@ -132,12 +132,18 @@ static wsrep_cb_status_t wsrep_apply_events(THD* thd,
case FORMAT_DESCRIPTION_EVENT:
wsrep_set_apply_format(thd, (Format_description_log_event*)ev);
continue;
- case WRITE_ROWS_EVENT:
- case UPDATE_ROWS_EVENT:
- case DELETE_ROWS_EVENT:
- DBUG_ASSERT(buf_len != 0 ||
- ((Rows_log_event*)ev)->get_flags(Rows_log_event::STMT_END_F));
- break;
+#ifdef GTID_SUPPORT
+ case GTID_LOG_EVENT:
+ {
+ Gtid_log_event* gev= (Gtid_log_event*)ev;
+ if (gev->get_gno() == 0)
+ {
+ /* Skip GTID log event to make binlog to generate LTID on commit */
+ delete ev;
+ continue;
+ }
+ }
+#endif /* GTID_SUPPORT */
default:
break;
}
@@ -146,18 +152,22 @@ static wsrep_cb_status_t wsrep_apply_events(THD* thd,
thd->set_server_id(ev->server_id);
thd->set_time(); // time the query
wsrep_xid_init(&thd->transaction.xid_state.xid,
- &thd->wsrep_trx_meta.gtid.uuid,
+ thd->wsrep_trx_meta.gtid.uuid,
thd->wsrep_trx_meta.gtid.seqno);
thd->lex->current_select= 0;
if (!ev->when)
- ev->when = time(NULL);
+ {
+ my_hrtime_t hrtime= my_hrtime();
+ ev->when= hrtime_to_my_time(hrtime);
+ ev->when_sec_part= hrtime_sec_part(hrtime);
+ }
thd->variables.option_bits=
(thd->variables.option_bits & ~OPTION_SKIP_REPLICATION) |
(ev->flags & LOG_EVENT_SKIP_REPLICATION_F ? OPTION_SKIP_REPLICATION : 0);
ev->thd = thd;
- exec_res = ev->apply_event(thd->wsrep_rli);
+ exec_res = ev->apply_event(thd->wsrep_rgi);
DBUG_PRINT("info", ("exec_event result: %d", exec_res));
if (exec_res)
@@ -188,23 +198,7 @@ static wsrep_cb_status_t wsrep_apply_events(THD* thd,
DBUG_RETURN(WSREP_CB_FAILURE);
}
- if ((ev->get_type_code() == WRITE_ROWS_EVENT ||
- ev->get_type_code() == UPDATE_ROWS_EVENT ||
- ev->get_type_code() == DELETE_ROWS_EVENT) &&
- ((Rows_log_event *) ev)->get_flags(Rows_log_event::STMT_END_F))
- {
- thd->wsrep_rli->cleanup_context(thd, 0);
-
- if (error == 0)
- {
- thd->clear_error();
- }
- else
- WSREP_ERROR("Error in %s event: commit of row events failed: %lld",
- ev->get_type_str(), (long long)wsrep_thd_trx_seqno(thd));
- }
-
- delete_or_keep_event_post_apply(thd->wsrep_rli, typ, ev);
+ delete_or_keep_event_post_apply(thd->wsrep_rgi, typ, ev);
}
error:
@@ -229,6 +223,16 @@ wsrep_cb_status_t wsrep_apply_cb(void* const ctx,
{
THD* const thd((THD*)ctx);
+ // Allow tests to block the applier thread using the DBUG facilities.
+ DBUG_EXECUTE_IF("sync.wsrep_apply_cb",
+ {
+ const char act[]=
+ "now "
+ "wait_for signal.wsrep_apply_cb";
+ DBUG_ASSERT(!debug_sync_set_action(thd,
+ STRING_WITH_LEN(act)));
+ };);
+
thd->wsrep_trx_meta = *meta;
#ifdef WSREP_PROC_INFO
@@ -279,17 +283,16 @@ wsrep_cb_status_t wsrep_apply_cb(void* const ctx,
while ((tmp = thd->temporary_tables))
{
WSREP_DEBUG("Applier %lu, has temporary tables: %s.%s",
- thd->thread_id,
- (tmp->s) ? tmp->s->db.str : "void",
- (tmp->s) ? tmp->s->table_name.str : "void");
+ thd->thread_id,
+ (tmp->s) ? tmp->s->db.str : "void",
+ (tmp->s) ? tmp->s->table_name.str : "void");
close_temporary_table(thd, tmp, 1, 1);
}
return rcode;
}
-static wsrep_cb_status_t wsrep_commit(THD* const thd,
- wsrep_seqno_t const global_seqno)
+static wsrep_cb_status_t wsrep_commit(THD* const thd)
{
#ifdef WSREP_PROC_INFO
snprintf(thd->wsrep_info, sizeof(thd->wsrep_info) - 1,
@@ -302,24 +305,31 @@ static wsrep_cb_status_t wsrep_commit(THD* const thd,
wsrep_cb_status_t const rcode(trans_commit(thd) ?
WSREP_CB_FAILURE : WSREP_CB_SUCCESS);
+ if (WSREP_CB_SUCCESS == rcode)
+ {
+ thd->wsrep_rgi->cleanup_context(thd, false);
+#ifdef GTID_SUPPORT
+ thd->variables.gtid_next.set_automatic();
+#endif /* GTID_SUPPORT */
+ if (thd->wsrep_apply_toi)
+ {
+ wsrep_set_SE_checkpoint(thd->wsrep_trx_meta.gtid.uuid,
+ thd->wsrep_trx_meta.gtid.seqno);
+ }
+ }
+
#ifdef WSREP_PROC_INFO
snprintf(thd->wsrep_info, sizeof(thd->wsrep_info) - 1,
- "committed %lld", (long long)wsrep_thd_trx_seqno(thd));
+ "committed %lld", (long long) wsrep_thd_trx_seqno(thd));
thd_proc_info(thd, thd->wsrep_info);
#else
thd_proc_info(thd, "committed");
#endif /* WSREP_PROC_INFO */
- if (WSREP_CB_SUCCESS == rcode)
- {
- // TODO: mark snapshot with global_seqno.
- }
-
return rcode;
}
-static wsrep_cb_status_t wsrep_rollback(THD* const thd,
- wsrep_seqno_t const global_seqno)
+static wsrep_cb_status_t wsrep_rollback(THD* const thd)
{
#ifdef WSREP_PROC_INFO
snprintf(thd->wsrep_info, sizeof(thd->wsrep_info) - 1,
@@ -356,9 +366,9 @@ wsrep_cb_status_t wsrep_commit_cb(void* const ctx,
wsrep_cb_status_t rcode;
if (commit)
- rcode = wsrep_commit(thd, meta->gtid.seqno);
+ rcode = wsrep_commit(thd);
else
- rcode = wsrep_rollback(thd, meta->gtid.seqno);
+ rcode = wsrep_rollback(thd);
wsrep_set_apply_format(thd, NULL);
thd->mdl_context.release_transactional_locks();
diff --git a/sql/wsrep_applier.h b/sql/wsrep_applier.h
index 816970db67c..424db466e53 100644
--- a/sql/wsrep_applier.h
+++ b/sql/wsrep_applier.h
@@ -16,7 +16,8 @@
#ifndef WSREP_APPLIER_H
#define WSREP_APPLIER_H
-#include <sys/types.h>
+#include <my_config.h>
+#include "../wsrep/wsrep_api.h"
/* wsrep callback prototypes */
diff --git a/sql/wsrep_binlog.cc b/sql/wsrep_binlog.cc
index af4ec84db85..5cb910248a2 100644
--- a/sql/wsrep_binlog.cc
+++ b/sql/wsrep_binlog.cc
@@ -55,8 +55,8 @@ int wsrep_write_cache_buf(IO_CACHE *cache, uchar **buf, size_t *buf_len)
wsrep_max_ws_size, total_length);
goto error;
}
-
- uchar* tmp = (uchar *)my_realloc(*buf, total_length, MYF(0));
+ uchar* tmp = (uchar *)my_realloc(*buf, total_length,
+ MYF(MY_ALLOW_ZERO_PTR));
if (!tmp)
{
WSREP_ERROR("could not (re)allocate buffer: %zu + %u",
@@ -81,7 +81,7 @@ int wsrep_write_cache_buf(IO_CACHE *cache, uchar **buf, size_t *buf_len)
error:
if (reinit_io_cache(cache, WRITE_CACHE, saved_pos, 0, 0))
{
- WSREP_ERROR("failed to initialize io-cache");
+ WSREP_WARN("failed to initialize io-cache");
}
cleanup:
my_free(*buf);
@@ -173,7 +173,8 @@ static int wsrep_write_cache_once(wsrep_t* const wsrep,
if (total_length > allocated)
{
size_t const new_size(heap_size(total_length));
- uchar* tmp = (uchar *)my_realloc(heap_buf, new_size, MYF(0));
+ uchar* tmp = (uchar *)my_realloc(heap_buf, new_size,
+ MYF(MY_ALLOW_ZERO_PTR));
if (!tmp)
{
WSREP_ERROR("could not (re)allocate buffer: %zu + %u",
@@ -319,6 +320,36 @@ void wsrep_dump_rbr_buf(THD *thd, const void* rbr_buf, size_t buf_len)
filename, errno, strerror(errno));
}
}
+extern handlerton *binlog_hton;
+
+/*
+ wsrep exploits binlog's caches even if binlogging itself is not
+ activated. In such case connection close needs calling
+ actual binlog's method.
+ Todo: split binlog hton from its caches to use ones by wsrep
+ without referring to binlog's stuff.
+*/
+int wsrep_binlog_close_connection(THD* thd)
+{
+ DBUG_ENTER("wsrep_binlog_close_connection");
+ if (thd_get_ha_data(thd, binlog_hton) != NULL)
+ binlog_hton->close_connection (binlog_hton, thd);
+ DBUG_RETURN(0);
+}
+
+int wsrep_binlog_savepoint_set(THD *thd, void *sv)
+{
+ if (!wsrep_emulate_bin_log) return 0;
+ int rcode = binlog_hton->savepoint_set(binlog_hton, thd, sv);
+ return rcode;
+}
+
+int wsrep_binlog_savepoint_rollback(THD *thd, void *sv)
+{
+ if (!wsrep_emulate_bin_log) return 0;
+ int rcode = binlog_hton->savepoint_rollback(binlog_hton, thd, sv);
+ return rcode;
+}
void wsrep_dump_rbr_direct(THD* thd, IO_CACHE* cache)
{
@@ -375,4 +406,3 @@ cleanup:
// close file
if (of) fclose(of);
}
-
diff --git a/sql/wsrep_binlog.h b/sql/wsrep_binlog.h
index 76192f6e119..a7b680f616b 100644
--- a/sql/wsrep_binlog.h
+++ b/sql/wsrep_binlog.h
@@ -49,4 +49,8 @@ void wsrep_dump_rbr_buf(THD *thd, const void* rbr_buf, size_t buf_len);
/* Dump replication buffer to disk without intermediate buffer */
void wsrep_dump_rbr_direct(THD* thd, IO_CACHE* cache);
+int wsrep_binlog_close_connection(THD* thd);
+int wsrep_binlog_savepoint_set(THD *thd, void *sv);
+int wsrep_binlog_savepoint_rollback(THD *thd, void *sv);
+
#endif /* WSREP_BINLOG_H */
diff --git a/sql/wsrep_hton.cc b/sql/wsrep_hton.cc
index 0572230b18b..e5ff462eb19 100644
--- a/sql/wsrep_hton.cc
+++ b/sql/wsrep_hton.cc
@@ -1,4 +1,4 @@
-/* Copyright 2008 Codership Oy <http://www.codership.com>
+/* Copyright 2008-2015 Codership Oy <http://www.codership.com>
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
@@ -19,11 +19,11 @@
#include <sql_class.h>
#include "wsrep_mysqld.h"
#include "wsrep_binlog.h"
+#include "wsrep_xid.h"
#include <cstdio>
#include <cstdlib>
+#include "debug_sync.h"
-extern handlerton *binlog_hton;
-extern int binlog_close_connection(handlerton *hton, THD *thd);
extern ulonglong thd_to_trx_id(THD *thd);
extern "C" int thd_binlog_format(const MYSQL_THD thd);
@@ -70,18 +70,30 @@ void wsrep_register_hton(THD* thd, bool all)
{
if (thd->wsrep_exec_mode != TOTAL_ORDER && !thd->wsrep_apply_toi)
{
+ if (thd->wsrep_exec_mode == LOCAL_STATE &&
+ (thd_sql_command(thd) == SQLCOM_OPTIMIZE ||
+ thd_sql_command(thd) == SQLCOM_ANALYZE ||
+ thd_sql_command(thd) == SQLCOM_REPAIR) &&
+ thd->lex->no_write_to_binlog == 1)
+ {
+ WSREP_DEBUG("Skipping wsrep_register_hton for LOCAL sql admin command : %s",
+ thd->query());
+ return;
+ }
+
THD_TRANS *trans=all ? &thd->transaction.all : &thd->transaction.stmt;
for (Ha_trx_info *i= trans->ha_list; WSREP(thd) && i; i = i->next())
{
- if (i->ht()->db_type == DB_TYPE_INNODB)
+ if ((i->ht()->db_type == DB_TYPE_INNODB) ||
+ (i->ht()->db_type == DB_TYPE_TOKUDB))
{
trans_register_ha(thd, all, wsrep_hton);
/* follow innodb read/write settting
- * but, as an exception: CTAS with empty result set will not be
+ * but, as an exception: CTAS with empty result set will not be
* replicated unless we declare wsrep hton as read/write here
*/
- if (i->is_trx_read_write() ||
+ if (i->is_trx_read_write() ||
(thd->lex->sql_command == SQLCOM_CREATE_TABLE &&
thd->wsrep_exec_mode == LOCAL_STATE))
{
@@ -99,17 +111,40 @@ void wsrep_register_hton(THD* thd, bool all)
*/
void wsrep_post_commit(THD* thd, bool all)
{
- if (thd->wsrep_exec_mode == LOCAL_COMMIT)
+ /*
+ TODO: It can perhaps be fixed in a more elegant fashion by turning off
+ wsrep_emulate_binlog if wsrep_on=0 on server start.
+ https://github.com/codership/mysql-wsrep/issues/112
+ */
+ if (!WSREP_ON)
+ return;
+
+ switch (thd->wsrep_exec_mode)
{
- DBUG_ASSERT(thd->wsrep_trx_meta.gtid.seqno != WSREP_SEQNO_UNDEFINED);
- if (wsrep->post_commit(wsrep, &thd->wsrep_ws_handle))
+ case LOCAL_COMMIT:
{
+ DBUG_ASSERT(thd->wsrep_trx_meta.gtid.seqno != WSREP_SEQNO_UNDEFINED);
+ if (wsrep->post_commit(wsrep, &thd->wsrep_ws_handle))
+ {
DBUG_PRINT("wsrep", ("set committed fail"));
- WSREP_WARN("set committed fail: %llu %d",
- (long long)thd->real_id, thd->stmt_da->status());
+ WSREP_WARN("set committed fail: %llu %d",
+ (long long)thd->real_id, thd->get_stmt_da()->status());
+ }
+ wsrep_cleanup_transaction(thd);
+ break;
}
- wsrep_cleanup_transaction(thd);
+ case LOCAL_STATE:
+ {
+ /*
+ Non-InnoDB statements may have populated events in stmt cache => cleanup
+ */
+ WSREP_DEBUG("cleanup transaction for LOCAL_STATE: %s", thd->query());
+ wsrep_cleanup_transaction(thd);
+ break;
+ }
+ default: break;
}
+
}
/*
@@ -128,10 +163,7 @@ wsrep_close_connection(handlerton* hton, THD* thd)
{
DBUG_RETURN(0);
}
-
- if (wsrep_emulate_bin_log && thd_get_ha_data(thd, binlog_hton) != NULL)
- binlog_hton->close_connection (binlog_hton, thd);
- DBUG_RETURN(0);
+ DBUG_RETURN(wsrep_binlog_close_connection (thd));
}
/*
@@ -174,10 +206,11 @@ static int wsrep_savepoint_set(handlerton *hton, THD *thd, void *sv)
DBUG_RETURN(0);
}
- if (!wsrep_emulate_bin_log) return 0;
- int rcode = binlog_hton->savepoint_set(binlog_hton, thd, sv);
- return rcode;
+ if (!wsrep_emulate_bin_log) DBUG_RETURN(0);
+ int rcode = wsrep_binlog_savepoint_set(thd, sv);
+ DBUG_RETURN(rcode);
}
+
static int wsrep_savepoint_rollback(handlerton *hton, THD *thd, void *sv)
{
DBUG_ENTER("wsrep_savepoint_rollback");
@@ -187,9 +220,9 @@ static int wsrep_savepoint_rollback(handlerton *hton, THD *thd, void *sv)
DBUG_RETURN(0);
}
- if (!wsrep_emulate_bin_log) return 0;
- int rcode = binlog_hton->savepoint_rollback(binlog_hton, thd, sv);
- return rcode;
+ if (!wsrep_emulate_bin_log) DBUG_RETURN(0);
+ int rcode = wsrep_binlog_savepoint_rollback(thd, sv);
+ DBUG_RETURN(rcode);
}
static int wsrep_rollback(handlerton *hton, THD *thd, bool all)
@@ -202,14 +235,25 @@ static int wsrep_rollback(handlerton *hton, THD *thd, bool all)
}
mysql_mutex_lock(&thd->LOCK_wsrep_thd);
+ switch (thd->wsrep_exec_mode)
+ {
+ case TOTAL_ORDER:
+ case REPL_RECV:
+ mysql_mutex_unlock(&thd->LOCK_wsrep_thd);
+ WSREP_DEBUG("Avoiding wsrep rollback for failed DDL: %s", thd->query());
+ DBUG_RETURN(0);
+ default: break;
+ }
+
if ((all || !thd_test_options(thd, OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) &&
- (thd->variables.wsrep_on && thd->wsrep_conflict_state != MUST_REPLAY))
+ thd->wsrep_conflict_state != MUST_REPLAY)
{
- if (wsrep->post_rollback(wsrep, &thd->wsrep_ws_handle))
+ if (WSREP(thd) && wsrep->post_rollback(wsrep, &thd->wsrep_ws_handle))
{
DBUG_PRINT("wsrep", ("setting rollback fail"));
- WSREP_ERROR("settting rollback fail: thd: %llu SQL: %s",
- (long long)thd->real_id, thd->query());
+ WSREP_ERROR("settting rollback fail: thd: %llu, schema: %s, SQL: %s",
+ (long long)thd->real_id, (thd->db ? thd->db : "(null)"),
+ thd->query());
}
wsrep_cleanup_transaction(thd);
}
@@ -245,13 +289,12 @@ int wsrep_commit(handlerton *hton, THD *thd, bool all)
Transaction didn't go through wsrep->pre_commit() so just roll back
possible changes to clean state.
*/
- if (WSREP_PROVIDER_EXISTS) {
- if (wsrep->post_rollback(wsrep, &thd->wsrep_ws_handle))
- {
- DBUG_PRINT("wsrep", ("setting rollback fail"));
- WSREP_ERROR("settting rollback fail: thd: %llu SQL: %s",
- (long long)thd->real_id, thd->query());
- }
+ if (WSREP(thd) && wsrep->post_rollback(wsrep, &thd->wsrep_ws_handle))
+ {
+ DBUG_PRINT("wsrep", ("setting rollback fail"));
+ WSREP_ERROR("settting rollback fail: thd: %llu, schema: %s, SQL: %s",
+ (long long)thd->real_id, (thd->db ? thd->db : "(null)"),
+ thd->query());
}
wsrep_cleanup_transaction(thd);
}
@@ -272,13 +315,15 @@ wsrep_run_wsrep_commit(THD *thd, handlerton *hton, bool all)
IO_CACHE *cache;
int replay_round= 0;
- if (thd->stmt_da->is_error()) {
+ if (thd->get_stmt_da()->is_error()) {
WSREP_ERROR("commit issue, error: %d %s",
- thd->stmt_da->sql_errno(), thd->stmt_da->message());
+ thd->get_stmt_da()->sql_errno(), thd->get_stmt_da()->message());
}
DBUG_ENTER("wsrep_run_wsrep_commit");
+ DEBUG_SYNC(thd, "wsrep_before_replication");
+
if (thd->slave_thread && !opt_log_slave_updates) DBUG_RETURN(WSREP_TRX_OK);
if (thd->wsrep_exec_mode == REPL_RECV) {
@@ -324,7 +369,7 @@ wsrep_run_wsrep_commit(THD *thd, handlerton *hton, bool all)
while (wsrep_replaying > 0 &&
thd->wsrep_conflict_state == NO_CONFLICT &&
- thd->killed == NOT_KILLED &&
+ thd->killed == NOT_KILLED &&
!shutdown_in_progress)
{
@@ -387,8 +432,8 @@ wsrep_run_wsrep_commit(THD *thd, handlerton *hton, bool all)
if (data_len == 0)
{
- if (thd->stmt_da->is_ok() &&
- thd->stmt_da->affected_rows() > 0 &&
+ if (thd->get_stmt_da()->is_ok() &&
+ thd->get_stmt_da()->affected_rows() > 0 &&
!binlog_filter->is_on())
{
WSREP_DEBUG("empty rbr buffer, query: %s, "
@@ -396,7 +441,7 @@ wsrep_run_wsrep_commit(THD *thd, handlerton *hton, bool all)
"changed tables: %d, "
"sql_log_bin: %d, "
"wsrep status (%d %d %d)",
- thd->query(), thd->stmt_da->affected_rows(),
+ thd->query(), thd->get_stmt_da()->affected_rows(),
stmt_has_updated_trans_table(thd), thd->variables.sql_log_bin,
thd->wsrep_exec_mode, thd->wsrep_query_state,
thd->wsrep_conflict_state);
@@ -412,9 +457,11 @@ wsrep_run_wsrep_commit(THD *thd, handlerton *hton, bool all)
if (WSREP_UNDEFINED_TRX_ID == thd->wsrep_ws_handle.trx_id)
{
WSREP_WARN("SQL statement was ineffective, THD: %lu, buf: %zu\n"
- "QUERY: %s\n"
- " => Skipping replication",
- thd->thread_id, data_len, thd->query());
+ "schema: %s \n"
+ "QUERY: %s\n"
+ " => Skipping replication",
+ thd->thread_id, data_len,
+ (thd->db ? thd->db : "(null)"), thd->query());
rcode = WSREP_TRX_FAIL;
}
else if (!rcode)
@@ -429,14 +476,15 @@ wsrep_run_wsrep_commit(THD *thd, handlerton *hton, bool all)
&thd->wsrep_trx_meta);
if (rcode == WSREP_TRX_MISSING) {
- WSREP_WARN("Transaction missing in provider, thd: %ld, SQL: %s",
- thd->thread_id, thd->query());
+ WSREP_WARN("Transaction missing in provider, thd: %ld, schema: %s, SQL: %s",
+ thd->thread_id, (thd->db ? thd->db : "(null)"), thd->query());
rcode = WSREP_TRX_FAIL;
} else if (rcode == WSREP_BF_ABORT) {
WSREP_DEBUG("thd %lu seqno %lld BF aborted by provider, will replay",
thd->thread_id, (long long)thd->wsrep_trx_meta.gtid.seqno);
mysql_mutex_lock(&thd->LOCK_wsrep_thd);
thd->wsrep_conflict_state = MUST_REPLAY;
+ DBUG_ASSERT(wsrep_thd_trx_seqno(thd) > 0);
mysql_mutex_unlock(&thd->LOCK_wsrep_thd);
mysql_mutex_lock(&LOCK_wsrep_replaying);
wsrep_replaying++;
@@ -476,7 +524,7 @@ wsrep_run_wsrep_commit(THD *thd, handlerton *hton, bool all)
if (thd->transaction.xid_state.xid.get_my_xid())
{
wsrep_xid_init(&thd->transaction.xid_state.xid,
- &thd->wsrep_trx_meta.gtid.uuid,
+ thd->wsrep_trx_meta.gtid.uuid,
thd->wsrep_trx_meta.gtid.seqno);
}
DBUG_PRINT("wsrep", ("replicating commit success"));
diff --git a/sql/wsrep_mysqld.cc b/sql/wsrep_mysqld.cc
index 48114fcba34..0dd5c4dba14 100644
--- a/sql/wsrep_mysqld.cc
+++ b/sql/wsrep_mysqld.cc
@@ -1,4 +1,4 @@
-/* Copyright 2008-2013 Codership Oy <http://www.codership.com>
+/* Copyright 2008-2015 Codership Oy <http://www.codership.com>
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
@@ -23,6 +23,7 @@
#include "wsrep_var.h"
#include "wsrep_binlog.h"
#include "wsrep_applier.h"
+#include "wsrep_xid.h"
#include <cstdio>
#include <cstdlib>
#include "log_event.h"
@@ -30,6 +31,11 @@
wsrep_t *wsrep = NULL;
my_bool wsrep_emulate_bin_log = FALSE; // activating parts of binlog interface
+#ifdef GTID_SUPPORT
+/* Sidno in global_sid_map corresponding to group uuid */
+rpl_sidno wsrep_sidno= -1;
+#endif /* GTID_SUPPORT */
+my_bool wsrep_preordered_opt= FALSE;
/*
* Begin configuration options and their default values
@@ -82,10 +88,6 @@ my_bool wsrep_creating_startup_threads = 0;
my_bool wsrep_inited = 0; // initialized ?
static const wsrep_uuid_t cluster_uuid = WSREP_UUID_UNDEFINED;
-const wsrep_uuid_t* wsrep_cluster_uuid()
-{
- return &cluster_uuid;
-}
static char cluster_uuid_str[40]= { 0, };
static const char* cluster_status_str[WSREP_VIEW_MAX] =
{
@@ -164,72 +166,25 @@ static void wsrep_log_states (wsrep_log_level_t const level,
wsrep_log_cb (level, msg);
}
-static my_bool set_SE_checkpoint(THD* unused, plugin_ref plugin, void* arg)
-{
- XID* xid= reinterpret_cast<XID*>(arg);
- handlerton* hton= plugin_data(plugin, handlerton *);
- if (hton->db_type == DB_TYPE_INNODB)
- {
- const wsrep_uuid_t* uuid(wsrep_xid_uuid(xid));
- char uuid_str[40] = {0, };
- wsrep_uuid_print(uuid, uuid_str, sizeof(uuid_str));
- WSREP_DEBUG("Set WSREPXid for InnoDB: %s:%lld",
- uuid_str, (long long)wsrep_xid_seqno(xid));
- hton->wsrep_set_checkpoint(hton, xid);
- }
- return FALSE;
-}
-
-void wsrep_set_SE_checkpoint(XID* xid)
-{
- plugin_foreach(NULL, set_SE_checkpoint, MYSQL_STORAGE_ENGINE_PLUGIN, xid);
-}
-
-static my_bool get_SE_checkpoint(THD* unused, plugin_ref plugin, void* arg)
-{
- XID* xid= reinterpret_cast<XID*>(arg);
- handlerton* hton= plugin_data(plugin, handlerton *);
- if (hton->db_type == DB_TYPE_INNODB)
- {
- hton->wsrep_get_checkpoint(hton, xid);
- const wsrep_uuid_t* uuid(wsrep_xid_uuid(xid));
- char uuid_str[40] = {0, };
- wsrep_uuid_print(uuid, uuid_str, sizeof(uuid_str));
- WSREP_DEBUG("Read WSREPXid from InnoDB: %s:%lld",
- uuid_str, (long long)wsrep_xid_seqno(xid));
-
- }
- return FALSE;
-}
-
-void wsrep_get_SE_checkpoint(XID* xid)
-{
- plugin_foreach(NULL, get_SE_checkpoint, MYSQL_STORAGE_ENGINE_PLUGIN, xid);
-}
-
-void wsrep_get_SE_checkpoint(wsrep_uuid_t& uuid, wsrep_seqno_t& seqno)
+#ifdef GTID_SUPPORT
+void wsrep_init_sidno(const wsrep_uuid_t& wsrep_uuid)
{
- uuid= WSREP_UUID_UNDEFINED;
- seqno= WSREP_SEQNO_UNDEFINED;
-
- XID xid;
- memset(&xid, 0, sizeof(xid));
- xid.formatID= -1;
-
- wsrep_get_SE_checkpoint(&xid);
+ /* generate new Sid map entry from inverted uuid */
+ rpl_sid sid;
+ wsrep_uuid_t ltid_uuid;
- if (xid.formatID == -1) return; // nil XID
-
- if (!wsrep_is_wsrep_xid(&xid))
+ for (size_t i= 0; i < sizeof(ltid_uuid.data); ++i)
{
- WSREP_WARN("Read non-wsrep XID from storage engines.");
- return;
+ ltid_uuid.data[i] = ~wsrep_uuid.data[i];
}
- uuid= *wsrep_xid_uuid(&xid);
- seqno= wsrep_xid_seqno(&xid);
+ sid.copy_from(ltid_uuid.data);
+ global_sid_lock->wrlock();
+ wsrep_sidno= global_sid_map->add_sid(sid);
+ WSREP_INFO("Initialized wsrep sidno %d", wsrep_sidno);
+ global_sid_lock->unlock();
}
-
+#endif /* GTID_SUPPORT */
static wsrep_cb_status_t
wsrep_view_handler_cb (void* app_ctx,
@@ -367,9 +322,10 @@ wsrep_view_handler_cb (void* app_ctx,
local_seqno= view->state_id.seqno;
}
/* Init storage engine XIDs from first view */
- XID xid;
- wsrep_xid_init(&xid, &local_uuid, local_seqno);
- wsrep_set_SE_checkpoint(&xid);
+ wsrep_set_SE_checkpoint(local_uuid, local_seqno);
+#ifdef GTID_SUPPORT
+ wsrep_init_sidno(local_uuid);
+#endif /* GTID_SUPPORT */
new_status= WSREP_MEMBER_JOINED;
}
@@ -468,7 +424,7 @@ static void wsrep_synced_cb(void* app_ctx)
active_mi,
master_info_file,
relay_log_info_file,
- SLAVE_SQL)))
+ SLAVE_SQL)))
{
WSREP_WARN("Failed to create slave threads: %d", rcode);
}
@@ -480,38 +436,28 @@ static void wsrep_synced_cb(void* app_ctx)
static void wsrep_init_position()
{
/* read XIDs from storage engines */
- XID xid;
- memset(&xid, 0, sizeof(xid));
- xid.formatID= -1;
- wsrep_get_SE_checkpoint(&xid);
+ wsrep_uuid_t uuid;
+ wsrep_seqno_t seqno;
+ wsrep_get_SE_checkpoint(uuid, seqno);
- if (xid.formatID == -1)
+ if (!memcmp(&uuid, &WSREP_UUID_UNDEFINED, sizeof(wsrep_uuid_t)))
{
WSREP_INFO("Read nil XID from storage engines, skipping position init");
return;
}
- else if (!wsrep_is_wsrep_xid(&xid))
- {
- WSREP_WARN("Read non-wsrep XID from storage engines, skipping position init");
- return;
- }
-
- const wsrep_uuid_t* uuid= wsrep_xid_uuid(&xid);
- const wsrep_seqno_t seqno= wsrep_xid_seqno(&xid);
char uuid_str[40] = {0, };
- wsrep_uuid_print(uuid, uuid_str, sizeof(uuid_str));
+ wsrep_uuid_print(&uuid, uuid_str, sizeof(uuid_str));
WSREP_INFO("Initial position: %s:%lld", uuid_str, (long long)seqno);
-
if (!memcmp(&local_uuid, &WSREP_UUID_UNDEFINED, sizeof(local_uuid)) &&
local_seqno == WSREP_SEQNO_UNDEFINED)
{
// Initial state
- local_uuid= *uuid;
+ local_uuid= uuid;
local_seqno= seqno;
}
- else if (memcmp(&local_uuid, uuid, sizeof(local_uuid)) ||
+ else if (memcmp(&local_uuid, &uuid, sizeof(local_uuid)) ||
local_seqno != seqno)
{
WSREP_WARN("Initial position was provided by configuration or SST, "
@@ -519,7 +465,7 @@ static void wsrep_init_position()
}
}
-extern const char* my_bind_addr_str;
+extern char* my_bind_addr_str;
int wsrep_init()
{
@@ -528,6 +474,7 @@ int wsrep_init()
wsrep_ready_set(FALSE);
assert(wsrep_provider);
+
wsrep_init_position();
if ((rcode= wsrep_load(wsrep_provider, &wsrep, wsrep_log_cb)) != WSREP_OK)
@@ -715,7 +662,7 @@ int wsrep_init()
return rcode;
}
-extern "C" int wsrep_on(void *);
+extern int wsrep_on(void *);
void wsrep_init_startup (bool first)
{
@@ -767,14 +714,12 @@ void wsrep_recover()
uuid_str, (long long)local_seqno);
return;
}
- XID xid;
- memset(&xid, 0, sizeof(xid));
- xid.formatID= -1;
- wsrep_get_SE_checkpoint(&xid);
+ wsrep_uuid_t uuid;
+ wsrep_seqno_t seqno;
+ wsrep_get_SE_checkpoint(uuid, seqno);
char uuid_str[40];
- wsrep_uuid_print(wsrep_xid_uuid(&xid), uuid_str, sizeof(uuid_str));
- WSREP_INFO("Recovered position: %s:%lld", uuid_str,
- (long long)wsrep_xid_seqno(&xid));
+ wsrep_uuid_print(&uuid, uuid_str, sizeof(uuid_str));
+ WSREP_INFO("Recovered position: %s:%lld", uuid_str, (long long)seqno);
}
@@ -801,36 +746,6 @@ void wsrep_stop_replication(THD *thd)
return;
}
-/* This one is set to true when --wsrep-new-cluster is found in the command
- * line arguments */
-static my_bool wsrep_new_cluster= FALSE;
-#define WSREP_NEW_CLUSTER "--wsrep-new-cluster"
-/* Finds and hides --wsrep-new-cluster from the arguments list
- * by moving it to the end of the list and decrementing argument count */
-void wsrep_filter_new_cluster (int* argc, char* argv[])
-{
- int i;
- for (i= *argc - 1; i > 0; i--)
- {
- /* make a copy of the argument to convert possible underscores to hyphens.
- * the copy need not to be longer than WSREP_NEW_CLUSTER option */
- char arg[sizeof(WSREP_NEW_CLUSTER) + 1]= { 0, };
- strncpy(arg, argv[i], sizeof(arg) - 1);
- char* underscore(arg);
- while (NULL != (underscore= strchr(underscore, '_'))) *underscore= '-';
-
- if (!strcmp(arg, WSREP_NEW_CLUSTER))
- {
- wsrep_new_cluster= TRUE;
- *argc -= 1;
- /* preserve the order of remaining arguments AND
- * preserve the original argument pointers - just in case */
- char* wnc= argv[i];
- memmove(&argv[i], &argv[i + 1], (*argc - i)*sizeof(argv[i]));
- argv[*argc]= wnc; /* this will be invisible to the rest of the program */
- }
- }
-}
bool wsrep_start_replication()
{
@@ -854,11 +769,16 @@ bool wsrep_start_replication()
return true;
}
- bool const bootstrap(TRUE == wsrep_new_cluster);
- wsrep_new_cluster= FALSE;
+ bool const bootstrap= wsrep_new_cluster;
WSREP_INFO("Start replication");
+ if (wsrep_new_cluster)
+ {
+ WSREP_INFO("'wsrep-new-cluster' option used, bootstrapping the cluster");
+ wsrep_new_cluster= false;
+ }
+
if ((rcode = wsrep->connect(wsrep,
wsrep_cluster_name,
wsrep_cluster_address,
@@ -1030,9 +950,15 @@ static bool wsrep_prepare_keys_for_isolation(THD* thd,
if (db || table)
{
TABLE_LIST tmp_table;
- bzero((char*) &tmp_table,sizeof(tmp_table));
- tmp_table.table_name= (char*)db;
- tmp_table.db= (char*)table;
+ MDL_request mdl_request;
+
+ memset(&tmp_table, 0, sizeof(tmp_table));
+ tmp_table.table_name= (char*)table;
+ tmp_table.db= (char*)db;
+ tmp_table.mdl_request.init(MDL_key::GLOBAL, (db) ? db : "",
+ (table) ? table : "",
+ MDL_INTENTION_EXCLUSIVE, MDL_STATEMENT);
+
if (!table || !find_temporary_table(thd, &tmp_table))
{
if (!(ka->keys= (wsrep_key_t*)my_malloc(sizeof(wsrep_key_t), MYF(0))))
@@ -1065,7 +991,9 @@ static bool wsrep_prepare_keys_for_isolation(THD* thd,
{
wsrep_key_t* tmp;
tmp= (wsrep_key_t*)my_realloc(
- ka->keys, (ka->keys_len + 1) * sizeof(wsrep_key_t), MYF(0));
+ ka->keys, (ka->keys_len + 1) * sizeof(wsrep_key_t),
+ MYF(MY_ALLOW_ZERO_PTR));
+
if (!tmp)
{
WSREP_ERROR("Can't allocate memory for key_array");
@@ -1155,7 +1083,6 @@ int wsrep_to_buf_helper(
if (open_cached_file(&tmp_io_cache, mysql_tmpdir, TEMP_PREFIX,
65536, MYF(MY_WME)))
return 1;
-
int ret(0);
Format_description_log_event *tmp_fd= new Format_description_log_event(4);
@@ -1163,21 +1090,28 @@ int wsrep_to_buf_helper(
tmp_fd->write(&tmp_io_cache);
delete tmp_fd;
+#ifdef GTID_SUPPORT
+ if (thd->variables.gtid_next.type == GTID_GROUP)
+ {
+ Gtid_log_event gtid_ev(thd, FALSE, &thd->variables.gtid_next);
+ if (!gtid_ev.is_valid()) ret= 0;
+ if (!ret && gtid_ev.write(&tmp_io_cache)) ret= 1;
+ }
+#endif /* GTID_SUPPORT */
+
/* if there is prepare query, add event for it */
- if (thd->wsrep_TOI_pre_query)
+ if (!ret && thd->wsrep_TOI_pre_query)
{
Query_log_event ev(thd, thd->wsrep_TOI_pre_query,
- thd->wsrep_TOI_pre_query_len,
- FALSE, FALSE, FALSE, 0);
+ thd->wsrep_TOI_pre_query_len,
+ FALSE, FALSE, FALSE, 0);
if (ev.write(&tmp_io_cache)) ret= 1;
}
- /* append the actual query */
+ /* continue to append the actual query */
Query_log_event ev(thd, query, query_len, FALSE, FALSE, FALSE, 0);
- if (ev.write(&tmp_io_cache)) ret= 1;
-
+ if (!ret && ev.write(&tmp_io_cache)) ret= 1;
if (!ret && wsrep_write_cache_buf(&tmp_io_cache, buf, buf_len)) ret= 1;
-
close_cached_file(&tmp_io_cache);
return ret;
}
@@ -1200,7 +1134,13 @@ create_view_query(THD *thd, uchar** buf, size_t* buf_len)
buff.append(command[thd->lex->create_view_mode].str,
command[thd->lex->create_view_mode].length);
- if (!lex->definer)
+ LEX_USER *definer;
+
+ if (lex->definer)
+ {
+ definer= get_current_user(thd, lex->definer);
+ }
+ else
{
/*
DEFINER-clause is missing; we have to create default definer in
@@ -1208,16 +1148,19 @@ create_view_query(THD *thd, uchar** buf, size_t* buf_len)
If this is an ALTER VIEW then the current user should be set as
the definer.
*/
+ definer= create_default_definer(thd, false);
+ }
- if (!(lex->definer= create_default_definer(thd)))
- {
- WSREP_WARN("view default definer issue");
- }
+ if (definer)
+ {
+ views->definer.user = definer->user;
+ views->definer.host = definer->host;
+ } else {
+ WSREP_ERROR("Failed to get DEFINER for VIEW.");
+ return 1;
}
views->algorithm = lex->create_view_algorithm;
- views->definer.user = lex->definer->user;
- views->definer.host = lex->definer->host;
views->view_suid = lex->create_view_suid;
views->with_check = lex->create_view_check;
@@ -1256,6 +1199,12 @@ create_view_query(THD *thd, uchar** buf, size_t* buf_len)
return wsrep_to_buf_helper(thd, buff.ptr(), buff.length(), buf, buf_len);
}
+/*
+ returns:
+ 0: statement was replicated as TOI
+ 1: TOI replication was skipped
+ -1: TOI replication failed
+ */
static int wsrep_TOI_begin(THD *thd, char *db_, char *table_,
const TABLE_LIST* table_list)
{
@@ -1263,6 +1212,7 @@ static int wsrep_TOI_begin(THD *thd, char *db_, char *table_,
uchar* buf(0);
size_t buf_len(0);
int buf_err;
+ int rc= 0;
WSREP_DEBUG("TO BEGIN: %lld, %d : %s", (long long)wsrep_thd_trx_seqno(thd),
thd->wsrep_exec_mode, thd->query() );
@@ -1292,32 +1242,41 @@ static int wsrep_TOI_begin(THD *thd, char *db_, char *table_,
wsrep_key_arr_t key_arr= {0, 0};
struct wsrep_buf buff = { buf, buf_len };
- if (!buf_err &&
+ if (!buf_err &&
wsrep_prepare_keys_for_isolation(thd, db_, table_, table_list, &key_arr)&&
+ key_arr.keys_len > 0 &&
WSREP_OK == (ret = wsrep->to_execute_start(wsrep, thd->thread_id,
- key_arr.keys, key_arr.keys_len,
- &buff, 1,
- &thd->wsrep_trx_meta)))
+ key_arr.keys, key_arr.keys_len,
+ &buff, 1,
+ &thd->wsrep_trx_meta)))
{
thd->wsrep_exec_mode= TOTAL_ORDER;
wsrep_to_isolation++;
- if (buf) my_free(buf);
wsrep_keys_free(&key_arr);
WSREP_DEBUG("TO BEGIN: %lld, %d",(long long)wsrep_thd_trx_seqno(thd),
- thd->wsrep_exec_mode);
+ thd->wsrep_exec_mode);
}
- else {
+ else if (key_arr.keys_len > 0) {
/* jump to error handler in mysql_execute_command() */
- WSREP_WARN("TO isolation failed for: %d, sql: %s. Check wsrep "
+ WSREP_WARN("TO isolation failed for: %d, schema: %s, sql: %s. Check wsrep "
"connection state and retry the query.",
- ret, (thd->query()) ? thd->query() : "void");
+ ret,
+ (thd->db ? thd->db : "(null)"),
+ (thd->query()) ? thd->query() : "void");
my_error(ER_LOCK_DEADLOCK, MYF(0), "WSREP replication failed. Check "
- "your wsrep connection state and retry the query.");
- if (buf) my_free(buf);
+ "your wsrep connection state and retry the query.");
wsrep_keys_free(&key_arr);
- return -1;
+ rc= -1;
}
- return 0;
+ else {
+ /* non replicated DDL, affecting temporary tables only */
+ WSREP_DEBUG("TO isolation skipped for: %d, sql: %s."
+ "Only temporary tables affected.",
+ ret, (thd->query()) ? thd->query() : "void");
+ rc= 1;
+ }
+ if (buf) my_free(buf);
+ return rc;
}
static void wsrep_TOI_end(THD *thd) {
@@ -1326,11 +1285,9 @@ static void wsrep_TOI_end(THD *thd) {
WSREP_DEBUG("TO END: %lld, %d : %s", (long long)wsrep_thd_trx_seqno(thd),
thd->wsrep_exec_mode, (thd->query()) ? thd->query() : "void");
-
- XID xid;
- wsrep_xid_init(&xid, &thd->wsrep_trx_meta.gtid.uuid,
- thd->wsrep_trx_meta.gtid.seqno);
- wsrep_set_SE_checkpoint(&xid);
+
+ wsrep_set_SE_checkpoint(thd->wsrep_trx_meta.gtid.uuid,
+ thd->wsrep_trx_meta.gtid.seqno);
WSREP_DEBUG("TO END: %lld, update seqno",
(long long)wsrep_thd_trx_seqno(thd));
@@ -1338,8 +1295,10 @@ static void wsrep_TOI_end(THD *thd) {
WSREP_DEBUG("TO END: %lld", (long long)wsrep_thd_trx_seqno(thd));
}
else {
- WSREP_WARN("TO isolation end failed for: %d, sql: %s",
- ret, (thd->query()) ? thd->query() : "void");
+ WSREP_WARN("TO isolation end failed for: %d, schema: %s, sql: %s",
+ ret,
+ (thd->db ? thd->db : "(null)"),
+ (thd->query()) ? thd->query() : "void");
}
}
@@ -1349,13 +1308,19 @@ static int wsrep_RSU_begin(THD *thd, char *db_, char *table_)
WSREP_DEBUG("RSU BEGIN: %lld, %d : %s", (long long)wsrep_thd_trx_seqno(thd),
thd->wsrep_exec_mode, thd->query() );
- ret = wsrep->desync(wsrep);
- if (ret != WSREP_OK)
+ if (!wsrep_desync)
{
- WSREP_WARN("RSU desync failed %d for %s", ret, thd->query());
- my_error(ER_LOCK_DEADLOCK, MYF(0));
- return(ret);
+ ret = wsrep->desync(wsrep);
+ if (ret != WSREP_OK)
+ {
+ WSREP_WARN("RSU desync failed %d for schema: %s, query: %s",
+ ret, (thd->db ? thd->db : "(null)"), thd->query());
+ my_error(ER_LOCK_DEADLOCK, MYF(0));
+ return(ret);
+ }
}
+ else
+ WSREP_DEBUG("RSU desync skipped: %d", wsrep_desync);
mysql_mutex_lock(&LOCK_wsrep_replaying);
wsrep_replaying++;
mysql_mutex_unlock(&LOCK_wsrep_replaying);
@@ -1363,15 +1328,21 @@ static int wsrep_RSU_begin(THD *thd, char *db_, char *table_)
if (wsrep_wait_committing_connections_close(5000))
{
/* no can do, bail out from DDL */
- WSREP_WARN("RSU failed due to pending transactions, %s", thd->query());
+ WSREP_WARN("RSU failed due to pending transactions, schema: %s, query %s",
+ (thd->db ? thd->db : "(null)"),
+ thd->query());
mysql_mutex_lock(&LOCK_wsrep_replaying);
wsrep_replaying--;
mysql_mutex_unlock(&LOCK_wsrep_replaying);
- ret = wsrep->resync(wsrep);
- if (ret != WSREP_OK)
+ if (!wsrep_desync)
{
- WSREP_WARN("resync failed %d for %s", ret, thd->query());
+ ret = wsrep->resync(wsrep);
+ if (ret != WSREP_OK)
+ {
+ WSREP_WARN("resync failed %d for schema: %s, query: %s",
+ ret, (thd->db ? thd->db : "(null)"), thd->query());
+ }
}
my_error(ER_LOCK_DEADLOCK, MYF(0));
return(1);
@@ -1380,7 +1351,9 @@ static int wsrep_RSU_begin(THD *thd, char *db_, char *table_)
wsrep_seqno_t seqno = wsrep->pause(wsrep);
if (seqno == WSREP_SEQNO_UNDEFINED)
{
- WSREP_WARN("pause failed %lld for %s", (long long)seqno, thd->query());
+ WSREP_WARN("pause failed %lld for schema: %s, query: %s", (long long)seqno,
+ (thd->db ? thd->db : "(null)"),
+ thd->query());
return(1);
}
WSREP_DEBUG("paused at %lld", (long long)seqno);
@@ -1402,14 +1375,22 @@ static void wsrep_RSU_end(THD *thd)
ret = wsrep->resume(wsrep);
if (ret != WSREP_OK)
{
- WSREP_WARN("resume failed %d for %s", ret, thd->query());
+ WSREP_WARN("resume failed %d for schema: %s, query: %s", ret,
+ (thd->db ? thd->db : "(null)"),
+ thd->query());
}
- ret = wsrep->resync(wsrep);
- if (ret != WSREP_OK)
+ if (!wsrep_desync)
{
- WSREP_WARN("resync failed %d for %s", ret, thd->query());
- return;
+ ret = wsrep->resync(wsrep);
+ if (ret != WSREP_OK)
+ {
+ WSREP_WARN("resync failed %d for schema: %s, query: %s", ret,
+ (thd->db ? thd->db : "(null)"), thd->query());
+ return;
+ }
}
+ else
+ WSREP_DEBUG("RSU resync skipped: %d", wsrep_desync);
thd->variables.wsrep_on = 1;
}
@@ -1427,8 +1408,10 @@ int wsrep_to_isolation_begin(THD *thd, char *db_, char *table_,
if (thd->wsrep_conflict_state == MUST_ABORT)
{
- WSREP_INFO("thread: %lu, %s has been aborted due to multi-master conflict",
- thd->thread_id, thd->query());
+ WSREP_INFO("thread: %lu, schema: %s, query: %s has been aborted due to multi-master conflict",
+ thd->thread_id,
+ (thd->db ? thd->db : "(null)"),
+ thd->query());
mysql_mutex_unlock(&thd->LOCK_wsrep_thd);
return WSREP_TRX_FAIL;
}
@@ -1465,14 +1448,25 @@ int wsrep_to_isolation_begin(THD *thd, char *db_, char *table_,
if (thd->variables.wsrep_on && thd->wsrep_exec_mode==LOCAL_STATE)
{
- switch (wsrep_OSU_method_options) {
+ switch (thd->variables.wsrep_OSU_method) {
case WSREP_OSU_TOI: ret = wsrep_TOI_begin(thd, db_, table_,
table_list); break;
case WSREP_OSU_RSU: ret = wsrep_RSU_begin(thd, db_, table_); break;
+ default:
+ WSREP_ERROR("Unsupported OSU method: %lu",
+ thd->variables.wsrep_OSU_method);
+ ret= -1;
+ break;
}
- if (!ret)
- {
- thd->wsrep_exec_mode= TOTAL_ORDER;
+ switch (ret) {
+ case 0: thd->wsrep_exec_mode= TOTAL_ORDER; break;
+ case 1:
+ /* TOI replication skipped, treat as success */
+ ret = 0;
+ break;
+ case -1:
+ /* TOI replication failed, treat as error */
+ break;
}
}
return ret;
@@ -1482,27 +1476,32 @@ void wsrep_to_isolation_end(THD *thd)
{
if (thd->wsrep_exec_mode == TOTAL_ORDER)
{
- switch(wsrep_OSU_method_options)
+ switch(thd->variables.wsrep_OSU_method)
{
case WSREP_OSU_TOI: wsrep_TOI_end(thd); break;
case WSREP_OSU_RSU: wsrep_RSU_end(thd); break;
+ default:
+ WSREP_WARN("Unsupported wsrep OSU method at isolation end: %lu",
+ thd->variables.wsrep_OSU_method);
+ break;
}
wsrep_cleanup_transaction(thd);
}
}
-#define WSREP_MDL_LOG(severity, msg, req, gra) \
+#define WSREP_MDL_LOG(severity, msg, schema, schema_len, req, gra) \
WSREP_##severity( \
"%s\n" \
+ "schema: %.*s\n" \
"request: (%lu \tseqno %lld \twsrep (%d, %d, %d) cmd %d %d \t%s)\n" \
"granted: (%lu \tseqno %lld \twsrep (%d, %d, %d) cmd %d %d \t%s)", \
- msg, \
+ msg, schema_len, schema, \
req->thread_id, (long long)wsrep_thd_trx_seqno(req), \
req->wsrep_exec_mode, req->wsrep_query_state, req->wsrep_conflict_state, \
- req->command, req->lex->sql_command, req->query(), \
+ req->get_command(), req->lex->sql_command, req->query(), \
gra->thread_id, (long long)wsrep_thd_trx_seqno(gra), \
gra->wsrep_exec_mode, gra->wsrep_query_state, gra->wsrep_conflict_state, \
- gra->command, gra->lex->sql_command, gra->query());
+ gra->get_command(), gra->lex->sql_command, gra->query());
/**
Check if request for the metadata lock should be granted to the requester.
@@ -1516,38 +1515,45 @@ void wsrep_to_isolation_end(THD *thd)
bool
wsrep_grant_mdl_exception(MDL_context *requestor_ctx,
- MDL_ticket *ticket
+ MDL_ticket *ticket,
+ const MDL_key *key
) {
/* Fallback to the non-wsrep behaviour */
if (!WSREP_ON) return FALSE;
- THD *request_thd = requestor_ctx->get_thd();
- THD *granted_thd = ticket->get_ctx()->get_thd();
+ THD *request_thd = requestor_ctx->wsrep_get_thd();
+ THD *granted_thd = ticket->get_ctx()->wsrep_get_thd();
bool ret = FALSE;
+ const char* schema= key->db_name();
+ int schema_len= key->db_name_length();
+
mysql_mutex_lock(&request_thd->LOCK_wsrep_thd);
if (request_thd->wsrep_exec_mode == TOTAL_ORDER ||
request_thd->wsrep_exec_mode == REPL_RECV)
{
mysql_mutex_unlock(&request_thd->LOCK_wsrep_thd);
- WSREP_MDL_LOG(DEBUG, "MDL conflict ", request_thd, granted_thd);
+ WSREP_MDL_LOG(DEBUG, "MDL conflict ", schema, schema_len,
+ request_thd, granted_thd);
ticket->wsrep_report(wsrep_debug);
mysql_mutex_lock(&granted_thd->LOCK_wsrep_thd);
if (granted_thd->wsrep_exec_mode == TOTAL_ORDER ||
granted_thd->wsrep_exec_mode == REPL_RECV)
{
- WSREP_MDL_LOG(INFO, "MDL BF-BF conflict", request_thd, granted_thd);
+ WSREP_MDL_LOG(INFO, "MDL BF-BF conflict", schema, schema_len,
+ request_thd, granted_thd);
ticket->wsrep_report(true);
mysql_mutex_unlock(&granted_thd->LOCK_wsrep_thd);
ret = TRUE;
}
- else if (granted_thd->lex->sql_command == SQLCOM_FLUSH)
+ else if (granted_thd->lex->sql_command == SQLCOM_FLUSH ||
+ granted_thd->mdl_context.wsrep_has_explicit_locks())
{
- WSREP_DEBUG("mdl granted over FLUSH BF");
+ WSREP_DEBUG("BF thread waiting for FLUSH");
ticket->wsrep_report(wsrep_debug);
mysql_mutex_unlock(&granted_thd->LOCK_wsrep_thd);
- ret = TRUE;
+ ret = FALSE;
}
else if (request_thd->lex->sql_command == SQLCOM_DROP_TABLE)
{
@@ -1567,7 +1573,8 @@ wsrep_grant_mdl_exception(MDL_context *requestor_ctx,
}
else
{
- WSREP_MDL_LOG(DEBUG, "MDL conflict-> BF abort", request_thd, granted_thd);
+ WSREP_MDL_LOG(DEBUG, "MDL conflict-> BF abort", schema, schema_len,
+ request_thd, granted_thd);
ticket->wsrep_report(wsrep_debug);
mysql_mutex_unlock(&granted_thd->LOCK_wsrep_thd);
wsrep_abort_thd((void*)request_thd, (void*)granted_thd, 1);
diff --git a/sql/wsrep_mysqld.h b/sql/wsrep_mysqld.h
index 3fb5189937e..242227fd2a0 100644
--- a/sql/wsrep_mysqld.h
+++ b/sql/wsrep_mysqld.h
@@ -1,4 +1,4 @@
-/* Copyright 2008-2013 Codership Oy <http://www.codership.com>
+/* Copyright 2008-2015 Codership Oy <http://www.codership.com>
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
@@ -19,6 +19,7 @@
#include "mysqld.h"
typedef struct st_mysql_show_var SHOW_VAR;
#include <sql_priv.h>
+//#include "rpl_gtid.h"
#include "../wsrep/wsrep_api.h"
#define WSREP_UNDEFINED_TRX_ID ULONGLONG_MAX
@@ -27,10 +28,17 @@ class set_var;
class THD;
enum wsrep_exec_mode {
- LOCAL_STATE,
- REPL_RECV,
- TOTAL_ORDER,
- LOCAL_COMMIT
+ /* Transaction processing before replication. */
+ LOCAL_STATE,
+ /* Slave thread applying write sets from other nodes or replaying thread. */
+ REPL_RECV,
+ /* Total-order-isolation mode */
+ TOTAL_ORDER,
+ /*
+ Transaction procession after it has been replicated in prepare stage and
+ has passed certification
+ */
+ LOCAL_COMMIT
};
enum wsrep_query_state {
@@ -88,7 +96,6 @@ extern my_bool wsrep_certify_nonPK;
extern long wsrep_max_protocol_version;
extern long wsrep_protocol_version;
extern ulong wsrep_forced_binlog_format;
-extern ulong wsrep_OSU_method_options;
extern my_bool wsrep_desync;
extern my_bool wsrep_recovery;
extern my_bool wsrep_replicate_myisam;
@@ -99,9 +106,15 @@ extern my_bool wsrep_restart_slave;
extern my_bool wsrep_restart_slave_activated;
extern my_bool wsrep_slave_FK_checks;
extern my_bool wsrep_slave_UK_checks;
+extern bool wsrep_new_cluster; // bootstrap the cluster ?
extern my_bool wsrep_creating_startup_threads;
-enum enum_wsrep_OSU_method { WSREP_OSU_TOI, WSREP_OSU_RSU };
+enum enum_wsrep_OSU_method {
+ WSREP_OSU_TOI,
+ WSREP_OSU_RSU,
+ WSREP_OSU_NONE,
+};
+
enum enum_wsrep_sync_wait {
WSREP_SYNC_WAIT_NONE = 0x0,
// show, select, begin
@@ -124,16 +137,9 @@ extern const char* wsrep_provider_name;
extern const char* wsrep_provider_version;
extern const char* wsrep_provider_vendor;
-// Other wsrep global variables
-extern my_bool wsrep_inited; // whether wsrep is initialized ?
-
int wsrep_show_status(THD *thd, SHOW_VAR *var, char *buff);
void wsrep_free_status(THD *thd);
-/* Filters out --wsrep-new-cluster oprtion from argv[]
- * should be called in the very beginning of main() */
-void wsrep_filter_new_cluster (int* argc, char* argv[]);
-
int wsrep_init();
void wsrep_deinit(bool free_options);
void wsrep_recover();
@@ -143,6 +149,8 @@ bool wsrep_before_SE(); // initialize wsrep before storage
* @param before wsrep_before_SE() value */
void wsrep_init_startup(bool before);
+// Other wsrep global variables
+extern my_bool wsrep_inited; // whether wsrep is initialized ?
extern "C" enum wsrep_exec_mode wsrep_thd_exec_mode(THD *thd);
extern "C" enum wsrep_conflict_state wsrep_thd_conflict_state(THD *thd);
@@ -170,8 +178,9 @@ extern "C" query_id_t wsrep_thd_query_id(THD *thd);
extern "C" char * wsrep_thd_query(THD *thd);
extern "C" query_id_t wsrep_thd_wsrep_last_query_id(THD *thd);
extern "C" void wsrep_thd_set_wsrep_last_query_id(THD *thd, query_id_t id);
-extern "C" void wsrep_thd_awake(THD* bf_thd, THD *thd, my_bool signal);
-
+extern "C" void wsrep_thd_awake(THD *thd, my_bool signal);
+extern "C" int wsrep_thd_retry_counter(THD *thd);
+extern "C" bool wsrep_thd_skip_append_keys(THD *thd);
extern void wsrep_close_client_connections(my_bool wait_to_end);
extern int wsrep_wait_committing_connections_close(int wait_time);
@@ -187,7 +196,6 @@ extern bool wsrep_must_sync_wait (THD* thd, uint mask = WSREP_SYNC_WAIT_BEFORE_R
extern bool wsrep_sync_wait (THD* thd, uint mask = WSREP_SYNC_WAIT_BEFORE_READ);
extern int wsrep_check_opts (int argc, char* const* argv);
extern void wsrep_prepend_PATH (const char* path);
-/* some inline functions are defined in wsrep_mysqld_inl.h */
/* Other global variables */
extern wsrep_seqno_t wsrep_locked_seqno;
@@ -236,7 +244,7 @@ extern wsrep_seqno_t wsrep_locked_seqno;
WSREP_LOG(sql_print_information, "cluster conflict due to %s for threads:",\
(bf_abort) ? "high priority abort" : "certification failure" \
); \
- if (bf_thd) WSREP_LOG_CONFLICT_THD(bf_thd, "Winning thread"); \
+ if (bf_thd != NULL) WSREP_LOG_CONFLICT_THD(bf_thd, "Winning thread"); \
if (victim_thd) WSREP_LOG_CONFLICT_THD(victim_thd, "Victim thread"); \
}
@@ -285,6 +293,11 @@ extern mysql_mutex_t LOCK_wsrep_desync;
extern wsrep_aborting_thd_t wsrep_aborting_thd;
extern my_bool wsrep_emulate_bin_log;
extern int wsrep_to_isolation;
+#ifdef GTID_SUPPORT
+extern rpl_sidno wsrep_sidno;
+#endif /* GTID_SUPPORT */
+extern my_bool wsrep_preordered_opt;
+
#ifdef HAVE_PSI_INTERFACE
extern PSI_mutex_key key_LOCK_wsrep_ready;
extern PSI_mutex_key key_COND_wsrep_ready;
@@ -313,13 +326,8 @@ int wsrep_create_trigger_query(THD *thd, uchar** buf, size_t* buf_len);
int wsrep_create_event_query(THD *thd, uchar** buf, size_t* buf_len);
int wsrep_alter_event_query(THD *thd, uchar** buf, size_t* buf_len);
-const wsrep_uuid_t* wsrep_cluster_uuid();
-struct xid_t;
-void wsrep_set_SE_checkpoint(xid_t*);
-void wsrep_get_SE_checkpoint(wsrep_uuid_t&, wsrep_seqno_t&);
-void wsrep_xid_init(xid_t*, const wsrep_uuid_t*, wsrep_seqno_t);
-const wsrep_uuid_t* wsrep_xid_uuid(const xid_t*);
-wsrep_seqno_t wsrep_xid_seqno(const xid_t*);
-extern "C" int wsrep_is_wsrep_xid(const void* xid);
+#ifdef GTID_SUPPORT
+void wsrep_init_sidno(const wsrep_uuid_t&);
+#endif /* GTID_SUPPORT */
#endif /* WSREP_MYSQLD_H */
diff --git a/sql/wsrep_priv.h b/sql/wsrep_priv.h
index 93640fbcc03..30dce78c1a4 100644
--- a/sql/wsrep_priv.h
+++ b/sql/wsrep_priv.h
@@ -40,8 +40,8 @@ extern wsrep_uuid_t local_uuid;
extern wsrep_seqno_t local_seqno;
// a helper function
-void wsrep_sst_received(wsrep_t*, const wsrep_uuid_t*, wsrep_seqno_t,
- const void*, size_t);
+void wsrep_sst_received(wsrep_t*, const wsrep_uuid_t&, wsrep_seqno_t,
+ const void*, size_t);
/*! SST thread signals init thread about sst completion */
void wsrep_sst_complete(const wsrep_uuid_t*, wsrep_seqno_t, bool);
diff --git a/sql/wsrep_sst.cc b/sql/wsrep_sst.cc
index 8008a977a84..b697a557476 100644
--- a/sql/wsrep_sst.cc
+++ b/sql/wsrep_sst.cc
@@ -13,7 +13,8 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-#include <my_config.h>
+#include "wsrep_sst.h"
+
#include <mysqld.h>
#include <m_ctype.h>
#include <my_sys.h>
@@ -23,14 +24,14 @@
#include <sql_acl.h>
#include <sql_reload.h>
#include <sql_parse.h>
-#include <cstdio>
-#include <cstdlib>
-
#include "wsrep_priv.h"
#include "wsrep_utils.h"
-#include "wsrep_sst.h"
+#include "wsrep_xid.h"
+#include <cstdio>
+#include <cstdlib>
extern const char wsrep_defaults_file[];
+extern const char wsrep_defaults_group_suffix[];
const char* wsrep_sst_method = WSREP_SST_DEFAULT;
const char* wsrep_sst_receive_address = WSREP_SST_ADDRESS_AUTO;
@@ -244,17 +245,42 @@ void wsrep_sst_complete (const wsrep_uuid_t* sst_uuid,
mysql_mutex_unlock (&LOCK_wsrep_sst);
}
-void wsrep_sst_received (wsrep_t* const wsrep,
- const wsrep_uuid_t* const uuid,
- wsrep_seqno_t const seqno,
- const void* const state,
- size_t const state_len)
+void wsrep_sst_received (wsrep_t* const wsrep,
+ const wsrep_uuid_t& uuid,
+ wsrep_seqno_t const seqno,
+ const void* const state,
+ size_t const state_len)
{
- int const rcode(seqno < 0 ? seqno : 0);
- wsrep_gtid_t const state_id = {
- *uuid, (rcode ? WSREP_SEQNO_UNDEFINED : seqno)
- };
- wsrep->sst_received(wsrep, &state_id, state, state_len, rcode);
+ wsrep_get_SE_checkpoint(local_uuid, local_seqno);
+
+ if (memcmp(&local_uuid, &uuid, sizeof(wsrep_uuid_t)) ||
+ local_seqno < seqno || seqno < 0)
+ {
+ wsrep_set_SE_checkpoint(uuid, seqno);
+ local_uuid = uuid;
+ local_seqno = seqno;
+ }
+ else if (local_seqno > seqno)
+ {
+ WSREP_WARN("SST postion is in the past: %lld, current: %lld. "
+ "Can't continue.",
+ (long long)seqno, (long long)local_seqno);
+ unireg_abort(1);
+ }
+
+#ifdef GTID_SUPPORT
+ wsrep_init_sidno(uuid);
+#endif /* GTID_SUPPORT */
+
+ if (wsrep)
+ {
+ int const rcode(seqno < 0 ? seqno : 0);
+ wsrep_gtid_t const state_id = {
+ uuid, (rcode ? WSREP_SEQNO_UNDEFINED : seqno)
+ };
+
+ wsrep->sst_received(wsrep, &state_id, state, state_len, rcode);
+ }
}
// Let applier threads to continue
@@ -263,7 +289,7 @@ void wsrep_sst_continue ()
if (sst_needed)
{
WSREP_INFO("Signalling provider to continue.");
- wsrep_sst_received (wsrep, &local_uuid, local_seqno, NULL, 0);
+ wsrep_sst_received (wsrep, local_uuid, local_seqno, NULL, 0);
}
}
@@ -322,6 +348,33 @@ static char* my_fgets (char* buf, size_t buf_len, FILE* stream)
return ret;
}
+/*
+ Generate opt_binlog_opt_val for sst_donate_other(), sst_prepare_other().
+
+ Returns zero on success, negative error code otherwise.
+
+ String containing binlog name is stored in param ret if binlog is enabled
+ and GTID mode is on, otherwise empty string. Returned string should be
+ freed with my_free().
+ */
+static int generate_binlog_opt_val(char** ret)
+{
+ DBUG_ASSERT(ret);
+ *ret= NULL;
+ if (opt_bin_log)
+ {
+ assert(opt_bin_logname);
+ *ret= strcmp(opt_bin_logname, "0") ?
+ my_strdup(opt_bin_logname, MYF(0)) : my_strdup("", MYF(0));
+ }
+ else
+ {
+ *ret= my_strdup("", MYF(0));
+ }
+ if (!*ret) return -ENOMEM;
+ return 0;
+}
+
static void* sst_joiner_thread (void* a)
{
sst_thread_arg* arg= (sst_thread_arg*) a;
@@ -447,16 +500,31 @@ static ssize_t sst_prepare_other (const char* method,
return -ENOMEM;
}
- int ret= snprintf (cmd_str(), cmd_len,
- "wsrep_sst_%s "
- WSREP_SST_OPT_ROLE" 'joiner' "
- WSREP_SST_OPT_ADDR" '%s' "
- WSREP_SST_OPT_DATA" '%s' "
- WSREP_SST_OPT_CONF" '%s' "
- WSREP_SST_OPT_PARENT" '%d'",
- method, addr_in,
- mysql_real_data_home,
- wsrep_defaults_file, (int)getpid());
+ const char* binlog_opt= "";
+ char* binlog_opt_val= NULL;
+
+ int ret;
+ if ((ret= generate_binlog_opt_val(&binlog_opt_val)))
+ {
+ WSREP_ERROR("sst_prepare_other(): generate_binlog_opt_val() failed: %d",
+ ret);
+ return ret;
+ }
+ if (strlen(binlog_opt_val)) binlog_opt= WSREP_SST_OPT_BINLOG;
+
+ ret= snprintf (cmd_str(), cmd_len,
+ "wsrep_sst_%s "
+ WSREP_SST_OPT_ROLE" 'joiner' "
+ WSREP_SST_OPT_ADDR" '%s' "
+ WSREP_SST_OPT_DATA" '%s' "
+ WSREP_SST_OPT_CONF" '%s' "
+ WSREP_SST_OPT_CONF_SUFFIX" '%s' "
+ WSREP_SST_OPT_PARENT" '%d'"
+ " %s '%s' ",
+ method, addr_in, mysql_real_data_home,
+ wsrep_defaults_file, wsrep_defaults_group_suffix,
+ (int)getpid(), binlog_opt, binlog_opt_val);
+ my_free(binlog_opt_val);
if (ret < 0 || ret >= cmd_len)
{
@@ -504,7 +572,6 @@ static ssize_t sst_prepare_other (const char* method,
return ret;
}
-//extern ulong my_bind_addr;
extern uint mysqld_port;
/*! Just tells donor where to send mysqldump */
@@ -742,12 +809,11 @@ static int sst_donate_mysqldump (const char* addr,
WSREP_SST_OPT_PORT" '%s' "
WSREP_SST_OPT_LPORT" '%u' "
WSREP_SST_OPT_SOCKET" '%s' "
- WSREP_SST_OPT_DATA" '%s' "
WSREP_SST_OPT_CONF" '%s' "
WSREP_SST_OPT_GTID" '%s:%lld'"
"%s",
host, port, mysqld_port, mysqld_unix_port,
- mysql_real_data_home, wsrep_defaults_file, uuid_str,
+ wsrep_defaults_file, uuid_str,
(long long)seqno, bypass ? " "WSREP_SST_OPT_BYPASS : "");
if (ret < 0 || ret >= cmd_len)
@@ -783,9 +849,9 @@ static int run_sql_command(THD *thd, const char *query)
mysql_parse(thd, thd->query(), thd->query_length(), &ps);
if (thd->is_error())
{
- int const err= thd->stmt_da->sql_errno();
+ int const err= thd->get_stmt_da()->sql_errno();
WSREP_WARN ("error executing '%s': %d (%s)%s",
- query, err, thd->stmt_da->message(),
+ query, err, thd->get_stmt_da()->message(),
err == ER_UNKNOWN_SYSTEM_VARIABLE ?
". Was mysqld built with --with-innodb-disallow-writes ?" : "");
thd->clear_error();
@@ -821,8 +887,8 @@ static int sst_flush_tables(THD* thd)
else
{
/* make sure logs are flushed after global read lock acquired */
- err= reload_acl_and_cache(thd, REFRESH_ENGINE_LOG,
- (TABLE_LIST*) 0, &not_used);
+ err= reload_acl_and_cache(thd, REFRESH_ENGINE_LOG | REFRESH_BINARY_LOG,
+ (TABLE_LIST*) 0, &not_used);
}
thd->variables.character_set_client = current_charset;
@@ -994,6 +1060,8 @@ wait_signal:
return NULL;
}
+
+
static int sst_donate_other (const char* method,
const char* addr,
const char* uuid,
@@ -1011,19 +1079,34 @@ static int sst_donate_other (const char* method,
return -ENOMEM;
}
- int ret= snprintf (cmd_str(), cmd_len,
- "wsrep_sst_%s "
- WSREP_SST_OPT_ROLE" 'donor' "
- WSREP_SST_OPT_ADDR" '%s' "
- WSREP_SST_OPT_SOCKET" '%s' "
- WSREP_SST_OPT_DATA" '%s' "
- WSREP_SST_OPT_CONF" '%s' "
- WSREP_SST_OPT_GTID" '%s:%lld'"
- "%s",
- method, addr, mysqld_unix_port,
- mysql_real_data_home, wsrep_defaults_file,
- uuid, (long long) seqno,
- bypass ? " "WSREP_SST_OPT_BYPASS : "");
+ const char* binlog_opt= "";
+ char* binlog_opt_val= NULL;
+
+ int ret;
+ if ((ret= generate_binlog_opt_val(&binlog_opt_val)))
+ {
+ WSREP_ERROR("sst_donate_other(): generate_binlog_opt_val() failed: %d",ret);
+ return ret;
+ }
+ if (strlen(binlog_opt_val)) binlog_opt= WSREP_SST_OPT_BINLOG;
+
+ ret= snprintf (cmd_str(), cmd_len,
+ "wsrep_sst_%s "
+ WSREP_SST_OPT_ROLE" 'donor' "
+ WSREP_SST_OPT_ADDR" '%s' "
+ WSREP_SST_OPT_SOCKET" '%s' "
+ WSREP_SST_OPT_DATA" '%s' "
+ WSREP_SST_OPT_CONF" '%s' "
+ WSREP_SST_OPT_CONF_SUFFIX" '%s' "
+ " %s '%s' "
+ WSREP_SST_OPT_GTID" '%s:%lld'"
+ "%s",
+ method, addr, mysqld_unix_port, mysql_real_data_home,
+ wsrep_defaults_file, wsrep_defaults_group_suffix,
+ binlog_opt, binlog_opt_val,
+ uuid, (long long) seqno,
+ bypass ? " "WSREP_SST_OPT_BYPASS : "");
+ my_free(binlog_opt_val);
if (ret < 0 || ret >= cmd_len)
{
@@ -1041,7 +1124,7 @@ static int sst_donate_other (const char* method,
{
WSREP_ERROR("sst_donate_other(): pthread_create() failed: %d (%s)",
ret, strerror(ret));
- return -ret;
+ return ret;
}
mysql_cond_wait (&arg.cond, &arg.lock);
diff --git a/sql/wsrep_sst.h b/sql/wsrep_sst.h
index ad02aa55114..42f1055bde2 100644
--- a/sql/wsrep_sst.h
+++ b/sql/wsrep_sst.h
@@ -16,6 +16,7 @@
#ifndef WSREP_SST_H
#define WSREP_SST_H
+#include <my_config.h>
#include <mysql.h> // my_bool
#define WSREP_SST_OPT_ROLE "--role"
@@ -23,7 +24,9 @@
#define WSREP_SST_OPT_AUTH "--auth"
#define WSREP_SST_OPT_DATA "--datadir"
#define WSREP_SST_OPT_CONF "--defaults-file"
+#define WSREP_SST_OPT_CONF_SUFFIX "--defaults-group-suffix"
#define WSREP_SST_OPT_PARENT "--parent"
+#define WSREP_SST_OPT_BINLOG "--binlog"
// mysqldump-specific options
#define WSREP_SST_OPT_USER "--user"
diff --git a/sql/wsrep_thd.cc b/sql/wsrep_thd.cc
index 50e8a09706b..464a68a8221 100644
--- a/sql/wsrep_thd.cc
+++ b/sql/wsrep_thd.cc
@@ -19,10 +19,14 @@
#include "rpl_rli.h"
#include "log_event.h"
#include "sql_parse.h"
-#include "slave.h" // opt_log_slave_updates
+//#include "global_threads.h" // LOCK_thread_count, etc.
#include "sql_base.h" // close_thread_tables()
#include "mysqld.h" // start_wsrep_THD();
+#include "slave.h" // opt_log_slave_updates
+#include "rpl_filter.h"
+#include "rpl_rli.h"
+#include "rpl_mi.h"
#if (__LP64__)
static volatile int64 wsrep_bf_aborts_counter(0);
@@ -82,7 +86,10 @@ void wsrep_client_rollback(THD *thd)
thd->wsrep_conflict_state= ABORTED;
}
-static Relay_log_info* wsrep_relay_log_init(const char* log_fname)
+#define NUMBER_OF_FIELDS_TO_IDENTIFY_COORDINATOR 1
+#define NUMBER_OF_FIELDS_TO_IDENTIFY_WORKER 2
+
+static rpl_group_info* wsrep_relay_group_init(const char* log_fname)
{
Relay_log_info* rli= new Relay_log_info(false);
@@ -93,14 +100,36 @@ static Relay_log_info* wsrep_relay_log_init(const char* log_fname)
new Format_description_log_event(4);
}
- rli->sql_thd= current_thd;
+ static LEX_STRING connection_name= { C_STRING_WITH_LEN("wsrep") };
+
+ /*
+ Master_info's constructor initializes rpl_filter by either an already
+ constructed Rpl_filter object from global 'rpl_filters' list if the
+ specified connection name is same, or it constructs a new Rpl_filter
+ object and adds it to rpl_filters. This object is later destructed by
+ Mater_info's destructor by looking it up based on connection name in
+ rpl_filters list.
+
+ However, since all Master_info objects created here would share same
+ connection name ("wsrep"), destruction of any of the existing Master_info
+ objects (in wsrep_return_from_bf_mode()) would free rpl_filter referenced
+ by any/all existing Master_info objects.
- if ((rli->deferred_events_collecting= rpl_filter->is_on()))
+ In order to avoid that, we have added a check in Master_info's destructor
+ to not free the "wsrep" rpl_filter. It will eventually be freed by
+ free_all_rpl_filters() when server terminates.
+ */
+ rli->mi = new Master_info(&connection_name, false);
+
+ struct rpl_group_info *rgi= new rpl_group_info(rli);
+ rgi->thd= rli->sql_driver_thd= current_thd;
+
+ if ((rgi->deferred_events_collecting= rli->mi->rpl_filter->is_on()))
{
- rli->deferred_events= new Deferred_log_events(rli);
+ rgi->deferred_events= new Deferred_log_events(rli);
}
- return rli;
+ return rgi;
}
static void wsrep_prepare_bf_thd(THD *thd, struct wsrep_thd_shadow* shadow)
@@ -115,7 +144,11 @@ static void wsrep_prepare_bf_thd(THD *thd, struct wsrep_thd_shadow* shadow)
else
thd->variables.option_bits&= ~(OPTION_BIN_LOG);
- if (!thd->wsrep_rli) thd->wsrep_rli= wsrep_relay_log_init("wsrep_relay");
+ if (!thd->wsrep_rgi) thd->wsrep_rgi= wsrep_relay_group_init("wsrep_relay");
+
+ /* thd->system_thread_info.rpl_sql_info isn't initialized. */
+ thd->system_thread_info.rpl_sql_info=
+ new rpl_sql_thread_info(thd->wsrep_rgi->rli->mi->rpl_filter);
thd->wsrep_exec_mode= REPL_RECV;
thd->net.vio= 0;
@@ -139,21 +172,26 @@ static void wsrep_return_from_bf_mode(THD *thd, struct wsrep_thd_shadow* shadow)
thd->variables.tx_isolation = shadow->tx_isolation;
thd->reset_db(shadow->db, shadow->db_length);
- thd->wsrep_rli->cleanup_after_session();
- delete thd->wsrep_rli;
- thd->wsrep_rli= NULL;
+ delete thd->system_thread_info.rpl_sql_info;
+ delete thd->wsrep_rgi->rli->mi;
+ delete thd->wsrep_rgi->rli;
+
+ thd->wsrep_rgi->cleanup_after_session();
+ delete thd->wsrep_rgi;
+ thd->wsrep_rgi = NULL;
}
void wsrep_replay_transaction(THD *thd)
{
/* checking if BF trx must be replayed */
if (thd->wsrep_conflict_state== MUST_REPLAY) {
+ DBUG_ASSERT(wsrep_thd_trx_seqno(thd));
if (thd->wsrep_exec_mode!= REPL_RECV) {
- if (thd->stmt_da->is_sent)
+ if (thd->get_stmt_da()->is_sent())
{
WSREP_ERROR("replay issue, thd has reported status already");
}
- thd->stmt_da->reset_diagnostics_area();
+ thd->get_stmt_da()->reset_diagnostics_area();
thd->wsrep_conflict_state= REPLAYING;
mysql_mutex_unlock(&thd->LOCK_wsrep_thd);
@@ -169,7 +207,14 @@ void wsrep_replay_transaction(THD *thd)
thd->variables.option_bits&= ~(OPTION_TABLE_LOCK);
}
thd->mdl_context.release_transactional_locks();
-
+ /*
+ Replaying will call MYSQL_START_STATEMENT when handling
+ BEGIN Query_log_event so end statement must be called before
+ replaying.
+ */
+ MYSQL_END_STATEMENT(thd->m_statement_psi, thd->get_stmt_da());
+ thd->m_statement_psi= NULL;
+ thd->m_digest= NULL;
thd_proc_info(thd, "wsrep replaying trx");
WSREP_DEBUG("replay trx: %s %lld",
thd->query() ? thd->query() : "void",
@@ -198,16 +243,16 @@ void wsrep_replay_transaction(THD *thd)
wsrep->post_commit(wsrep, &thd->wsrep_ws_handle);
WSREP_DEBUG("trx_replay successful for: %ld %llu",
thd->thread_id, (long long)thd->real_id);
- if (thd->stmt_da->is_sent)
+ if (thd->get_stmt_da()->is_sent())
{
WSREP_WARN("replay ok, thd has reported status");
}
- else if (thd->stmt_da->is_set())
+ else if (thd->get_stmt_da()->is_set())
{
- if (thd->stmt_da->status() != Diagnostics_area::DA_OK)
+ if (thd->get_stmt_da()->status() != Diagnostics_area::DA_OK)
{
WSREP_WARN("replay ok, thd has error status %d",
- thd->stmt_da->status());
+ thd->get_stmt_da()->status());
}
}
else
@@ -216,22 +261,28 @@ void wsrep_replay_transaction(THD *thd)
}
break;
case WSREP_TRX_FAIL:
- if (thd->stmt_da->is_sent)
+ if (thd->get_stmt_da()->is_sent())
{
WSREP_ERROR("replay failed, thd has reported status");
}
else
{
WSREP_DEBUG("replay failed, rolling back");
- my_error(ER_LOCK_DEADLOCK, MYF(0), "wsrep aborted transaction");
+ //my_error(ER_LOCK_DEADLOCK, MYF(0), "wsrep aborted transaction");
}
thd->wsrep_conflict_state= ABORTED;
wsrep->post_rollback(wsrep, &thd->wsrep_ws_handle);
break;
default:
- WSREP_ERROR("trx_replay failed for: %d, query: %s",
- rcode, thd->query() ? thd->query() : "void");
+ WSREP_ERROR("trx_replay failed for: %d, schema: %s, query: %s",
+ rcode,
+ (thd->db ? thd->db : "(null)"),
+ thd->query() ? thd->query() : "void");
/* we're now in inconsistent state, must abort */
+
+ /* http://bazaar.launchpad.net/~codership/codership-mysql/5.6/revision/3962#sql/wsrep_thd.cc */
+ mysql_mutex_unlock(&thd->LOCK_wsrep_thd);
+
unireg_abort(1);
break;
}
@@ -305,9 +356,13 @@ static void wsrep_replication_process(THD *thd)
mysql_cond_broadcast(&COND_thread_count);
mysql_mutex_unlock(&LOCK_thread_count);
- if (thd->temporary_tables)
+ TABLE *tmp;
+ while ((tmp = thd->temporary_tables))
{
- WSREP_DEBUG("Applier %lu, has temporary tables at exit", thd->thread_id);
+ WSREP_WARN("Applier %lu, has temporary tables at exit: %s.%s",
+ thd->thread_id,
+ (tmp->s) ? tmp->s->db.str : "void",
+ (tmp->s) ? tmp->s->table_name.str : "void");
}
wsrep_return_from_bf_mode(thd, &shadow);
DBUG_VOID_RETURN;
@@ -393,6 +448,7 @@ static void wsrep_rollback_process(THD *thd)
mysql_mutex_unlock(&aborting->LOCK_wsrep_thd);
+ set_current_thd(aborting);
aborting->store_globals();
mysql_mutex_lock(&aborting->LOCK_wsrep_thd);
@@ -401,6 +457,9 @@ static void wsrep_rollback_process(THD *thd)
aborting->thread_id, (long long)aborting->real_id);
mysql_mutex_unlock(&aborting->LOCK_wsrep_thd);
+ set_current_thd(thd);
+ thd->store_globals();
+
mysql_mutex_lock(&LOCK_wsrep_rollback);
}
}
@@ -424,8 +483,6 @@ void wsrep_create_rollbacker()
}
}
-
-extern "C"
void wsrep_thd_set_PA_safe(void *thd_ptr, my_bool safe)
{
if (thd_ptr)
@@ -435,19 +492,50 @@ void wsrep_thd_set_PA_safe(void *thd_ptr, my_bool safe)
}
}
-extern "C"
-my_bool wsrep_thd_is_BF(void *thd_ptr, my_bool sync)
+int wsrep_thd_conflict_state(void *thd_ptr, my_bool sync)
{
- my_bool status = FALSE;
+ int state = -1;
if (thd_ptr)
{
THD* thd = (THD*)thd_ptr;
if (sync) mysql_mutex_lock(&thd->LOCK_wsrep_thd);
- status = ((thd->wsrep_exec_mode == REPL_RECV) ||
- (thd->wsrep_exec_mode == TOTAL_ORDER));
+ state = thd->wsrep_conflict_state;
if (sync) mysql_mutex_unlock(&thd->LOCK_wsrep_thd);
}
+ return state;
+}
+
+my_bool wsrep_thd_is_wsrep(void *thd_ptr)
+{
+ my_bool status = FALSE;
+ if (thd_ptr)
+ {
+ THD* thd = (THD*)thd_ptr;
+
+ status = (WSREP(thd) && WSREP_PROVIDER_EXISTS);
+ }
+ return status;
+}
+
+my_bool wsrep_thd_is_BF(void *thd_ptr, my_bool sync)
+{
+ my_bool status = FALSE;
+ if (thd_ptr)
+ {
+ THD* thd = (THD*)thd_ptr;
+ // THD can be BF only if provider exists
+ if (wsrep_thd_is_wsrep(thd_ptr))
+ {
+ if (sync)
+ mysql_mutex_lock(&thd->LOCK_wsrep_thd);
+
+ status = ((thd->wsrep_exec_mode == REPL_RECV) ||
+ (thd->wsrep_exec_mode == TOTAL_ORDER));
+ if (sync)
+ mysql_mutex_unlock(&thd->LOCK_wsrep_thd);
+ }
+ }
return status;
}
@@ -483,7 +571,6 @@ my_bool wsrep_thd_is_local(void *thd_ptr, my_bool sync)
return status;
}
-extern "C"
int wsrep_abort_thd(void *bf_thd_ptr, void *victim_thd_ptr, my_bool signal)
{
THD *victim_thd = (THD *) victim_thd_ptr;
@@ -491,8 +578,8 @@ int wsrep_abort_thd(void *bf_thd_ptr, void *victim_thd_ptr, my_bool signal)
DBUG_ENTER("wsrep_abort_thd");
if ( (WSREP(bf_thd) ||
- ( (WSREP_ON || wsrep_OSU_method_options == WSREP_OSU_RSU) &&
- bf_thd->wsrep_exec_mode == TOTAL_ORDER) ) &&
+ ( (WSREP_ON || bf_thd->variables.wsrep_OSU_method == WSREP_OSU_RSU) &&
+ bf_thd->wsrep_exec_mode == TOTAL_ORDER) ) &&
victim_thd)
{
if ((victim_thd->wsrep_conflict_state == MUST_ABORT) ||
@@ -527,3 +614,8 @@ int wsrep_thd_in_locking_session(void *thd_ptr)
return 0;
}
+bool wsrep_thd_has_explicit_locks(THD *thd)
+{
+ assert(thd);
+ return (thd->mdl_context.wsrep_has_explicit_locks());
+}
diff --git a/sql/wsrep_thd.h b/sql/wsrep_thd.h
index 597033a7c70..700e0f1cc56 100644
--- a/sql/wsrep_thd.h
+++ b/sql/wsrep_thd.h
@@ -24,12 +24,17 @@ void wsrep_replay_transaction(THD *thd);
void wsrep_create_appliers(long threads);
void wsrep_create_rollbacker();
-extern "C" my_bool wsrep_thd_is_BF(void *thd_ptr, my_bool sync);
-extern "C" void wsrep_thd_set_PA_safe(void *thd_ptr, my_bool safe);
+int wsrep_abort_thd(void *bf_thd_ptr, void *victim_thd_ptr,
+ my_bool signal);
+
+extern void wsrep_thd_set_PA_safe(void *thd_ptr, my_bool safe);
+extern my_bool wsrep_thd_is_BF(void *thd_ptr, my_bool sync);
+extern my_bool wsrep_thd_is_wsrep(void *thd_ptr);
+
+extern int wsrep_thd_conflict_state(void *thd_ptr, my_bool sync);
+//extern "C" my_bool wsrep_thd_is_BF(void *thd_ptr, my_bool sync);
extern "C" my_bool wsrep_thd_is_BF_or_commit(void *thd_ptr, my_bool sync);
extern "C" my_bool wsrep_thd_is_local(void *thd_ptr, my_bool sync);
-extern "C" int wsrep_abort_thd(void *bf_thd_ptr, void *victim_thd_ptr,
- my_bool signal);
extern "C" int wsrep_thd_in_locking_session(void *thd_ptr);
#endif /* WSREP_THD_H */
diff --git a/sql/wsrep_utils.cc b/sql/wsrep_utils.cc
index 951007c2660..719e8e6b473 100644
--- a/sql/wsrep_utils.cc
+++ b/sql/wsrep_utils.cc
@@ -554,58 +554,3 @@ size_t wsrep_guess_ip (char* buf, size_t buf_len)
return 0;
}
-
-/*
- * WSREPXid
- */
-
-#define WSREP_XID_PREFIX "WSREPXid"
-#define WSREP_XID_PREFIX_LEN MYSQL_XID_PREFIX_LEN
-#define WSREP_XID_UUID_OFFSET 8
-#define WSREP_XID_SEQNO_OFFSET (WSREP_XID_UUID_OFFSET + sizeof(wsrep_uuid_t))
-#define WSREP_XID_GTRID_LEN (WSREP_XID_SEQNO_OFFSET + sizeof(wsrep_seqno_t))
-
-void wsrep_xid_init(XID* xid, const wsrep_uuid_t* uuid, wsrep_seqno_t seqno)
-{
- xid->formatID= 1;
- xid->gtrid_length= WSREP_XID_GTRID_LEN;
- xid->bqual_length= 0;
- memset(xid->data, 0, sizeof(xid->data));
- memcpy(xid->data, WSREP_XID_PREFIX, WSREP_XID_PREFIX_LEN);
- memcpy(xid->data + WSREP_XID_UUID_OFFSET, uuid, sizeof(wsrep_uuid_t));
- memcpy(xid->data + WSREP_XID_SEQNO_OFFSET, &seqno, sizeof(wsrep_seqno_t));
-}
-
-const wsrep_uuid_t* wsrep_xid_uuid(const XID* xid)
-{
- if (wsrep_is_wsrep_xid(xid))
- return reinterpret_cast<const wsrep_uuid_t*>(xid->data
- + WSREP_XID_UUID_OFFSET);
- else
- return &WSREP_UUID_UNDEFINED;
-}
-
-wsrep_seqno_t wsrep_xid_seqno(const XID* xid)
-{
-
- if (wsrep_is_wsrep_xid(xid))
- {
- wsrep_seqno_t seqno;
- memcpy(&seqno, xid->data + WSREP_XID_SEQNO_OFFSET, sizeof(wsrep_seqno_t));
- return seqno;
- }
- else
- {
- return WSREP_SEQNO_UNDEFINED;
- }
-}
-
-extern "C"
-int wsrep_is_wsrep_xid(const void* xid_ptr)
-{
- const XID* xid= reinterpret_cast<const XID*>(xid_ptr);
- return (xid->formatID == 1 &&
- xid->gtrid_length == WSREP_XID_GTRID_LEN &&
- xid->bqual_length == 0 &&
- !memcmp(xid->data, WSREP_XID_PREFIX, WSREP_XID_PREFIX_LEN));
-}
diff --git a/sql/wsrep_var.cc b/sql/wsrep_var.cc
index 66b33e7697c..7ac68df66bd 100644
--- a/sql/wsrep_var.cc
+++ b/sql/wsrep_var.cc
@@ -1,4 +1,4 @@
-/* Copyright 2008 Codership Oy <http://www.codership.com>
+/* Copyright 2008-2015 Codership Oy <http://www.codership.com>
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
@@ -22,6 +22,7 @@
#include <sql_acl.h>
#include "wsrep_priv.h"
#include "wsrep_thd.h"
+#include "wsrep_xid.h"
#include <my_dir.h>
#include <cstdio>
#include <cstdlib>
@@ -34,7 +35,6 @@ const char* wsrep_node_name = 0;
const char* wsrep_node_address = 0;
const char* wsrep_node_incoming_address = 0;
const char* wsrep_start_position = 0;
-ulong wsrep_OSU_method_options;
int wsrep_init_vars()
{
@@ -140,29 +140,30 @@ err:
return 1;
}
-void wsrep_set_local_position (const char* value)
+static
+void wsrep_set_local_position(const char* const value, bool const sst)
{
- size_t value_len = strlen (value);
- size_t uuid_len = wsrep_uuid_scan (value, value_len, &local_uuid);
+ size_t const value_len = strlen(value);
+ wsrep_uuid_t uuid;
+ size_t const uuid_len = wsrep_uuid_scan(value, value_len, &uuid);
+ wsrep_seqno_t const seqno = strtoll(value + uuid_len + 1, NULL, 10);
- local_seqno = strtoll (value + uuid_len + 1, NULL, 10);
-
- XID xid;
- wsrep_xid_init(&xid, &local_uuid, local_seqno);
- wsrep_set_SE_checkpoint(&xid);
- WSREP_INFO ("wsrep_start_position var submitted: '%s'", wsrep_start_position);
+ if (sst) {
+ wsrep_sst_received (wsrep, uuid, seqno, NULL, 0);
+ } else {
+ // initialization
+ local_uuid = uuid;
+ local_seqno = seqno;
+ }
}
bool wsrep_start_position_update (sys_var *self, THD* thd, enum_var_type type)
{
+ WSREP_INFO ("wsrep_start_position var submitted: '%s'",
+ wsrep_start_position);
// since this value passed wsrep_start_position_check, don't check anything
// here
- wsrep_set_local_position (wsrep_start_position);
-
- if (wsrep) {
- wsrep_sst_received (wsrep, &local_uuid, local_seqno, NULL, 0);
- }
-
+ wsrep_set_local_position (wsrep_start_position, true);
return 0;
}
@@ -175,7 +176,7 @@ void wsrep_start_position_init (const char* val)
return;
}
- wsrep_set_local_position (val);
+ wsrep_set_local_position (val, false);
}
static bool refresh_provider_options()
@@ -212,7 +213,7 @@ static int wsrep_provider_verify (const char* provider_str)
return 1;
/* check that provider file exists */
- bzero(&f_stat, sizeof(MY_STAT));
+ memset(&f_stat, 0, sizeof(MY_STAT));
if (!my_stat(path, &f_stat, MYF(0)))
{
return 1;
@@ -493,11 +494,11 @@ bool wsrep_desync_check (sys_var *self, THD* thd, set_var* var)
bool new_wsrep_desync= (bool) var->save_result.ulonglong_value;
if (wsrep_desync == new_wsrep_desync) {
if (new_wsrep_desync) {
- push_warning (thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning (thd, Sql_condition::WARN_LEVEL_WARN,
ER_WRONG_VALUE_FOR_VAR,
"'wsrep_desync' is already ON.");
} else {
- push_warning (thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning (thd, Sql_condition::WARN_LEVEL_WARN,
ER_WRONG_VALUE_FOR_VAR,
"'wsrep_desync' is already OFF.");
}
@@ -511,14 +512,18 @@ bool wsrep_desync_update (sys_var *self, THD* thd, enum_var_type type)
if (wsrep_desync) {
ret = wsrep->desync (wsrep);
if (ret != WSREP_OK) {
- WSREP_WARN ("SET desync failed %d for %s", ret, thd->query());
+ WSREP_WARN ("SET desync failed %d for schema: %s, query: %s", ret,
+ (thd->db ? thd->db : "(null)"),
+ thd->query());
my_error (ER_CANNOT_USER, MYF(0), "'desync'", thd->query());
return true;
}
} else {
ret = wsrep->resync (wsrep);
if (ret != WSREP_OK) {
- WSREP_WARN ("SET resync failed %d for %s", ret, thd->query());
+ WSREP_WARN ("SET resync failed %d for schema: %s, query: %s", ret,
+ (thd->db ? thd->db : "(null)"),
+ thd->query());
my_error (ER_CANNOT_USER, MYF(0), "'resync'", thd->query());
return true;
}
@@ -562,6 +567,8 @@ static void export_wsrep_status_to_mysql(THD* thd)
{
int wsrep_status_len, i;
+ wsrep_free_status(thd);
+
thd->wsrep_status_vars = wsrep->stats_get(wsrep);
if (!thd->wsrep_status_vars) {
diff --git a/sql/wsrep_xid.cc b/sql/wsrep_xid.cc
new file mode 100644
index 00000000000..056da5748b9
--- /dev/null
+++ b/sql/wsrep_xid.cc
@@ -0,0 +1,150 @@
+/* Copyright 2015 Codership Oy <http://www.codership.com>
+
+ 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
+ */
+
+//! @file some utility functions and classes not directly related to replication
+
+#include "wsrep_xid.h"
+#include "sql_class.h"
+#include "wsrep_mysqld.h" // for logging macros
+
+/*
+ * WSREPXid
+ */
+
+#define WSREP_XID_PREFIX "WSREPXid"
+#define WSREP_XID_PREFIX_LEN MYSQL_XID_PREFIX_LEN
+#define WSREP_XID_UUID_OFFSET 8
+#define WSREP_XID_SEQNO_OFFSET (WSREP_XID_UUID_OFFSET + sizeof(wsrep_uuid_t))
+#define WSREP_XID_GTRID_LEN (WSREP_XID_SEQNO_OFFSET + sizeof(wsrep_seqno_t))
+
+void wsrep_xid_init(XID* xid, const wsrep_uuid_t& uuid, wsrep_seqno_t seqno)
+{
+ xid->formatID= 1;
+ xid->gtrid_length= WSREP_XID_GTRID_LEN;
+ xid->bqual_length= 0;
+ memset(xid->data, 0, sizeof(xid->data));
+ memcpy(xid->data, WSREP_XID_PREFIX, WSREP_XID_PREFIX_LEN);
+ memcpy(xid->data + WSREP_XID_UUID_OFFSET, &uuid, sizeof(wsrep_uuid_t));
+ memcpy(xid->data + WSREP_XID_SEQNO_OFFSET, &seqno, sizeof(wsrep_seqno_t));
+}
+
+int wsrep_is_wsrep_xid(const void* xid_ptr)
+{
+ const XID* xid= reinterpret_cast<const XID*>(xid_ptr);
+ return (xid->formatID == 1 &&
+ xid->gtrid_length == WSREP_XID_GTRID_LEN &&
+ xid->bqual_length == 0 &&
+ !memcmp(xid->data, WSREP_XID_PREFIX, WSREP_XID_PREFIX_LEN));
+}
+
+const wsrep_uuid_t* wsrep_xid_uuid(const XID& xid)
+{
+ if (wsrep_is_wsrep_xid(&xid))
+ return reinterpret_cast<const wsrep_uuid_t*>(xid.data
+ + WSREP_XID_UUID_OFFSET);
+ else
+ return &WSREP_UUID_UNDEFINED;
+}
+
+wsrep_seqno_t wsrep_xid_seqno(const XID& xid)
+{
+ if (wsrep_is_wsrep_xid(&xid))
+ {
+ wsrep_seqno_t seqno;
+ memcpy(&seqno, xid.data + WSREP_XID_SEQNO_OFFSET, sizeof(wsrep_seqno_t));
+ return seqno;
+ }
+ else
+ {
+ return WSREP_SEQNO_UNDEFINED;
+ }
+}
+
+static my_bool set_SE_checkpoint(THD* unused, plugin_ref plugin, void* arg)
+{
+ XID* xid= static_cast<XID*>(arg);
+ handlerton* hton= plugin_data(plugin, handlerton *);
+
+ if (hton->db_type == DB_TYPE_INNODB)
+ {
+ const wsrep_uuid_t* uuid(wsrep_xid_uuid(*xid));
+ char uuid_str[40] = {0, };
+ wsrep_uuid_print(uuid, uuid_str, sizeof(uuid_str));
+ WSREP_DEBUG("Set WSREPXid for InnoDB: %s:%lld",
+ uuid_str, (long long)wsrep_xid_seqno(*xid));
+ hton->wsrep_set_checkpoint(hton, xid);
+ }
+
+ return FALSE;
+}
+
+void wsrep_set_SE_checkpoint(XID& xid)
+{
+ plugin_foreach(NULL, set_SE_checkpoint, MYSQL_STORAGE_ENGINE_PLUGIN, &xid);
+}
+
+void wsrep_set_SE_checkpoint(const wsrep_uuid_t& uuid, wsrep_seqno_t seqno)
+{
+ XID xid;
+ wsrep_xid_init(&xid, uuid, seqno);
+ wsrep_set_SE_checkpoint(xid);
+}
+
+static my_bool get_SE_checkpoint(THD* unused, plugin_ref plugin, void* arg)
+{
+ XID* xid= reinterpret_cast<XID*>(arg);
+ handlerton* hton= plugin_data(plugin, handlerton *);
+
+ if (hton->db_type == DB_TYPE_INNODB)
+ {
+ hton->wsrep_get_checkpoint(hton, xid);
+ const wsrep_uuid_t* uuid(wsrep_xid_uuid(*xid));
+ char uuid_str[40] = {0, };
+ wsrep_uuid_print(uuid, uuid_str, sizeof(uuid_str));
+ WSREP_DEBUG("Read WSREPXid from InnoDB: %s:%lld",
+ uuid_str, (long long)wsrep_xid_seqno(*xid));
+ }
+
+ return FALSE;
+}
+
+void wsrep_get_SE_checkpoint(XID& xid)
+{
+ plugin_foreach(NULL, get_SE_checkpoint, MYSQL_STORAGE_ENGINE_PLUGIN, &xid);
+}
+
+void wsrep_get_SE_checkpoint(wsrep_uuid_t& uuid, wsrep_seqno_t& seqno)
+{
+ uuid= WSREP_UUID_UNDEFINED;
+ seqno= WSREP_SEQNO_UNDEFINED;
+
+ XID xid;
+ memset(&xid, 0, sizeof(xid));
+ xid.formatID= -1;
+
+ wsrep_get_SE_checkpoint(xid);
+
+ if (xid.formatID == -1) return; // nil XID
+
+ if (!wsrep_is_wsrep_xid(&xid))
+ {
+ WSREP_WARN("Read non-wsrep XID from storage engines.");
+ return;
+ }
+
+ uuid= *wsrep_xid_uuid(xid);
+ seqno= wsrep_xid_seqno(xid);
+}
diff --git a/sql/wsrep_xid.h b/sql/wsrep_xid.h
new file mode 100644
index 00000000000..8a43e49c733
--- /dev/null
+++ b/sql/wsrep_xid.h
@@ -0,0 +1,33 @@
+/* Copyright (C) 2015 Codership Oy <info@codership.com>
+
+ 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.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
+
+#ifndef WSREP_XID_H
+#define WSREP_XID_H
+
+#include <my_config.h>
+#include "../wsrep/wsrep_api.h"
+#include "handler.h" // XID typedef
+
+void wsrep_xid_init(xid_t*, const wsrep_uuid_t&, wsrep_seqno_t);
+int wsrep_is_wsrep_xid(const void* xid);
+const wsrep_uuid_t* wsrep_xid_uuid(const XID&);
+wsrep_seqno_t wsrep_xid_seqno(const XID&);
+
+//void wsrep_get_SE_checkpoint(XID&); /* uncomment if needed */
+void wsrep_get_SE_checkpoint(wsrep_uuid_t&, wsrep_seqno_t&);
+//void wsrep_set_SE_checkpoint(XID&); /* uncomment if needed */
+void wsrep_set_SE_checkpoint(const wsrep_uuid_t&, wsrep_seqno_t);
+
+#endif /* WSREP_UTILS_H */